@matthesketh/utopia-compiler 0.0.4 → 0.1.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/dist/index.cjs CHANGED
@@ -381,6 +381,7 @@ var CodeGenerator = class {
381
381
  varCounter = 0;
382
382
  helpers = /* @__PURE__ */ new Set();
383
383
  scopeId;
384
+ deferredCallsStack = [];
384
385
  generate(ast) {
385
386
  const scope = /* @__PURE__ */ new Set();
386
387
  const rootElements = ast.filter(
@@ -399,7 +400,7 @@ var CodeGenerator = class {
399
400
  this.emit(`const ${fragVar} = createElement('div')`);
400
401
  if (this.scopeId) {
401
402
  this.helpers.add("setAttr");
402
- this.emit(`setAttr(${fragVar}, '${this.scopeId}', '')`);
403
+ this.emit(`setAttr(${fragVar}, '${escapeStr(this.scopeId)}', '')`);
403
404
  }
404
405
  for (const node of ast) {
405
406
  const childVar = this.genNode(node, scope);
@@ -452,7 +453,7 @@ ${fnBody}
452
453
  this.emit(`const ${elVar} = createElement('${node.tag}')`);
453
454
  if (this.scopeId) {
454
455
  this.helpers.add("setAttr");
455
- this.emit(`setAttr(${elVar}, '${this.scopeId}', '')`);
456
+ this.emit(`setAttr(${elVar}, '${escapeStr(this.scopeId)}', '')`);
456
457
  }
457
458
  for (const attr of node.attrs) {
458
459
  this.helpers.add("setAttr");
@@ -466,6 +467,7 @@ ${fnBody}
466
467
  if (dir.kind === "if" || dir.kind === "for") continue;
467
468
  this.genDirective(elVar, dir, scope);
468
469
  }
470
+ this.deferredCallsStack.push([]);
469
471
  for (const child of node.children) {
470
472
  const childVar = this.genNode(child, scope);
471
473
  if (childVar) {
@@ -473,6 +475,10 @@ ${fnBody}
473
475
  this.emit(`appendChild(${elVar}, ${childVar})`);
474
476
  }
475
477
  }
478
+ const deferred = this.deferredCallsStack.pop();
479
+ for (const line of deferred) {
480
+ this.emit(line);
481
+ }
476
482
  return elVar;
477
483
  }
478
484
  // ---- Text & Interpolation -----------------------------------------------
@@ -554,7 +560,7 @@ ${fnBody}
554
560
  }
555
561
  this.emit(` return ${innerVar}`);
556
562
  this.emit(`}`);
557
- this.emit(`createIf(${anchorVar}, () => Boolean(${condition}), ${trueFnVar})`);
563
+ this.emitOrDefer(`createIf(${anchorVar}, () => Boolean(${condition}), ${trueFnVar})`);
558
564
  return anchorVar;
559
565
  }
560
566
  // ---- Structural: u-for --------------------------------------------------
@@ -588,7 +594,7 @@ ${fnBody}
588
594
  }
589
595
  this.emit(` return ${innerVar}`);
590
596
  this.emit(`}`);
591
- this.emit(`createFor(${anchorVar}, () => ${listExpr}, ${renderFnVar})`);
597
+ this.emitOrDefer(`createFor(${anchorVar}, () => ${listExpr}, ${renderFnVar})`);
592
598
  return anchorVar;
593
599
  }
594
600
  // ---- Component generation -----------------------------------------------
@@ -597,14 +603,14 @@ ${fnBody}
597
603
  const propEntries = [];
598
604
  for (const a of node.attrs) {
599
605
  if (a.value !== null) {
600
- propEntries.push(`${a.name}: '${escapeStr(a.value)}'`);
606
+ propEntries.push(`'${escapeStr(a.name)}': '${escapeStr(a.value)}'`);
601
607
  } else {
602
- propEntries.push(`${a.name}: true`);
608
+ propEntries.push(`'${escapeStr(a.name)}': true`);
603
609
  }
604
610
  }
