Skip to content

Commit 01d202f

Browse files
authored
Merge pull request #4 from redocmx/feat/typescript
Migrate to Typescript, implement methods to manage assets, allow multiple Redoc instances and allow to set apiUrl
2 parents 8d262d8 + 1a7da3e commit 01d202f

29 files changed

+869
-152
lines changed

.github/workflows/npm-publish.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ jobs:
2323
- name: Install dependencies
2424
run: npm ci
2525

26+
- name: Build project
27+
run: npm run build
28+
2629
- name: Publish package
2730
run: npm publish --access public
2831
env:

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,10 @@ dist
128128
.yarn/build-state.yml
129129
.yarn/install-state.gz
130130
.pnp.*
131+
132+
# Examples
133+
examples/
134+
135+
# Build
136+
cjs/
137+
esm/

.npmignore

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# This file is written to be a whitelist instead of a blacklist. Start by
2+
# ignoring everything, then add back the files we want to be included in the
3+
# final NPM package.
4+
*
5+
6+
# And these are the files that are allowed.
7+
!/README.md
8+
!/LICENSE.md
9+
!/README_EN.md
10+
!/package.json
11+
!/cjs/**/*
12+
!/types/**/*
13+
!/esm/**/*

package-lock.json

Lines changed: 55 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,36 @@
11
{
22
"name": "redocmx",
3-
"version": "0.0.6",
3+
"version": "0.0.7",
44
"description": "Conversión CFDI a PDF",
5-
"main": "src/index.js",
6-
"types": "src/types.d.ts",
5+
"main": "cjs/redoc.cjs.js",
6+
"types": "types/index.d.ts",
7+
"exports": {
8+
"types": "./types/index.d.ts",
9+
"default": {
10+
"import": "./esm/redoc.esm.js",
11+
"require": "./cjs/redoc.cjs.js"
12+
}
13+
},
714
"type": "module",
815
"private": false,
916
"engines": {
1017
"node": ">= 18.0.0"
1118
},
12-
"keywords": ["cfdi a pdf", "cfdi to pdf", "sat mexico", "conversión cfdi a pdf", "cfdi", "personaliza cfdi a pdf", "redoc.mx", "redocmx"],
19+
"keywords": [
20+
"cfdi a pdf",
21+
"cfdi to pdf",
22+
"sat mexico",
23+
"conversión cfdi a pdf",
24+
"cfdi",
25+
"personaliza cfdi a pdf",
26+
"redoc.mx",
27+
"redocmx"
28+
],
1329
"scripts": {
30+
"build": "npm run extract-version && npm run build-esm && npm run build-cjs",
31+
"build-esm": "mkdir -p esm && tsc -p tsconfig.esm.json && echo '{\"type\":\"module\"}' > esm/package.json",
32+
"build-cjs": "mkdir -p cjs && tsc -p tsconfig.cjs.json && echo '{\"type\":\"commonjs\"}' > cjs/package.json",
33+
"extract-version": "jq -r '.version' package.json | awk '{print \"export const version = \\\"\"$0\"\\\";\"}' > src/version.ts",
1434
"test": "echo \"Error: no test specified\" && exit 1"
1535
},
1636
"author": {
@@ -24,5 +44,11 @@
2444
"homepage": "https://github.com/redocmx/client-node#readme",
2545
"bugs": {
2646
"url": "https://github.com/redocmx/client-node/issues"
47+
},
48+
"dependencies": {
49+
"@types/node": "^20.14.9"
50+
},
51+
"devDependencies": {
52+
"typescript": "^5.5.3"
2753
}
2854
}

src/addenda.js renamed to src/addenda.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,21 @@ export default class Addenda extends File {
55
super()
66
}
77

8-
replaceValues(content, options = null) {
9-
if(!options) return content
8+
replaceValues(content: string, options: { [key: string]: string } | null = null) {
9+
if (!options) return content
1010

11-
for(const option of Object.entries(options)) {
11+
for (const option of Object.entries(options)) {
1212
const [key, value] = option
1313
content = content.split(key).join(value)
1414
}
1515

1616
return content
1717
}
1818

19-
async getFileContent(replaceValues) {
19+
async getFileContent(replaceValues: { [key: string]: string }) {
2020
const file = await this.getFile()
2121
const fileContent = file.content.toString()
22-
22+
2323
return this.replaceValues(fileContent, replaceValues)
2424
}
2525

src/assets.ts

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import File from './file.js'
2+
import Service from './service.js';
3+
4+
import { FetchPaginationOptions, FileComputed } from './types.js';
5+
6+
export default class Assets {
7+
8+
service: Service;
9+
10+
constructor(service: Service) {
11+
this.service = service
12+
}
13+
14+
async get(path: string) {
15+
if (!path || path === '/') {
16+
throw new TypeError('Path must be provided to get an asset.');
17+
}
18+
19+
if (typeof path !== 'string') {
20+
throw new TypeError('Path must be a valid string.');
21+
}
22+
23+
if (path.endsWith('/')) {
24+
throw new TypeError('Path must be a valid asset path.');
25+
}
26+
27+
const asset = await this.service.fetchAssets({ path })
28+
29+
return asset
30+
}
31+
32+
async delete(path: string) {
33+
if (!path || path === '/') {
34+
throw new TypeError('Path must be provided to get an asset.');
35+
}
36+
37+
if (typeof path !== 'string') {
38+
throw new TypeError('Path must be a valid string.');
39+
}
40+
41+
await this.service.deleteAsset({ path })
42+
43+
return true
44+
}
45+
46+
async list(path: string, options: FetchPaginationOptions = {}) {
47+
if (!path){
48+
path = ''
49+
}
50+
51+
if (path === '/') {
52+
throw new TypeError('Path must be provided to get an asset.');
53+
}
54+
55+
if (typeof path !== 'string') {
56+
throw new TypeError('Path must be a valid string.');
57+
}
58+
59+
if (path !== '' && !path.endsWith('/')) {
60+
throw new TypeError('Path must be a valid path (Ends with "/").');
61+
}
62+
63+
if (options) {
64+
options.limit = options?.limit ?? 10
65+
}
66+
67+
const asset = await this.service.fetchAssets({ path, options })
68+
69+
return asset
70+
}
71+
72+
async put(path: string, source: string | Buffer) {
73+
if (!path || path === '/') {
74+
throw new TypeError('Path must be provided to put an asset.');
75+
}
76+
77+
if (typeof path !== 'string') {
78+
throw new TypeError('Path must be a valid string.');
79+
}
80+
81+
if (path.endsWith('/')) {
82+
throw new TypeError('Path must be a valid asset path.');
83+
}
84+
85+
if (!source) {
86+
throw new TypeError('Source must be provided to put an asset.');
87+
}
88+
89+
if (typeof source !== 'string' && !(source instanceof Uint8Array)) {
90+
throw new TypeError('Source must be a valid string or a buffer array.');
91+
}
92+
93+
const isBufferSource = source instanceof Uint8Array
94+
95+
let file: FileComputed
96+
97+
if (isBufferSource) {
98+
file = { type: 'buffer', content: source }
99+
} else {
100+
file =await ( new File().fromFile(source)).getFile()
101+
}
102+
103+
const asset = await this.service.putAsset({ path, file })
104+
105+
return asset
106+
}
107+
108+
}

src/cfdi.js renamed to src/cfdi.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,27 @@
1-
import Service from './service.js';
21
import Pdf from './pdf.js';
32
import Addenda from './addenda.js';
43
import File from './file.js';
4+
import Service from './service.js';
5+
import { PdfPayload } from './types.js';
56

67
export default class Cfdi extends File {
7-
constructor() {
8+
9+
pdf: Pdf | null;
10+
service: Service;
11+
addenda: Addenda | null;
12+
addendaReplaceValues: { [key:string]: string } | null;
13+
14+
constructor(service: Service) {
815
super();
916
this.pdf = null;
1017

1118
this.addenda = null
1219
this.addendaReplaceValues = null
1320

14-
this.service = Service.getInstance();
21+
this.service = service;
1522
}
1623

17-
setAddenda(addenda, replaceValues = null) {
24+
setAddenda(addenda: Addenda, replaceValues = null) {
1825
if (addenda && !(addenda instanceof Addenda)) {
1926
throw new TypeError('Addenda must be Addenda instance.');
2027
}
@@ -27,7 +34,7 @@ export default class Cfdi extends File {
2734
this.addendaReplaceValues = replaceValues
2835
}
2936

30-
async toPdf(payload = {}) {
37+
async toPdf(payload: PdfPayload = {}) {
3138
if (this.pdf) {
3239
return this.pdf;
3340
}
@@ -39,7 +46,7 @@ export default class Cfdi extends File {
3946
const file = await this.getFile();
4047

4148
if (this.addenda) {
42-
const addendaContent = await this.addenda.getFileContent(this.addendaReplaceValues);
49+
const addendaContent = await this.addenda.getFileContent(this.addendaReplaceValues ?? {});
4350
payload.addenda = addendaContent;
4451
}
4552

src/file.js renamed to src/file.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,30 @@
1-
import fs from 'fs/promises';
1+
import * as fs from 'fs/promises';
22

33
const { F_OK, R_OK } = fs.constants;
44

55
export default class File {
6+
7+
filePath: string | null;
8+
fileBuffer: Buffer | null;
9+
fileContent: string | null;
10+
611
constructor() {
712
this.filePath = null;
813
this.fileBuffer = null;
914
this.fileContent = null;
1015
}
1116

12-
fromFile(filePath) {
17+
fromFile(filePath: string) {
1318
this.filePath = filePath;
1419
return this
1520
}
1621

17-
fromString(fileContent) {
22+
fromBuffer(fileBuffer: Buffer) {
23+
this.fileBuffer = fileBuffer;
24+
return this
25+
}
26+
27+
fromString(fileContent: string) {
1828
this.fileContent = fileContent;
1929
return this
2030
}
@@ -31,7 +41,7 @@ export default class File {
3141
if (this.filePath) {
3242
try {
3343
await fs.access(this.filePath, F_OK | R_OK);
34-
} catch (err) {
44+
} catch (err: any) {
3545
if (err.code === 'ENOENT') {
3646
throw new Error(`Failed to read content from file: ${this.filePath}. The file does not exist.`);
3747
} else if (err.code === 'EACCES') {

0 commit comments

Comments
 (0)