@matthesketh/utopia-compiler 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +49 -23
- package/dist/index.js +49 -23
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -415,7 +415,7 @@ var CodeGenerator = class {
|
|
|
415
415
|
|
|
416
416
|
` : "";
|
|
417
417
|
const fnBody = this.code.map((l) => ` ${l}`).join("\n");
|
|
418
|
-
const moduleCode = `${importLine}
|
|
418
|
+
const moduleCode = `${importLine}function __render() {
|
|
419
419
|
${fnBody}
|
|
420
420
|
}
|
|
421
421
|
`;
|
|
@@ -480,7 +480,8 @@ ${fnBody}
|
|
|
480
480
|
if (!node.content) return null;
|
|
481
481
|
this.helpers.add("createTextNode");
|
|
482
482
|
const v = this.freshVar();
|
|
483
|
-
|
|
483
|
+
const decoded = decodeEntities(node.content);
|
|
484
|
+
this.emit(`const ${v} = createTextNode(${JSON.stringify(decoded)})`);
|
|
484
485
|
return v;
|
|
485
486
|
}
|
|
486
487
|
genInterpolation(node, scope) {
|
|
@@ -562,12 +563,13 @@ ${fnBody}
|
|
|
562
563
|
const anchorVar = this.freshVar();
|
|
563
564
|
this.helpers.add("createComment");
|
|
564
565
|
this.emit(`const ${anchorVar} = createComment('u-for')`);
|
|
565
|
-
const forMatch = dir.expression.match(/^\s*(\w+)\s+in\s+(.+)$/);
|
|
566
|
+
const forMatch = dir.expression.match(/^\s*(?:\(\s*(\w+)\s*(?:,\s*(\w+)\s*)?\)|(\w+))\s+in\s+(.+)$/);
|
|
566
567
|
if (!forMatch) {
|
|
567
568
|
throw new Error(`Invalid u-for expression: "${dir.expression}"`);
|
|
568
569
|
}
|
|
569
|
-
const itemName = forMatch[1];
|
|
570
|
-
const
|
|
570
|
+
const itemName = forMatch[1] ?? forMatch[3];
|
|
571
|
+
const indexName = forMatch[2] ?? "_index";
|
|
572
|
+
const listExpr = this.resolveExpression(forMatch[4].trim(), scope);
|
|
571
573
|
const innerScope = new Set(scope);
|
|
572
574
|
innerScope.add(itemName);
|
|
573
575
|
const strippedNode = {
|
|
@@ -580,7 +582,7 @@ ${fnBody}
|
|
|
580
582
|
const innerLines = [...this.code];
|
|
581
583
|
this.code = savedCode;
|
|
582
584
|
const renderFnVar = this.freshVar();
|
|
583
|
-
this.emit(`const ${renderFnVar} = (${itemName},
|
|
585
|
+
this.emit(`const ${renderFnVar} = (${itemName}, ${indexName}) => {`);
|
|
584
586
|
for (const line of innerLines) {
|
|
585
587
|
this.emit(` ${line}`);
|
|
586
588
|
}
|
|
@@ -606,32 +608,21 @@ ${fnBody}
|
|
|
606
608
|
}
|
|
607
609
|
}
|
|
608
610
|
const propsStr = propEntries.length > 0 ? `{ ${propEntries.join(", ")} }` : "{}";
|
|
609
|
-
this.
|
|
611
|
+
this.helpers.add("createComponent");
|
|
612
|
+
this.emit(`const ${compVar} = createComponent(${node.tag}, ${propsStr})`);
|
|
610
613
|
return compVar;
|
|
611
614
|
}
|
|
612
615
|
// ---- Expression resolution ----------------------------------------------
|
|
613
616
|
/**
|
|
614
617
|
* Resolve a template expression to a JS expression.
|
|
615
618
|
*
|
|
616
|
-
*
|
|
617
|
-
*
|
|
618
|
-
* `_ctx.` to access the component context.
|
|
619
|
+
* All identifiers are emitted as bare references — user script variables
|
|
620
|
+
* live at module scope and are accessible via closure.
|
|
619
621
|
*/
|
|
620
|
-
resolveExpression(expr,
|
|
622
|
+
resolveExpression(expr, _scope) {
|
|
621
623
|
const trimmed = expr.trim();
|
|
622
624
|
if (!trimmed) return "''";
|
|
623
|
-
|
|
624
|
-
if (!leadIdMatch) {
|
|
625
|
-
return trimmed;
|
|
626
|
-
}
|
|
627
|
-
const leadId = leadIdMatch[1];
|
|
628
|
-
if (scope.has(leadId)) {
|
|
629
|
-
return trimmed;
|
|
630
|
-
}
|
|
631
|
-
if (trimmed.includes("=>")) {
|
|
632
|
-
return trimmed;
|
|
633
|
-
}
|
|
634
|
-
return `_ctx.${trimmed}`;
|
|
625
|
+
return trimmed;
|
|
635
626
|
}
|
|
636
627
|
// ---- Utilities ----------------------------------------------------------
|
|
637
628
|
freshVar() {
|
|
@@ -647,6 +638,40 @@ function isComponentTag(tag) {
|
|
|
647
638
|
function escapeStr(s) {
|
|
648
639
|
return s.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
649
640
|
}
|
|
641
|
+
var ENTITY_MAP = {
|
|
642
|
+
"&": "&",
|
|
643
|
+
"<": "<",
|
|
644
|
+
">": ">",
|
|
645
|
+
""": '"',
|
|
646
|
+
"'": "'",
|
|
647
|
+
"'": "'",
|
|
648
|
+
" ": "\xA0",
|
|
649
|
+
"—": "\u2014",
|
|
650
|
+
"–": "\u2013",
|
|
651
|
+
"‘": "\u2018",
|
|
652
|
+
"’": "\u2019",
|
|
653
|
+
"“": "\u201C",
|
|
654
|
+
"”": "\u201D",
|
|
655
|
+
"•": "\u2022",
|
|
656
|
+
"…": "\u2026",
|
|
657
|
+
"©": "\xA9",
|
|
658
|
+
"®": "\xAE",
|
|
659
|
+
"™": "\u2122",
|
|
660
|
+
"→": "\u2192",
|
|
661
|
+
"←": "\u2190",
|
|
662
|
+
"↑": "\u2191",
|
|
663
|
+
"↓": "\u2193",
|
|
664
|
+
"×": "\xD7",
|
|
665
|
+
"÷": "\xF7"
|
|
666
|
+
};
|
|
667
|
+
function decodeEntities(text) {
|
|
668
|
+
return text.replace(/&(?:#(\d+)|#x([0-9a-fA-F]+)|(\w+));/g, (match, dec, hex, named) => {
|
|
669
|
+
if (dec) return String.fromCodePoint(parseInt(dec, 10));
|
|
670
|
+
if (hex) return String.fromCodePoint(parseInt(hex, 16));
|
|
671
|
+
if (named) return ENTITY_MAP[`&${named};`] ?? match;
|
|
672
|
+
return match;
|
|
673
|
+
});
|
|
674
|
+
}
|
|
650
675
|
function generate(ast, options) {
|
|
651
676
|
const gen = new CodeGenerator(options);
|
|
652
677
|
return gen.generate(ast);
|
|
@@ -810,6 +835,7 @@ function compile(source, options = {}) {
|
|
|
810
835
|
if (body) {
|
|
811
836
|
parts.push(body);
|
|
812
837
|
}
|
|
838
|
+
parts.push("export default { render: __render }");
|
|
813
839
|
const code = parts.join("\n\n") + "\n";
|
|
814
840
|
return { code, css };
|
|
815
841
|
}
|
package/dist/index.js
CHANGED
|
@@ -383,7 +383,7 @@ var CodeGenerator = class {
|
|
|
383
383
|
|
|
384
384
|
` : "";
|
|
385
385
|
const fnBody = this.code.map((l) => ` ${l}`).join("\n");
|
|
386
|
-
const moduleCode = `${importLine}
|
|
386
|
+
const moduleCode = `${importLine}function __render() {
|
|
387
387
|
${fnBody}
|
|
388
388
|
}
|
|
389
389
|
`;
|
|
@@ -448,7 +448,8 @@ ${fnBody}
|
|
|
448
448
|
if (!node.content) return null;
|
|
449
449
|
this.helpers.add("createTextNode");
|
|
450
450
|
const v = this.freshVar();
|
|
451
|
-
|
|
451
|
+
const decoded = decodeEntities(node.content);
|
|
452
|
+
this.emit(`const ${v} = createTextNode(${JSON.stringify(decoded)})`);
|
|
452
453
|
return v;
|
|
453
454
|
}
|
|
454
455
|
genInterpolation(node, scope) {
|
|
@@ -530,12 +531,13 @@ ${fnBody}
|
|
|
530
531
|
const anchorVar = this.freshVar();
|
|
531
532
|
this.helpers.add("createComment");
|
|
532
533
|
this.emit(`const ${anchorVar} = createComment('u-for')`);
|
|
533
|
-
const forMatch = dir.expression.match(/^\s*(\w+)\s+in\s+(.+)$/);
|
|
534
|
+
const forMatch = dir.expression.match(/^\s*(?:\(\s*(\w+)\s*(?:,\s*(\w+)\s*)?\)|(\w+))\s+in\s+(.+)$/);
|
|
534
535
|
if (!forMatch) {
|
|
535
536
|
throw new Error(`Invalid u-for expression: "${dir.expression}"`);
|
|
536
537
|
}
|
|
537
|
-
const itemName = forMatch[1];
|
|
538
|
-
const
|
|
538
|
+
const itemName = forMatch[1] ?? forMatch[3];
|
|
539
|
+
const indexName = forMatch[2] ?? "_index";
|
|
540
|
+
const listExpr = this.resolveExpression(forMatch[4].trim(), scope);
|
|
539
541
|
const innerScope = new Set(scope);
|
|
540
542
|
innerScope.add(itemName);
|
|
541
543
|
const strippedNode = {
|
|
@@ -548,7 +550,7 @@ ${fnBody}
|
|
|
548
550
|
const innerLines = [...this.code];
|
|
549
551
|
this.code = savedCode;
|
|
550
552
|
const renderFnVar = this.freshVar();
|
|
551
|
-
this.emit(`const ${renderFnVar} = (${itemName},
|
|
553
|
+
this.emit(`const ${renderFnVar} = (${itemName}, ${indexName}) => {`);
|
|
552
554
|
for (const line of innerLines) {
|
|
553
555
|
this.emit(` ${line}`);
|
|
554
556
|
}
|
|
@@ -574,32 +576,21 @@ ${fnBody}
|
|
|
574
576
|
}
|
|
575
577
|
}
|
|
576
578
|
const propsStr = propEntries.length > 0 ? `{ ${propEntries.join(", ")} }` : "{}";
|
|
577
|
-
this.
|
|
579
|
+
this.helpers.add("createComponent");
|
|
580
|
+
this.emit(`const ${compVar} = createComponent(${node.tag}, ${propsStr})`);
|
|
578
581
|
return compVar;
|
|
579
582
|
}
|
|
580
583
|
// ---- Expression resolution ----------------------------------------------
|
|
581
584
|
/**
|
|
582
585
|
* Resolve a template expression to a JS expression.
|
|
583
586
|
*
|
|
584
|
-
*
|
|
585
|
-
*
|
|
586
|
-
* `_ctx.` to access the component context.
|
|
587
|
+
* All identifiers are emitted as bare references — user script variables
|
|
588
|
+
* live at module scope and are accessible via closure.
|
|
587
589
|
*/
|
|
588
|
-
resolveExpression(expr,
|
|
590
|
+
resolveExpression(expr, _scope) {
|
|
589
591
|
const trimmed = expr.trim();
|
|
590
592
|
if (!trimmed) return "''";
|
|
591
|
-
|
|
592
|
-
if (!leadIdMatch) {
|
|
593
|
-
return trimmed;
|
|
594
|
-
}
|
|
595
|
-
const leadId = leadIdMatch[1];
|
|
596
|
-
if (scope.has(leadId)) {
|
|
597
|
-
return trimmed;
|
|
598
|
-
}
|
|
599
|
-
if (trimmed.includes("=>")) {
|
|
600
|
-
return trimmed;
|
|
601
|
-
}
|
|
602
|
-
return `_ctx.${trimmed}`;
|
|
593
|
+
return trimmed;
|
|
603
594
|
}
|
|
604
595
|
// ---- Utilities ----------------------------------------------------------
|
|
605
596
|
freshVar() {
|
|
@@ -615,6 +606,40 @@ function isComponentTag(tag) {
|
|
|
615
606
|
function escapeStr(s) {
|
|
616
607
|
return s.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
617
608
|
}
|
|
609
|
+
var ENTITY_MAP = {
|
|
610
|
+
"&": "&",
|
|
611
|
+
"<": "<",
|
|
612
|
+
">": ">",
|
|
613
|
+
""": '"',
|
|
614
|
+
"'": "'",
|
|
615
|
+
"'": "'",
|
|
616
|
+
" ": "\xA0",
|
|
617
|
+
"—": "\u2014",
|
|
618
|
+
"–": "\u2013",
|
|
619
|
+
"‘": "\u2018",
|
|
620
|
+
"’": "\u2019",
|
|
621
|
+
"“": "\u201C",
|
|
622
|
+
"”": "\u201D",
|
|
623
|
+
"•": "\u2022",
|
|
624
|
+
"…": "\u2026",
|
|
625
|
+
"©": "\xA9",
|
|
626
|
+
"®": "\xAE",
|
|
627
|
+
"™": "\u2122",
|
|
628
|
+
"→": "\u2192",
|
|
629
|
+
"←": "\u2190",
|
|
630
|
+
"↑": "\u2191",
|
|
631
|
+
"↓": "\u2193",
|
|
632
|
+
"×": "\xD7",
|
|
633
|
+
"÷": "\xF7"
|
|
634
|
+
};
|
|
635
|
+
function decodeEntities(text) {
|
|
636
|
+
return text.replace(/&(?:#(\d+)|#x([0-9a-fA-F]+)|(\w+));/g, (match, dec, hex, named) => {
|
|
637
|
+
if (dec) return String.fromCodePoint(parseInt(dec, 10));
|
|
638
|
+
if (hex) return String.fromCodePoint(parseInt(hex, 16));
|
|
639
|
+
if (named) return ENTITY_MAP[`&${named};`] ?? match;
|
|
640
|
+
return match;
|
|
641
|
+
});
|
|
642
|
+
}
|
|
618
643
|
function generate(ast, options) {
|
|
619
644
|
const gen = new CodeGenerator(options);
|
|
620
645
|
return gen.generate(ast);
|
|
@@ -778,6 +803,7 @@ function compile(source, options = {}) {
|
|
|
778
803
|
if (body) {
|
|
779
804
|
parts.push(body);
|
|
780
805
|
}
|
|
806
|
+
parts.push("export default { render: __render }");
|
|
781
807
|
const code = parts.join("\n\n") + "\n";
|
|
782
808
|
return { code, css };
|
|
783
809
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@matthesketh/utopia-compiler",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "Compiler for .utopia single-file components",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"dist"
|
|
40
40
|
],
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@matthesketh/utopia-core": "0.0.
|
|
42
|
+
"@matthesketh/utopia-core": "0.0.3"
|
|
43
43
|
},
|
|
44
44
|
"scripts": {
|
|
45
45
|
"build": "tsup src/index.ts --format esm,cjs --dts",
|