@dogsbay/minja 0.2.0-beta.100
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 +623 -0
- package/bin/minja.js +1225 -0
- package/dist/browser.d.ts +12 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +11 -0
- package/dist/browser.js.map +1 -0
- package/dist/cli.d.ts +5 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +98 -0
- package/dist/cli.js.map +1 -0
- package/dist/context.d.ts +47 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +112 -0
- package/dist/context.js.map +1 -0
- package/dist/evaluator.d.ts +20 -0
- package/dist/evaluator.d.ts.map +1 -0
- package/dist/evaluator.js +213 -0
- package/dist/evaluator.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/index.umd.js +1160 -0
- package/dist/index.umd.js.map +7 -0
- package/dist/index.umd.min.js +12 -0
- package/dist/index.umd.min.js.map +7 -0
- package/dist/loader-fetch.d.ts +8 -0
- package/dist/loader-fetch.d.ts.map +1 -0
- package/dist/loader-fetch.js +15 -0
- package/dist/loader-fetch.js.map +1 -0
- package/dist/loader-memory.d.ts +11 -0
- package/dist/loader-memory.d.ts.map +1 -0
- package/dist/loader-memory.js +36 -0
- package/dist/loader-memory.js.map +1 -0
- package/dist/loader.d.ts +73 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +159 -0
- package/dist/loader.js.map +1 -0
- package/dist/parser.d.ts +7 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +609 -0
- package/dist/parser.js.map +1 -0
- package/dist/renderer.d.ts +13 -0
- package/dist/renderer.d.ts.map +1 -0
- package/dist/renderer.js +494 -0
- package/dist/renderer.js.map +1 -0
- package/dist/scan.d.ts +46 -0
- package/dist/scan.d.ts.map +1 -0
- package/dist/scan.js +46 -0
- package/dist/scan.js.map +1 -0
- package/dist/types.d.ts +175 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/package.json +72 -0
package/dist/renderer.js
ADDED
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template renderer
|
|
3
|
+
* Combines parser, evaluator, and loader to render templates
|
|
4
|
+
*/
|
|
5
|
+
import { parse } from './parser.js';
|
|
6
|
+
import { Context } from './context.js';
|
|
7
|
+
import { evaluateExpression } from './evaluator.js';
|
|
8
|
+
import { FetchLoader } from './loader-fetch.js';
|
|
9
|
+
/**
|
|
10
|
+
* Transform markdown headings by applying level offset
|
|
11
|
+
* Clamps to valid markdown range (1-6)
|
|
12
|
+
* @param text - Text content to transform
|
|
13
|
+
* @param offset - Level offset to apply
|
|
14
|
+
* @returns Transformed text with adjusted heading levels
|
|
15
|
+
*/
|
|
16
|
+
function transformHeadings(text, offset, initialFence = null, fenceOut) {
|
|
17
|
+
if (offset === 0) {
|
|
18
|
+
if (fenceOut)
|
|
19
|
+
fenceOut.fence = initialFence;
|
|
20
|
+
return text;
|
|
21
|
+
}
|
|
22
|
+
// Fence-aware: only shift ATX headings that are NOT inside a fenced code
|
|
23
|
+
// block. A line-anchored regex would otherwise rewrite `# oc get pods`
|
|
24
|
+
// (a shell prompt / comment) inside a ```terminal block to `## …`,
|
|
25
|
+
// mutating code-block content — which the no-code-block-mutation rule
|
|
26
|
+
// forbids. Issue #020 / task #404.
|
|
27
|
+
const lines = text.split('\n');
|
|
28
|
+
// Fence state CARRIES ACROSS text nodes: a directive inside a listing
|
|
29
|
+
// splits the template into several text nodes, and restarting the state
|
|
30
|
+
// per node lost the open fence — `#` comments at column 0 inside the
|
|
31
|
+
// second half were shifted to `###` (issue #043, managing-ce's
|
|
32
|
+
// ClusterRole manifest; the same class as issue #020).
|
|
33
|
+
let fence = initialFence; // active opening fence run (e.g. "```") while inside a code block
|
|
34
|
+
for (let i = 0; i < lines.length; i++) {
|
|
35
|
+
const line = lines[i];
|
|
36
|
+
const fenceMatch = line.match(/^\s*(`{3,}|~{3,})/);
|
|
37
|
+
if (fenceMatch) {
|
|
38
|
+
const run = fenceMatch[1];
|
|
39
|
+
if (fence === null) {
|
|
40
|
+
// Opening fence (an info string may follow on this line).
|
|
41
|
+
fence = run;
|
|
42
|
+
}
|
|
43
|
+
else if (run[0] === fence[0] && run.length >= fence.length && line.trim() === run) {
|
|
44
|
+
// Closing fence: bare marker, same char, length >= opening (CommonMark).
|
|
45
|
+
fence = null;
|
|
46
|
+
}
|
|
47
|
+
continue; // never treat a fence line as a heading
|
|
48
|
+
}
|
|
49
|
+
if (fence !== null) {
|
|
50
|
+
continue; // inside a code block — leave the line verbatim
|
|
51
|
+
}
|
|
52
|
+
const m = line.match(/^(#{1,6})(\s+)/);
|
|
53
|
+
if (m) {
|
|
54
|
+
const newLevel = Math.max(1, Math.min(6, m[1].length + offset));
|
|
55
|
+
lines[i] = '#'.repeat(newLevel) + line.slice(m[1].length);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (fenceOut)
|
|
59
|
+
fenceOut.fence = fence;
|
|
60
|
+
return lines.join('\n');
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Resolve a relative path against a base path
|
|
64
|
+
* @param path - Relative or absolute path
|
|
65
|
+
* @param basePath - Base path to resolve against
|
|
66
|
+
* @returns Resolved path
|
|
67
|
+
*/
|
|
68
|
+
function resolvePath(path, basePath) {
|
|
69
|
+
// If path is absolute, return as-is
|
|
70
|
+
if (path.startsWith('/')) {
|
|
71
|
+
return path;
|
|
72
|
+
}
|
|
73
|
+
// Ensure basePath ends with /
|
|
74
|
+
const base = basePath.endsWith('/') ? basePath : basePath + '/';
|
|
75
|
+
// Combine and normalize
|
|
76
|
+
const combined = base + path;
|
|
77
|
+
// Normalize . and .. segments
|
|
78
|
+
const parts = combined.split('/');
|
|
79
|
+
const resolved = [];
|
|
80
|
+
for (const part of parts) {
|
|
81
|
+
if (part === '.' || part === '') {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
else if (part === '..') {
|
|
85
|
+
resolved.pop();
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
resolved.push(part);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// Preserve leading slash if original had one
|
|
92
|
+
const prefix = combined.startsWith('/') ? '/' : '';
|
|
93
|
+
return prefix + resolved.join('/');
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Render a template string
|
|
97
|
+
* @param template - Template string to render
|
|
98
|
+
* @param options - Render options
|
|
99
|
+
* @returns Rendered output
|
|
100
|
+
*/
|
|
101
|
+
export async function render(template, options = {}) {
|
|
102
|
+
const { loader = new FetchLoader(), context: initialContext = {}, basePath = '', maxIncludeDepth = 10, timeout = 5000, hyphenToUnderscore = false, undefinedBehavior = 'empty', undefinedConditions = 'preserve', } = options;
|
|
103
|
+
// Create timeout promise
|
|
104
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
105
|
+
setTimeout(() => reject(new Error('Template rendering timeout')), timeout);
|
|
106
|
+
});
|
|
107
|
+
// Render with timeout
|
|
108
|
+
const renderPromise = renderInternal(template, {
|
|
109
|
+
loader,
|
|
110
|
+
context: Context.from({
|
|
111
|
+
...initialContext,
|
|
112
|
+
// Built-in variables
|
|
113
|
+
date: new Date().toISOString().split('T')[0],
|
|
114
|
+
timestamp: Date.now(),
|
|
115
|
+
_levelOffset: 0,
|
|
116
|
+
}, { hyphenToUnderscore }),
|
|
117
|
+
includeDepth: 0,
|
|
118
|
+
maxIncludeDepth,
|
|
119
|
+
basePath,
|
|
120
|
+
undefinedBehavior,
|
|
121
|
+
undefinedConditions,
|
|
122
|
+
});
|
|
123
|
+
return await Promise.race([renderPromise, timeoutPromise]);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Internal rendering function with depth tracking
|
|
127
|
+
*/
|
|
128
|
+
async function renderInternal(template, options) {
|
|
129
|
+
const { includeDepth, maxIncludeDepth } = options;
|
|
130
|
+
// Check recursion depth
|
|
131
|
+
if (includeDepth > maxIncludeDepth) {
|
|
132
|
+
throw new Error(`Maximum include depth (${maxIncludeDepth}) exceeded`);
|
|
133
|
+
}
|
|
134
|
+
// Parse template
|
|
135
|
+
const parseResult = parse(template);
|
|
136
|
+
if (parseResult.errors.length > 0) {
|
|
137
|
+
const errorMessages = parseResult.errors.map((e) => e.message).join(', ');
|
|
138
|
+
throw new Error(`Parse errors: ${errorMessages}`);
|
|
139
|
+
}
|
|
140
|
+
// Render AST
|
|
141
|
+
return await renderNodes(parseResult.ast, options);
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Render an array of AST nodes
|
|
145
|
+
*/
|
|
146
|
+
async function renderNodes(nodes, options) {
|
|
147
|
+
const parts = [];
|
|
148
|
+
for (const node of nodes) {
|
|
149
|
+
let rendered = await renderNode(node, options);
|
|
150
|
+
// An {% include %} that sits alone on an INDENTED line splices content
|
|
151
|
+
// into that indentation context (a list item, an open block in a step):
|
|
152
|
+
// re-indent every non-empty spliced line to the directive's own column.
|
|
153
|
+
// Verbatim splicing put included bullets/fences at column 0 inside a
|
|
154
|
+
// level-2 step, shattering the list and orphaning everything after
|
|
155
|
+
// (the ":::important in a code block" class, cnf-image-based-upgrade).
|
|
156
|
+
// Top-level includes have no leading indent and are untouched.
|
|
157
|
+
if (node.type === 'include' && rendered && parts.length > 0) {
|
|
158
|
+
const tail = /(?:^|\n)([ \t]+)$/.exec(parts[parts.length - 1]);
|
|
159
|
+
if (tail) {
|
|
160
|
+
rendered = rendered.replace(/\n(?=[^\n])/g, '\n' + tail[1]);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
parts.push(rendered);
|
|
164
|
+
}
|
|
165
|
+
return parts.join('');
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Render a single AST node
|
|
169
|
+
*/
|
|
170
|
+
async function renderNode(node, options) {
|
|
171
|
+
const { loader, context, includeDepth, maxIncludeDepth, basePath } = options;
|
|
172
|
+
switch (node.type) {
|
|
173
|
+
case 'text': {
|
|
174
|
+
// Apply leveloffset if active
|
|
175
|
+
const currentOffset = context.get('_levelOffset') || 0;
|
|
176
|
+
if (currentOffset === 0) {
|
|
177
|
+
return node.value;
|
|
178
|
+
}
|
|
179
|
+
const fenceOut = { fence: null };
|
|
180
|
+
const out = transformHeadings(node.value, currentOffset, context.get('_levelOffsetFence') ?? null, fenceOut);
|
|
181
|
+
context.set('_levelOffsetFence', fenceOut.fence);
|
|
182
|
+
return out;
|
|
183
|
+
}
|
|
184
|
+
case 'variable': {
|
|
185
|
+
const value = context.get(node.name);
|
|
186
|
+
if (value === undefined || value === null) {
|
|
187
|
+
// Apply the undefined-variable policy. See RenderOptions.undefinedBehavior.
|
|
188
|
+
switch (options.undefinedBehavior) {
|
|
189
|
+
case 'throw':
|
|
190
|
+
throw new Error(`Undefined variable: ${node.name}`);
|
|
191
|
+
case 'preserve':
|
|
192
|
+
// Round-trip the source form so downstream tooling (or a
|
|
193
|
+
// second-pass minja run with the value supplied) can pick
|
|
194
|
+
// up where we left off.
|
|
195
|
+
return `{{ ${node.name} }}`;
|
|
196
|
+
case 'asciidoc-literal':
|
|
197
|
+
// Terminal build of an AsciiDoc-migrated corpus: every ref
|
|
198
|
+
// came from an `{name}` attribute reference, so an
|
|
199
|
+
// unresolvable one is a literal placeholder — render what
|
|
200
|
+
// asciidoctor renders for an undefined attribute.
|
|
201
|
+
return `{${node.name}}`;
|
|
202
|
+
case 'empty':
|
|
203
|
+
default:
|
|
204
|
+
return '';
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
const str = String(value);
|
|
208
|
+
// If the value contains template syntax, re-process it
|
|
209
|
+
if (str.includes('{{') || str.includes('{%')) {
|
|
210
|
+
return await renderInternal(str, options);
|
|
211
|
+
}
|
|
212
|
+
return str;
|
|
213
|
+
}
|
|
214
|
+
case 'set': {
|
|
215
|
+
const value = evaluateExpression(node.value, context);
|
|
216
|
+
context.set(node.name, value);
|
|
217
|
+
return '';
|
|
218
|
+
}
|
|
219
|
+
case 'comment':
|
|
220
|
+
return '';
|
|
221
|
+
case 'if': {
|
|
222
|
+
// In preserve mode, an `if` whose condition references an
|
|
223
|
+
// undefined identifier can't be evaluated — we don't know
|
|
224
|
+
// which branch to take. Preserve the whole `{% if … %}…{% endif %}`
|
|
225
|
+
// verbatim so a second pass (with the value supplied) can
|
|
226
|
+
// resolve it. See plans/format-asciidoc-import.md.
|
|
227
|
+
//
|
|
228
|
+
// Except when `undefinedConditions === 'falsy'`: this is a
|
|
229
|
+
// terminal build with no later pass, so treat the undefined
|
|
230
|
+
// reference as falsy (AsciiDoc `ifdef::attr[]` semantics) and
|
|
231
|
+
// evaluate now rather than leak the directive to readers.
|
|
232
|
+
if ((options.undefinedBehavior === 'preserve' || options.undefinedBehavior === 'asciidoc-literal') && options.undefinedConditions !== 'falsy') {
|
|
233
|
+
const undefinedRefs = findUndefinedRefs(node.condition, context);
|
|
234
|
+
if (undefinedRefs.length > 0) {
|
|
235
|
+
return ifNodeToSource(node);
|
|
236
|
+
}
|
|
237
|
+
if (node.elifBranches) {
|
|
238
|
+
for (const elif of node.elifBranches) {
|
|
239
|
+
if (findUndefinedRefs(elif.condition, context).length > 0) {
|
|
240
|
+
return ifNodeToSource(node);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
const condition = evaluateExpression(node.condition, context);
|
|
246
|
+
if (isTruthy(condition)) {
|
|
247
|
+
return await renderNodes(node.trueBranch, options);
|
|
248
|
+
}
|
|
249
|
+
// Check elif branches
|
|
250
|
+
if (node.elifBranches) {
|
|
251
|
+
for (const elifBranch of node.elifBranches) {
|
|
252
|
+
const elifCondition = evaluateExpression(elifBranch.condition, context);
|
|
253
|
+
if (isTruthy(elifCondition)) {
|
|
254
|
+
return await renderNodes(elifBranch.body, options);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
// Render else branch if present
|
|
259
|
+
if (node.elseBranch) {
|
|
260
|
+
return await renderNodes(node.elseBranch, options);
|
|
261
|
+
}
|
|
262
|
+
return '';
|
|
263
|
+
}
|
|
264
|
+
case 'include': {
|
|
265
|
+
try {
|
|
266
|
+
// Load the included file
|
|
267
|
+
const includedContent = await loader.load(node.path, basePath);
|
|
268
|
+
// Determine new base path for nested includes
|
|
269
|
+
let newBasePath = basePath;
|
|
270
|
+
// If path looks like a URL, extract base
|
|
271
|
+
if (node.path.includes('://')) {
|
|
272
|
+
const url = new URL(node.path);
|
|
273
|
+
newBasePath = url.href.substring(0, url.href.lastIndexOf('/') + 1);
|
|
274
|
+
}
|
|
275
|
+
else if (basePath) {
|
|
276
|
+
// Relative path - resolve against current base
|
|
277
|
+
if (basePath.includes('://')) {
|
|
278
|
+
const url = new URL(node.path, basePath);
|
|
279
|
+
newBasePath = url.href.substring(0, url.href.lastIndexOf('/') + 1);
|
|
280
|
+
}
|
|
281
|
+
else {
|
|
282
|
+
// File path - resolve relative to current base and extract directory
|
|
283
|
+
const resolvedPath = resolvePath(node.path, basePath);
|
|
284
|
+
const lastSlash = resolvedPath.lastIndexOf('/');
|
|
285
|
+
newBasePath = lastSlash >= 0 ? resolvedPath.substring(0, lastSlash + 1) : basePath;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
else if (node.path.includes('/')) {
|
|
289
|
+
// No base path but include has directory - use that as base
|
|
290
|
+
const lastSlash = node.path.lastIndexOf('/');
|
|
291
|
+
newBasePath = node.path.substring(0, lastSlash + 1);
|
|
292
|
+
}
|
|
293
|
+
// Render the included template (recursive)
|
|
294
|
+
return await renderInternal(includedContent, {
|
|
295
|
+
loader,
|
|
296
|
+
context,
|
|
297
|
+
includeDepth: includeDepth + 1,
|
|
298
|
+
maxIncludeDepth,
|
|
299
|
+
basePath: newBasePath,
|
|
300
|
+
undefinedBehavior: options.undefinedBehavior,
|
|
301
|
+
undefinedConditions: options.undefinedConditions,
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
catch (error) {
|
|
305
|
+
// A containment refusal is a SECURITY event, not a missing file:
|
|
306
|
+
// neither preserved for a later pass (no later pass may resolve
|
|
307
|
+
// it) nor degraded to a comment — the render fails hard (#027).
|
|
308
|
+
if (error instanceof Error && error.name === 'IncludeContainmentError') {
|
|
309
|
+
throw error;
|
|
310
|
+
}
|
|
311
|
+
// In `preserve` mode, an unresolvable include is preserved
|
|
312
|
+
// as the original directive so a later renderer pass (with
|
|
313
|
+
// the file in place) can resolve it. Matches the semantic
|
|
314
|
+
// of `preserve` for undefined variables. Without this,
|
|
315
|
+
// multi-stage pipelines (e.g. dogsbay's convert + build
|
|
316
|
+
// preprocessor for AsciiDoc imports) lose the directive at
|
|
317
|
+
// stage 1 because targets only exist after stage 2 has
|
|
318
|
+
// converted sibling files.
|
|
319
|
+
if (options.undefinedBehavior === 'preserve' || options.undefinedBehavior === 'asciidoc-literal') {
|
|
320
|
+
return `{% include "${node.path}" %}`;
|
|
321
|
+
}
|
|
322
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
323
|
+
console.error(`Failed to include ${node.path}:`, message);
|
|
324
|
+
return `<!-- Include error: ${node.path} -->`;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
case 'switch': {
|
|
328
|
+
const switchValue = evaluateExpression(node.expression, context);
|
|
329
|
+
// Find matching case
|
|
330
|
+
for (const caseItem of node.cases) {
|
|
331
|
+
const caseValue = evaluateExpression(caseItem.value, context);
|
|
332
|
+
// eslint-disable-next-line eqeqeq
|
|
333
|
+
if (switchValue == caseValue) {
|
|
334
|
+
return await renderNodes(caseItem.body, options);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
// No matching case found
|
|
338
|
+
return '';
|
|
339
|
+
}
|
|
340
|
+
case 'leveloffset': {
|
|
341
|
+
// Calculate new offset (relative or absolute)
|
|
342
|
+
const parentOffset = context.get('_levelOffset') || 0;
|
|
343
|
+
const newOffset = node.isRelative ? parentOffset + node.offset : node.offset;
|
|
344
|
+
// Store current offset to restore later
|
|
345
|
+
const previousOffset = parentOffset;
|
|
346
|
+
// Set new offset in context
|
|
347
|
+
context.set('_levelOffset', newOffset);
|
|
348
|
+
// Fresh scope: a fence left open by unrelated earlier content must
|
|
349
|
+
// not suppress heading shifts here. Save the outer state so a NESTED
|
|
350
|
+
// leveloffset sitting inside an open fence doesn't wipe it — without
|
|
351
|
+
// the restore, the outer fence's real closer was later mis-read as an
|
|
352
|
+
// opener, flipping fence state for the rest of the render.
|
|
353
|
+
const previousFence = context.get('_levelOffsetFence') ?? null;
|
|
354
|
+
context.set('_levelOffsetFence', null);
|
|
355
|
+
try {
|
|
356
|
+
// Render body with offset active
|
|
357
|
+
const result = await renderNodes(node.body, options);
|
|
358
|
+
return result;
|
|
359
|
+
}
|
|
360
|
+
finally {
|
|
361
|
+
// Always restore previous offset + the outer fence state
|
|
362
|
+
context.set('_levelOffset', previousOffset);
|
|
363
|
+
context.set('_levelOffsetFence', previousFence);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
default:
|
|
367
|
+
// TypeScript exhaustiveness check
|
|
368
|
+
const _exhaustive = node;
|
|
369
|
+
return _exhaustive;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Determine if a value is truthy
|
|
374
|
+
*/
|
|
375
|
+
function isTruthy(value) {
|
|
376
|
+
if (value === undefined || value === null || value === false) {
|
|
377
|
+
return false;
|
|
378
|
+
}
|
|
379
|
+
if (value === 0 || value === '') {
|
|
380
|
+
return false;
|
|
381
|
+
}
|
|
382
|
+
return true;
|
|
383
|
+
}
|
|
384
|
+
// ============================================================================
|
|
385
|
+
// Preserve-mode helpers — round-trip AST back to Jinja source so an
|
|
386
|
+
// undefined-referencing block can survive into the rendered output for
|
|
387
|
+
// a downstream pass to resolve.
|
|
388
|
+
// ============================================================================
|
|
389
|
+
/**
|
|
390
|
+
* Walk an expression AST, return the names of any `variable` references
|
|
391
|
+
* whose context lookup is undefined. Empty array = all refs are bound.
|
|
392
|
+
*/
|
|
393
|
+
function findUndefinedRefs(expr, context) {
|
|
394
|
+
const out = [];
|
|
395
|
+
function walk(e) {
|
|
396
|
+
switch (e.type) {
|
|
397
|
+
case 'variable':
|
|
398
|
+
if (context.get(e.name) === undefined)
|
|
399
|
+
out.push(e.name);
|
|
400
|
+
break;
|
|
401
|
+
case 'binary':
|
|
402
|
+
walk(e.left);
|
|
403
|
+
walk(e.right);
|
|
404
|
+
break;
|
|
405
|
+
case 'unary':
|
|
406
|
+
walk(e.operand);
|
|
407
|
+
break;
|
|
408
|
+
case 'literal':
|
|
409
|
+
break;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
walk(expr);
|
|
413
|
+
return out;
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Serialize an Expression back to Jinja source. Used by preserve mode
|
|
417
|
+
* to reconstruct `{% if … %}` headers when the condition can't be
|
|
418
|
+
* evaluated. Operator precedence isn't tracked; the output is
|
|
419
|
+
* conservatively parenthesized for binary/unary so a re-parse round-
|
|
420
|
+
* trips to the same AST shape (modulo redundant parens).
|
|
421
|
+
*/
|
|
422
|
+
function expressionToSource(expr) {
|
|
423
|
+
switch (expr.type) {
|
|
424
|
+
case 'literal':
|
|
425
|
+
if (typeof expr.value === 'string')
|
|
426
|
+
return JSON.stringify(expr.value);
|
|
427
|
+
if (expr.value === null)
|
|
428
|
+
return 'null';
|
|
429
|
+
return String(expr.value);
|
|
430
|
+
case 'variable':
|
|
431
|
+
return expr.name;
|
|
432
|
+
case 'unary':
|
|
433
|
+
return `not ${expressionToSource(expr.operand)}`;
|
|
434
|
+
case 'binary':
|
|
435
|
+
return `(${expressionToSource(expr.left)} ${expr.operator} ${expressionToSource(expr.right)})`;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Serialize ASTNode[] back to Jinja source. Mirrors the parser's syntax
|
|
440
|
+
* for every node type so a preserved block can be re-rendered later
|
|
441
|
+
* with the missing values supplied.
|
|
442
|
+
*
|
|
443
|
+
* Throws on shapes we don't support (deep nested includes, switch);
|
|
444
|
+
* preserve mode is opt-in and a hard error here beats silent
|
|
445
|
+
* truncation. The shapes covered are everything md2md emits in the
|
|
446
|
+
* AsciiDoc pipeline.
|
|
447
|
+
*/
|
|
448
|
+
function nodesToSource(nodes) {
|
|
449
|
+
return nodes.map(nodeToSource).join('');
|
|
450
|
+
}
|
|
451
|
+
function nodeToSource(node) {
|
|
452
|
+
switch (node.type) {
|
|
453
|
+
case 'text':
|
|
454
|
+
return node.value;
|
|
455
|
+
case 'variable':
|
|
456
|
+
return `{{ ${node.name} }}`;
|
|
457
|
+
case 'set':
|
|
458
|
+
return `{% set ${node.name} = ${expressionToSource(node.value)} %}`;
|
|
459
|
+
case 'comment':
|
|
460
|
+
// Parser strips comment content; emit a placeholder so the block
|
|
461
|
+
// structure round-trips even when the prose inside doesn't.
|
|
462
|
+
return '{# … #}';
|
|
463
|
+
case 'if':
|
|
464
|
+
return ifNodeToSource(node);
|
|
465
|
+
case 'include':
|
|
466
|
+
return `{% include "${node.path}" %}`;
|
|
467
|
+
case 'leveloffset': {
|
|
468
|
+
const sign = node.isRelative && node.offset >= 0 ? '+' : '';
|
|
469
|
+
return `{% leveloffset ${sign}${node.offset} %}${nodesToSource(node.body)}{% endleveloffset %}`;
|
|
470
|
+
}
|
|
471
|
+
case 'switch': {
|
|
472
|
+
// Conservative round-trip; switch isn't on the AsciiDoc hot path
|
|
473
|
+
// but we have an AST shape, so emit it.
|
|
474
|
+
const cases = node.cases
|
|
475
|
+
.map((c) => `{% case ${expressionToSource(c.value)} %}${nodesToSource(c.body)}`)
|
|
476
|
+
.join('');
|
|
477
|
+
return `{% switch ${expressionToSource(node.expression)} %}${cases}{% endswitch %}`;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
function ifNodeToSource(node) {
|
|
482
|
+
const parts = [`{% if ${expressionToSource(node.condition)} %}`, nodesToSource(node.trueBranch)];
|
|
483
|
+
if (node.elifBranches) {
|
|
484
|
+
for (const elif of node.elifBranches) {
|
|
485
|
+
parts.push(`{% elif ${expressionToSource(elif.condition)} %}`, nodesToSource(elif.body));
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
if (node.elseBranch) {
|
|
489
|
+
parts.push(`{% else %}`, nodesToSource(node.elseBranch));
|
|
490
|
+
}
|
|
491
|
+
parts.push(`{% endif %}`);
|
|
492
|
+
return parts.join('');
|
|
493
|
+
}
|
|
494
|
+
//# sourceMappingURL=renderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderer.js","sourceRoot":"","sources":["../src/renderer.ts"],"names":[],"mappings":"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"}
|
package/dist/scan.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scanning-grammar mirrors of parser statements, for tools that need to
|
|
3
|
+
* FIND directives in raw text without running the full parser (workspace
|
|
4
|
+
* indexers, linters). Kept next to the parser so grammar changes update
|
|
5
|
+
* both; tests/scan.test.ts asserts parser agreement for every form the
|
|
6
|
+
* scan regex matches.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Fresh regex matching a complete `{% include "path" %}` statement,
|
|
10
|
+
* capturing the path. Factory (not a shared const) because the global
|
|
11
|
+
* flag makes RegExp objects stateful.
|
|
12
|
+
*
|
|
13
|
+
* Grammar notes, matching parser.ts:
|
|
14
|
+
* - `{%-` / `-%}` whitespace-trim markers are accepted.
|
|
15
|
+
* - At least one whitespace character is required after `include`.
|
|
16
|
+
* - The path is quoted with `"` or `'` and contains no quote chars.
|
|
17
|
+
*/
|
|
18
|
+
export declare const includeDirectiveRe: () => RegExp;
|
|
19
|
+
/** All `{% include %}` paths in a text, in order. */
|
|
20
|
+
export declare const scanIncludes: (text: string) => string[];
|
|
21
|
+
/**
|
|
22
|
+
* Fresh regex matching a complete `{{ … }}` variable/expression output,
|
|
23
|
+
* capturing the raw inner source (whitespace-trim markers accepted, not
|
|
24
|
+
* captured). Grammar mirror of parser.ts's `{{`→`}}` scan.
|
|
25
|
+
*/
|
|
26
|
+
export declare const variableRe: () => RegExp;
|
|
27
|
+
/**
|
|
28
|
+
* Fresh regex matching a complete `{% … %}` statement tag, capturing the
|
|
29
|
+
* keyword and the raw remainder (up to the closing marker). Grammar mirror
|
|
30
|
+
* of parser.ts's `{%`→`%}` scan — the keyword set is NOT validated here;
|
|
31
|
+
* consumers classify (if/elif/else/endif/include/set/…) themselves.
|
|
32
|
+
*/
|
|
33
|
+
export declare const directiveTagRe: () => RegExp;
|
|
34
|
+
/**
|
|
35
|
+
* Fresh regex matching a complete `{# … #}` comment. Grammar mirror of
|
|
36
|
+
* parser.ts's `{#`→`#}` scan.
|
|
37
|
+
*/
|
|
38
|
+
export declare const commentRe: () => RegExp;
|
|
39
|
+
/**
|
|
40
|
+
* Fresh regex matching a complete `{% raw %}…{% endraw %}` block, capturing
|
|
41
|
+
* the verbatim inner content. Grammar mirror of parser.ts's raw handling.
|
|
42
|
+
* Consumers that classify individual tags (directiveTagRe) should match
|
|
43
|
+
* raw blocks FIRST — the inner content is not directive syntax.
|
|
44
|
+
*/
|
|
45
|
+
export declare const rawBlockRe: () => RegExp;
|
|
46
|
+
//# sourceMappingURL=scan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan.d.ts","sourceRoot":"","sources":["../src/scan.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB,QAAO,MACS,CAAC;AAEhD,qDAAqD;AACrD,eAAO,MAAM,YAAY,GAAI,MAAM,MAAM,KAAG,MAAM,EACiB,CAAC;AAEpE;;;;GAIG;AACH,eAAO,MAAM,UAAU,QAAO,MAAmC,CAAC;AAElE;;;;;GAKG;AACH,eAAO,MAAM,cAAc,QAAO,MACkB,CAAC;AAErD;;;GAGG;AACH,eAAO,MAAM,SAAS,QAAO,MAA2B,CAAC;AAEzD;;;;;GAKG;AACH,eAAO,MAAM,UAAU,QAAO,MAC0B,CAAC"}
|
package/dist/scan.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scanning-grammar mirrors of parser statements, for tools that need to
|
|
3
|
+
* FIND directives in raw text without running the full parser (workspace
|
|
4
|
+
* indexers, linters). Kept next to the parser so grammar changes update
|
|
5
|
+
* both; tests/scan.test.ts asserts parser agreement for every form the
|
|
6
|
+
* scan regex matches.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Fresh regex matching a complete `{% include "path" %}` statement,
|
|
10
|
+
* capturing the path. Factory (not a shared const) because the global
|
|
11
|
+
* flag makes RegExp objects stateful.
|
|
12
|
+
*
|
|
13
|
+
* Grammar notes, matching parser.ts:
|
|
14
|
+
* - `{%-` / `-%}` whitespace-trim markers are accepted.
|
|
15
|
+
* - At least one whitespace character is required after `include`.
|
|
16
|
+
* - The path is quoted with `"` or `'` and contains no quote chars.
|
|
17
|
+
*/
|
|
18
|
+
export const includeDirectiveRe = () => /\{%-?\s*include\s+["']([^"']+)["']\s*-?%\}/g;
|
|
19
|
+
/** All `{% include %}` paths in a text, in order. */
|
|
20
|
+
export const scanIncludes = (text) => [...text.matchAll(includeDirectiveRe())].map((match) => match[1]);
|
|
21
|
+
/**
|
|
22
|
+
* Fresh regex matching a complete `{{ … }}` variable/expression output,
|
|
23
|
+
* capturing the raw inner source (whitespace-trim markers accepted, not
|
|
24
|
+
* captured). Grammar mirror of parser.ts's `{{`→`}}` scan.
|
|
25
|
+
*/
|
|
26
|
+
export const variableRe = () => /\{\{-?([\s\S]*?)-?\}\}/g;
|
|
27
|
+
/**
|
|
28
|
+
* Fresh regex matching a complete `{% … %}` statement tag, capturing the
|
|
29
|
+
* keyword and the raw remainder (up to the closing marker). Grammar mirror
|
|
30
|
+
* of parser.ts's `{%`→`%}` scan — the keyword set is NOT validated here;
|
|
31
|
+
* consumers classify (if/elif/else/endif/include/set/…) themselves.
|
|
32
|
+
*/
|
|
33
|
+
export const directiveTagRe = () => /\{%-?\s*([A-Za-z_][A-Za-z0-9_]*)([\s\S]*?)-?%\}/g;
|
|
34
|
+
/**
|
|
35
|
+
* Fresh regex matching a complete `{# … #}` comment. Grammar mirror of
|
|
36
|
+
* parser.ts's `{#`→`#}` scan.
|
|
37
|
+
*/
|
|
38
|
+
export const commentRe = () => /\{#[\s\S]*?#\}/g;
|
|
39
|
+
/**
|
|
40
|
+
* Fresh regex matching a complete `{% raw %}…{% endraw %}` block, capturing
|
|
41
|
+
* the verbatim inner content. Grammar mirror of parser.ts's raw handling.
|
|
42
|
+
* Consumers that classify individual tags (directiveTagRe) should match
|
|
43
|
+
* raw blocks FIRST — the inner content is not directive syntax.
|
|
44
|
+
*/
|
|
45
|
+
export const rawBlockRe = () => /\{%-?\s*raw\s*-?%\}([\s\S]*?)\{%-?\s*endraw\s*-?%\}/g;
|
|
46
|
+
//# sourceMappingURL=scan.js.map
|
package/dist/scan.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan.js","sourceRoot":"","sources":["../src/scan.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAW,EAAE,CAC7C,6CAA6C,CAAC;AAEhD,qDAAqD;AACrD,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,IAAY,EAAY,EAAE,CACrD,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,kBAAkB,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AAEpE;;;;GAIG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,GAAW,EAAE,CAAC,yBAAyB,CAAC;AAElE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,GAAW,EAAE,CACzC,kDAAkD,CAAC;AAErD;;;GAGG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,GAAW,EAAE,CAAC,iBAAiB,CAAC;AAEzD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,GAAW,EAAE,CACrC,sDAAsD,CAAC"}
|