0x-lang 0.1.16 → 0.1.17
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/generators/react.js +82 -18
- package/dist/generators/react.js.map +1 -1
- package/dist/generators/shared.d.ts +21 -0
- package/dist/generators/shared.js +118 -0
- package/dist/generators/shared.js.map +1 -1
- package/dist/generators/svelte.js +92 -20
- package/dist/generators/svelte.js.map +1 -1
- package/dist/generators/vue.js +88 -22
- package/dist/generators/vue.js.map +1 -1
- package/dist/parser.js +76 -11
- package/dist/parser.js.map +1 -1
- package/package.json +1 -1
package/dist/generators/react.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
|
414
|
+
return `${sc}useEffect(() => {\n (async () => {\n ${body}\n })();\n }, []);`;
|
|
412
415
|
}
|
|
413
|
-
return
|
|
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
|
-
|
|
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
|
|
431
|
+
return `${sc}useEffect(() => {\n (async () => {\n ${body}\n })();\n }, [${vars}]);`;
|
|
427
432
|
}
|
|
428
|
-
return
|
|
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;
|
|
@@ -600,17 +609,23 @@ function genLayout(node, c) {
|
|
|
600
609
|
}
|
|
601
610
|
}
|
|
602
611
|
const styleStr = genStyleObj(style, dynamicKeys);
|
|
612
|
+
const classAttr = className ? ` className="${className}"` : '';
|
|
613
|
+
const extra = getPassthroughProps(node.props, KNOWN_LAYOUT_PROPS, e => genExpr(e, c), 'react');
|
|
603
614
|
const children = node.children.map(ch => genUINode(ch, c)).join('\n');
|
|
604
615
|
const sc = srcComment(node);
|
|
605
|
-
return `${sc}<div style={${styleStr}}>\n${children}\n</div>`;
|
|
616
|
+
return `${sc}<div${classAttr} style={${styleStr}}${extra}>\n${children}\n</div>`;
|
|
606
617
|
}
|
|
607
618
|
function genText(node, c) {
|
|
608
619
|
const style = {};
|
|
609
620
|
const dynamicKeys = new Set();
|
|
621
|
+
let className = null;
|
|
610
622
|
for (const [key, val] of Object.entries(node.props)) {
|
|
611
623
|
const v = genExpr(val, c);
|
|
612
624
|
const isDynamic = val.kind === 'braced' || val.kind === 'ternary' || val.kind === 'binary' || val.kind === 'member' || val.kind === 'call';
|
|
613
625
|
switch (key) {
|
|
626
|
+
case 'class':
|
|
627
|
+
className = unquote(v);
|
|
628
|
+
break;
|
|
614
629
|
case 'size': {
|
|
615
630
|
const uv = unquote(v);
|
|
616
631
|
style['fontSize'] = SIZE_MAP[uv] || `${uv}px`;
|
|
@@ -658,12 +673,14 @@ function genText(node, c) {
|
|
|
658
673
|
}
|
|
659
674
|
}
|
|
660
675
|
const content = genTextContent(node.content, c);
|
|
676
|
+
const classAttr = className ? ` className="${className}"` : '';
|
|
661
677
|
const styleStr = Object.keys(style).length > 0 ? ` style={${genStyleObj(style, dynamicKeys)}}` : '';
|
|
678
|
+
const extra = getPassthroughProps(node.props, KNOWN_TEXT_PROPS, e => genExpr(e, c), 'react');
|
|
662
679
|
// Badge prop: render a badge indicator next to content
|
|
663
680
|
const badgeExpr = node.props['badge'];
|
|
664
681
|
const tooltipExpr = node.props['tooltip'];
|
|
665
682
|
const sc = srcComment(node);
|
|
666
|
-
let result = `${sc}<span${styleStr}>${content}</span>`;
|
|
683
|
+
let result = `${sc}<span${classAttr}${styleStr}${extra}>${content}</span>`;
|
|
667
684
|
if (badgeExpr) {
|
|
668
685
|
const badge = genExpr(badgeExpr, c);
|
|
669
686
|
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>`;
|
|
@@ -678,24 +695,37 @@ function genButton(node, c) {
|
|
|
678
695
|
const label = genTextContent(node.label, c);
|
|
679
696
|
const actionCode = genActionExpr(node.action, c);
|
|
680
697
|
const styleProps = [];
|
|
698
|
+
let className = null;
|
|
681
699
|
for (const [key, val] of Object.entries(node.props)) {
|
|
682
700
|
const v = genExpr(val, c);
|
|
683
701
|
switch (key) {
|
|
684
|
-
case '
|
|
685
|
-
|
|
702
|
+
case 'class':
|
|
703
|
+
className = unquote(v);
|
|
686
704
|
break;
|
|
705
|
+
case 'style': {
|
|
706
|
+
const sv = unquote(v);
|
|
707
|
+
if (sv === 'primary')
|
|
708
|
+
styleProps.push('style={{ backgroundColor: "#3b82f6", color: "white", border: "none", padding: "8px 16px", borderRadius: "6px", cursor: "pointer" }}');
|
|
709
|
+
else if (sv === 'danger')
|
|
710
|
+
styleProps.push('style={{ backgroundColor: "#ef4444", color: "white", border: "none", padding: "8px 16px", borderRadius: "6px", cursor: "pointer" }}');
|
|
711
|
+
break;
|
|
712
|
+
}
|
|
687
713
|
case 'disabled':
|
|
688
714
|
styleProps.push(`disabled={${v}}`);
|
|
689
715
|
break;
|
|
690
716
|
case 'size': /* handled in style */ break;
|
|
691
717
|
}
|
|
692
718
|
}
|
|
719
|
+
if (className)
|
|
720
|
+
styleProps.push(`className="${className}"`);
|
|
721
|
+
const extra = getPassthroughProps(node.props, KNOWN_BUTTON_PROPS, e => genExpr(e, c), 'react');
|
|
693
722
|
const propsStr = styleProps.join(' ');
|
|
694
723
|
const sc = srcComment(node);
|
|
695
|
-
return `${sc}<button onClick={() => ${actionCode}}${propsStr ? ' ' + propsStr : ''}>${label}</button>`;
|
|
724
|
+
return `${sc}<button onClick={() => ${actionCode}}${propsStr ? ' ' + propsStr : ''}${extra}>${label}</button>`;
|
|
696
725
|
}
|
|
697
726
|
function genInput(node, c) {
|
|
698
727
|
const setter = 'set' + capitalize(node.binding);
|
|
728
|
+
let className = null;
|
|
699
729
|
const props = [
|
|
700
730
|
`value={${node.binding}}`,
|
|
701
731
|
`onChange={e => ${setter}(e.target.value)}`,
|
|
@@ -703,6 +733,9 @@ function genInput(node, c) {
|
|
|
703
733
|
for (const [key, val] of Object.entries(node.props)) {
|
|
704
734
|
const v = genExpr(val, c);
|
|
705
735
|
switch (key) {
|
|
736
|
+
case 'class':
|
|
737
|
+
className = unquote(v);
|
|
738
|
+
break;
|
|
706
739
|
case 'placeholder':
|
|
707
740
|
props.push(`placeholder=${quoteJsx(v)}`);
|
|
708
741
|
break;
|
|
@@ -718,16 +751,23 @@ function genInput(node, c) {
|
|
|
718
751
|
props.push(`onKeyPress={e => ${handler}(e.key)}`);
|
|
719
752
|
}
|
|
720
753
|
}
|
|
754
|
+
if (className)
|
|
755
|
+
props.push(`className="${className}"`);
|
|
756
|
+
const extra = getPassthroughProps(node.props, KNOWN_INPUT_PROPS, e => genExpr(e, c), 'react');
|
|
721
757
|
const sc = srcComment(node);
|
|
722
|
-
return `${sc}<input ${props.join(' ')} />`;
|
|
758
|
+
return `${sc}<input ${props.join(' ')}${extra} />`;
|
|
723
759
|
}
|
|
724
760
|
function genImage(node, c) {
|
|
725
761
|
const src = genExpr(node.src, c);
|
|
726
762
|
const props = [`src={${src}}`];
|
|
727
763
|
const style = {};
|
|
764
|
+
let className = null;
|
|
728
765
|
for (const [key, val] of Object.entries(node.props)) {
|
|
729
766
|
const v = genExpr(val, c);
|
|
730
767
|
switch (key) {
|
|
768
|
+
case 'class':
|
|
769
|
+
className = unquote(v);
|
|
770
|
+
break;
|
|
731
771
|
case 'width':
|
|
732
772
|
props.push(`width="${v}"`);
|
|
733
773
|
break;
|
|
@@ -751,16 +791,26 @@ function genImage(node, c) {
|
|
|
751
791
|
}
|
|
752
792
|
}
|
|
753
793
|
}
|
|
794
|
+
if (className)
|
|
795
|
+
props.push(`className="${className}"`);
|
|
754
796
|
if (Object.keys(style).length > 0) {
|
|
755
797
|
const entries = Object.entries(style).map(([k, v]) => `${k}: ${v}`).join(', ');
|
|
756
798
|
props.push(`style={{ ${entries} }}`);
|
|
757
799
|
}
|
|
758
|
-
|
|
800
|
+
const extra = getPassthroughProps(node.props, KNOWN_IMAGE_PROPS, e => genExpr(e, c), 'react');
|
|
801
|
+
return `<img ${props.join(' ')}${extra} />`;
|
|
759
802
|
}
|
|
760
803
|
function genLink(node, c) {
|
|
761
804
|
const label = genTextContent(node.label, c);
|
|
762
805
|
const href = genExpr(node.href, c);
|
|
763
|
-
|
|
806
|
+
let className = null;
|
|
807
|
+
for (const [key, val] of Object.entries(node.props || {})) {
|
|
808
|
+
if (key === 'class')
|
|
809
|
+
className = unquote(genExpr(val, c));
|
|
810
|
+
}
|
|
811
|
+
const classAttr = className ? ` className="${className}"` : '';
|
|
812
|
+
const extra = getPassthroughProps(node.props || {}, KNOWN_LINK_PROPS, e => genExpr(e, c), 'react');
|
|
813
|
+
return `<a href={${href}}${classAttr}${extra}>${label}</a>`;
|
|
764
814
|
}
|
|
765
815
|
function genToggle(node, c) {
|
|
766
816
|
const binding = node.binding;
|
|
@@ -773,12 +823,26 @@ function genToggle(node, c) {
|
|
|
773
823
|
else {
|
|
774
824
|
setter = `set${capitalize(parts[0])}(prev => ({...prev, ${parts.slice(1).join('.')}: !prev.${parts.slice(1).join('.')}}))`;
|
|
775
825
|
}
|
|
776
|
-
|
|
826
|
+
let className = null;
|
|
827
|
+
for (const [key, val] of Object.entries(node.props || {})) {
|
|
828
|
+
if (key === 'class')
|
|
829
|
+
className = unquote(genExpr(val, c));
|
|
830
|
+
}
|
|
831
|
+
const classAttr = className ? ` className="${className}"` : '';
|
|
832
|
+
const extra = getPassthroughProps(node.props || {}, KNOWN_TOGGLE_PROPS, e => genExpr(e, c), 'react');
|
|
833
|
+
return `<input type="checkbox" checked={${binding}} onChange={() => ${setter}}${classAttr}${extra} />`;
|
|
777
834
|
}
|
|
778
835
|
function genSelect(node, c) {
|
|
779
836
|
const setter = 'set' + capitalize(node.binding);
|
|
780
837
|
const options = genExpr(node.options, c);
|
|
781
|
-
|
|
838
|
+
let className = null;
|
|
839
|
+
for (const [key, val] of Object.entries(node.props || {})) {
|
|
840
|
+
if (key === 'class')
|
|
841
|
+
className = unquote(genExpr(val, c));
|
|
842
|
+
}
|
|
843
|
+
const classAttr = className ? ` className="${className}"` : '';
|
|
844
|
+
const extra = getPassthroughProps(node.props || {}, KNOWN_SELECT_PROPS, e => genExpr(e, c), 'react');
|
|
845
|
+
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
846
|
}
|
|
783
847
|
function genComponentCall(node, c) {
|
|
784
848
|
const parts = [];
|