@jianwen-lang/parser 0.1.1
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/LICENSE +21 -0
- package/README.md +95 -0
- package/dist/cli/render.d.ts +1 -0
- package/dist/cli/render.js +300 -0
- package/dist/core/ast.d.ts +286 -0
- package/dist/core/ast.js +2 -0
- package/dist/core/block/rules/heading.d.ts +9 -0
- package/dist/core/block/rules/heading.js +75 -0
- package/dist/core/block/rules/horizontal-rule.d.ts +9 -0
- package/dist/core/block/rules/horizontal-rule.js +87 -0
- package/dist/core/block/rules/include.d.ts +9 -0
- package/dist/core/block/rules/include.js +64 -0
- package/dist/core/block/rules/index.d.ts +2 -0
- package/dist/core/block/rules/index.js +16 -0
- package/dist/core/block/rules/types.d.ts +21 -0
- package/dist/core/block/rules/types.js +2 -0
- package/dist/core/block/types.d.ts +10 -0
- package/dist/core/block/types.js +2 -0
- package/dist/core/block-parser.d.ts +3 -0
- package/dist/core/block-parser.js +1385 -0
- package/dist/core/clone.d.ts +9 -0
- package/dist/core/clone.js +32 -0
- package/dist/core/diagnostics.d.ts +12 -0
- package/dist/core/diagnostics.js +24 -0
- package/dist/core/errors.d.ts +12 -0
- package/dist/core/errors.js +2 -0
- package/dist/core/inline/rules/backtick.d.ts +4 -0
- package/dist/core/inline/rules/backtick.js +90 -0
- package/dist/core/inline/rules/bracket.d.ts +4 -0
- package/dist/core/inline/rules/bracket.js +721 -0
- package/dist/core/inline/rules/disabled.d.ts +2 -0
- package/dist/core/inline/rules/disabled.js +34 -0
- package/dist/core/inline/rules/escape.d.ts +2 -0
- package/dist/core/inline/rules/escape.js +11 -0
- package/dist/core/inline/rules/index.d.ts +2 -0
- package/dist/core/inline/rules/index.js +25 -0
- package/dist/core/inline/rules/marker-highlight.d.ts +4 -0
- package/dist/core/inline/rules/marker-highlight.js +56 -0
- package/dist/core/inline/rules/style.d.ts +4 -0
- package/dist/core/inline/rules/style.js +120 -0
- package/dist/core/inline/rules/types.d.ts +22 -0
- package/dist/core/inline/rules/types.js +2 -0
- package/dist/core/inline/types.d.ts +1 -0
- package/dist/core/inline/types.js +2 -0
- package/dist/core/inline-parser.d.ts +3 -0
- package/dist/core/inline-parser.js +52 -0
- package/dist/core/location.d.ts +9 -0
- package/dist/core/location.js +30 -0
- package/dist/core/parser.d.ts +9 -0
- package/dist/core/parser.js +423 -0
- package/dist/core/traverse.d.ts +7 -0
- package/dist/core/traverse.js +71 -0
- package/dist/html/convert.d.ts +29 -0
- package/dist/html/convert.js +61 -0
- package/dist/html/format.d.ts +1 -0
- package/dist/html/format.js +17 -0
- package/dist/html/render/blocks.d.ts +4 -0
- package/dist/html/render/blocks.js +433 -0
- package/dist/html/render/html.d.ts +8 -0
- package/dist/html/render/html.js +89 -0
- package/dist/html/render/inlines.d.ts +4 -0
- package/dist/html/render/inlines.js +110 -0
- package/dist/html/render/meta.d.ts +3 -0
- package/dist/html/render/meta.js +51 -0
- package/dist/html/render/utils.d.ts +31 -0
- package/dist/html/render/utils.js +213 -0
- package/dist/html/theme/base/css.d.ts +2 -0
- package/dist/html/theme/base/css.js +383 -0
- package/dist/html/theme/dark/css.d.ts +2 -0
- package/dist/html/theme/dark/css.js +108 -0
- package/dist/html/theme/default/colors.d.ts +13 -0
- package/dist/html/theme/default/colors.js +2 -0
- package/dist/html/theme/default/css.d.ts +2 -0
- package/dist/html/theme/default/css.js +6 -0
- package/dist/html/theme/default/runtime.d.ts +0 -0
- package/dist/html/theme/default/runtime.js +31 -0
- package/dist/html/theme/light/css.d.ts +2 -0
- package/dist/html/theme/light/css.js +55 -0
- package/dist/html/theme/preset/colors.d.ts +10 -0
- package/dist/html/theme/preset/colors.js +14 -0
- package/dist/html/theme/runtime.d.ts +0 -0
- package/dist/html/theme/runtime.js +31 -0
- package/dist/html/theme/theme.d.ts +9 -0
- package/dist/html/theme/theme.js +21 -0
- package/dist/lexer/lexer.d.ts +17 -0
- package/dist/lexer/lexer.js +47 -0
- package/dist/parser.d.ts +6 -0
- package/dist/parser.js +22 -0
- package/package.json +50 -0
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseJianwen = parseJianwen;
|
|
4
|
+
exports.parseJianwenWithErrors = parseJianwenWithErrors;
|
|
5
|
+
const diagnostics_1 = require("./diagnostics");
|
|
6
|
+
const block_parser_1 = require("./block-parser");
|
|
7
|
+
const inline_parser_1 = require("./inline-parser");
|
|
8
|
+
const location_1 = require("./location");
|
|
9
|
+
const clone_1 = require("./clone");
|
|
10
|
+
const traverse_1 = require("./traverse");
|
|
11
|
+
function parseJianwen(source, options = {}) {
|
|
12
|
+
return parseJianwenWithErrors(source, options).ast;
|
|
13
|
+
}
|
|
14
|
+
function parseJianwenWithErrors(source, options = {}) {
|
|
15
|
+
const errors = [];
|
|
16
|
+
const { meta, body } = parseInitializationTemplate(source);
|
|
17
|
+
const blocks = (0, block_parser_1.parseBlocks)(body, errors);
|
|
18
|
+
applyInlineParsing(blocks, errors);
|
|
19
|
+
const ast = {
|
|
20
|
+
type: 'document',
|
|
21
|
+
source,
|
|
22
|
+
meta,
|
|
23
|
+
children: blocks,
|
|
24
|
+
};
|
|
25
|
+
postProcessDocument(ast, options, errors);
|
|
26
|
+
return { ast, errors };
|
|
27
|
+
}
|
|
28
|
+
function parseInitializationTemplate(source) {
|
|
29
|
+
const lines = source.split('\n');
|
|
30
|
+
let start = -1;
|
|
31
|
+
let end = -1;
|
|
32
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
33
|
+
const line = lines[i];
|
|
34
|
+
if (line !== undefined && isTemplateBoundaryLine(line)) {
|
|
35
|
+
start = i;
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (start === -1) {
|
|
40
|
+
return { body: source };
|
|
41
|
+
}
|
|
42
|
+
for (let i = start + 1; i < lines.length; i += 1) {
|
|
43
|
+
const line = lines[i];
|
|
44
|
+
if (line !== undefined && isTemplateBoundaryLine(line)) {
|
|
45
|
+
end = i;
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (end === -1) {
|
|
50
|
+
return { body: source };
|
|
51
|
+
}
|
|
52
|
+
const meta = {};
|
|
53
|
+
for (let i = start + 1; i < end; i += 1) {
|
|
54
|
+
const line = lines[i];
|
|
55
|
+
if (line === undefined) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
const bracketIndex = line.indexOf('[');
|
|
59
|
+
if (bracketIndex === -1) {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
const segment = line.slice(bracketIndex);
|
|
63
|
+
const pattern = /\[([^\]]+?)\]=([^[]*)/g;
|
|
64
|
+
let match;
|
|
65
|
+
while ((match = pattern.exec(segment)) !== null) {
|
|
66
|
+
const rawKey = match[1];
|
|
67
|
+
const rawValue = match[2];
|
|
68
|
+
if (rawKey === undefined || rawValue === undefined) {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const key = rawKey.trim();
|
|
72
|
+
const value = rawValue.trim();
|
|
73
|
+
applyMetaKey(meta, key, value);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const bodyLines = lines.slice(0, start).concat(lines.slice(end + 1));
|
|
77
|
+
const body = bodyLines.join('\n');
|
|
78
|
+
if (isMetaEmpty(meta)) {
|
|
79
|
+
return { body };
|
|
80
|
+
}
|
|
81
|
+
return { meta, body };
|
|
82
|
+
}
|
|
83
|
+
function isTemplateBoundaryLine(line) {
|
|
84
|
+
const trimmed = line.trim();
|
|
85
|
+
if (trimmed.length === 0) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
for (let i = 0; i < trimmed.length; i += 1) {
|
|
89
|
+
if (trimmed[i] !== '_') {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
function applyMetaKey(meta, key, rawValue) {
|
|
96
|
+
const value = rawValue.trim();
|
|
97
|
+
if (key === 'title') {
|
|
98
|
+
if (value.length > 0) {
|
|
99
|
+
meta.title = value;
|
|
100
|
+
}
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (key === 'author') {
|
|
104
|
+
if (value.length > 0) {
|
|
105
|
+
const match = /^(.*)\(([^)]+)\)$/.exec(value);
|
|
106
|
+
if (match && match[1] !== undefined && match[2] !== undefined) {
|
|
107
|
+
meta.author = match[1].trim();
|
|
108
|
+
meta.authorUrl = match[2].trim();
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
meta.author = value;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (key === 'author_url') {
|
|
117
|
+
if (value.length > 0) {
|
|
118
|
+
meta.authorUrl = value;
|
|
119
|
+
}
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (key === 'time') {
|
|
123
|
+
if (value.length > 0) {
|
|
124
|
+
meta.time = value;
|
|
125
|
+
}
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (key === 'add_info') {
|
|
129
|
+
if (value.length > 0) {
|
|
130
|
+
meta.addInfo = value;
|
|
131
|
+
}
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (key === 'tag(s)') {
|
|
135
|
+
if (value.length === 0) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const parts = value.split(',');
|
|
139
|
+
const tags = [];
|
|
140
|
+
for (const part of parts) {
|
|
141
|
+
const t = part.trim();
|
|
142
|
+
if (t.length > 0) {
|
|
143
|
+
tags.push(t);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (tags.length > 0) {
|
|
147
|
+
meta.tags = tags;
|
|
148
|
+
}
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (key === 'global_font') {
|
|
152
|
+
if (value.length === 0) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
const parts = value.split(',');
|
|
156
|
+
const fonts = [];
|
|
157
|
+
for (const part of parts) {
|
|
158
|
+
const f = part.trim();
|
|
159
|
+
if (f === 'italic' ||
|
|
160
|
+
f === 'bold' ||
|
|
161
|
+
f === 'heavy' ||
|
|
162
|
+
f === 'slim' ||
|
|
163
|
+
f === 'serif' ||
|
|
164
|
+
f === 'mono') {
|
|
165
|
+
fonts.push(f);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
if (fonts.length > 0) {
|
|
169
|
+
meta.globalFont = fonts;
|
|
170
|
+
}
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
function isMetaEmpty(meta) {
|
|
175
|
+
if (meta.title)
|
|
176
|
+
return false;
|
|
177
|
+
if (meta.author)
|
|
178
|
+
return false;
|
|
179
|
+
if (meta.authorUrl)
|
|
180
|
+
return false;
|
|
181
|
+
if (meta.time)
|
|
182
|
+
return false;
|
|
183
|
+
if (meta.addInfo)
|
|
184
|
+
return false;
|
|
185
|
+
if (meta.tags && meta.tags.length > 0)
|
|
186
|
+
return false;
|
|
187
|
+
if (meta.globalFont && meta.globalFont.length > 0)
|
|
188
|
+
return false;
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
function getInlineBasePosition(node) {
|
|
192
|
+
const loc = (0, location_1.getNodeLocation)(node);
|
|
193
|
+
return { line: loc?.line ?? 1, column: loc?.column ?? 1 };
|
|
194
|
+
}
|
|
195
|
+
function applyInlineParsing(blocks, errors) {
|
|
196
|
+
(0, traverse_1.forEachInlineContainerInBlocks)(blocks, (children, owner) => {
|
|
197
|
+
const text = collectInlineSource(children);
|
|
198
|
+
if (text.length === 0) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
const base = getInlineBasePosition(owner);
|
|
202
|
+
const parsed = (0, inline_parser_1.parseInlines)(text, errors, base.line, base.column);
|
|
203
|
+
if (owner && typeof owner === 'object' && 'children' in owner) {
|
|
204
|
+
owner.children = parsed;
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
function collectInlineSource(children) {
|
|
209
|
+
let result = '';
|
|
210
|
+
for (const child of children) {
|
|
211
|
+
if (child.type === 'text') {
|
|
212
|
+
result += child.value;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return result;
|
|
216
|
+
}
|
|
217
|
+
function postProcessDocument(document, options, errors) {
|
|
218
|
+
if (options.expandInclude) {
|
|
219
|
+
const tagIndex = buildTagIndex(document.children);
|
|
220
|
+
const context = {
|
|
221
|
+
stack: [],
|
|
222
|
+
maxDepth: typeof options.includeMaxDepth === 'number' ? options.includeMaxDepth : 16,
|
|
223
|
+
loadFile: options.loadFile,
|
|
224
|
+
tagIndex,
|
|
225
|
+
fileCache: new Map(),
|
|
226
|
+
};
|
|
227
|
+
document.children = expandIncludesInBlocks(document.children, document, options, errors, context);
|
|
228
|
+
}
|
|
229
|
+
checkFootnotes(document, errors);
|
|
230
|
+
}
|
|
231
|
+
function buildTagIndex(blocks) {
|
|
232
|
+
const index = new Map();
|
|
233
|
+
for (const block of blocks) {
|
|
234
|
+
if (block.type === 'taggedBlock') {
|
|
235
|
+
if (!index.has(block.name)) {
|
|
236
|
+
index.set(block.name, block);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return index;
|
|
241
|
+
}
|
|
242
|
+
function expandIncludesInBlocks(blocks, document, options, errors, context) {
|
|
243
|
+
const result = [];
|
|
244
|
+
for (const block of blocks) {
|
|
245
|
+
if (block.type === 'include') {
|
|
246
|
+
const expanded = expandSingleInclude(block, document, options, errors, context);
|
|
247
|
+
if (expanded && expanded.length > 0) {
|
|
248
|
+
for (const b of expanded) {
|
|
249
|
+
result.push(b);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
result.push(block);
|
|
254
|
+
}
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
if (block.type === 'quote') {
|
|
258
|
+
const children = expandIncludesInBlocks(block.children, document, options, errors, context);
|
|
259
|
+
result.push({ ...block, children });
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
if (block.type === 'list') {
|
|
263
|
+
const items = block.children.map((item) => {
|
|
264
|
+
const itemChildren = expandIncludesInBlocks(item.children, document, options, errors, context);
|
|
265
|
+
return { ...item, children: itemChildren };
|
|
266
|
+
});
|
|
267
|
+
result.push({ ...block, children: items });
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
if (block.type === 'footnotes') {
|
|
271
|
+
const defs = block.children.map((def) => {
|
|
272
|
+
const defChildren = expandIncludesInBlocks(def.children, document, options, errors, context);
|
|
273
|
+
return { ...def, children: defChildren };
|
|
274
|
+
});
|
|
275
|
+
result.push({ ...block, children: defs });
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
if (block.type === 'footnoteDef') {
|
|
279
|
+
const children = expandIncludesInBlocks(block.children, document, options, errors, context);
|
|
280
|
+
result.push({ ...block, children });
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
if (block.type === 'commentBlock') {
|
|
284
|
+
const children = expandIncludesInBlocks(block.children, document, options, errors, context);
|
|
285
|
+
result.push({ ...block, children });
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
result.push(block);
|
|
289
|
+
}
|
|
290
|
+
return result;
|
|
291
|
+
}
|
|
292
|
+
function expandSingleInclude(block, document, options, errors, context) {
|
|
293
|
+
if (block.type !== 'include') {
|
|
294
|
+
return undefined;
|
|
295
|
+
}
|
|
296
|
+
const includeLoc = (0, location_1.getNodeLocation)(block);
|
|
297
|
+
const includeLine = includeLoc?.line ?? 1;
|
|
298
|
+
const includeColumn = includeLoc?.column;
|
|
299
|
+
if (block.mode === 'tag') {
|
|
300
|
+
const taggedFromIndex = context.tagIndex.get(block.target);
|
|
301
|
+
const tagged = taggedFromIndex ?? findTaggedBlock(document.children, block.target);
|
|
302
|
+
if (!tagged) {
|
|
303
|
+
(0, diagnostics_1.reportParseWarning)(errors, {
|
|
304
|
+
message: `Include tag target "${block.target}" not found`,
|
|
305
|
+
line: includeLine,
|
|
306
|
+
column: includeColumn,
|
|
307
|
+
});
|
|
308
|
+
return [block];
|
|
309
|
+
}
|
|
310
|
+
const cloned = (0, clone_1.cloneBlock)(tagged.child);
|
|
311
|
+
return [cloned];
|
|
312
|
+
}
|
|
313
|
+
if (context.stack.length >= context.maxDepth) {
|
|
314
|
+
(0, diagnostics_1.reportParseWarning)(errors, {
|
|
315
|
+
message: `Include max depth ${context.maxDepth} exceeded for target "${block.target}"`,
|
|
316
|
+
line: includeLine,
|
|
317
|
+
column: includeColumn,
|
|
318
|
+
});
|
|
319
|
+
return [block];
|
|
320
|
+
}
|
|
321
|
+
if (context.stack.indexOf(block.target) !== -1) {
|
|
322
|
+
(0, diagnostics_1.reportParseWarning)(errors, {
|
|
323
|
+
message: `Include cycle detected for target "${block.target}"`,
|
|
324
|
+
line: includeLine,
|
|
325
|
+
column: includeColumn,
|
|
326
|
+
});
|
|
327
|
+
return [block];
|
|
328
|
+
}
|
|
329
|
+
if (!context.loadFile) {
|
|
330
|
+
(0, diagnostics_1.reportParseWarning)(errors, {
|
|
331
|
+
message: `IncludeBlock with mode "file" requires loadFile option to expand target "${block.target}"`,
|
|
332
|
+
line: includeLine,
|
|
333
|
+
column: includeColumn,
|
|
334
|
+
});
|
|
335
|
+
return [block];
|
|
336
|
+
}
|
|
337
|
+
const cached = context.fileCache.get(block.target);
|
|
338
|
+
if (cached) {
|
|
339
|
+
for (const e of cached.errors) {
|
|
340
|
+
errors.push((0, diagnostics_1.prefixIncludeError)(e, block.target));
|
|
341
|
+
}
|
|
342
|
+
return cached.ast.children.map((child) => (0, clone_1.cloneBlock)(child, { origin: block.target }));
|
|
343
|
+
}
|
|
344
|
+
const nextStack = context.stack.concat(block.target);
|
|
345
|
+
const source = context.loadFile(block.target, nextStack);
|
|
346
|
+
if (source === undefined) {
|
|
347
|
+
(0, diagnostics_1.reportParseWarning)(errors, {
|
|
348
|
+
message: `Include target "${block.target}" could not be loaded`,
|
|
349
|
+
line: includeLine,
|
|
350
|
+
column: includeColumn,
|
|
351
|
+
});
|
|
352
|
+
return [block];
|
|
353
|
+
}
|
|
354
|
+
const childResult = parseJianwenWithErrors(source, {
|
|
355
|
+
...options,
|
|
356
|
+
expandInclude: true,
|
|
357
|
+
loadFile: undefined,
|
|
358
|
+
});
|
|
359
|
+
context.fileCache.set(block.target, childResult);
|
|
360
|
+
for (const e of childResult.errors) {
|
|
361
|
+
errors.push((0, diagnostics_1.prefixIncludeError)(e, block.target));
|
|
362
|
+
}
|
|
363
|
+
return childResult.ast.children.map((child) => (0, clone_1.cloneBlock)(child, { origin: block.target }));
|
|
364
|
+
}
|
|
365
|
+
function findTaggedBlock(blocks, name) {
|
|
366
|
+
for (const block of blocks) {
|
|
367
|
+
if (block.type === 'taggedBlock' && block.name === name) {
|
|
368
|
+
return block;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
return undefined;
|
|
372
|
+
}
|
|
373
|
+
function checkFootnotes(document, errors) {
|
|
374
|
+
const defsById = new Map();
|
|
375
|
+
collectFootnoteDefs(document.children, defsById);
|
|
376
|
+
const referencedIds = new Map();
|
|
377
|
+
collectFootnoteRefsInBlocks(document.children, referencedIds);
|
|
378
|
+
for (const [id, location] of referencedIds) {
|
|
379
|
+
if (!defsById.has(id)) {
|
|
380
|
+
const originSuffix = location.origin ? ` (from include "${location.origin}")` : '';
|
|
381
|
+
(0, diagnostics_1.reportParseWarning)(errors, {
|
|
382
|
+
message: `Footnote reference "${id}" has no corresponding FootnoteDefBlock${originSuffix}`,
|
|
383
|
+
line: location.line,
|
|
384
|
+
column: location.column,
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
function collectFootnoteDefs(blocks, defsById) {
|
|
390
|
+
(0, traverse_1.forEachBlock)(blocks, (block) => {
|
|
391
|
+
if (block.type !== 'footnoteDef') {
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
let list = defsById.get(block.id);
|
|
395
|
+
if (!list) {
|
|
396
|
+
list = [];
|
|
397
|
+
defsById.set(block.id, list);
|
|
398
|
+
}
|
|
399
|
+
list.push(block);
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
function collectFootnoteRefsInBlocks(blocks, referencedIds) {
|
|
403
|
+
(0, traverse_1.forEachInlineContainerInBlocks)(blocks, (nodes) => {
|
|
404
|
+
collectFootnoteRefsInInlines(nodes, referencedIds);
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
function collectFootnoteRefsInInlines(nodes, referencedIds) {
|
|
408
|
+
(0, traverse_1.walkInlines)(nodes, (node) => {
|
|
409
|
+
if (node.type !== 'footnoteRef') {
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
if (referencedIds.has(node.id)) {
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
const loc = (0, location_1.getNodeLocation)(node);
|
|
416
|
+
const origin = (0, location_1.getNodeOrigin)(node);
|
|
417
|
+
referencedIds.set(node.id, {
|
|
418
|
+
line: loc?.line ?? 1,
|
|
419
|
+
column: loc?.column,
|
|
420
|
+
origin,
|
|
421
|
+
});
|
|
422
|
+
});
|
|
423
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { BlockNode, InlineNode } from './ast';
|
|
2
|
+
export type BlockVisitor = (block: BlockNode) => void;
|
|
3
|
+
export declare function forEachBlock(blocks: BlockNode[], visit: BlockVisitor): void;
|
|
4
|
+
export type InlineContainerVisitor = (nodes: InlineNode[], owner: object) => void;
|
|
5
|
+
export declare function forEachInlineContainerInBlocks(blocks: BlockNode[], visit: InlineContainerVisitor): void;
|
|
6
|
+
export type InlineVisitor = (node: InlineNode) => void;
|
|
7
|
+
export declare function walkInlines(nodes: InlineNode[], visit: InlineVisitor): void;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.forEachBlock = forEachBlock;
|
|
4
|
+
exports.forEachInlineContainerInBlocks = forEachInlineContainerInBlocks;
|
|
5
|
+
exports.walkInlines = walkInlines;
|
|
6
|
+
function forEachBlock(blocks, visit) {
|
|
7
|
+
for (const block of blocks) {
|
|
8
|
+
visit(block);
|
|
9
|
+
if (block.type === 'taggedBlock') {
|
|
10
|
+
forEachBlock([block.child], visit);
|
|
11
|
+
continue;
|
|
12
|
+
}
|
|
13
|
+
if (block.type === 'quote') {
|
|
14
|
+
forEachBlock(block.children, visit);
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
if (block.type === 'list') {
|
|
18
|
+
forEachBlock(block.children, visit);
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
if (block.type === 'listItem') {
|
|
22
|
+
forEachBlock(block.children, visit);
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
if (block.type === 'footnotes') {
|
|
26
|
+
forEachBlock(block.children, visit);
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (block.type === 'footnoteDef') {
|
|
30
|
+
forEachBlock(block.children, visit);
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (block.type === 'commentBlock') {
|
|
34
|
+
forEachBlock(block.children, visit);
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function forEachInlineContainerInBlocks(blocks, visit) {
|
|
40
|
+
forEachBlock(blocks, (block) => {
|
|
41
|
+
if (block.type === 'paragraph' || block.type === 'heading' || block.type === 'contentTitle') {
|
|
42
|
+
visit(block.children, block);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (block.type === 'table') {
|
|
46
|
+
for (const row of block.rows) {
|
|
47
|
+
for (const cell of row.cells) {
|
|
48
|
+
visit(cell.children, cell);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
function walkInlines(nodes, visit) {
|
|
55
|
+
for (const node of nodes) {
|
|
56
|
+
visit(node);
|
|
57
|
+
if (node.type === 'em' ||
|
|
58
|
+
node.type === 'strong' ||
|
|
59
|
+
node.type === 'underline' ||
|
|
60
|
+
node.type === 'strike' ||
|
|
61
|
+
node.type === 'wave' ||
|
|
62
|
+
node.type === 'sup' ||
|
|
63
|
+
node.type === 'sub' ||
|
|
64
|
+
node.type === 'highlight' ||
|
|
65
|
+
node.type === 'link' ||
|
|
66
|
+
node.type === 'commentInline' ||
|
|
67
|
+
node.type === 'inlineAttrs') {
|
|
68
|
+
walkInlines(node.children, visit);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { ParseOptions } from '../core/parser';
|
|
2
|
+
import { JianwenDocument } from '../core/ast';
|
|
3
|
+
import { ParseError } from '../core/errors';
|
|
4
|
+
import { RenderHtmlOptions } from './render/utils';
|
|
5
|
+
export declare const DEFAULT_RUNTIME_SRC = "dist/html/theme/runtime.js";
|
|
6
|
+
export interface HtmlDocumentOptions {
|
|
7
|
+
title?: string;
|
|
8
|
+
lang?: string;
|
|
9
|
+
charset?: string;
|
|
10
|
+
headHtml?: string;
|
|
11
|
+
includeCss?: boolean;
|
|
12
|
+
cssText?: string;
|
|
13
|
+
cssHref?: string;
|
|
14
|
+
includeRuntime?: boolean;
|
|
15
|
+
runtimeSrc?: string;
|
|
16
|
+
format?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface RenderJianwenToHtmlDocumentOptions {
|
|
19
|
+
parse?: ParseOptions;
|
|
20
|
+
render?: RenderHtmlOptions;
|
|
21
|
+
document?: HtmlDocumentOptions;
|
|
22
|
+
}
|
|
23
|
+
export interface RenderJianwenToHtmlDocumentResult {
|
|
24
|
+
html: string;
|
|
25
|
+
ast: JianwenDocument;
|
|
26
|
+
errors: ParseError[];
|
|
27
|
+
}
|
|
28
|
+
export declare function buildHtmlDocument(bodyHtml: string, options?: HtmlDocumentOptions): string;
|
|
29
|
+
export declare function renderJianwenToHtmlDocument(source: string, options?: RenderJianwenToHtmlDocumentOptions): RenderJianwenToHtmlDocumentResult;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_RUNTIME_SRC = void 0;
|
|
4
|
+
exports.buildHtmlDocument = buildHtmlDocument;
|
|
5
|
+
exports.renderJianwenToHtmlDocument = renderJianwenToHtmlDocument;
|
|
6
|
+
const parser_1 = require("../core/parser");
|
|
7
|
+
const format_1 = require("./format");
|
|
8
|
+
const html_1 = require("./render/html");
|
|
9
|
+
const utils_1 = require("./render/utils");
|
|
10
|
+
const theme_1 = require("./theme/theme");
|
|
11
|
+
exports.DEFAULT_RUNTIME_SRC = 'dist/html/theme/runtime.js';
|
|
12
|
+
function buildHtmlDocument(bodyHtml, options = {}) {
|
|
13
|
+
const lang = options.lang ?? 'zh-CN';
|
|
14
|
+
const charset = options.charset ?? 'utf-8';
|
|
15
|
+
const title = options.title ?? 'Jianwen';
|
|
16
|
+
const headParts = [];
|
|
17
|
+
headParts.push(`<meta charset="${(0, utils_1.escapeAttr)(charset)}">`);
|
|
18
|
+
headParts.push(`<title>${(0, utils_1.escapeHtml)(title)}</title>`);
|
|
19
|
+
const includeCss = options.includeCss ?? true;
|
|
20
|
+
if (includeCss) {
|
|
21
|
+
if (options.cssHref) {
|
|
22
|
+
headParts.push(`<link rel="stylesheet" href="${(0, utils_1.escapeAttr)(options.cssHref)}">`);
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
const cssText = options.cssText ?? theme_1.DEFAULT_CSS;
|
|
26
|
+
headParts.push(`<style>${cssText}</style>`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (options.headHtml) {
|
|
30
|
+
headParts.push(options.headHtml);
|
|
31
|
+
}
|
|
32
|
+
const runtimeHtml = options.includeRuntime && (options.runtimeSrc ?? exports.DEFAULT_RUNTIME_SRC)
|
|
33
|
+
? `<script src="${(0, utils_1.escapeAttr)(options.runtimeSrc ?? exports.DEFAULT_RUNTIME_SRC)}"></script>`
|
|
34
|
+
: '';
|
|
35
|
+
const html = `<!DOCTYPE html>
|
|
36
|
+
<html lang="${(0, utils_1.escapeAttr)(lang)}">
|
|
37
|
+
<head>
|
|
38
|
+
${headParts.join('\n')}
|
|
39
|
+
</head>
|
|
40
|
+
<body>
|
|
41
|
+
${bodyHtml}
|
|
42
|
+
${runtimeHtml}
|
|
43
|
+
</body>
|
|
44
|
+
</html>`;
|
|
45
|
+
return options.format ? (0, format_1.formatHtml)(html) : html;
|
|
46
|
+
}
|
|
47
|
+
function renderJianwenToHtmlDocument(source, options = {}) {
|
|
48
|
+
const parseResult = (0, parser_1.parseJianwenWithErrors)(source, options.parse);
|
|
49
|
+
const renderOptions = {
|
|
50
|
+
includeMeta: true,
|
|
51
|
+
includeComments: false,
|
|
52
|
+
...options.render,
|
|
53
|
+
};
|
|
54
|
+
const bodyHtml = (0, html_1.renderDocumentToHtml)(parseResult.ast, renderOptions);
|
|
55
|
+
const title = options.document?.title ?? parseResult.ast.meta?.title;
|
|
56
|
+
const html = buildHtmlDocument(bodyHtml, {
|
|
57
|
+
...options.document,
|
|
58
|
+
title,
|
|
59
|
+
});
|
|
60
|
+
return { html, ast: parseResult.ast, errors: parseResult.errors };
|
|
61
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function formatHtml(html: string): string;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.formatHtml = formatHtml;
|
|
4
|
+
const js_beautify_1 = require("js-beautify");
|
|
5
|
+
const DEFAULT_HTML_FORMAT_OPTIONS = {
|
|
6
|
+
indent_size: 2,
|
|
7
|
+
indent_char: ' ',
|
|
8
|
+
indent_inner_html: true,
|
|
9
|
+
preserve_newlines: true,
|
|
10
|
+
max_preserve_newlines: 2,
|
|
11
|
+
wrap_line_length: 0,
|
|
12
|
+
content_unformatted: [],
|
|
13
|
+
end_with_newline: false,
|
|
14
|
+
};
|
|
15
|
+
function formatHtml(html) {
|
|
16
|
+
return (0, js_beautify_1.html)(html, DEFAULT_HTML_FORMAT_OPTIONS);
|
|
17
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { BlockNode } from '../../core/ast';
|
|
2
|
+
import { RenderHtmlOptions } from './utils';
|
|
3
|
+
export declare function renderBlocksToHtml(blocks: BlockNode[], options?: RenderHtmlOptions): string;
|
|
4
|
+
export declare function renderBlockToHtml(block: BlockNode, options: RenderHtmlOptions): string;
|