@markitdownjs/pdf 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/pdf-converter.test.d.ts +2 -0
- package/dist/__tests__/pdf-converter.test.d.ts.map +1 -0
- package/dist/__tests__/pdf-converter.test.js +39 -0
- package/dist/__tests__/pdf-converter.test.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/pdf-converter.d.ts +52 -0
- package/dist/pdf-converter.d.ts.map +1 -0
- package/dist/pdf-converter.js +268 -0
- package/dist/pdf-converter.js.map +1 -0
- package/package.json +33 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pdf-converter.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/pdf-converter.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { PdfConverter } from '../pdf-converter.js';
|
|
3
|
+
describe('PdfConverter', () => {
|
|
4
|
+
const converter = new PdfConverter();
|
|
5
|
+
it('should detect PDF by MIME type', async () => {
|
|
6
|
+
expect(await converter.canConvert({ data: '', mimeType: 'application/pdf' })).toBe(true);
|
|
7
|
+
});
|
|
8
|
+
it('should detect PDF by extension', async () => {
|
|
9
|
+
expect(await converter.canConvert({ data: '', fileName: 'test.pdf' })).toBe(true);
|
|
10
|
+
expect(await converter.canConvert({ data: '', fileName: 'DOCUMENT.PDF' })).toBe(true);
|
|
11
|
+
});
|
|
12
|
+
it('should detect PDF by magic bytes', async () => {
|
|
13
|
+
const pdfHeader = new Uint8Array([0x25, 0x50, 0x44, 0x46, 0x2d, 0x31, 0x2e, 0x34]);
|
|
14
|
+
expect(await converter.canConvert({ data: pdfHeader })).toBe(true);
|
|
15
|
+
});
|
|
16
|
+
it('should detect PDF from ArrayBuffer', async () => {
|
|
17
|
+
const buffer = new ArrayBuffer(8);
|
|
18
|
+
const view = new Uint8Array(buffer);
|
|
19
|
+
view[0] = 0x25;
|
|
20
|
+
view[1] = 0x50;
|
|
21
|
+
view[2] = 0x44;
|
|
22
|
+
view[3] = 0x46;
|
|
23
|
+
expect(await converter.canConvert({ data: buffer })).toBe(true);
|
|
24
|
+
});
|
|
25
|
+
it('should reject non-PDF files', async () => {
|
|
26
|
+
expect(await converter.canConvert({ data: 'hello', fileName: 'test.txt' })).toBe(false);
|
|
27
|
+
expect(await converter.canConvert({ data: new Uint8Array([0x00, 0x01, 0x02, 0x03]) })).toBe(false);
|
|
28
|
+
});
|
|
29
|
+
it('should return fallback markdown for invalid PDF data', async () => {
|
|
30
|
+
const result = await converter.convert({
|
|
31
|
+
data: new Uint8Array([0x25, 0x50, 0x44, 0x46, 0x00, 0x00]),
|
|
32
|
+
mimeType: 'application/pdf',
|
|
33
|
+
});
|
|
34
|
+
expect(result.markdown).toContain('[PDF content could not be extracted]');
|
|
35
|
+
expect(result.ast).toBeDefined();
|
|
36
|
+
expect(result.ast.type).toBe('document');
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
//# sourceMappingURL=pdf-converter.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pdf-converter.test.js","sourceRoot":"","sources":["../../src/__tests__/pdf-converter.test.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { Converter, ConversionInput, ConversionResult } from "@markitdownjs/shared";
|
|
2
|
+
/** Convert PDF files to markdown via AST-first processing. */
|
|
3
|
+
export declare class PdfConverter implements Converter {
|
|
4
|
+
readonly id: "pdf";
|
|
5
|
+
readonly supportedMimeTypes: string[];
|
|
6
|
+
readonly supportedExtensions: string[];
|
|
7
|
+
/**
|
|
8
|
+
* Determine whether this converter can handle the given input.
|
|
9
|
+
* @param input - The conversion input to inspect.
|
|
10
|
+
* @returns True if the input is a supported PDF.
|
|
11
|
+
*/
|
|
12
|
+
canConvert(input: ConversionInput): Promise<boolean>;
|
|
13
|
+
/**
|
|
14
|
+
* Convert a PDF document into markdown via an AST intermediate.
|
|
15
|
+
*
|
|
16
|
+
* The converter dynamically imports pdfjs-dist, disables the worker, loads
|
|
17
|
+
* the PDF, and walks every page extracting text with positional data,
|
|
18
|
+
* annotations (links), and font-size information for heading detection.
|
|
19
|
+
* An AST is assembled first and then rendered to markdown.
|
|
20
|
+
*
|
|
21
|
+
* @param input - The conversion input containing PDF data.
|
|
22
|
+
* @returns A complete ConversionResult with markdown, AST, and metadata.
|
|
23
|
+
*/
|
|
24
|
+
convert(input: ConversionInput): Promise<ConversionResult>;
|
|
25
|
+
/**
|
|
26
|
+
* Extract the font size from a PDF text item's transform matrix.
|
|
27
|
+
*
|
|
28
|
+
* The transform array is `[scaleX, skewX, skewY, scaleY, x, y]`.
|
|
29
|
+
* The font size is derived from the absolute value of `scaleX`.
|
|
30
|
+
*
|
|
31
|
+
* @param item - A PDF text content item.
|
|
32
|
+
* @returns The detected font size, or 0 if unavailable.
|
|
33
|
+
*/
|
|
34
|
+
private extractFontSize;
|
|
35
|
+
/**
|
|
36
|
+
* Classify a text line as a heading based on its font size relative to the
|
|
37
|
+
* document body font size.
|
|
38
|
+
*
|
|
39
|
+
* @param text - The raw text of the line.
|
|
40
|
+
* @param fontSize - The font size of the line.
|
|
41
|
+
* @param bodyFontSize - The most common (body) font size in the document.
|
|
42
|
+
* @returns A HeadingInfo if the line qualifies as a heading, otherwise undefined.
|
|
43
|
+
*/
|
|
44
|
+
private classifyHeading;
|
|
45
|
+
/**
|
|
46
|
+
* Convert input data to a Uint8Array.
|
|
47
|
+
* @param data - The input data in any supported format.
|
|
48
|
+
* @returns The data as a Uint8Array.
|
|
49
|
+
*/
|
|
50
|
+
private toByteArray;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=pdf-converter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pdf-converter.d.ts","sourceRoot":"","sources":["../src/pdf-converter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,SAAS,EACT,eAAe,EACf,gBAAgB,EAIjB,MAAM,sBAAsB,CAAC;AAW9B,8DAA8D;AAC9D,qBAAa,YAAa,YAAW,SAAS;IAC5C,QAAQ,CAAC,EAAE,EAAG,KAAK,CAAU;IAC7B,QAAQ,CAAC,kBAAkB,WAAuB;IAClD,QAAQ,CAAC,mBAAmB,WAAY;IAExC;;;;OAIG;IACG,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC;IA4B1D;;;;;;;;;;OAUG;IACG,OAAO,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAqLhE;;;;;;;;OAQG;IACH,OAAO,CAAC,eAAe;IAOvB;;;;;;;;OAQG;IACH,OAAO,CAAC,eAAe;IAwBvB;;;;OAIG;YACW,WAAW;CAQ1B"}
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import { createNode, } from "@markitdownjs/shared";
|
|
2
|
+
import { MarkdownRenderer } from "@markitdownjs/ast";
|
|
3
|
+
/** Convert PDF files to markdown via AST-first processing. */
|
|
4
|
+
export class PdfConverter {
|
|
5
|
+
id = "pdf";
|
|
6
|
+
supportedMimeTypes = ["application/pdf"];
|
|
7
|
+
supportedExtensions = [".pdf"];
|
|
8
|
+
/**
|
|
9
|
+
* Determine whether this converter can handle the given input.
|
|
10
|
+
* @param input - The conversion input to inspect.
|
|
11
|
+
* @returns True if the input is a supported PDF.
|
|
12
|
+
*/
|
|
13
|
+
async canConvert(input) {
|
|
14
|
+
if (input.mimeType && this.supportedMimeTypes.includes(input.mimeType)) {
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
if (input.fileName) {
|
|
18
|
+
const ext = input.fileName.toLowerCase();
|
|
19
|
+
if (this.supportedExtensions.some((e) => ext.endsWith(e))) {
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
if (input.data instanceof Uint8Array || input.data instanceof ArrayBuffer) {
|
|
24
|
+
const bytes = input.data instanceof Uint8Array
|
|
25
|
+
? input.data
|
|
26
|
+
: new Uint8Array(input.data);
|
|
27
|
+
if (bytes.length >= 4 &&
|
|
28
|
+
bytes[0] === 0x25 &&
|
|
29
|
+
bytes[1] === 0x50 &&
|
|
30
|
+
bytes[2] === 0x44 &&
|
|
31
|
+
bytes[3] === 0x46) {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Convert a PDF document into markdown via an AST intermediate.
|
|
39
|
+
*
|
|
40
|
+
* The converter dynamically imports pdfjs-dist, disables the worker, loads
|
|
41
|
+
* the PDF, and walks every page extracting text with positional data,
|
|
42
|
+
* annotations (links), and font-size information for heading detection.
|
|
43
|
+
* An AST is assembled first and then rendered to markdown.
|
|
44
|
+
*
|
|
45
|
+
* @param input - The conversion input containing PDF data.
|
|
46
|
+
* @returns A complete ConversionResult with markdown, AST, and metadata.
|
|
47
|
+
*/
|
|
48
|
+
async convert(input) {
|
|
49
|
+
const startTime = Date.now();
|
|
50
|
+
const data = await this.toByteArray(input.data);
|
|
51
|
+
const renderer = new MarkdownRenderer();
|
|
52
|
+
let ast;
|
|
53
|
+
let markdown;
|
|
54
|
+
const headings = [];
|
|
55
|
+
const tables = [];
|
|
56
|
+
let pageCount = 0;
|
|
57
|
+
try {
|
|
58
|
+
const pdfjsLib = await import("pdfjs-dist");
|
|
59
|
+
// Disable the worker so we don't need a separate worker file.
|
|
60
|
+
pdfjsLib.GlobalWorkerOptions.workerSrc = "";
|
|
61
|
+
const loadingTask = pdfjsLib.getDocument({ data });
|
|
62
|
+
const pdf = await loadingTask.promise;
|
|
63
|
+
pageCount = pdf.numPages;
|
|
64
|
+
const pageNodes = [];
|
|
65
|
+
for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) {
|
|
66
|
+
const page = await pdf.getPage(pageNum);
|
|
67
|
+
const textContent = await page.getTextContent();
|
|
68
|
+
const annotations = await page.getAnnotations();
|
|
69
|
+
// Collect font sizes for heading classification.
|
|
70
|
+
const fontSizes = new Map();
|
|
71
|
+
for (const item of textContent.items) {
|
|
72
|
+
if (!("str" in item) || !item.str)
|
|
73
|
+
continue;
|
|
74
|
+
const fontSize = this.extractFontSize(item);
|
|
75
|
+
if (fontSize > 0) {
|
|
76
|
+
fontSizes.set(fontSize, (fontSizes.get(fontSize) ?? 0) + 1);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Determine the body font size (most common).
|
|
80
|
+
let bodyFontSize = 0;
|
|
81
|
+
let maxCount = 0;
|
|
82
|
+
for (const [size, count] of fontSizes) {
|
|
83
|
+
if (count > maxCount) {
|
|
84
|
+
maxCount = count;
|
|
85
|
+
bodyFontSize = size;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// Build a link map from annotations.
|
|
89
|
+
const linkMap = new Map();
|
|
90
|
+
for (const annot of annotations) {
|
|
91
|
+
if (annot.subtype === "Link" && annot.url) {
|
|
92
|
+
const rect = annot.rect;
|
|
93
|
+
// Store by approximate bounding box for lookup.
|
|
94
|
+
const key = `${Math.round(rect[0])},${Math.round(rect[1])},${Math.round(rect[2])},${Math.round(rect[3])}`;
|
|
95
|
+
linkMap.set(key, annot.url);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// Group text items into logical lines / paragraphs.
|
|
99
|
+
const items = textContent.items;
|
|
100
|
+
let currentLine = "";
|
|
101
|
+
let lastFontSize = 0;
|
|
102
|
+
let lastY = 0;
|
|
103
|
+
const flushLine = () => {
|
|
104
|
+
const trimmed = currentLine.trim();
|
|
105
|
+
if (!trimmed)
|
|
106
|
+
return;
|
|
107
|
+
const heading = this.classifyHeading(trimmed, lastFontSize, bodyFontSize);
|
|
108
|
+
if (heading) {
|
|
109
|
+
headings.push(heading);
|
|
110
|
+
const level = heading.level;
|
|
111
|
+
pageNodes.push(createNode({
|
|
112
|
+
type: "heading",
|
|
113
|
+
level,
|
|
114
|
+
children: [
|
|
115
|
+
createNode({ type: "text", value: heading.text }),
|
|
116
|
+
],
|
|
117
|
+
}));
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
pageNodes.push(createNode({
|
|
121
|
+
type: "paragraph",
|
|
122
|
+
children: [
|
|
123
|
+
createNode({ type: "text", value: trimmed }),
|
|
124
|
+
],
|
|
125
|
+
}));
|
|
126
|
+
}
|
|
127
|
+
currentLine = "";
|
|
128
|
+
};
|
|
129
|
+
for (const item of items) {
|
|
130
|
+
if (!("str" in item))
|
|
131
|
+
continue;
|
|
132
|
+
const str = item.str;
|
|
133
|
+
if (!str)
|
|
134
|
+
continue;
|
|
135
|
+
const fontSize = this.extractFontSize(item);
|
|
136
|
+
const y = "transform" in item && item.transform ? item.transform[5] : 0;
|
|
137
|
+
// New line when Y position changes significantly or font size changes.
|
|
138
|
+
if (currentLine &&
|
|
139
|
+
(Math.abs(y - lastY) > fontSize * 0.5 ||
|
|
140
|
+
(fontSize !== lastFontSize && lastFontSize > 0))) {
|
|
141
|
+
flushLine();
|
|
142
|
+
}
|
|
143
|
+
currentLine += str;
|
|
144
|
+
lastFontSize = fontSize;
|
|
145
|
+
lastY = y;
|
|
146
|
+
}
|
|
147
|
+
flushLine();
|
|
148
|
+
// Add page break between pages (not after the last page).
|
|
149
|
+
if (pageNum < pdf.numPages) {
|
|
150
|
+
pageNodes.push(createNode({
|
|
151
|
+
type: "page-break",
|
|
152
|
+
pageNumber: pageNum,
|
|
153
|
+
}));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
ast = createNode({
|
|
157
|
+
type: "document",
|
|
158
|
+
children: pageNodes,
|
|
159
|
+
});
|
|
160
|
+
markdown = renderer.render(ast);
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
ast = createNode({
|
|
164
|
+
type: "document",
|
|
165
|
+
children: [
|
|
166
|
+
createNode({
|
|
167
|
+
type: "paragraph",
|
|
168
|
+
children: [
|
|
169
|
+
createNode({
|
|
170
|
+
type: "text",
|
|
171
|
+
value: "[PDF content could not be extracted]",
|
|
172
|
+
}),
|
|
173
|
+
],
|
|
174
|
+
}),
|
|
175
|
+
],
|
|
176
|
+
});
|
|
177
|
+
markdown = renderer.render(ast);
|
|
178
|
+
}
|
|
179
|
+
const endTime = Date.now();
|
|
180
|
+
return {
|
|
181
|
+
markdown,
|
|
182
|
+
metadata: {
|
|
183
|
+
pageCount,
|
|
184
|
+
format: "application/pdf",
|
|
185
|
+
source: "pdfjs-dist",
|
|
186
|
+
},
|
|
187
|
+
assets: [],
|
|
188
|
+
tables,
|
|
189
|
+
images: [],
|
|
190
|
+
headings,
|
|
191
|
+
ast,
|
|
192
|
+
format: "markdown",
|
|
193
|
+
converterId: this.id,
|
|
194
|
+
stats: {
|
|
195
|
+
startTime,
|
|
196
|
+
endTime,
|
|
197
|
+
duration: endTime - startTime,
|
|
198
|
+
inputSize: data.length,
|
|
199
|
+
outputSize: markdown.length,
|
|
200
|
+
pagesProcessed: pageCount,
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Extract the font size from a PDF text item's transform matrix.
|
|
206
|
+
*
|
|
207
|
+
* The transform array is `[scaleX, skewX, skewY, scaleY, x, y]`.
|
|
208
|
+
* The font size is derived from the absolute value of `scaleX`.
|
|
209
|
+
*
|
|
210
|
+
* @param item - A PDF text content item.
|
|
211
|
+
* @returns The detected font size, or 0 if unavailable.
|
|
212
|
+
*/
|
|
213
|
+
extractFontSize(item) {
|
|
214
|
+
const transform = item.transform;
|
|
215
|
+
if (!transform || transform.length < 1)
|
|
216
|
+
return 0;
|
|
217
|
+
const val = transform[0];
|
|
218
|
+
return val !== undefined ? Math.abs(val) : 0;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Classify a text line as a heading based on its font size relative to the
|
|
222
|
+
* document body font size.
|
|
223
|
+
*
|
|
224
|
+
* @param text - The raw text of the line.
|
|
225
|
+
* @param fontSize - The font size of the line.
|
|
226
|
+
* @param bodyFontSize - The most common (body) font size in the document.
|
|
227
|
+
* @returns A HeadingInfo if the line qualifies as a heading, otherwise undefined.
|
|
228
|
+
*/
|
|
229
|
+
classifyHeading(text, fontSize, bodyFontSize) {
|
|
230
|
+
const trimmed = text.trim();
|
|
231
|
+
if (!trimmed)
|
|
232
|
+
return undefined;
|
|
233
|
+
// Use relative sizing when a body font size is known.
|
|
234
|
+
if (bodyFontSize > 0) {
|
|
235
|
+
const ratio = fontSize / bodyFontSize;
|
|
236
|
+
if (ratio > 1.8)
|
|
237
|
+
return { level: 1, text: trimmed };
|
|
238
|
+
if (ratio > 1.4)
|
|
239
|
+
return { level: 2, text: trimmed };
|
|
240
|
+
if (ratio > 1.15)
|
|
241
|
+
return { level: 3, text: trimmed };
|
|
242
|
+
return undefined;
|
|
243
|
+
}
|
|
244
|
+
// Fallback to absolute thresholds.
|
|
245
|
+
if (fontSize > 20)
|
|
246
|
+
return { level: 1, text: trimmed };
|
|
247
|
+
if (fontSize > 16)
|
|
248
|
+
return { level: 2, text: trimmed };
|
|
249
|
+
if (fontSize > 13)
|
|
250
|
+
return { level: 3, text: trimmed };
|
|
251
|
+
return undefined;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Convert input data to a Uint8Array.
|
|
255
|
+
* @param data - The input data in any supported format.
|
|
256
|
+
* @returns The data as a Uint8Array.
|
|
257
|
+
*/
|
|
258
|
+
async toByteArray(data) {
|
|
259
|
+
if (data instanceof Uint8Array)
|
|
260
|
+
return data;
|
|
261
|
+
if (data instanceof ArrayBuffer)
|
|
262
|
+
return new Uint8Array(data);
|
|
263
|
+
if (data instanceof Blob)
|
|
264
|
+
return new Uint8Array(await data.arrayBuffer());
|
|
265
|
+
return new TextEncoder().encode(data);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
//# sourceMappingURL=pdf-converter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pdf-converter.js","sourceRoot":"","sources":["../src/pdf-converter.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@markitdownjs/pdf",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "PDF converter for MarkItDownJS",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"module": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"pdfjs-dist": "^4.0.0",
|
|
21
|
+
"@markitdownjs/shared": "0.1.0",
|
|
22
|
+
"@markitdownjs/ast": "0.1.0"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"typescript": "^5.5.0"
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsc --project tsconfig.json",
|
|
29
|
+
"dev": "tsc --watch --project tsconfig.json",
|
|
30
|
+
"typecheck": "tsc --noEmit",
|
|
31
|
+
"clean": "rm -rf dist tsconfig.tsbuildinfo"
|
|
32
|
+
}
|
|
33
|
+
}
|