605
611
  for (const d of node.directives) {
606
612
  if (d.kind === "bind" && d.arg) {
607
- propEntries.push(`${d.arg}: ${this.resolveExpression(d.expression, scope)}`);
613
+ propEntries.push(`'${escapeStr(d.arg)}': ${this.resolveExpression(d.expression, scope)}`);
608
614
  }
609
615
  }
610
616
  const propsStr = propEntries.length > 0 ? `{ ${propEntries.join(", ")} }` : "{}";
@@ -631,9 +637,17 @@ ${fnBody}
631
637
  emit(line) {
632
638
  this.code.push(line);
633
639
  }
640
+ emitOrDefer(line) {
641
+ const stack = this.deferredCallsStack;
642
+ if (stack.length > 0) {
643
+ stack[stack.length - 1].push(line);
644
+ } else {
645
+ this.emit(line);
646
+ }
647
+ }
634
648
  };
635
649
  function isComponentTag(tag) {
636
- return /^[A-Z]/.test(tag);
650
+ return /^[A-Z][a-zA-Z0-9_$]*$/.test(tag);
637
651
  }
638
652
  function escapeStr(s) {
639
653
  return s.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
@@ -666,8 +680,28 @@ var ENTITY_MAP = {
666
680
  };
667
681
  function decodeEntities(text) {
668
682
  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));
683
+ if (dec) {
684
+ const code = parseInt(dec, 10);
685
+ if (code >= 0 && code <= 1114111) {
686
+ try {
687
+ return String.fromCodePoint(code);
688
+ } catch {
689
+ return match;
690
+ }
691
+ }
692
+ return match;
693
+ }
694
+ if (hex) {
695
+ const code = parseInt(hex, 16);
696
+ if (code >= 0 && code <= 1114111) {
697
+ try {
698
+ return String.fromCodePoint(code);
699
+ } catch {
700
+ return match;
701
+ }
702
+ }
703
+ return match;
704
+ }
671
705
  if (named) return ENTITY_MAP[`&${named};`] ?? match;
672
706
  return match;
673
707
  });
package/dist/index.js CHANGED
@@ -349,6 +349,7 @@ var CodeGenerator = class {
349
349
  varCounter = 0;
350
350
  helpers = /* @__PURE__ */ new Set();
351
351
  scopeId;
352
+ deferredCallsStack = [];
352
353
  generate(ast) {
353
354
  const scope = /* @__PURE__ */ new Set();
354
355
  const rootElements = ast.filter(
@@ -367,7 +368,7 @@ var CodeGenerator = class {
367
368
  this.emit(`const ${fragVar} = createElement('div')`);
368
369
  if (this.scopeId) {
369
370
  this.helpers.add("setAttr");
370
- this.emit(`setAttr(${fragVar}, '${this.scopeId}', '')`);
371
+ this.emit(`setAttr(${fragVar}, '${escapeStr(this.scopeId)}', '')`);
371
372
  }
372
373
  for (const node of ast) {
373
374
  const childVar = this.genNode(node, scope);
@@ -420,7 +421,7 @@ ${fnBody}
420
421
  this.emit(`const ${elVar} = createElement('${node.tag}')`);
421
422
  if (this.scopeId) {
422
423
  this.helpers.add("setAttr");
423
- this.emit(`setAttr(${elVar}, '${this.scopeId}', '')`);
424
+ this.emit(`setAttr(${elVar}, '${escapeStr(this.scopeId)}', '')`);
424
425
  }
425
426
  for (const attr of node.attrs) {
426
427
  this.helpers.add("setAttr");
@@ -434,6 +435,7 @@ ${fnBody}
434
435
  if (dir.kind === "if" || dir.kind === "for") continue;
435
436
  this.genDirective(elVar, dir, scope);
436
437
  }
438
+ this.deferredCallsStack.push([]);
437
439
  for (const child of node.children) {
438
440
  const childVar = this.genNode(child, scope);
439
441
  if (childVar) {
@@ -441,6 +443,10 @@ ${fnBody}
441
443
  this.emit(`appendChild(${elVar}, ${childVar})`);
442
444
  }
443
445
  }
446
+ const deferred = this.deferredCallsStack.pop();
447
+ for (const line of deferred) {
448
+ this.emit(line);
449
+ }
444
450
  return elVar;
445
451
  }
446
452
  // ---- Text & Interpolation -----------------------------------------------
@@ -522,7 +528,7 @@ ${fnBody}
522
528
  }
523
529
  this.emit(` return ${innerVar}`);
524
530
  this.emit(`}`);
525
- this.emit(`createIf(${anchorVar}, () => Boolean(${condition}), ${trueFnVar})`);
531
+ this.emitOrDefer(`createIf(${anchorVar}, () => Boolean(${condition}), ${trueFnVar})`);
526
532
  return anchorVar;
527
533
  }
528
534
  // ---- Structural: u-for --------------------------------------------------
@@ -556,7 +562,7 @@ ${fnBody}
556
562
  }
557
563
  this.emit(` return ${innerVar}`);
558
564
  this.emit(`}`);
559
- this.emit(`createFor(${anchorVar}, () => ${listExpr}, ${renderFnVar})`);
565
+ this.emitOrDefer(`createFor(${anchorVar}, () => ${listExpr}, ${renderFnVar})`);
560
566
  return anchorVar;
561
567
  }
