@chilts/weave 0.5.0 → 0.7.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/CHANGELOG.md +178 -0
- package/ReadMe.md +145 -1
- package/dist/cli/index.d.ts +19 -1
- package/dist/cli/index.js +323 -14
- package/dist/codegen/index.d.ts +17 -3
- package/dist/codegen/index.js +208 -56
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/lexer/index.js +335 -108
- package/dist/parser/index.js +27 -3
- package/dist/runtime/index.d.ts +6 -0
- package/dist/runtime/index.js +11 -1
- package/package.json +9 -4
- package/dist/cli/index.d.ts.map +0 -1
- package/dist/cli/index.js.map +0 -1
- package/dist/codegen/index.d.ts.map +0 -1
- package/dist/codegen/index.js.map +0 -1
- package/dist/codegen/sourcemap.d.ts.map +0 -1
- package/dist/codegen/sourcemap.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/lexer/index.d.ts.map +0 -1
- package/dist/lexer/index.js.map +0 -1
- package/dist/parser/index.d.ts.map +0 -1
- package/dist/parser/index.js.map +0 -1
- package/dist/parser/types.d.ts.map +0 -1
- package/dist/parser/types.js.map +0 -1
- package/dist/runtime/index.d.ts.map +0 -1
- package/dist/runtime/index.js.map +0 -1
package/dist/codegen/index.js
CHANGED
|
@@ -14,6 +14,29 @@ export class CodegenError extends Error {
|
|
|
14
14
|
function capitalize(s) {
|
|
15
15
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
16
16
|
}
|
|
17
|
+
// Appends a compiler-supplied argument (`attrs`, `blocks`, ...) to a
|
|
18
|
+
// captured argument or parameter list. A multi-line list may already end in
|
|
19
|
+
// a trailing comma, and when its ')' sat on a line of its own the extra goes
|
|
20
|
+
// on its own line too, so the output keeps the shape the source was written in.
|
|
21
|
+
// `ownLine` is false for an extra that opens a multi-line literal of its own.
|
|
22
|
+
function appendArg(args, extra, ownLine = true) {
|
|
23
|
+
if (args === '')
|
|
24
|
+
return extra;
|
|
25
|
+
const body = args.trimEnd();
|
|
26
|
+
const tail = args.slice(body.length);
|
|
27
|
+
if (ownLine && tail.includes('\n')) {
|
|
28
|
+
const lastLine = body.slice(body.lastIndexOf('\n') + 1);
|
|
29
|
+
const indent = /^ */.exec(lastLine)[0];
|
|
30
|
+
return `${body}${body.endsWith(',') ? '' : ','}\n${indent}${extra},${tail}`;
|
|
31
|
+
}
|
|
32
|
+
return `${body}${body.endsWith(',') ? ' ' : ', '}${extra}`;
|
|
33
|
+
}
|
|
34
|
+
// Indents the continuation lines of a multi-line captured expression to sit
|
|
35
|
+
// under the output line it starts on. Captured text never holds a string that
|
|
36
|
+
// spans lines, so every newline is safe to indent after.
|
|
37
|
+
function reindent(text, prefix) {
|
|
38
|
+
return prefix === '' ? text : text.replace(/\n(?!\n|$)/g, `\n${prefix}`);
|
|
39
|
+
}
|
|
17
40
|
function camelToKebab(name) {
|
|
18
41
|
return name.replace(/[A-Z]/g, m => '-' + m.toLowerCase());
|
|
19
42
|
}
|
|
@@ -110,9 +133,10 @@ class CodeGenerator {
|
|
|
110
133
|
case 'MixinDefinition':
|
|
111
134
|
return this.genMixinDefinition(node, indent);
|
|
112
135
|
case 'MixinCall':
|
|
113
|
-
return this.genMixinCall(node);
|
|
136
|
+
return this.genMixinCall(node, indent);
|
|
114
137
|
case 'Include':
|
|
115
|
-
|
|
138
|
+
// Hoisted: generate() emits the import once it knows the names.
|
|
139
|
+
return null;
|
|
116
140
|
case 'Block':
|
|
117
141
|
return this.genBlock(node, indent);
|
|
118
142
|
case 'Extends':
|
|
@@ -157,20 +181,22 @@ class CodeGenerator {
|
|
|
157
181
|
: variable;
|
|
158
182
|
const bi = this.formatted ? this.ind(indent + 1) : ' ';
|
|
159
183
|
const pi = this.formatted ? this.ind(indent) : '';
|
|
160
|
-
const
|
|
184
|
+
const head = reindent(`${iterable}.map(${param} =>`, this.formatted ? this.ind(indent) : '');
|
|
185
|
+
const code = `${head}\n${bi}${bodyChunk.code}\n${pi})`;
|
|
161
186
|
const mappings = [
|
|
162
187
|
mapping(node.loc),
|
|
163
|
-
...offsetMappings(bodyChunk.mappings, 1),
|
|
188
|
+
...offsetMappings(bodyChunk.mappings, 1 + lineCount(head)),
|
|
164
189
|
];
|
|
165
190
|
return { code, mappings };
|
|
166
191
|
}
|
|
167
192
|
parseForParams(params, loc) {
|
|
168
|
-
|
|
169
|
-
|
|
193
|
+
// A multi-line header may put `of` at the start or end of a line.
|
|
194
|
+
const of = /\s+of\s+/.exec(params);
|
|
195
|
+
if (of === null) {
|
|
170
196
|
throw new CodegenError(`Invalid for params: only "for...of" is supported, got "${params}"`, loc.line, loc.col);
|
|
171
197
|
}
|
|
172
|
-
const binding = params.slice(0,
|
|
173
|
-
const iterable = params.slice(
|
|
198
|
+
const binding = params.slice(0, of.index).trim();
|
|
199
|
+
const iterable = params.slice(of.index + of[0].length).trim();
|
|
174
200
|
const variable = binding.replace(/^(const|let)\s+/, '');
|
|
175
201
|
return { variable, iterable };
|
|
176
202
|
}
|
|
@@ -259,11 +285,10 @@ class CodeGenerator {
|
|
|
259
285
|
if (attr.spread) {
|
|
260
286
|
parts.push(`...${attr.name}`);
|
|
261
287
|
}
|
|
262
|
-
else if (attr.value === null) {
|
|
263
|
-
const key = this.attrKey(attr.name);
|
|
264
|
-
parts.push(`${key}: true`);
|
|
265
|
-
}
|
|
266
288
|
else {
|
|
289
|
+
// Every non-spread attribute carries a value (the parser rejects
|
|
290
|
+
// valueless attributes); booleans arrive as the verbatim
|
|
291
|
+
// expression `true` / `false`.
|
|
267
292
|
const key = this.attrKey(attr.name);
|
|
268
293
|
parts.push(`${key}: ${attr.value}`);
|
|
269
294
|
}
|
|
@@ -273,13 +298,16 @@ class CodeGenerator {
|
|
|
273
298
|
return `{ ${parts.join(', ')} }`;
|
|
274
299
|
}
|
|
275
300
|
mergeClassValue(prefix, value) {
|
|
276
|
-
|
|
277
|
-
|
|
301
|
+
// String and template literals are always strings, so merge them now.
|
|
302
|
+
// Anything else may be undefined/null/false at render time, which must
|
|
303
|
+
// add nothing rather than stringify into the class list.
|
|
304
|
+
if (value === '""' || value === "''" || value === '``') {
|
|
305
|
+
return `"${prefix}"`;
|
|
278
306
|
}
|
|
279
|
-
if (value.startsWith('"')) {
|
|
280
|
-
return
|
|
307
|
+
if (value.startsWith('`') || value.startsWith('"') || value.startsWith("'")) {
|
|
308
|
+
return `${value[0]}${prefix} ${value.slice(1)}`;
|
|
281
309
|
}
|
|
282
|
-
return
|
|
310
|
+
return `mergeClass("${prefix}", ${value})`;
|
|
283
311
|
}
|
|
284
312
|
attrKey(name) {
|
|
285
313
|
if (/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name))
|
|
@@ -331,19 +359,19 @@ class CodeGenerator {
|
|
|
331
359
|
// A mid-layer template both fills its parent's slots and exposes its
|
|
332
360
|
// own (nested `block` nodes), so it needs a `blocks` parameter too.
|
|
333
361
|
const params = templateHasBlocks(node)
|
|
334
|
-
?
|
|
362
|
+
? appendArg(node.params, `blocks: ${capitalize(node.name)}Blocks = {}`)
|
|
335
363
|
: node.params;
|
|
336
364
|
const code = `export function ${node.name}(${params}) {\n${i1}return ${extendsChunk.code}\n}`;
|
|
337
365
|
const mappings = [
|
|
338
366
|
mapping(node.loc),
|
|
339
|
-
...offsetMappings(extendsChunk.mappings, 1),
|
|
367
|
+
...offsetMappings(extendsChunk.mappings, 1 + lineCount(params)),
|
|
340
368
|
];
|
|
341
369
|
return { code, mappings };
|
|
342
370
|
}
|
|
343
371
|
// Check if body tree contains BlockNodes (layout template)
|
|
344
372
|
const hasBlocks = templateHasBlocks(node);
|
|
345
373
|
const params = hasBlocks
|
|
346
|
-
?
|
|
374
|
+
? appendArg(node.params, `blocks: ${capitalize(node.name)}Blocks = {}`)
|
|
347
375
|
: node.params;
|
|
348
376
|
const bodyChunks = node.body
|
|
349
377
|
.map(child => this.genNode(child, indent + 2))
|
|
@@ -372,16 +400,14 @@ class CodeGenerator {
|
|
|
372
400
|
const code = `export function ${node.name}(${params}) {\n${commentPrefix}${i1}return ${bodyChunk.code}\n}`;
|
|
373
401
|
const mappings = [
|
|
374
402
|
mapping(node.loc),
|
|
375
|
-
...offsetMappings(bodyChunk.mappings, 1 + commentLines), // body starts after fn sig + comments
|
|
403
|
+
...offsetMappings(bodyChunk.mappings, 1 + lineCount(params) + commentLines), // body starts after fn sig + comments
|
|
376
404
|
];
|
|
377
405
|
return { code, mappings };
|
|
378
406
|
}
|
|
379
407
|
// --- Mixin generation ---
|
|
380
408
|
genMixinDefinition(node, indent) {
|
|
381
409
|
const i1 = this.ind(indent + 1);
|
|
382
|
-
const params = node.params
|
|
383
|
-
? `${node.params}, attrs: Attrs = {}`
|
|
384
|
-
: 'attrs: Attrs = {}';
|
|
410
|
+
const params = appendArg(node.params, 'attrs: Attrs = {}');
|
|
385
411
|
const bodyChunks = node.body
|
|
386
412
|
.map(child => this.genNode(child, indent + 1))
|
|
387
413
|
.filter((e) => e !== null);
|
|
@@ -389,19 +415,16 @@ class CodeGenerator {
|
|
|
389
415
|
const code = `export function ${node.name}(${params}) {\n${i1}return ${bodyChunk.code}\n}`;
|
|
390
416
|
const mappings = [
|
|
391
417
|
mapping(node.loc),
|
|
392
|
-
...offsetMappings(bodyChunk.mappings, 1),
|
|
418
|
+
...offsetMappings(bodyChunk.mappings, 1 + lineCount(params)),
|
|
393
419
|
];
|
|
394
420
|
return { code, mappings };
|
|
395
421
|
}
|
|
396
|
-
genMixinCall(node) {
|
|
397
|
-
const
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
argParts.push(this.genCallAttrs(node.attrs));
|
|
403
|
-
}
|
|
404
|
-
return chunk(`${node.name}(${argParts.join(', ')})`, node.loc);
|
|
422
|
+
genMixinCall(node, indent = 0) {
|
|
423
|
+
const args = node.attrs.length > 0
|
|
424
|
+
? appendArg(node.args, this.genCallAttrs(node.attrs))
|
|
425
|
+
: node.args;
|
|
426
|
+
const prefix = this.formatted ? this.ind(indent) : '';
|
|
427
|
+
return chunk(reindent(`${node.name}(${args})`, prefix), node.loc);
|
|
405
428
|
}
|
|
406
429
|
genCallAttrs(attrs) {
|
|
407
430
|
const parts = [];
|
|
@@ -409,11 +432,10 @@ class CodeGenerator {
|
|
|
409
432
|
if (attr.spread) {
|
|
410
433
|
parts.push(`...${attr.name}`);
|
|
411
434
|
}
|
|
412
|
-
else if (attr.value === null) {
|
|
413
|
-
const key = this.attrKey(attr.name);
|
|
414
|
-
parts.push(`${key}: true`);
|
|
415
|
-
}
|
|
416
435
|
else {
|
|
436
|
+
// Every non-spread attribute carries a value (the parser rejects
|
|
437
|
+
// valueless attributes); booleans arrive as the verbatim
|
|
438
|
+
// expression `true` / `false`.
|
|
417
439
|
const key = this.attrKey(attr.name);
|
|
418
440
|
parts.push(`${key}: ${attr.value}`);
|
|
419
441
|
}
|
|
@@ -448,17 +470,17 @@ class CodeGenerator {
|
|
|
448
470
|
blockChunks.push({ code: blockCode, mappings: blockMappings });
|
|
449
471
|
}
|
|
450
472
|
const joinedBlocks = joinChunks(blockChunks, ',\n');
|
|
451
|
-
const
|
|
473
|
+
const args = reindent(node.args, pi);
|
|
474
|
+
const head = `${node.target}(${appendArg(args, '{', false)}`;
|
|
475
|
+
const code = `${head}\n${joinedBlocks.code}\n${pi}})`;
|
|
452
476
|
const mappings = [
|
|
453
477
|
mapping(node.loc),
|
|
454
|
-
...offsetMappings(joinedBlocks.mappings, 1),
|
|
478
|
+
...offsetMappings(joinedBlocks.mappings, 1 + lineCount(head)),
|
|
455
479
|
];
|
|
456
480
|
return { code, mappings };
|
|
457
481
|
}
|
|
458
|
-
genInclude(node) {
|
|
459
|
-
|
|
460
|
-
const name = segments[segments.length - 1];
|
|
461
|
-
return chunk(`import { ${name} } from '${node.path}.js'`, node.loc);
|
|
482
|
+
genInclude(node, names) {
|
|
483
|
+
return chunk(`import { ${names.join(', ')} } from '${node.path}.js'`, node.loc);
|
|
462
484
|
}
|
|
463
485
|
// --- Simple generators ---
|
|
464
486
|
genBufferedCode(node) {
|
|
@@ -472,12 +494,21 @@ class CodeGenerator {
|
|
|
472
494
|
function countEffective(nodes) {
|
|
473
495
|
return nodes.filter(n => n.type !== 'Comment' && n.type !== 'PassthroughComment').length;
|
|
474
496
|
}
|
|
497
|
+
/** True when shorthand classes merge with a `class=` value that is not a string literal. */
|
|
498
|
+
function needsRuntimeClassMerge(node) {
|
|
499
|
+
const explicitClass = node.attrs.find(a => a.name === 'class' && !a.spread);
|
|
500
|
+
if (!explicitClass || explicitClass.value === null)
|
|
501
|
+
return false;
|
|
502
|
+
return !/^["'`]/.test(explicitClass.value);
|
|
503
|
+
}
|
|
475
504
|
function scanUsedHelpers(node) {
|
|
476
505
|
const helpers = new Set();
|
|
477
506
|
function walk(n) {
|
|
478
507
|
switch (n.type) {
|
|
479
508
|
case 'Element':
|
|
480
509
|
helpers.add('h');
|
|
510
|
+
if (n.classes.length > 0 && needsRuntimeClassMerge(n))
|
|
511
|
+
helpers.add('mergeClass');
|
|
481
512
|
if (n.rawBlock)
|
|
482
513
|
helpers.add('raw');
|
|
483
514
|
for (const c of n.children)
|
|
@@ -534,6 +565,10 @@ function scanUsedHelpers(node) {
|
|
|
534
565
|
break;
|
|
535
566
|
}
|
|
536
567
|
case 'MixinDefinition':
|
|
568
|
+
if (countEffective(n.body) > 1) {
|
|
569
|
+
helpers.add('h');
|
|
570
|
+
helpers.add('Fragment');
|
|
571
|
+
}
|
|
537
572
|
for (const c of n.body)
|
|
538
573
|
walk(c);
|
|
539
574
|
break;
|
|
@@ -542,6 +577,10 @@ function scanUsedHelpers(node) {
|
|
|
542
577
|
walk(c);
|
|
543
578
|
break;
|
|
544
579
|
case 'Block':
|
|
580
|
+
if (countEffective(n.children) > 1) {
|
|
581
|
+
helpers.add('h');
|
|
582
|
+
helpers.add('Fragment');
|
|
583
|
+
}
|
|
545
584
|
for (const c of n.children)
|
|
546
585
|
walk(c);
|
|
547
586
|
break;
|
|
@@ -564,6 +603,121 @@ function scanUsedHelpers(node) {
|
|
|
564
603
|
walk(node);
|
|
565
604
|
return helpers;
|
|
566
605
|
}
|
|
606
|
+
// --- Include resolution ---
|
|
607
|
+
/** Names of the mixins a document defines at top level. */
|
|
608
|
+
export function definedMixins(doc) {
|
|
609
|
+
return doc.children.flatMap(c => (c.type === 'MixinDefinition' ? [c.name] : []));
|
|
610
|
+
}
|
|
611
|
+
/** Every mixin call in the document, first call of each name, in source order. */
|
|
612
|
+
function collectMixinCalls(doc) {
|
|
613
|
+
const calls = new Map();
|
|
614
|
+
function walkAll(nodes) {
|
|
615
|
+
for (const n of nodes)
|
|
616
|
+
walk(n);
|
|
617
|
+
}
|
|
618
|
+
function walk(n) {
|
|
619
|
+
switch (n.type) {
|
|
620
|
+
case 'MixinCall':
|
|
621
|
+
if (!calls.has(n.name))
|
|
622
|
+
calls.set(n.name, n);
|
|
623
|
+
walkAll(n.block);
|
|
624
|
+
break;
|
|
625
|
+
case 'Element':
|
|
626
|
+
walkAll(n.children);
|
|
627
|
+
break;
|
|
628
|
+
case 'Conditional':
|
|
629
|
+
walkAll(n.consequent);
|
|
630
|
+
for (const alt of n.alternates)
|
|
631
|
+
walkAll(alt.body);
|
|
632
|
+
if (n.fallback)
|
|
633
|
+
walkAll(n.fallback);
|
|
634
|
+
break;
|
|
635
|
+
case 'Iteration':
|
|
636
|
+
case 'Template':
|
|
637
|
+
case 'MixinDefinition':
|
|
638
|
+
walkAll(n.body);
|
|
639
|
+
break;
|
|
640
|
+
case 'Block':
|
|
641
|
+
walkAll(n.children);
|
|
642
|
+
break;
|
|
643
|
+
case 'Extends':
|
|
644
|
+
for (const block of n.blocks)
|
|
645
|
+
walkAll(block.children);
|
|
646
|
+
break;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
walkAll(doc.children);
|
|
650
|
+
return calls;
|
|
651
|
+
}
|
|
652
|
+
/**
|
|
653
|
+
* Value bindings a pass-through `import` statement introduces. A loose read,
|
|
654
|
+
* not a TS parse: it only needs to spot a mixin imported by hand.
|
|
655
|
+
*/
|
|
656
|
+
function importedBindings(statement) {
|
|
657
|
+
if (/^import\s+type\b/.test(statement))
|
|
658
|
+
return [];
|
|
659
|
+
const clause = /^import\s+([\s\S]*?)\s+from\b/.exec(statement)?.[1];
|
|
660
|
+
if (!clause)
|
|
661
|
+
return [];
|
|
662
|
+
const names = [];
|
|
663
|
+
const defaultName = /^([A-Za-z_$][\w$]*)/.exec(clause)?.[1];
|
|
664
|
+
if (defaultName)
|
|
665
|
+
names.push(defaultName);
|
|
666
|
+
const namespace = /\*\s*as\s+([A-Za-z_$][\w$]*)/.exec(clause)?.[1];
|
|
667
|
+
if (namespace)
|
|
668
|
+
names.push(namespace);
|
|
669
|
+
const named = /\{([^}]*)\}/.exec(clause)?.[1];
|
|
670
|
+
if (named) {
|
|
671
|
+
for (const spec of named.split(',')) {
|
|
672
|
+
const s = spec.trim();
|
|
673
|
+
if (s === '' || /^type\s/.test(s))
|
|
674
|
+
continue;
|
|
675
|
+
names.push(s.split(/\s+as\s+/).pop().trim());
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
return names;
|
|
679
|
+
}
|
|
680
|
+
/** Which called mixins each include must import, in first-call order. */
|
|
681
|
+
function resolveIncludes(doc, resolveInclude) {
|
|
682
|
+
const includes = doc.children.filter((c) => c.type === 'Include');
|
|
683
|
+
const picks = new Map(includes.map(inc => [inc, []]));
|
|
684
|
+
if (includes.length === 0)
|
|
685
|
+
return picks;
|
|
686
|
+
const provided = new Set(definedMixins(doc));
|
|
687
|
+
for (const child of doc.children) {
|
|
688
|
+
if (child.type === 'Import') {
|
|
689
|
+
for (const name of importedBindings(child.statement))
|
|
690
|
+
provided.add(name);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
const needed = [...collectMixinCalls(doc).values()].filter(call => !provided.has(call.name));
|
|
694
|
+
if (!resolveInclude) {
|
|
695
|
+
if (includes.length > 1 && needed.length > 0) {
|
|
696
|
+
const inc = includes[1];
|
|
697
|
+
throw new CodegenError(`Cannot tell which include defines mixin "${needed[0].name}" without an include resolver`, inc.loc.line, inc.loc.col);
|
|
698
|
+
}
|
|
699
|
+
picks.set(includes[0], needed.map(call => call.name));
|
|
700
|
+
return picks;
|
|
701
|
+
}
|
|
702
|
+
const available = new Map();
|
|
703
|
+
for (const inc of includes) {
|
|
704
|
+
try {
|
|
705
|
+
available.set(inc, new Set(resolveInclude(inc.path)));
|
|
706
|
+
}
|
|
707
|
+
catch (err) {
|
|
708
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
709
|
+
throw new CodegenError(`Cannot include ${inc.path}: ${msg}`, inc.loc.line, inc.loc.col);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
for (const call of needed) {
|
|
713
|
+
const inc = includes.find(i => available.get(i).has(call.name));
|
|
714
|
+
if (!inc) {
|
|
715
|
+
throw new CodegenError(`Mixin "${call.name}" is not defined in this file or any include`, call.loc.line, call.loc.col);
|
|
716
|
+
}
|
|
717
|
+
picks.get(inc).push(call.name);
|
|
718
|
+
}
|
|
719
|
+
return picks;
|
|
720
|
+
}
|
|
567
721
|
// --- Public API ---
|
|
568
722
|
/** Generate a TypeScript expression from a single AST node. */
|
|
569
723
|
export function generateExpr(node) {
|
|
@@ -574,14 +728,14 @@ export function generateExpr(node) {
|
|
|
574
728
|
return result.code;
|
|
575
729
|
}
|
|
576
730
|
/** Generate TypeScript source from a DocumentNode. */
|
|
577
|
-
export function generate(doc) {
|
|
731
|
+
export function generate(doc, options = {}) {
|
|
578
732
|
const gen = new CodeGenerator(true);
|
|
579
733
|
const parts = [];
|
|
580
734
|
// Scan for used runtime helpers
|
|
581
735
|
const helpers = scanUsedHelpers(doc);
|
|
582
736
|
// Emit runtime import (canonical order)
|
|
583
737
|
if (helpers.size > 0) {
|
|
584
|
-
const order = ['h', 'doctype', 'Fragment', 'raw'];
|
|
738
|
+
const order = ['h', 'doctype', 'Fragment', 'raw', 'mergeClass'];
|
|
585
739
|
const used = order.filter(h => helpers.has(h));
|
|
586
740
|
parts.push(chunk(`import { ${used.join(', ')} } from "@chilts/weave"`));
|
|
587
741
|
}
|
|
@@ -610,13 +764,11 @@ export function generate(doc) {
|
|
|
610
764
|
parts.push(chunk(statement, child.loc));
|
|
611
765
|
}
|
|
612
766
|
}
|
|
613
|
-
// Emit include imports
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
parts.push(result);
|
|
619
|
-
}
|
|
767
|
+
// Emit include imports: only the mixins this file calls, so an include
|
|
768
|
+
// that provides nothing used emits nothing.
|
|
769
|
+
for (const [include, names] of resolveIncludes(doc, options.resolveInclude)) {
|
|
770
|
+
if (names.length > 0)
|
|
771
|
+
parts.push(gen.genInclude(include, names));
|
|
620
772
|
}
|
|
621
773
|
// Emit extends imports (auto-import layout function)
|
|
622
774
|
for (const child of doc.children) {
|
|
@@ -641,16 +793,16 @@ export function generate(doc) {
|
|
|
641
793
|
return { code: final.code + '\n', mappings: final.mappings };
|
|
642
794
|
}
|
|
643
795
|
/** Full pipeline: source string → TypeScript source. */
|
|
644
|
-
export function compile(source) {
|
|
796
|
+
export function compile(source, options = {}) {
|
|
645
797
|
const tokens = lex(source);
|
|
646
798
|
const doc = parse(tokens);
|
|
647
|
-
return generate(doc).code;
|
|
799
|
+
return generate(doc, options).code;
|
|
648
800
|
}
|
|
649
801
|
/** Full pipeline with source map generation. */
|
|
650
802
|
export function compileWithSourceMap(source, opts) {
|
|
651
803
|
const tokens = lex(source);
|
|
652
804
|
const doc = parse(tokens);
|
|
653
|
-
const result = generate(doc);
|
|
805
|
+
const result = generate(doc, opts);
|
|
654
806
|
const sourceMap = buildSourceMap(result.mappings, opts.file, opts.sourceFile);
|
|
655
807
|
return { code: result.code, sourceMap };
|
|
656
808
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export { h, doctype, Fragment, raw, escape, SafeHtml } from './runtime/index.js';
|
|
1
|
+
export { h, doctype, Fragment, raw, mergeClass, escape, SafeHtml } from './runtime/index.js';
|
|
2
2
|
export type { Attrs } from './runtime/index.js';
|
|
3
|
-
export { compile, compileWithSourceMap, generate, generateExpr, CodegenError } from './codegen/index.js';
|
|
4
|
-
export type { GenerateResult, CodeChunk, Mapping, SourceMap } from './codegen/index.js';
|
|
3
|
+
export { compile, compileWithSourceMap, generate, generateExpr, definedMixins, CodegenError } from './codegen/index.js';
|
|
4
|
+
export type { GenerateResult, GenerateOptions, IncludeResolver, CodeChunk, Mapping, SourceMap } from './codegen/index.js';
|
|
5
5
|
export { lex, LexerError } from './lexer/index.js';
|
|
6
6
|
export type { Token, TokenType } from './lexer/index.js';
|
|
7
7
|
export { parse, ParserError } from './parser/index.js';
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Runtime
|
|
2
|
-
export { h, doctype, Fragment, raw, escape, SafeHtml } from './runtime/index.js';
|
|
2
|
+
export { h, doctype, Fragment, raw, mergeClass, escape, SafeHtml } from './runtime/index.js';
|
|
3
3
|
// Compiler
|
|
4
|
-
export { compile, compileWithSourceMap, generate, generateExpr, CodegenError } from './codegen/index.js';
|
|
4
|
+
export { compile, compileWithSourceMap, generate, generateExpr, definedMixins, CodegenError } from './codegen/index.js';
|
|
5
5
|
export { lex, LexerError } from './lexer/index.js';
|
|
6
6
|
export { parse, ParserError } from './parser/index.js';
|
|
7
7
|
//# sourceMappingURL=index.js.map
|