mmi-md 1.0.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/.editorconfig +7 -0
- package/.gitattributes +2 -0
- package/.prettierrc.json +6 -0
- package/README.md +2 -0
- package/lib/ast/nodes.d.ts +77 -0
- package/lib/ast/nodes.js +1 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +15 -0
- package/lib/parser/BlockParser.d.ts +4 -0
- package/lib/parser/BlockParser.js +137 -0
- package/lib/parser/InlineParser.d.ts +5 -0
- package/lib/parser/InlineParser.js +132 -0
- package/lib/parser/preprocess.d.ts +1 -0
- package/lib/parser/preprocess.js +3 -0
- package/lib/renderer/HTMLRenderer.d.ts +6 -0
- package/lib/renderer/HTMLRenderer.js +71 -0
- package/package.json +35 -0
- package/tsconfig.json +24 -0
package/.editorconfig
ADDED
package/.gitattributes
ADDED
package/.prettierrc.json
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
export interface DocumentNode {
|
|
2
|
+
type: 'document';
|
|
3
|
+
children: BlockNode[];
|
|
4
|
+
}
|
|
5
|
+
export type BlockNode = HeadingNode | ParagraphNode | ListNode | BlockquoteNode | CodeBlockNode | TableNode;
|
|
6
|
+
export type InlineNode = TextNode | EmphasisNode | StrongNode | UnderlineNode | StrikethroughNode | CodeInlineNode | LinkNode | ImageNode | LineBreakNode;
|
|
7
|
+
export interface TextNode {
|
|
8
|
+
type: 'text';
|
|
9
|
+
content: string;
|
|
10
|
+
}
|
|
11
|
+
export interface EmphasisNode {
|
|
12
|
+
type: 'emphasis';
|
|
13
|
+
children: InlineNode[];
|
|
14
|
+
}
|
|
15
|
+
export interface StrongNode {
|
|
16
|
+
type: 'strong';
|
|
17
|
+
children: InlineNode[];
|
|
18
|
+
}
|
|
19
|
+
export interface UnderlineNode {
|
|
20
|
+
type: 'underline';
|
|
21
|
+
children: InlineNode[];
|
|
22
|
+
}
|
|
23
|
+
export interface StrikethroughNode {
|
|
24
|
+
type: 'strikethrough';
|
|
25
|
+
children: InlineNode[];
|
|
26
|
+
}
|
|
27
|
+
export interface CodeInlineNode {
|
|
28
|
+
type: 'code_inline';
|
|
29
|
+
content: string;
|
|
30
|
+
}
|
|
31
|
+
export interface LinkNode {
|
|
32
|
+
type: 'link';
|
|
33
|
+
text: string;
|
|
34
|
+
href: string;
|
|
35
|
+
external: boolean;
|
|
36
|
+
}
|
|
37
|
+
export interface ImageNode {
|
|
38
|
+
type: 'image';
|
|
39
|
+
alt: string;
|
|
40
|
+
src: string;
|
|
41
|
+
}
|
|
42
|
+
export interface LineBreakNode {
|
|
43
|
+
type: 'line_break';
|
|
44
|
+
}
|
|
45
|
+
export interface HeadingNode {
|
|
46
|
+
type: 'heading';
|
|
47
|
+
level: number;
|
|
48
|
+
subtitle: boolean;
|
|
49
|
+
sample: boolean;
|
|
50
|
+
children: InlineNode[];
|
|
51
|
+
}
|
|
52
|
+
export interface ParagraphNode {
|
|
53
|
+
type: 'paragraph';
|
|
54
|
+
children: InlineNode[];
|
|
55
|
+
}
|
|
56
|
+
export interface BlockquoteNode {
|
|
57
|
+
type: 'blockquote';
|
|
58
|
+
children: BlockNode[];
|
|
59
|
+
}
|
|
60
|
+
export interface ListNode {
|
|
61
|
+
type: 'list';
|
|
62
|
+
ordered: boolean;
|
|
63
|
+
items: ListItemNode[];
|
|
64
|
+
}
|
|
65
|
+
export interface ListItemNode {
|
|
66
|
+
type: 'list_item';
|
|
67
|
+
children: BlockNode[];
|
|
68
|
+
}
|
|
69
|
+
export interface CodeBlockNode {
|
|
70
|
+
type: 'code_block';
|
|
71
|
+
content: string;
|
|
72
|
+
}
|
|
73
|
+
export interface TableNode {
|
|
74
|
+
type: 'table';
|
|
75
|
+
header: InlineNode[][][];
|
|
76
|
+
rows: InlineNode[][][];
|
|
77
|
+
}
|
package/lib/ast/nodes.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function renderMMarkdown(source: string): string;
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { preprocess } from './parser/preprocess.js';
|
|
2
|
+
import { BlockParser } from './parser/BlockParser.js';
|
|
3
|
+
import { HTMLRenderer } from './renderer/HTMLRenderer.js';
|
|
4
|
+
export function renderMMarkdown(source) {
|
|
5
|
+
const cleaned = preprocess(source);
|
|
6
|
+
const lines = cleaned.replace(/\r\n/g, '\n').split('\n');
|
|
7
|
+
const blockParser = new BlockParser();
|
|
8
|
+
const blocks = blockParser.parse(lines);
|
|
9
|
+
const doc = {
|
|
10
|
+
type: 'document',
|
|
11
|
+
children: blocks,
|
|
12
|
+
};
|
|
13
|
+
const renderer = new HTMLRenderer();
|
|
14
|
+
return renderer.render(doc);
|
|
15
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { InlineParser } from './InlineParser.js';
|
|
2
|
+
export class BlockParser {
|
|
3
|
+
parse(lines) {
|
|
4
|
+
const blocks = [];
|
|
5
|
+
let i = 0;
|
|
6
|
+
while (i < lines.length) {
|
|
7
|
+
const line = lines[i];
|
|
8
|
+
if (line.trim() === '') {
|
|
9
|
+
i++;
|
|
10
|
+
continue;
|
|
11
|
+
}
|
|
12
|
+
// Blockquote
|
|
13
|
+
if (line.trim().startsWith('>')) {
|
|
14
|
+
const quote = [];
|
|
15
|
+
while (i < lines.length && lines[i].trim().startsWith('>')) {
|
|
16
|
+
let stripped = lines[i].replace(/^\s*>+\s?/, '');
|
|
17
|
+
quote.push(stripped);
|
|
18
|
+
i++;
|
|
19
|
+
}
|
|
20
|
+
blocks.push({
|
|
21
|
+
type: 'blockquote',
|
|
22
|
+
children: this.parse(quote),
|
|
23
|
+
});
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
// List (nested)
|
|
27
|
+
const listMatch = line.match(/^(\s*)([-*+]|\d+\.)\s+(.*)/);
|
|
28
|
+
if (listMatch) {
|
|
29
|
+
const indent = listMatch[1].length;
|
|
30
|
+
const ordered = /\d+\./.test(listMatch[2]);
|
|
31
|
+
const items = [];
|
|
32
|
+
while (i < lines.length) {
|
|
33
|
+
const m = lines[i].match(/^(\s*)([-*+]|\d+\.)\s+(.*)/);
|
|
34
|
+
if (!m || m[1].length < indent)
|
|
35
|
+
break;
|
|
36
|
+
const content = m[3];
|
|
37
|
+
const children = [
|
|
38
|
+
{
|
|
39
|
+
type: 'paragraph',
|
|
40
|
+
children: InlineParser.parse(content),
|
|
41
|
+
},
|
|
42
|
+
];
|
|
43
|
+
i++;
|
|
44
|
+
const sub = [];
|
|
45
|
+
while (i < lines.length && lines[i].startsWith(' '.repeat(indent + 2))) {
|
|
46
|
+
sub.push(lines[i].slice(indent + 2));
|
|
47
|
+
i++;
|
|
48
|
+
}
|
|
49
|
+
children.push(...this.parse(sub));
|
|
50
|
+
items.push({ type: 'list_item', children });
|
|
51
|
+
}
|
|
52
|
+
blocks.push({ type: 'list', ordered, items });
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
// Heading / Subtitle / Sample
|
|
56
|
+
const h = line.match(/^([~:]?)(#{1,6})\s+(.*)/);
|
|
57
|
+
if (h) {
|
|
58
|
+
blocks.push({
|
|
59
|
+
type: 'heading',
|
|
60
|
+
level: h[2].length,
|
|
61
|
+
subtitle: h[1] === ':',
|
|
62
|
+
sample: h[1] === '~',
|
|
63
|
+
children: InlineParser.parse(h[3]),
|
|
64
|
+
});
|
|
65
|
+
i++;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
// Code block
|
|
69
|
+
if (line.startsWith('```')) {
|
|
70
|
+
const content = [];
|
|
71
|
+
i++;
|
|
72
|
+
while (i < lines.length && !lines[i].startsWith('```')) {
|
|
73
|
+
content.push(lines[i]);
|
|
74
|
+
i++;
|
|
75
|
+
}
|
|
76
|
+
i++;
|
|
77
|
+
blocks.push({
|
|
78
|
+
type: 'code_block',
|
|
79
|
+
content: content.join('\n'),
|
|
80
|
+
});
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
// Table
|
|
84
|
+
if (line.includes('|')) {
|
|
85
|
+
const headerLine = line.split('|').map(c => c.trim()).filter(c => c);
|
|
86
|
+
// Check if next line is a separator
|
|
87
|
+
if (i + 1 < lines.length) {
|
|
88
|
+
const nextLine = lines[i + 1];
|
|
89
|
+
const isSeparator = /^\s*\|?\s*[-:\s|]+\s*\|?\s*$/.test(nextLine) &&
|
|
90
|
+
nextLine.split('|').filter(c => c.trim()).length === headerLine.length;
|
|
91
|
+
if (isSeparator) {
|
|
92
|
+
const header = [headerLine.map(cell => InlineParser.parse(cell))];
|
|
93
|
+
const rows = [];
|
|
94
|
+
i += 2; // Skip header and separator
|
|
95
|
+
// Collect table rows
|
|
96
|
+
while (i < lines.length && lines[i].includes('|')) {
|
|
97
|
+
const rowCells = lines[i].split('|').map(c => c.trim()).filter(c => c);
|
|
98
|
+
if (rowCells.length === headerLine.length) {
|
|
99
|
+
rows.push(rowCells.map(cell => InlineParser.parse(cell)));
|
|
100
|
+
i++;
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
blocks.push({
|
|
107
|
+
type: 'table',
|
|
108
|
+
header,
|
|
109
|
+
rows,
|
|
110
|
+
});
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// Paragraph
|
|
116
|
+
const p = [line];
|
|
117
|
+
i++;
|
|
118
|
+
while (i < lines.length && lines[i].trim() !== '') {
|
|
119
|
+
const nextLine = lines[i];
|
|
120
|
+
// Check if next line starts a new block element
|
|
121
|
+
const isBlockStart = nextLine.trim().startsWith('>') || // blockquote
|
|
122
|
+
nextLine.match(/^(\s*)([-*+]|\d+\.)\s+/) || // list
|
|
123
|
+
nextLine.startsWith('```') || // code block
|
|
124
|
+
nextLine.match(/^([~:]?)(#{1,6})\s+/); // heading
|
|
125
|
+
if (isBlockStart)
|
|
126
|
+
break;
|
|
127
|
+
p.push(nextLine);
|
|
128
|
+
i++;
|
|
129
|
+
}
|
|
130
|
+
blocks.push({
|
|
131
|
+
type: 'paragraph',
|
|
132
|
+
children: InlineParser.parse(p.join('\n')),
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
return blocks;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
export class InlineParser {
|
|
2
|
+
static parse(text) {
|
|
3
|
+
const nodes = [];
|
|
4
|
+
let i = 0;
|
|
5
|
+
while (i < text.length) {
|
|
6
|
+
// Backslash escape
|
|
7
|
+
if (text[i] === '\\' && i + 1 < text.length) {
|
|
8
|
+
const nextChar = text[i + 1];
|
|
9
|
+
// Escape special characters
|
|
10
|
+
if ('\\`*_{}[]()#+-.!~|>:'.includes(nextChar)) {
|
|
11
|
+
nodes.push({
|
|
12
|
+
type: 'text',
|
|
13
|
+
content: nextChar,
|
|
14
|
+
});
|
|
15
|
+
i += 2;
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
// Image
|
|
20
|
+
const img = text.slice(i).match(/^!\[(.*?)\]\((.*?)\)/);
|
|
21
|
+
if (img) {
|
|
22
|
+
nodes.push({
|
|
23
|
+
type: 'image',
|
|
24
|
+
alt: img[1],
|
|
25
|
+
src: img[2],
|
|
26
|
+
});
|
|
27
|
+
i += img[0].length;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
// Link
|
|
31
|
+
const link = text.slice(i).match(/^(:?)\[(.*?)\]\((.*?)\)/);
|
|
32
|
+
if (link) {
|
|
33
|
+
nodes.push({
|
|
34
|
+
type: 'link',
|
|
35
|
+
text: link[2],
|
|
36
|
+
href: link[3],
|
|
37
|
+
external: link[1] === ':',
|
|
38
|
+
});
|
|
39
|
+
i += link[0].length;
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
// Code inline
|
|
43
|
+
if (text[i] === '`') {
|
|
44
|
+
const end = text.indexOf('`', i + 1);
|
|
45
|
+
if (end !== -1) {
|
|
46
|
+
nodes.push({
|
|
47
|
+
type: 'code_inline',
|
|
48
|
+
content: text.slice(i + 1, end),
|
|
49
|
+
});
|
|
50
|
+
i = end + 1;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// Strong
|
|
55
|
+
if (text.startsWith('**', i)) {
|
|
56
|
+
const end = text.indexOf('**', i + 2);
|
|
57
|
+
if (end !== -1) {
|
|
58
|
+
nodes.push({
|
|
59
|
+
type: 'strong',
|
|
60
|
+
children: this.parse(text.slice(i + 2, end)),
|
|
61
|
+
});
|
|
62
|
+
i = end + 2;
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// Underline
|
|
67
|
+
if (text.startsWith('__', i)) {
|
|
68
|
+
const end = text.indexOf('__', i + 2);
|
|
69
|
+
if (end !== -1) {
|
|
70
|
+
nodes.push({
|
|
71
|
+
type: 'underline',
|
|
72
|
+
children: this.parse(text.slice(i + 2, end)),
|
|
73
|
+
});
|
|
74
|
+
i = end + 2;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// Strikethrough
|
|
79
|
+
if (text.startsWith('~~', i)) {
|
|
80
|
+
const end = text.indexOf('~~', i + 2);
|
|
81
|
+
if (end !== -1) {
|
|
82
|
+
nodes.push({
|
|
83
|
+
type: 'strikethrough',
|
|
84
|
+
children: this.parse(text.slice(i + 2, end)),
|
|
85
|
+
});
|
|
86
|
+
i = end + 2;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// Italic
|
|
91
|
+
if (text[i] === '*' || text[i] === '_') {
|
|
92
|
+
const end = text.indexOf(text[i], i + 1);
|
|
93
|
+
if (end !== -1) {
|
|
94
|
+
nodes.push({
|
|
95
|
+
type: 'emphasis',
|
|
96
|
+
children: this.parse(text.slice(i + 1, end)),
|
|
97
|
+
});
|
|
98
|
+
i = end + 1;
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// Line break
|
|
103
|
+
if (text[i] === '\n') {
|
|
104
|
+
nodes.push({
|
|
105
|
+
type: 'line_break',
|
|
106
|
+
});
|
|
107
|
+
i++;
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
// Text
|
|
111
|
+
nodes.push({
|
|
112
|
+
type: 'text',
|
|
113
|
+
content: text[i],
|
|
114
|
+
});
|
|
115
|
+
i++;
|
|
116
|
+
}
|
|
117
|
+
return this.mergeText(nodes);
|
|
118
|
+
}
|
|
119
|
+
static mergeText(nodes) {
|
|
120
|
+
const out = [];
|
|
121
|
+
for (const n of nodes) {
|
|
122
|
+
const last = out[out.length - 1];
|
|
123
|
+
if (n.type === 'text' && last?.type === 'text') {
|
|
124
|
+
last.content += n.content;
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
out.push(n);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return out;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function preprocess(source: string): string;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
const escapeHTML = (s) => s
|
|
2
|
+
.replace(/&/g, '&')
|
|
3
|
+
.replace(/</g, '<')
|
|
4
|
+
.replace(/>/g, '>')
|
|
5
|
+
.replace(/"/g, '"')
|
|
6
|
+
.replace(/'/g, ''');
|
|
7
|
+
const isAllowedImage = (src) => /\.(png|jpe?g|gif)$/i.test(src);
|
|
8
|
+
export class HTMLRenderer {
|
|
9
|
+
render(doc) {
|
|
10
|
+
return doc.children.map((b) => this.renderBlock(b)).join('\n');
|
|
11
|
+
}
|
|
12
|
+
renderBlock(b) {
|
|
13
|
+
switch (b.type) {
|
|
14
|
+
case 'heading':
|
|
15
|
+
if (b.sample) {
|
|
16
|
+
return `<span class="sample-h${b.level}">${this.renderInline(b.children)}</span>`;
|
|
17
|
+
}
|
|
18
|
+
else if (b.subtitle) {
|
|
19
|
+
return `<span class="subtitle">${this.renderInline(b.children)}</span>`;
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
return `<h${b.level}>${this.renderInline(b.children)}</h${b.level}>`;
|
|
23
|
+
}
|
|
24
|
+
case 'paragraph':
|
|
25
|
+
return `<p>${this.renderInline(b.children)}</p>`;
|
|
26
|
+
case 'blockquote':
|
|
27
|
+
return `<blockquote>${b.children.map((c) => this.renderBlock(c)).join('')}</blockquote>`;
|
|
28
|
+
case 'list':
|
|
29
|
+
const tag = b.ordered ? 'ol' : 'ul';
|
|
30
|
+
return `<${tag}>${b.items.map((i) => `<li>${i.children.map((c) => this.renderBlock(c)).join('')}</li>`).join('')}</${tag}>`;
|
|
31
|
+
case 'code_block':
|
|
32
|
+
return `<pre><code>${escapeHTML(b.content)}</code></pre>`;
|
|
33
|
+
case 'table':
|
|
34
|
+
return `<table>
|
|
35
|
+
<thead>${b.header.map((r) => `<tr>${r.map((c) => `<th>${this.renderInline(c)}</th>`).join('')}</tr>`).join('')}</thead>
|
|
36
|
+
<tbody>${b.rows.map((r) => `<tr>${r.map((c) => `<td>${this.renderInline(c)}</td>`).join('')}</tr>`).join('')}</tbody>
|
|
37
|
+
</table>`;
|
|
38
|
+
default:
|
|
39
|
+
return '';
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
renderInline(nodes) {
|
|
43
|
+
return nodes
|
|
44
|
+
.map((n) => {
|
|
45
|
+
switch (n.type) {
|
|
46
|
+
case 'text':
|
|
47
|
+
return escapeHTML(n.content);
|
|
48
|
+
case 'emphasis':
|
|
49
|
+
return `<em>${this.renderInline(n.children)}</em>`;
|
|
50
|
+
case 'strong':
|
|
51
|
+
return `<strong>${this.renderInline(n.children)}</strong>`;
|
|
52
|
+
case 'underline':
|
|
53
|
+
return `<u>${this.renderInline(n.children)}</u>`;
|
|
54
|
+
case 'strikethrough':
|
|
55
|
+
return `<del>${this.renderInline(n.children)}</del>`;
|
|
56
|
+
case 'code_inline':
|
|
57
|
+
return `<code>${escapeHTML(n.content)}</code>`;
|
|
58
|
+
case 'link':
|
|
59
|
+
return `<a href="${escapeHTML(n.href)}"${n.external ? ' target="_blank" rel="noopener noreferrer"' : ''}>${escapeHTML(n.text)}</a>`;
|
|
60
|
+
case 'image':
|
|
61
|
+
if (!isAllowedImage(n.src)) {
|
|
62
|
+
return `<span class="image-error">[image non supportée]</span>`;
|
|
63
|
+
}
|
|
64
|
+
return `<img src="${escapeHTML(n.src)}" alt="${escapeHTML(n.alt)}" loading="lazy" />`;
|
|
65
|
+
case 'line_break':
|
|
66
|
+
return '<br />';
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
.join('');
|
|
70
|
+
}
|
|
71
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mmi-md",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Markdown créé spécialement pour MMIpedia",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"markdown",
|
|
7
|
+
"mmi"
|
|
8
|
+
],
|
|
9
|
+
"homepage": "https://github.com/MMI-CODES/mmarkdown#readme",
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/MMI-CODES/mmarkdown/issues"
|
|
12
|
+
},
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/MMI-CODES/mmarkdown.git"
|
|
16
|
+
},
|
|
17
|
+
"license": "UNLICENSED",
|
|
18
|
+
"author": "Loan",
|
|
19
|
+
"type": "module",
|
|
20
|
+
"main": "lib/index.js",
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsc",
|
|
23
|
+
"dev": "tsx watch src --out-dir lib --clean",
|
|
24
|
+
"start": "tsx lib/index.ts",
|
|
25
|
+
"lint": "eslint . --fix",
|
|
26
|
+
"format": "prettier --write src/"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/node": "^25.0.9",
|
|
30
|
+
"eslint": "^9.39.2",
|
|
31
|
+
"prettier": "^3.8.0",
|
|
32
|
+
"tsx": "^4.21.0",
|
|
33
|
+
"typescript": "^5.9.3"
|
|
34
|
+
}
|
|
35
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"rootDir": "./src",
|
|
4
|
+
"outDir": "./lib",
|
|
5
|
+
|
|
6
|
+
"module": "nodenext",
|
|
7
|
+
"target": "esnext",
|
|
8
|
+
"lib": ["esnext"],
|
|
9
|
+
"types": ["node"],
|
|
10
|
+
|
|
11
|
+
"declaration": true,
|
|
12
|
+
|
|
13
|
+
"noUncheckedIndexedAccess": true,
|
|
14
|
+
"exactOptionalPropertyTypes": true,
|
|
15
|
+
|
|
16
|
+
"strict": true,
|
|
17
|
+
"jsx": "react-jsx",
|
|
18
|
+
"verbatimModuleSyntax": true,
|
|
19
|
+
"isolatedModules": true,
|
|
20
|
+
"noUncheckedSideEffectImports": true,
|
|
21
|
+
"moduleDetection": "force",
|
|
22
|
+
"skipLibCheck": true,
|
|
23
|
+
}
|
|
24
|
+
}
|