562
568
  // ---- Component generation -----------------------------------------------
@@ -565,14 +571,14 @@ ${fnBody}
565
571
  const propEntries = [];
566
572
  for (const a of node.attrs) {
567
573
  if (a.value !== null) {
568
- propEntries.push(`${a.name}: '${escapeStr(a.value)}'`);
574
+ propEntries.push(`'${escapeStr(a.name)}': '${escapeStr(a.value)}'`);
569
575
  } else {
570
- propEntries.push(`${a.name}: true`);
576
+ propEntries.push(`'${escapeStr(a.name)}': true`);
571
577
  }
572
578
  }
573
579
  for (const d of node.directives) {
574
580
  if (d.kind === "bind" && d.arg) {
575
- propEntries.push(`${d.arg}: ${this.resolveExpression(d.expression, scope)}`);
581
+ propEntries.push(`'${escapeStr(d.arg)}': ${this.resolveExpression(d.expression, scope)}`);
576
582
  }
577
583
  }
578
584
  const propsStr = propEntries.length > 0 ? `{ ${propEntries.join(", ")} }` : "{}";
@@ -599,9 +605,17 @@ ${fnBody}
599
605
  emit(line) {
600
606
  this.code.push(line);
601
607
  }
608
+ emitOrDefer(line) {
609
+ const stack = this.deferredCallsStack;
610
+ if (stack.length > 0) {
611
+ stack[stack.length - 1].push(line);
612
+ } else {
613
+ this.emit(line);
614
+ }
615
+ }
602
616
  };
603
617
  function isComponentTag(tag) {
604
- return /^[A-Z]/.test(tag);
618
+ return /^[A-Z][a-zA-Z0-9_$]*$/.test(tag);
605
619
  }
606
620
  function escapeStr(s) {
607
621
  return s.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
@@ -634,8 +648,28 @@ var ENTITY_MAP = {
634
648
  };
635
649
  function decodeEntities(text) {
636
650
  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));
651
+ if (dec) {
652
+ const code = parseInt(dec, 10);
653
+ if (code >= 0 && code <= 1114111) {
654
+ try {
655
+ return String.fromCodePoint(code);
656
+ } catch {
657
+ return match;
658
+ }
659
+ }
660
+ return match;
661
+ }
662
+ if (hex) {
663
+ const code = parseInt(hex, 16);
664
+ if (code >= 0 && code <= 1114111) {
665
+ try {
666
+ return String.fromCodePoint(code);
667
+ } catch {
668
+ return match;
669
+ }
670
+ }
671
+ return match;
672
+ }
639
673
  if (named) return ENTITY_MAP[`&${named};`] ?? match;
640
674
  return match;
641
675
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@matthesketh/utopia-compiler",
3
- "version": "0.0.4",
3
+ "version": "0.1.0",
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.4"
42
+ "@matthesketh/utopia-core": "0.1.0"
43
43
  },
44
44
  "scripts": {
45
45
  "build": "tsup src/index.ts --format esm,cjs --dts",