0x-lang 0.1.16 → 0.1.18

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/compiler.js CHANGED
@@ -42,7 +42,7 @@ export function compile(source, options) {
42
42
  }
43
43
  // Post-process: strip source map comments and V3 source map if disabled
44
44
  if (options.sourceMap === false) {
45
- result = { ...result, code: result.code.replace(/\{\/\* 0x:L\d+ \*\/\}/g, '').replace(/<!-- 0x:L\d+ -->/g, ''), sourceMap: undefined };
45
+ result = { ...result, code: result.code.replace(/\{\/\* 0x:L\d+ \*\/\}/g, '').replace(/<!-- 0x:L\d+ -->/g, '').replace(/\/\/ 0x:L\d+\n\s*/g, ''), sourceMap: undefined };
46
46
  }
47
47
  else if (result.sourceMap) {
48
48
  // Append inline sourceMappingURL as base64 data URL
@@ -1 +1 @@
1
- {"version":3,"file":"compiler.js","sourceRoot":"","sources":["../src/compiler.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"compiler.js","sourceRoot":"","sources":["../src/compiler.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  // 0x → React Code Generator
2
- import { SIZE_MAP, unquote, capitalize, parseGradient, addPx } from './shared.js';
2
+ import { SIZE_MAP, unquote, capitalize, parseGradient, addPx, getPassthroughProps, KNOWN_LAYOUT_PROPS, KNOWN_TEXT_PROPS, KNOWN_BUTTON_PROPS, KNOWN_INPUT_PROPS, KNOWN_IMAGE_PROPS, KNOWN_LINK_PROPS, KNOWN_TOGGLE_PROPS, KNOWN_SELECT_PROPS } from './shared.js';
3
3
  import { SourceMapBuilder } from './source-map.js';
4
4
  export function ctx() {
5
5
  return {
@@ -369,7 +369,8 @@ function genDerived(node, c) {
369
369
  c.readOnly = prevReadOnly;
370
370
  const { deps, warning } = extractDepsWithWarning(node.expression, c);
371
371
  const warnComment = warning ? ` ${warning}` : '';
372
- return `${warnComment ? warnComment + '\n ' : ''}const ${node.name} = useMemo(() => ${expr}, [${deps.join(', ')}]);`;
372
+ const sc = node.loc?.line ? `// 0x:L${node.loc.line}\n ` : '';
373
+ return `${sc}${warnComment ? warnComment + '\n ' : ''}const ${node.name} = useMemo(() => ${expr}, [${deps.join(', ')}]);`;
373
374
  }
374
375
  function genCheck(node, c) {
375
376
  const cond = genExpr(node.condition, c);
@@ -400,32 +401,36 @@ function genFunction(node, c) {
400
401
  // requires
401
402
  const requireChecks = node.requires.map(r => `if (!(${genExpr(r, c)})) throw new Error('Precondition failed');`).join('\n ');
402
403
  const allBody = [requireChecks, body].filter(Boolean).join('\n ');
403
- return `const ${node.name} = ${asyncKw}(${params}) => {\n ${allBody}\n };`;
404
+ const sc = node.loc?.line ? `// 0x:L${node.loc.line}\n ` : '';
405
+ return `${sc}const ${node.name} = ${asyncKw}(${params}) => {\n ${allBody}\n };`;
404
406
  }
405
407
  // ── Lifecycle ───────────────────────────────────────
406
408
  function genOnMount(node, c) {
407
409
  c.imports.add('useEffect');
408
410
  const body = node.body.map(s => genStatement(s, c)).join('\n ');
409
411
  const hasAwait = bodyContainsAwait(node.body);
412
+ const sc = node.loc?.line ? `// 0x:L${node.loc.line}\n ` : '';
410
413
  if (hasAwait) {
411
- return `useEffect(() => {\n (async () => {\n ${body}\n })();\n }, []);`;
414
+ return `${sc}useEffect(() => {\n (async () => {\n ${body}\n })();\n }, []);`;
412
415
  }
413
- return `useEffect(() => {\n ${body}\n }, []);`;
416
+ return `${sc}useEffect(() => {\n ${body}\n }, []);`;
414
417
  }
415
418
  function genOnDestroy(node, c) {
416
419
  c.imports.add('useEffect');
417
420
  const body = node.body.map(s => genStatement(s, c)).join('\n ');
418
- return `useEffect(() => {\n return () => {\n ${body}\n };\n }, []);`;
421
+ const sc = node.loc?.line ? `// 0x:L${node.loc.line}\n ` : '';
422
+ return `${sc}useEffect(() => {\n return () => {\n ${body}\n };\n }, []);`;
419
423
  }
420
424
  function genWatch(node, c) {
421
425
  c.imports.add('useEffect');
422
426
  const body = node.body.map(s => genStatement(s, c)).join('\n ');
423
427
  const vars = (node.variables || [node.variable]).join(', ');
424
428
  const hasAwait = bodyContainsAwait(node.body);
429
+ const sc = node.loc?.line ? `// 0x:L${node.loc.line}\n ` : '';
425
430
  if (hasAwait) {
426
- return `useEffect(() => {\n (async () => {\n ${body}\n })();\n }, [${vars}]);`;
431
+ return `${sc}useEffect(() => {\n (async () => {\n ${body}\n })();\n }, [${vars}]);`;
427
432
  }
428
- return `useEffect(() => {\n ${body}\n }, [${vars}]);`;
433
+ return `${sc}useEffect(() => {\n ${body}\n }, [${vars}]);`;
429
434
  }
430
435
  // ── UI Nodes ────────────────────────────────────────
431
436
  function srcComment(node) {
@@ -522,10 +527,14 @@ function genLayout(node, c) {
522
527
  style['flexDirection'] = node.direction === 'row' ? 'row' : 'column';
523
528
  }
524
529
  // Process layout props
530
+ let className = null;
525
531
  for (const [key, val] of Object.entries(node.props)) {
526
532
  const v = genExpr(val, c);
527
533
  const isDynamic = val.kind === 'braced' || val.kind === 'ternary' || val.kind === 'binary' || val.kind === 'member' || val.kind === 'call';
528
534
  switch (key) {
535
+ case 'class':
536
+ className = unquote(v);
537
+ break;
529
538
  case 'gap':
530
539
  style['gap'] = addPx(v);
531
540
  break;
@@ -570,9 +579,17 @@ function genLayout(node, c) {
570
579
  break;
571
580
  case 'scroll': {
572
581
  const sv = unquote(v);
573
- style['overflow' + (sv === 'y' ? 'Y' : 'X')] = 'auto';
582
+ if (sv === 'y')
583
+ style['overflowY'] = 'auto';
584
+ else if (sv === 'x')
585
+ style['overflowX'] = 'auto';
586
+ else
587
+ style['overflow'] = 'auto';
574
588
  break;
575
589
  }
590
+ case 'wrap':
591
+ style['flexWrap'] = 'wrap';
592
+ break;
576
593
  case 'radius':
577
594
  style['borderRadius'] = addPx(v);
578
595
  break;
@@ -600,17 +617,23 @@ function genLayout(node, c) {
600
617
  }
601
618
  }
602
619
  const styleStr = genStyleObj(style, dynamicKeys);
620
+ const classAttr = className ? ` className="${className}"` : '';
621
+ const extra = getPassthroughProps(node.props, KNOWN_LAYOUT_PROPS, e => genExpr(e, c), 'react');
603
622
  const children = node.children.map(ch => genUINode(ch, c)).join('\n');
604
623
  const sc = srcComment(node);
605
- return `${sc}<div style={${styleStr}}>\n${children}\n</div>`;
624
+ return `${sc}<div${classAttr} style={${styleStr}}${extra}>\n${children}\n</div>`;
606
625
  }
607
626
  function genText(node, c) {
608
627
  const style = {};
609
628
  const dynamicKeys = new Set();
629
+ let className = null;
610
630
  for (const [key, val] of Object.entries(node.props)) {
611
631
  const v = genExpr(val, c);
612
632
  const isDynamic = val.kind === 'braced' || val.kind === 'ternary' || val.kind === 'binary' || val.kind === 'member' || val.kind === 'call';
613
633
  switch (key) {
634
+ case 'class':
635
+ className = unquote(v);
636
+ break;
614
637
  case 'size': {
615
638
  const uv = unquote(v);
616
639
  style['fontSize'] = SIZE_MAP[uv] || `${uv}px`;
@@ -658,19 +681,21 @@ function genText(node, c) {
658
681
  }
659
682
  }
660
683
  const content = genTextContent(node.content, c);
684
+ const classAttr = className ? ` className="${className}"` : '';
661
685
  const styleStr = Object.keys(style).length > 0 ? ` style={${genStyleObj(style, dynamicKeys)}}` : '';
686
+ const extra = getPassthroughProps(node.props, KNOWN_TEXT_PROPS, e => genExpr(e, c), 'react');
662
687
  // Badge prop: render a badge indicator next to content
663
688
  const badgeExpr = node.props['badge'];
664
689
  const tooltipExpr = node.props['tooltip'];
665
690
  const sc = srcComment(node);
666
- let result = `${sc}<span${styleStr}>${content}</span>`;
691
+ let result = `${sc}<span${classAttr}${styleStr}${extra}>${content}</span>`;
667
692
  if (badgeExpr) {
668
693
  const badge = genExpr(badgeExpr, c);
669
- result = `<span style={{ position: 'relative', display: 'inline-flex', alignItems: 'center' }}>\n<span${styleStr}>${content}</span>\n<span style={{ marginLeft: '6px', padding: '2px 6px', fontSize: '12px', fontWeight: 'bold', borderRadius: '9999px', backgroundColor: '#ef4444', color: '#fff', minWidth: '20px', textAlign: 'center' }}>{${badge}}</span>\n</span>`;
694
+ result = `<span${classAttr} style={{ position: 'relative', display: 'inline-flex', alignItems: 'center' }}>\n<span${styleStr}>${content}</span>\n<span style={{ marginLeft: '6px', padding: '2px 6px', fontSize: '12px', fontWeight: 'bold', borderRadius: '9999px', backgroundColor: '#ef4444', color: '#fff', minWidth: '20px', textAlign: 'center' }}>{${badge}}</span>\n</span>`;
670
695
  }
671
696
  if (tooltipExpr) {
672
697
  const tooltip = genExpr(tooltipExpr, c);
673
- result = `<span title={${tooltip}}>${badgeExpr ? result.replace(/^<span/, '<span') : `<span${styleStr}>${content}</span>`}</span>`;
698
+ result = `<span${classAttr} title={${tooltip}}>${badgeExpr ? result.replace(/^<span[^>]*>/, '<span>') : `<span${styleStr}>${content}</span>`}</span>`;
674
699
  }
675
700
  return result;
676
701
  }
@@ -678,24 +703,37 @@ function genButton(node, c) {
678
703
  const label = genTextContent(node.label, c);
679
704
  const actionCode = genActionExpr(node.action, c);
680
705
  const styleProps = [];
706
+ let className = null;
681
707
  for (const [key, val] of Object.entries(node.props)) {
682
708
  const v = genExpr(val, c);
683
709
  switch (key) {
684
- case 'style':
685
- styleProps.push(`className="${v}"`);
710
+ case 'class':
711
+ className = unquote(v);
712
+ break;
713
+ case 'style': {
714
+ const sv = unquote(v);
715
+ if (sv === 'primary')
716
+ styleProps.push('style={{ backgroundColor: "#3b82f6", color: "white", border: "none", padding: "8px 16px", borderRadius: "6px", cursor: "pointer" }}');
717
+ else if (sv === 'danger')
718
+ styleProps.push('style={{ backgroundColor: "#ef4444", color: "white", border: "none", padding: "8px 16px", borderRadius: "6px", cursor: "pointer" }}');
686
719
  break;
720
+ }
687
721
  case 'disabled':
688
722
  styleProps.push(`disabled={${v}}`);
689
723
  break;
690
724
  case 'size': /* handled in style */ break;
691
725
  }
692
726
  }
727
+ if (className)
728
+ styleProps.push(`className="${className}"`);
729
+ const extra = getPassthroughProps(node.props, KNOWN_BUTTON_PROPS, e => genExpr(e, c), 'react');
693
730
  const propsStr = styleProps.join(' ');
694
731
  const sc = srcComment(node);
695
- return `${sc}<button onClick={() => ${actionCode}}${propsStr ? ' ' + propsStr : ''}>${label}</button>`;
732
+ return `${sc}<button onClick={() => ${actionCode}}${propsStr ? ' ' + propsStr : ''}${extra}>${label}</button>`;
696
733
  }
697
734
  function genInput(node, c) {
698
735
  const setter = 'set' + capitalize(node.binding);
736
+ let className = null;
699
737
  const props = [
700
738
  `value={${node.binding}}`,
701
739
  `onChange={e => ${setter}(e.target.value)}`,
@@ -703,6 +741,9 @@ function genInput(node, c) {
703
741
  for (const [key, val] of Object.entries(node.props)) {
704
742
  const v = genExpr(val, c);
705
743
  switch (key) {
744
+ case 'class':
745
+ className = unquote(v);
746
+ break;
706
747
  case 'placeholder':
707
748
  props.push(`placeholder=${quoteJsx(v)}`);
708
749
  break;
@@ -718,16 +759,23 @@ function genInput(node, c) {
718
759
  props.push(`onKeyPress={e => ${handler}(e.key)}`);
719
760
  }
720
761
  }
762
+ if (className)
763
+ props.push(`className="${className}"`);
764
+ const extra = getPassthroughProps(node.props, KNOWN_INPUT_PROPS, e => genExpr(e, c), 'react');
721
765
  const sc = srcComment(node);
722
- return `${sc}<input ${props.join(' ')} />`;
766
+ return `${sc}<input ${props.join(' ')}${extra} />`;
723
767
  }
724
768
  function genImage(node, c) {
725
769
  const src = genExpr(node.src, c);
726
770
  const props = [`src={${src}}`];
727
771
  const style = {};
772
+ let className = null;
728
773
  for (const [key, val] of Object.entries(node.props)) {
729
774
  const v = genExpr(val, c);
730
775
  switch (key) {
776
+ case 'class':
777
+ className = unquote(v);
778
+ break;
731
779
  case 'width':
732
780
  props.push(`width="${v}"`);
733
781
  break;
@@ -751,16 +799,26 @@ function genImage(node, c) {
751
799
  }
752
800
  }
753
801
  }
802
+ if (className)
803
+ props.push(`className="${className}"`);
754
804
  if (Object.keys(style).length > 0) {
755
805
  const entries = Object.entries(style).map(([k, v]) => `${k}: ${v}`).join(', ');
756
806
  props.push(`style={{ ${entries} }}`);
757
807
  }
758
- return `<img ${props.join(' ')} />`;
808
+ const extra = getPassthroughProps(node.props, KNOWN_IMAGE_PROPS, e => genExpr(e, c), 'react');
809
+ return `<img ${props.join(' ')}${extra} />`;
759
810
  }
760
811
  function genLink(node, c) {
761
812
  const label = genTextContent(node.label, c);
762
813
  const href = genExpr(node.href, c);
763
- return `<a href={${href}}>${label}</a>`;
814
+ let className = null;
815
+ for (const [key, val] of Object.entries(node.props || {})) {
816
+ if (key === 'class')
817
+ className = unquote(genExpr(val, c));
818
+ }
819
+ const classAttr = className ? ` className="${className}"` : '';
820
+ const extra = getPassthroughProps(node.props || {}, KNOWN_LINK_PROPS, e => genExpr(e, c), 'react');
821
+ return `<a href={${href}}${classAttr}${extra}>${label}</a>`;
764
822
  }
765
823
  function genToggle(node, c) {
766
824
  const binding = node.binding;
@@ -773,12 +831,26 @@ function genToggle(node, c) {
773
831
  else {
774
832
  setter = `set${capitalize(parts[0])}(prev => ({...prev, ${parts.slice(1).join('.')}: !prev.${parts.slice(1).join('.')}}))`;
775
833
  }
776
- return `<input type="checkbox" checked={${binding}} onChange={() => ${setter}} />`;
834
+ let className = null;
835
+ for (const [key, val] of Object.entries(node.props || {})) {
836
+ if (key === 'class')
837
+ className = unquote(genExpr(val, c));
838
+ }
839
+ const classAttr = className ? ` className="${className}"` : '';
840
+ const extra = getPassthroughProps(node.props || {}, KNOWN_TOGGLE_PROPS, e => genExpr(e, c), 'react');
841
+ return `<input type="checkbox" checked={${binding}} onChange={() => ${setter}}${classAttr}${extra} />`;
777
842
  }
778
843
  function genSelect(node, c) {
779
844
  const setter = 'set' + capitalize(node.binding);
780
845
  const options = genExpr(node.options, c);
781
- return `<select value={${node.binding}} onChange={e => ${setter}(e.target.value)}>\n {${options}.map(opt => <option key={opt} value={opt}>{opt}</option>)}\n</select>`;
846
+ let className = null;
847
+ for (const [key, val] of Object.entries(node.props || {})) {
848
+ if (key === 'class')
849
+ className = unquote(genExpr(val, c));
850
+ }
851
+ const classAttr = className ? ` className="${className}"` : '';
852
+ const extra = getPassthroughProps(node.props || {}, KNOWN_SELECT_PROPS, e => genExpr(e, c), 'react');
853
+ return `<select value={${node.binding}} onChange={e => ${setter}(e.target.value)}${classAttr}${extra}>\n {${options}.map(opt => <option key={opt} value={opt}>{opt}</option>)}\n</select>`;
782
854
  }
783
855
  function genComponentCall(node, c) {
784
856
  const parts = [];