subscript 9.1.0 → 10.0.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.
Files changed (82) hide show
  1. package/README.md +123 -171
  2. package/feature/access.js +67 -7
  3. package/feature/accessor.js +49 -0
  4. package/feature/asi.js +15 -0
  5. package/feature/async.js +45 -0
  6. package/feature/block.js +41 -0
  7. package/feature/class.js +69 -0
  8. package/feature/collection.js +40 -0
  9. package/feature/comment.js +25 -6
  10. package/feature/destruct.js +33 -0
  11. package/feature/function.js +44 -0
  12. package/feature/group.js +41 -12
  13. package/feature/if.js +28 -0
  14. package/feature/literal.js +13 -0
  15. package/feature/loop.js +123 -0
  16. package/feature/module.js +42 -0
  17. package/feature/number.js +45 -10
  18. package/feature/op/arithmetic.js +29 -0
  19. package/feature/op/arrow.js +33 -0
  20. package/feature/op/assign-logical.js +33 -0
  21. package/feature/op/assignment.js +47 -0
  22. package/feature/op/bitwise-unsigned.js +17 -0
  23. package/feature/op/bitwise.js +29 -0
  24. package/feature/op/comparison.js +19 -0
  25. package/feature/op/defer.js +15 -0
  26. package/feature/op/equality.js +16 -0
  27. package/feature/op/identity.js +15 -0
  28. package/feature/op/increment.js +23 -0
  29. package/feature/op/logical.js +21 -0
  30. package/feature/op/membership.js +17 -0
  31. package/feature/op/nullish.js +13 -0
  32. package/feature/op/optional.js +61 -0
  33. package/feature/op/pow.js +19 -0
  34. package/feature/op/range.js +26 -0
  35. package/feature/op/spread.js +15 -0
  36. package/feature/op/ternary.js +15 -0
  37. package/feature/op/type.js +18 -0
  38. package/feature/op/unary.js +41 -0
  39. package/feature/prop.js +34 -0
  40. package/feature/regex.js +31 -0
  41. package/feature/seq.js +21 -0
  42. package/feature/string.js +24 -16
  43. package/feature/switch.js +48 -0
  44. package/feature/template.js +39 -0
  45. package/feature/try.js +57 -0
  46. package/feature/unit.js +35 -0
  47. package/feature/var.js +59 -0
  48. package/jessie.js +31 -0
  49. package/jessie.min.js +8 -0
  50. package/justin.js +39 -48
  51. package/justin.min.js +8 -1
  52. package/package.json +18 -23
  53. package/parse.js +153 -0
  54. package/subscript.d.ts +45 -5
  55. package/subscript.js +62 -22
  56. package/subscript.min.js +5 -1
  57. package/util/bundle.js +507 -0
  58. package/util/stringify.js +172 -0
  59. package/feature/add.js +0 -22
  60. package/feature/array.js +0 -11
  61. package/feature/arrow.js +0 -23
  62. package/feature/assign.js +0 -11
  63. package/feature/bitwise.js +0 -11
  64. package/feature/bool.js +0 -5
  65. package/feature/call.js +0 -15
  66. package/feature/compare.js +0 -11
  67. package/feature/increment.js +0 -11
  68. package/feature/logic.js +0 -11
  69. package/feature/mult.js +0 -25
  70. package/feature/object.js +0 -17
  71. package/feature/optional.js +0 -30
  72. package/feature/pow.js +0 -5
  73. package/feature/shift.js +0 -12
  74. package/feature/spread.js +0 -6
  75. package/feature/ternary.js +0 -10
  76. package/src/compile.d.ts +0 -17
  77. package/src/compile.js +0 -28
  78. package/src/const.js +0 -42
  79. package/src/parse.d.ts +0 -22
  80. package/src/parse.js +0 -114
  81. package/src/stringify.js +0 -31
  82. /package/{LICENSE → license} +0 -0
package/subscript.js CHANGED
@@ -1,24 +1,64 @@
1
1
  /**
2
- * Subscript dialect includes common operators / primitives for all languages
2
+ * subscript: Minimal expression parser + compiler
3
+ *
4
+ * Usage:
5
+ * subscript`a + b`(ctx) - template tag, returns compiled evaluator
6
+ * subscript`a + ${x}`(ctx) - interpolations: primitives, objects, AST nodes
7
+ * subscript('a + b')(ctx) - direct call (no caching)
8
+ *
9
+ * For more features:
10
+ * import { parse, compile } from 'subscript/justin.js' // + JSON, arrows, templates
11
+ * import { parse, compile } from 'subscript/jessie.js' // + statements, functions
3
12
  */
4
- import './feature/number.js'
5
- import './feature/string.js'
6
- import './feature/call.js'
7
- import './feature/access.js'
8
- import './feature/group.js'
9
- import './feature/assign.js'
10
- import './feature/mult.js'
11
- import './feature/add.js'
12
- import './feature/increment.js'
13
- import './feature/bitwise.js'
14
- import './feature/logic.js'
15
- import './feature/compare.js'
16
- import './feature/shift.js'
17
- import compile from './src/compile.js'
18
- import parse from './src/parse.js'
19
-
20
- export { parse, access, binary, unary, nary, group, token } from './src/parse.js'
21
- export { compile, operator } from './src/compile.js'
22
- export { stringify } from './src/stringify.js'
23
-
24
- export default s => compile(parse(s))
13
+
14
+ // Expression features
15
+ import './feature/number.js'; // Decimal numbers: 123, 1.5, 1e3
16
+ import './feature/string.js'; // Double-quoted strings with escapes
17
+
18
+ // Operators (C-family common set) - order matters for token chain performance
19
+ import './feature/op/assignment.js'; // = += -= *= /= %= |= &= ^= >>= <<=
20
+ import './feature/op/logical.js'; // ! && ||
21
+ import './feature/op/bitwise.js'; // ~ | & ^ >> <<
22
+ import './feature/op/comparison.js'; // < > <= >=
23
+ import './feature/op/equality.js'; // == !=
24
+ import './feature/op/arithmetic.js'; // + - * / %
25
+ import './feature/op/increment.js'; // ++ --
26
+
27
+ import './feature/group.js'; // Grouping: (a), sequences: a, b; a; b
28
+ import './feature/access.js'; // Property access: a.b, a[b], f(), [a,b]
29
+
30
+ import { parse, compile } from './parse.js';
31
+ export * from './parse.js';
32
+
33
+ // Cache for compiled templates (keyed by template strings array reference)
34
+ const cache = new WeakMap();
35
+
36
+ // Template tag: subscript`a + b` or subscript`a + ${x}`
37
+ const subscript = (strings, ...values) =>
38
+ // Direct call subscript('code') - strings is just a string
39
+ typeof strings === 'string' ? compile(parse(strings)) :
40
+ // Template literal - use cache
41
+ cache.get(strings) || cache.set(strings, compileTemplate(strings, values)).get(strings);
42
+
43
+ // Compile template with placeholders (using Private Use Area chars)
44
+ const PUA = 0xE000;
45
+ const compileTemplate = (strings, values) => {
46
+ const code = strings.reduce((acc, s, i) => acc + (i ? String.fromCharCode(PUA + i - 1) : '') + s, '');
47
+ const ast = parse(code);
48
+ const inject = node => {
49
+ if (typeof node === 'string' && node.length === 1) {
50
+ let i = node.charCodeAt(0) - PUA, v;
51
+ if (i >= 0 && i < values.length) return v = values[i], isAST(v) ? v : [, v];
52
+ }
53
+ return Array.isArray(node) ? node.map(inject) : node;
54
+ };
55
+ return compile(inject(ast));
56
+ };
57
+
58
+ // Detect AST node vs regular value
59
+ // AST: string (identifier), or array with string/undefined first element
60
+ const isAST = v =>
61
+ typeof v === 'string' ||
62
+ (Array.isArray(v) && (typeof v[0] === 'string' || v[0] === undefined));
63
+
64
+ export default subscript;
package/subscript.min.js CHANGED
@@ -1 +1,5 @@
1
- let t,r,e=e=>(t=0,r=e,e=i(),r[t]?n():e||""),n=(e="Bad syntax",n=r.slice(0,t).split("\n"),l=n.pop())=>{const o=r.slice(t-108,t).split("\n").pop(),i=r.slice(t,t+108).split("\n").shift();throw EvalError(`${e} at ${n.length}:${l.length} \`${t>=108?"…":""}${o}┃${i}\``,"font-weight: bold")},l=(e,n=t,l)=>{for(;l=e(r.charCodeAt(t));)t+=l;return r.slice(n,t)},o=(e=1,n=t)=>(t+=e,r.slice(n,t)),i=(r=0,o)=>{let i,p,c,h;for(;(i=s())&&(c=((h=a[i])&&h(p,r))??(!p&&l(e.id)));)p=c;return o&&(i==o?t++:n()),p},s=e=>{for(;(e=r.charCodeAt(t))<=32;)t++;return e};e.id=t=>t>=48&&t<=57||t>=65&&t<=90||t>=97&&t<=122||36==t||95==t||t>=192&&215!=t&&247!=t;let a=[],p=(l,o=32,i,s=l.charCodeAt(0),p=l.length,c=a[s],h=l.toUpperCase()!==l)=>a[s]=(s,a,d,u=t)=>(d?l==d:(p<2||r.substr(t,p)==l)&&(d=l))&&a<o&&!(h&&e.id(r.charCodeAt(t+p)))&&(t+=p,i(s)||(t=u,!c&&n()))||c?.(s,a,d),c=(t,r,e=!1)=>p(t,r,((n,l)=>n&&(l=i(r-(e?.5:0)))&&[t,n,l])),h=(t,r,e)=>p(t,r,(n=>e?n&&[t,n]:!n&&(n=i(r-.5))&&[t,n])),d=(t,r,e)=>{p(t,r,((e,n)=>(n=i(r),e?.[0]!==t&&(e=[t,e||null]),n?.[0]===t?e.push(...n.slice(1)):e.push(n||null),e)))},u=(t,r)=>p(t[0],r,(r=>!r&&[t,i(0,t.charCodeAt(1))])),f=(t,r)=>p(t[0],r,(r=>r&&[t,r,i(0,t.charCodeAt(1))||null]));const g=(t,r)=>[,(t=+l((t=>46===t||t>=48&&t<=57||(69===t||101===t?2:0))))!=t?n():t];a[46]=t=>!t&&g();for(let t=48;t<=57;t++)a[t]=t=>t?n():g();const A={n:"\n",r:"\r",t:"\t",b:"\b",f:"\f",v:"\v"},y=e=>(l,i,s="")=>{for(l&&n("Unexpected string"),o();(i=r.charCodeAt(t))-e;)92===i?(o(),i=o(),s+=A[i]||i):s+=o();return o()||n("Bad string"),[,s]};a[34]=y(34),a[39]=y(39);const C=t=>Array.isArray(t)?t[0]?v[t[0]].call(...t):()=>t[1]:C.id(t);C.id=t=>r=>r?.[t];const v={},m=(t,r,e=v[t])=>v[t]=(...t)=>r(...t)||e?.(...t),$=(t,r,e,l,o)=>"()"===t[0]&&2==t.length?$(t[1],r,e):"string"==typeof t?e=>r(e,t,e):"."===t[0]?(l=C(t[1]),o=t[2],t=>r(l(t),o,t)):"[]"===t[0]&&3===t.length?(l=C(t[1]),o=C(t[2]),t=>r(l(t),o(t),t)):e?(t=C(t),e=>r([t(e)],0,e)):()=>n("Bad left value");f("()",170),m("()",((t,r,e)=>void 0!==r&&(e=r?","===r[0]?(r=r.slice(1).map((t=>t?C(t):err())),t=>r.map((r=>r(t)))):(r=C(r),t=>[r(t)]):()=>[],$(t,((t,r,n)=>t[r](...e(n))),!0)))),f("[]",170),m("[]",((t,r)=>r?(t=C(t),r=C(r),e=>t(e)[r(e)]):err())),c(".",170),m(".",((t,r)=>(t=C(t),r=r[0]?r:r[1],e=>t(e)[r]))),u("()",170),m("()",((t,r)=>void 0===r&&(!t&&n("Empty ()"),C(t))));const b=(...t)=>(t=t.map(C),r=>t.map((t=>t(r))).pop());function B(t){if(!t)return"";if(Array.isArray(t)){const[r,...e]=t;return r?"[]"==r||"{}"==r||"()"==r?(e.length>1?B(e.shift()):"")+r[0]+B(e[0])+r[1]:1===e.length?r+B(e[0]):2===e.length?B(e[0])+("."===r?r:" "+r+" ")+B(e[1]):e.filter(Boolean).map((t=>B(t))).join(r+"\n"):JSON.stringify(e[0])}return t}d(",",10),m(",",b),d(";",5),m(";",b),c("=",20,!0),m("=",((t,r)=>(r=C(r),$(t,((t,e,n)=>t[e]=r(n)))))),c("*",120),m("*",((t,r)=>r&&(t=C(t),r=C(r),e=>t(e)*r(e)))),c("/",120),m("/",((t,r)=>r&&(t=C(t),r=C(r),e=>t(e)/r(e)))),c("%",120),m("%",((t,r)=>r&&(t=C(t),r=C(r),e=>t(e)%r(e)))),c("*=",20,!0),m("*=",((t,r)=>(r=C(r),$(t,((t,e,n)=>t[e]*=r(n)))))),c("/=",20,!0),m("/=",((t,r)=>(r=C(r),$(t,((t,e,n)=>t[e]/=r(n)))))),c("%=",20,!0),m("%=",((t,r)=>(r=C(r),$(t,((t,e,n)=>t[e]%=r(n)))))),c("+",110),m("+",((t,r)=>r&&(t=C(t),r=C(r),e=>t(e)+r(e)))),c("-",110),m("-",((t,r)=>r&&(t=C(t),r=C(r),e=>t(e)-r(e)))),h("+",140),m("+",((t,r)=>!r&&(t=C(t),r=>+t(r)))),h("-",140),m("-",((t,r)=>!r&&(t=C(t),r=>-t(r)))),c("+=",20,!0),m("+=",((t,r)=>(r=C(r),$(t,((t,e,n)=>t[e]+=r(n)))))),c("-=",20,!0),m("-=",((t,r)=>(r=C(r),$(t,((t,e,n)=>t[e]-=r(n)))))),p("++",150,(t=>t?["++",t,null]:["++",i(149)])),m("++",((t,r)=>$(t,null===r?(t,r)=>t[r]++:(t,r)=>++t[r]))),p("--",150,(t=>t?["--",t,null]:["--",i(149)])),m("--",((t,r)=>$(t,null===r?(t,r)=>t[r]--:(t,r)=>--t[r]))),h("~",140),m("~",((t,r)=>!r&&(t=C(t),r=>~t(r)))),c("|",50),m("|",((t,r)=>r&&(t=C(t),r=C(r),e=>t(e)|r(e)))),c("&",70),m("&",((t,r)=>r&&(t=C(t),r=C(r),e=>t(e)&r(e)))),c("^",60),m("^",((t,r)=>r&&(t=C(t),r=C(r),e=>t(e)^r(e)))),h("!",140),m("!",((t,r)=>!r&&(t=C(t),r=>!t(r)))),c("||",30),m("||",((t,r)=>(t=C(t),r=C(r),e=>t(e)||r(e)))),c("&&",40),m("&&",((t,r)=>(t=C(t),r=C(r),e=>t(e)&&r(e)))),c("==",80),m("==",((t,r)=>r&&(t=C(t),r=C(r),e=>t(e)==r(e)))),c("!=",80),m("!=",((t,r)=>r&&(t=C(t),r=C(r),e=>t(e)!=r(e)))),c(">",90),m(">",((t,r)=>r&&(t=C(t),r=C(r),e=>t(e)>r(e)))),c("<",90),m("<",((t,r)=>r&&(t=C(t),r=C(r),e=>t(e)<r(e)))),c(">=",90),m(">=",((t,r)=>r&&(t=C(t),r=C(r),e=>t(e)>=r(e)))),c("<=",90),m("<=",((t,r)=>r&&(t=C(t),r=C(r),e=>t(e)<=r(e)))),c(">>",100),m(">>",((t,r)=>r&&(t=C(t),r=C(r),e=>t(e)>>r(e)))),c("<<",100),m("<<",((t,r)=>r&&(t=C(t),r=C(r),e=>t(e)<<r(e)))),c(">>=",20,!0),m(">>=",((t,r)=>(r=C(r),prop(t,((t,e,n)=>t[e]>>=r(n)))))),c("<<=",20,!0),m("<<=",((t,r)=>(r=C(r),prop(t,((t,e,n)=>t[e]<<=r(n))))));var x=t=>C(e(t));export{f as access,c as binary,C as compile,x as default,u as group,d as nary,m as operator,e as parse,B as stringify,p as token,h as unary};
1
+ var f,A,m=r=>(f=0,A=r,m.newline=!1,r=h(),A[f]?S():r||""),S=(r="Unexpected token",e=f,t=A.slice(0,e).split(`
2
+ `),n=t.pop(),o=A.slice(Math.max(0,e-40),e),s="\u032D",u=(A[e]||"\u2205")+s,c=A.slice(e+1,e+20))=>{throw SyntaxError(`${r} at ${t.length+1}:${n.length+1}
3
+ ${(A[e-41]!==`
4
+ `?"...":"")+o}${u}${c}`)},W=(r,e=f)=>(Array.isArray(r)&&(r.loc=e),r),N=(r,e=f,t)=>{for(;t=r(A.charCodeAt(f));)f+=t;return A.slice(e,f)},g=(r=1)=>A[f+=r],z=r=>f=r,h=(r=0,e)=>{let t,n,o,s,u=m.reserved,c;for(e&&m.asi&&(m.newline=!1),m.reserved=0;(t=m.space())&&(c=m.newline,1)&&t!==e&&(o=((s=C[t])&&s(n,r))??(m.asi&&n&&c&&(o=m.asi(n,r,h)))??(!n&&!m.reserved&&N(m.id)));)n=o,m.reserved=0;return m.reserved=u,e&&(t==e?f++:S("Unclosed "+String.fromCharCode(e-(e>42?2:1)))),n},k=m.space=(r,e=f)=>{for(;(r=A.charCodeAt(f))<=32;)m.asi&&r===10&&(m.newline=!0),f++;return r},Zr=m.id=r=>r>=48&&r<=57||r>=65&&r<=90||r>=97&&r<=122||r==36||r==95||r>=192&&r!=215&&r!=247,b=(r,e=r.length)=>A.substr(f,e)===r&&!m.id(A.charCodeAt(f+e)),M=()=>(g(),h(0,41)),C=[],v=(r,e=32,t,n=r.charCodeAt(0),o=r.length,s=C[n],u=r.toUpperCase()!==r,c,d)=>C[n]=(y,U,L,x=f)=>(c=L,(L?r==L:(o<2||r.charCodeAt(1)===A.charCodeAt(f+1)&&(o<3||A.substr(f,o)==r))&&(!u||!m.id(A.charCodeAt(f+o)))&&(c=L=r))&&U<e&&(f+=o,(d=t(y))?W(d,x):(f=x,c=0,u&&d!==!1&&(m.reserved=1),!u&&!s&&S()),d)||s?.(y,U,c)),p=(r,e,t=!1)=>v(r,e,(n,o)=>n&&(o=h(e-(t?.5:0)))&&[r,n,o]),T=(r,e,t)=>v(r,e,n=>t?n&&[r,n]:!n&&(n=h(e-.5))&&[r,n]),qr=(r,e)=>v(r,200,t=>!t&&[,e]),J=(r,e,t)=>{v(r,e,(n,o)=>(o=h(e-(t?.5:0)),n?.[0]!==r&&(n=[r,n||null]),o?.[0]===r?n.push(...o.slice(1)):n.push(o||null),n))},rr=(r,e)=>v(r[0],e,t=>!t&&[r,h(0,r.charCodeAt(1))||null]),V=(r,e)=>v(r[0],e,t=>t&&[r,t,h(0,r.charCodeAt(1))||null]),er,Cr=(r="Compile error",e=er)=>S(r,e?.loc),G={},l=(r,e,t=G[r])=>G[r]=(...n)=>e(...n)||t?.(...n),i=r=>(er=r,Array.isArray(r)?r[0]===void 0?(e=>()=>e)(r[1]):G[r[0]]?.(...r.slice(1))??Cr(`Unknown operator: ${r[0]}`):r===void 0?()=>{}:e=>e?.[r]);var $=46,_=48,F=57,Er=69,gr=101,wr=43,Sr=45,kr=97,vr=102,Tr=65,Ir=70,Y=r=>[,(r=+N(e=>e===$&&A.charCodeAt(f+1)!==$||e>=_&&e<=F||((e===Er||e===gr)&&((e=A.charCodeAt(f+1))>=_&&e<=F||e===wr||e===Sr)?2:0)))!=r?S():r],Rr={2:r=>r===48||r===49,8:r=>r>=48&&r<=55,16:r=>r>=_&&r<=F||r>=kr&&r<=vr||r>=Tr&&r<=Ir};m.number=null;C[$]=r=>!r&&A.charCodeAt(f+1)!==$&&Y();for(let r=_;r<=F;r++)C[r]=e=>e?void 0:Y();C[_]=r=>{if(r)return;let e=m.number;if(e){for(let[t,n]of Object.entries(e))if(t[0]==="0"&&A[f+1]?.toLowerCase()===t[1])return g(2),[,parseInt(N(Rr[n]),n)]}return Y()};var Nr=92,tr=34,nr=39,Ur={n:`
5
+ `,r:"\r",t:" ",b:"\b",f:"\f",v:"\v"},or=r=>(e,t,n="")=>{if(!(e||!m.string?.[String.fromCharCode(r)]))return g(),N(o=>o-r&&(o===Nr?(n+=Ur[A[f+1]]||A[f+1],2):(n+=A[f],1))),A[f]===String.fromCharCode(r)?g():S("Bad string"),[,n]};C[tr]=or(tr);C[nr]=or(nr);m.string={'"':!0};var w=20;p("=",w,!0);p("+=",w,!0);p("-=",w,!0);p("*=",w,!0);p("/=",w,!0);p("%=",w,!0);p("|=",w,!0);p("&=",w,!0);p("^=",w,!0);p(">>=",w,!0);p("<<=",w,!0);var E=(r,e,t,n)=>typeof r=="string"?o=>e(o,r,o):r[0]==="."?(t=i(r[1]),n=r[2],o=>e(t(o),n,o)):r[0]==="[]"&&r.length===3?(t=i(r[1]),n=i(r[2]),o=>e(t(o),n(o),o)):r[0]==="()"&&r.length===2?E(r[1],e):(()=>{throw Error("Invalid assignment target")})();l("=",(r,e)=>(e=i(e),E(r,(t,n,o)=>t[n]=e(o))));l("+=",(r,e)=>(e=i(e),E(r,(t,n,o)=>t[n]+=e(o))));l("-=",(r,e)=>(e=i(e),E(r,(t,n,o)=>t[n]-=e(o))));l("*=",(r,e)=>(e=i(e),E(r,(t,n,o)=>t[n]*=e(o))));l("/=",(r,e)=>(e=i(e),E(r,(t,n,o)=>t[n]/=e(o))));l("%=",(r,e)=>(e=i(e),E(r,(t,n,o)=>t[n]%=e(o))));l("|=",(r,e)=>(e=i(e),E(r,(t,n,o)=>t[n]|=e(o))));l("&=",(r,e)=>(e=i(e),E(r,(t,n,o)=>t[n]&=e(o))));l("^=",(r,e)=>(e=i(e),E(r,(t,n,o)=>t[n]^=e(o))));l(">>=",(r,e)=>(e=i(e),E(r,(t,n,o)=>t[n]>>=e(o))));l("<<=",(r,e)=>(e=i(e),E(r,(t,n,o)=>t[n]<<=e(o))));var Mr=30,_r=40,ir=140;p("!",ir);T("!",ir);p("||",Mr);p("&&",_r);l("!",r=>(r=i(r),e=>!r(e)));l("||",(r,e)=>(r=i(r),e=i(e),t=>r(t)||e(t)));l("&&",(r,e)=>(r=i(r),e=i(e),t=>r(t)&&e(t)));var Pr=50,Br=60,Dr=70,sr=100,Lr=140;p("|",Pr);p("&",Dr);p("^",Br);p(">>",sr);p("<<",sr);T("~",Lr);l("~",r=>(r=i(r),e=>~r(e)));l("|",(r,e)=>(r=i(r),e=i(e),t=>r(t)|e(t)));l("&",(r,e)=>(r=i(r),e=i(e),t=>r(t)&e(t)));l("^",(r,e)=>(r=i(r),e=i(e),t=>r(t)^e(t)));l(">>",(r,e)=>(r=i(r),e=i(e),t=>r(t)>>e(t)));l("<<",(r,e)=>(r=i(r),e=i(e),t=>r(t)<<e(t)));var X=90;p("<",X);p(">",X);p("<=",X);p(">=",X);l(">",(r,e)=>(r=i(r),e=i(e),t=>r(t)>e(t)));l("<",(r,e)=>(r=i(r),e=i(e),t=>r(t)<e(t)));l(">=",(r,e)=>(r=i(r),e=i(e),t=>r(t)>=e(t)));l("<=",(r,e)=>(r=i(r),e=i(e),t=>r(t)<=e(t)));var lr=80;p("==",lr);p("!=",lr);l("==",(r,e)=>(r=i(r),e=i(e),t=>r(t)==e(t)));l("!=",(r,e)=>(r=i(r),e=i(e),t=>r(t)!=e(t)));var ur=110,Z=120,fr=140;p("+",ur);p("-",ur);p("*",Z);p("/",Z);p("%",Z);T("+",fr);T("-",fr);l("+",(r,e)=>e!==void 0?(r=i(r),e=i(e),t=>r(t)+e(t)):(r=i(r),t=>+r(t)));l("-",(r,e)=>e!==void 0?(r=i(r),e=i(e),t=>r(t)-e(t)):(r=i(r),t=>-r(t)));l("*",(r,e)=>(r=i(r),e=i(e),t=>r(t)*e(t)));l("/",(r,e)=>(r=i(r),e=i(e),t=>r(t)/e(t)));l("%",(r,e)=>(r=i(r),e=i(e),t=>r(t)%e(t)));var O=150;v("++",O,r=>r?["++",r,null]:["++",h(O-1)]);v("--",O,r=>r?["--",r,null]:["--",h(O-1)]);var q=(r,e,t,n)=>typeof r=="string"?o=>e(o,r):r[0]==="."?(t=i(r[1]),n=r[2],o=>e(t(o),n)):r[0]==="[]"&&r.length===3?(t=i(r[1]),n=i(r[2]),o=>e(t(o),n(o))):r[0]==="()"&&r.length===2?q(r[1],e):(()=>{throw Error("Invalid increment target")})();l("++",(r,e)=>q(r,e===null?(t,n)=>t[n]++:(t,n)=>++t[n]));l("--",(r,e)=>q(r,e===null?(t,n)=>t[n]--:(t,n)=>--t[n]));var pr=5,$r=123,Fr=125,I=(r,e,t,n=r.charCodeAt(0),o=r.length,s=C[n],u)=>C[n]=(c,d,y,U=f)=>!c&&(y?r==y:(o<2||A.substr(f,o)==r)&&(y=r))&&d<e&&!m.id(A.charCodeAt(f+o))&&(z(f+o),(u=t())?W(u,U):(z(U),!s&&S()),u)||s?.(c,d,y);var P=()=>k()!==$r?h(pr+.5):(g(),["block",h(pr-.5,Fr)||null]);l("block",r=>r===void 0?()=>{}:(r=i(r),e=>r(e)));var B=(r,e,t)=>{if(typeof r=="string"){t[r]=e;return}let[n,...o]=r;if(n==="{}")for(let s of o){let u,c,d;s[0]==="="?[,[,u,c],d]=s:[,u,c]=s;let y=e[u];y===void 0&&d&&(y=i(d)(t)),B(c,y,t)}else if(n==="[]"){let s=0;for(let u of o){if(u===null){s++;continue}if(Array.isArray(u)&&u[0]==="..."){t[u[1]]=e.slice(s);break}let c=u,d;Array.isArray(u)&&u[0]==="="&&([,c,d]=u);let y=e[s++];y===void 0&&d&&(y=i(d)(t)),B(c,y,t)}}};var Q=Symbol("break"),H=Symbol("continue"),mr=Symbol("return"),D=(r,e)=>{try{return{v:r(e)}}catch(t){if(t?.type===Q)return{b:1};if(t?.type===H)return{c:1};if(t?.type===mr)return{r:1,v:t.value};throw t}},R=5,Xr=125,Or=59;I("while",R+1,()=>(k(),["while",M(),P()]));I("do",R+1,()=>(r=>(k(),g(5),k(),["do",r,M()]))(P()));I("for",R+1,()=>(k(),b("await")?(g(5),k(),["for await",M(),P()]):["for",M(),P()]));I("break",R+1,()=>["break"]);I("continue",R+1,()=>["continue"]);I("return",R+1,()=>{m.asi&&(m.newline=!1),k();let r=A.charCodeAt(f);return!r||r===Xr||r===Or||m.newline?["return"]:["return",h(R)]});l("while",(r,e)=>(r=i(r),e=i(e),t=>{let n,o;for(;r(t)&&!(n=D(e,t)).b;){if(n.r)return n.v;n.c||(o=n.v)}return o}));l("do",(r,e)=>(r=i(r),e=i(e),t=>{let n,o;do{if((n=D(r,t)).b)break;if(n.r)return n.v;n.c||(o=n.v)}while(e(t));return o}));l("for",(r,e)=>{if(Array.isArray(r)&&r[0]===";"){let[,t,n,o]=r;return t=t?i(t):null,n=n?i(n):()=>!0,o=o?i(o):null,e=i(e),s=>{let u,c;for(t?.(s);n(s)&&!(u=D(e,s)).b;o?.(s)){if(u.r)return u.v;u.c||(c=u.v)}return c}}if(Array.isArray(r)&&(r[0]==="in"||r[0]==="of")){let[t,n,o]=r;if(Array.isArray(n)&&(n[0]==="let"||n[0]==="const"||n[0]==="var")&&(n=n[1]),t==="in")return Hr(n,o,e);if(t==="of")return Qr(n,o,e)}});var Qr=(r,e,t)=>{e=i(e),t=i(t);let n=Array.isArray(r);return o=>{let s,u,c=n?null:o[r];for(let d of e(o)){if(n?B(r,d,o):o[r]=d,(s=D(t,o)).b)break;if(s.r)return s.v;s.c||(u=s.v)}return n||(o[r]=c),u}},Hr=(r,e,t)=>{e=i(e),t=i(t);let n=Array.isArray(r);return o=>{let s,u,c=n?null:o[r];for(let d in e(o)){if(n?B(r,d,o):o[r]=d,(s=D(t,o)).b)break;if(s.r)return s.v;s.c||(u=s.v)}return n||(o[r]=c),u}};l("break",()=>()=>{throw{type:Q}});l("continue",()=>()=>{throw{type:H}});l("return",r=>(r=r!==void 0?i(r):null,e=>{throw{type:mr,value:r?.(e)}}));var cr=r=>r?.[0]==="_"&&r[1]==="_"||r==="constructor"||r==="prototype",j=170;V("[]",j);p(".",j);V("()",j);var a=r=>{throw Error(r)};l("[]",(r,e)=>e===void 0?(r=r?r[0]===","?r.slice(1):[r]:[],r=r.map(t=>t==null?(()=>{}):t[0]==="..."?(t=i(t[1]),n=>t(n)):(t=i(t),n=>[t(n)])),t=>r.flatMap(n=>n(t))):(e==null&&a("Missing index"),r=i(r),e=i(e),t=>{let n=e(t);return cr(n)?void 0:r(t)[n]}));l(".",(r,e)=>(r=i(r),e=e[0]?e:e[1],cr(e)?()=>{}:t=>r(t)[e]));l("()",(r,e)=>{if(e===void 0)return r==null?a("Empty ()"):i(r);let t=o=>o?.[0]===","&&o.slice(1).some(s=>s==null||t(s));t(e)&&a("Empty argument");let n=e?e[0]===","?(e=e.slice(1).map(i),o=>e.map(s=>s(o))):(e=i(e),o=>[e(o)]):()=>[];return K(r,(o,s,u)=>o[s](...n(u)),!0)});var Kr=r=>{throw Error(r)},K=(r,e,t,n,o)=>r==null?Kr("Empty ()"):r[0]==="()"&&r.length==2?K(r[1],e,t):typeof r=="string"?s=>e(s,r,s):r[0]==="."?(n=i(r[1]),o=r[2],s=>e(n(s),o,s)):r[0]==="?."?(n=i(r[1]),o=r[2],s=>{let u=n(s);return u==null?void 0:e(u,o,s)}):r[0]==="[]"&&r.length===3?(n=i(r[1]),o=i(r[2]),s=>e(n(s),o(s),s)):r[0]==="?.[]"?(n=i(r[1]),o=i(r[2]),s=>{let u=n(s);return u==null?void 0:e(u,o(s),s)}):(r=i(r),s=>e([r(s)],0,s));var Gr=5,Wr=10,zr=170;rr("()",zr);J(",",Wr);J(";",Gr,!0);var Ar=r=>{throw Error(r)};l("()",(r,e)=>{if(e===void 0)return r==null?Ar("Empty ()"):i(r);let t=o=>o?.[0]===","&&o.slice(1).some(s=>s==null||t(s));t(e)&&Ar("Empty argument");let n=e?e[0]===","?(e=e.slice(1).map(i),o=>e.map(s=>s(o))):(e=i(e),o=>[e(o)]):()=>[];return K(r,(o,s,u)=>o[s](...n(u)),!0)});var dr=(...r)=>(r=r.map(i),e=>{let t;for(let n of r)try{t=n(e)}catch(o){throw(o?.type===Q||o?.type===H)&&(o.value=t),o}return t});l(",",dr);l(";",dr);var hr=new WeakMap,Jr=(r,...e)=>typeof r=="string"?i(m(r)):hr.get(r)||hr.set(r,Vr(r,e)).get(r),yr=57344,Vr=(r,e)=>{let t=r.reduce((s,u,c)=>s+(c?String.fromCharCode(yr+c-1):"")+u,""),n=m(t),o=s=>{if(typeof s=="string"&&s.length===1){let u=s.charCodeAt(0)-yr,c;if(u>=0&&u<e.length)return c=e[u],Yr(c)?c:[,c]}return Array.isArray(s)?s.map(o):s};return i(o(n))},Yr=r=>typeof r=="string"||Array.isArray(r)&&(typeof r[0]=="string"||r[0]===void 0),De=Jr;export{V as access,p as binary,i as compile,A as cur,De as default,S as err,h as expr,rr as group,Zr as id,f as idx,qr as literal,W as loc,C as lookup,J as nary,N as next,l as operator,G as operators,M as parens,m as parse,z as seek,g as skip,k as space,v as token,T as unary,b as word};
package/util/bundle.js ADDED
@@ -0,0 +1,507 @@
1
+ /**
2
+ * ESM Bundler using subscript's own parser (dogfooding)
3
+ *
4
+ * Thin layer: scope analysis + tree transform
5
+ * Parser comes from the dialect (jessie by default)
6
+ */
7
+ import { parse } from '../jessie.js';
8
+ import { codegen } from './stringify.js';
9
+ import { readFile } from 'fs/promises';
10
+ import { resolve } from 'path';
11
+
12
+ // === AST Utilities ===
13
+
14
+ /** Walk AST, call fn(node, parent, key) for each node */
15
+ const walk = (node, fn, parent = null, key = null) => {
16
+ if (!node || typeof node !== 'object') return;
17
+ fn(node, parent, key);
18
+ if (Array.isArray(node)) {
19
+ for (let i = 0; i < node.length; i++) walk(node[i], fn, node, i);
20
+ }
21
+ };
22
+
23
+ /** Deep clone AST */
24
+ const clone = node =>
25
+ !node ? node :
26
+ Array.isArray(node) ? node.map(clone) :
27
+ node instanceof RegExp ? new RegExp(node.source, node.flags) :
28
+ typeof node === 'object' ? Object.fromEntries(Object.entries(node).map(([k,v]) => [k, clone(v)])) :
29
+ node;
30
+
31
+ /** Rename identifier in AST - skip property access positions */
32
+ const renameId = (ast, old, neu) => {
33
+ walk(ast, (node, parent, key) => {
34
+ if (Array.isArray(node)) {
35
+ for (let i = 0; i < node.length; i++) {
36
+ if (node[i] === old) {
37
+ // Don't rename if this is a property name in a '.' or '?.' access
38
+ if ((node[0] === '.' || node[0] === '?.') && i === 2) continue;
39
+ // Don't rename if this is a property name in object literal {a: b} or shorthand {a}
40
+ if (node[0] === ':' && i === 1 && typeof node[1] === 'string') continue;
41
+ node[i] = neu;
42
+ }
43
+ }
44
+ }
45
+ });
46
+ return ast;
47
+ };
48
+
49
+ /** Flatten comma nodes into array: [',', 'a', 'b'] → ['a', 'b'], 'x' → ['x'] */
50
+ const flattenComma = node =>
51
+ Array.isArray(node) && node[0] === ',' ? node.slice(1) :
52
+ node ? [node] : [];
53
+
54
+ // === Module Analysis ===
55
+
56
+ /** Extract string from path node [null, 'path'] (string literal) */
57
+ const getPath = node => Array.isArray(node) && (node[0] === undefined || node[0] === null) ? node[1] : node;
58
+
59
+ /** Extract imports from AST
60
+ * New AST shapes:
61
+ * import './x.js' → ['import', [null, path]]
62
+ * import X from './x.js' → ['import', ['from', 'X', [null, path]]]
63
+ * import {a,b} from './x' → ['import', ['from', ['{}', ...], [null, path]]]
64
+ * import * as X from './x' → ['import', ['from', ['as', '*', 'X'], [null, path]]]
65
+ */
66
+ const getImports = ast => {
67
+ const imports = [];
68
+ walk(ast, node => {
69
+ if (!Array.isArray(node) || node[0] !== 'import') return;
70
+ const body = node[1];
71
+ const imp = { node };
72
+
73
+ // import './x.js' - bare import: [, 'path'] sparse array with undefined at index 0
74
+ if (Array.isArray(body) && (body[0] === undefined || body[0] === null)) {
75
+ imp.path = body[1];
76
+ imports.push(imp);
77
+ return;
78
+ }
79
+
80
+ // import X from './x.js' or import {...} from './x.js'
81
+ if (Array.isArray(body) && body[0] === 'from') {
82
+ const spec = body[1];
83
+ const pathNode = body[2];
84
+ imp.path = getPath(pathNode);
85
+
86
+ if (typeof spec === 'string') {
87
+ // import X from - default import
88
+ imp.default_ = spec;
89
+ } else if (Array.isArray(spec)) {
90
+ if (spec[0] === '{}') {
91
+ // import { a, b, c as d }
92
+ const items = spec.slice(1).flatMap(flattenComma);
93
+ imp.named = items.map(s =>
94
+ Array.isArray(s) && s[0] === 'as' ? { name: s[1], alias: s[2] } : { name: s, alias: s }
95
+ );
96
+ } else if (spec[0] === 'as' && spec[1] === '*') {
97
+ // import * as X
98
+ imp.namespace = spec[2];
99
+ } else if (spec[0] === '*') {
100
+ // import * as X (alternate shape)
101
+ imp.namespace = spec[1];
102
+ }
103
+ }
104
+ imports.push(imp);
105
+ }
106
+ });
107
+ return imports;
108
+ };
109
+
110
+ /** Extract exports from AST
111
+ * New AST shapes:
112
+ * export const x = 1 → ['export', ['const', ['=', 'x', val]]]
113
+ * export default x → ['export', ['default', 'x']]
114
+ * export { a } → ['export', ['{}', 'a']]
115
+ * export { a } from './x' → ['export', ['from', ['{}', 'a'], [null, path]]]
116
+ * export * from './x' → ['export', ['from', '*', [null, path]]]
117
+ */
118
+ const getExports = ast => {
119
+ const exports = { named: {}, reexports: [], default_: null };
120
+
121
+ walk(ast, node => {
122
+ if (!Array.isArray(node) || node[0] !== 'export') return;
123
+ const spec = node[1];
124
+
125
+ // export { a } from './x' or export * from './x'
126
+ if (Array.isArray(spec) && spec[0] === 'from') {
127
+ const what = spec[1];
128
+ const pathNode = spec[2];
129
+ const path = getPath(pathNode);
130
+
131
+ if (what === '*') {
132
+ exports.reexports.push({ star: true, path });
133
+ } else if (Array.isArray(what) && what[0] === '{}') {
134
+ const items = what.slice(1).flatMap(flattenComma);
135
+ const names = items.map(s =>
136
+ Array.isArray(s) && s[0] === 'as' ? { name: s[1], alias: s[2] } : { name: s, alias: s }
137
+ );
138
+ exports.reexports.push({ names, path });
139
+ }
140
+ return;
141
+ }
142
+
143
+ // export { a, b }
144
+ if (Array.isArray(spec) && spec[0] === '{}') {
145
+ const items = spec.slice(1).flatMap(flattenComma);
146
+ const names = items.map(s =>
147
+ Array.isArray(s) && s[0] === 'as' ? { name: s[1], alias: s[2] } : { name: s, alias: s }
148
+ );
149
+ for (const { name, alias } of names) exports.named[alias] = name;
150
+ return;
151
+ }
152
+
153
+ // export default x
154
+ if (Array.isArray(spec) && spec[0] === 'default') {
155
+ exports.default_ = spec[1];
156
+ return;
157
+ }
158
+
159
+ // export const/let/var x = ... - varargs: ['let', decl1, decl2, ...]
160
+ if (Array.isArray(spec) && (spec[0] === 'const' || spec[0] === 'let' || spec[0] === 'var')) {
161
+ for (let i = 1; i < spec.length; i++) {
162
+ const decl = spec[i];
163
+ if (typeof decl === 'string') {
164
+ exports.named[decl] = decl;
165
+ } else if (Array.isArray(decl) && decl[0] === '=') {
166
+ const name = decl[1];
167
+ if (typeof name === 'string') exports.named[name] = name;
168
+ }
169
+ }
170
+ return;
171
+ }
172
+
173
+ // export function x() {} or export class x {}
174
+ if (Array.isArray(spec) && (spec[0] === 'function' || spec[0] === 'class')) {
175
+ if (typeof spec[1] === 'string') exports.named[spec[1]] = spec[1];
176
+ }
177
+ });
178
+
179
+ return exports;
180
+ };
181
+
182
+ /** Get all declared names in AST
183
+ * New AST shapes:
184
+ * const x = 1 → ['const', ['=', 'x', val]]
185
+ * let x → ['let', 'x']
186
+ * function f() → ['function', 'f', ...]
187
+ * const a = 1, b = 2 → ['const', ['=', 'a', ...], ['=', 'b', ...]] (varargs)
188
+ */
189
+ const getDecls = ast => {
190
+ const decls = new Set();
191
+
192
+ const addDecl = node => {
193
+ if (typeof node === 'string') decls.add(node);
194
+ else if (Array.isArray(node)) {
195
+ if (node[0] === '=') {
196
+ if (typeof node[1] === 'string') decls.add(node[1]);
197
+ } else if (node[0] === ',') {
198
+ // Multiple declarations: const a = 1, b = 2 (older AST shape)
199
+ for (let i = 1; i < node.length; i++) addDecl(node[i]);
200
+ }
201
+ }
202
+ };
203
+
204
+ walk(ast, node => {
205
+ if (!Array.isArray(node)) return;
206
+ const op = node[0];
207
+
208
+ if (op === 'const' || op === 'let' || op === 'var') {
209
+ // Handle varargs: ['const', decl1, decl2, ...] for multiple declarations
210
+ for (let i = 1; i < node.length; i++) addDecl(node[i]);
211
+ }
212
+ if (op === 'function' || op === 'class') {
213
+ if (typeof node[1] === 'string') decls.add(node[1]);
214
+ }
215
+ if (op === 'export') {
216
+ const spec = node[1];
217
+ if (Array.isArray(spec) && (spec[0] === 'const' || spec[0] === 'let' || spec[0] === 'var')) {
218
+ addDecl(spec[1]);
219
+ }
220
+ if (Array.isArray(spec) && (spec[0] === 'function' || spec[0] === 'class') && typeof spec[1] === 'string') {
221
+ decls.add(spec[1]);
222
+ }
223
+ }
224
+ });
225
+
226
+ return decls;
227
+ };
228
+
229
+ // === AST Transforms ===
230
+
231
+ /** Remove import/export nodes, extract declarations */
232
+ /** Remove import/export nodes, extract declarations
233
+ * New AST shapes for export:
234
+ * export const x = 1 → ['export', ['const', ...]] → keep ['const', ...]
235
+ * export default x → ['export', ['default', x]] → keep, or convert to __default
236
+ * export { a } → ['export', ['{}', ...]] → remove
237
+ * export { a } from './x' → ['export', ['from', ['{}', ...], path]] → remove
238
+ * export * from './x' → ['export', ['from', '*', path]] → remove
239
+ */
240
+ const stripModuleSyntax = ast => {
241
+ const defaultExpr = { value: null };
242
+
243
+ const process = node => {
244
+ if (!Array.isArray(node)) return node;
245
+ const op = node[0];
246
+
247
+ if (op === 'import') return null;
248
+
249
+ if (op === 'export') {
250
+ const spec = node[1];
251
+ // Re-exports: export { a } from './x' or export * from './x'
252
+ if (Array.isArray(spec) && spec[0] === 'from') return null;
253
+ // Named exports: export { a, b }
254
+ if (Array.isArray(spec) && spec[0] === '{}') return null;
255
+ // Default export
256
+ if (Array.isArray(spec) && spec[0] === 'default') {
257
+ defaultExpr.value = spec[1];
258
+ if (typeof spec[1] === 'string') return null;
259
+ return ['const', ['=', '__default', spec[1]]];
260
+ }
261
+ // Declaration export: export const x = 1
262
+ return spec;
263
+ }
264
+
265
+ if (op === ';') {
266
+ const parts = node.slice(1).map(process).filter(Boolean);
267
+ if (parts.length === 0) return null;
268
+ if (parts.length === 1) return parts[0];
269
+ return [';', ...parts];
270
+ }
271
+
272
+ return node;
273
+ };
274
+
275
+ return { ast: process(ast), defaultExpr: defaultExpr.value };
276
+ };
277
+
278
+ // === Path Resolution ===
279
+
280
+ const resolvePath = (from, to) => {
281
+ if (!to.startsWith('.')) return to;
282
+ const base = from.split('/').slice(0, -1);
283
+ for (const part of to.split('/')) {
284
+ if (part === '..') base.pop();
285
+ else if (part !== '.') base.push(part);
286
+ }
287
+ let path = base.join('/');
288
+ if (!path.endsWith('.js')) path += '.js';
289
+ return path;
290
+ };
291
+
292
+ // === Bundler ===
293
+
294
+ /**
295
+ * Bundle ES modules into single file
296
+ * @param {string} entry - Entry file path
297
+ * @param {(path: string) => string|Promise<string>} read - File reader
298
+ */
299
+ export async function bundle(entry, read) {
300
+ const modules = new Map();
301
+ const order = [];
302
+
303
+ async function load(path) {
304
+ if (modules.has(path)) return;
305
+ modules.set(path, null);
306
+
307
+ const code = await read(path);
308
+ const ast = parse(code);
309
+ const imports = getImports(ast);
310
+ const exports = getExports(ast);
311
+ const decls = getDecls(ast);
312
+
313
+ for (const imp of imports) {
314
+ imp.resolved = resolvePath(path, imp.path);
315
+ await load(imp.resolved);
316
+ }
317
+ for (const re of exports.reexports) {
318
+ re.resolved = resolvePath(path, re.path);
319
+ await load(re.resolved);
320
+ }
321
+
322
+ modules.set(path, { ast: clone(ast), imports, exports, decls });
323
+ order.push(path);
324
+ }
325
+
326
+ await load(entry);
327
+
328
+ // Detect conflicts
329
+ const allDecls = new Map();
330
+ for (const [path, mod] of modules) {
331
+ const importAliases = new Set();
332
+ for (const imp of mod.imports) {
333
+ if (imp.default_) importAliases.add(imp.default_);
334
+ if (imp.namespace) importAliases.add(imp.namespace);
335
+ if (imp.named) for (const { alias } of imp.named) importAliases.add(alias);
336
+ }
337
+
338
+ for (const name of mod.decls) {
339
+ if (importAliases.has(name)) continue;
340
+ if (!allDecls.has(name)) allDecls.set(name, []);
341
+ allDecls.get(name).push(path);
342
+ }
343
+ }
344
+
345
+ // Build rename maps
346
+ const renames = new Map();
347
+ for (const [name, paths] of allDecls) {
348
+ if (paths.length > 1) {
349
+ for (const path of paths) {
350
+ if (!renames.has(path)) renames.set(path, {});
351
+ // Make valid JS identifier: replace non-alphanumeric with underscore
352
+ const prefix = path.split('/').pop().replace('.js', '').replace(/[^a-zA-Z0-9]/g, '_') + '_';
353
+ renames.get(path)[name] = prefix + name;
354
+ }
355
+ }
356
+ }
357
+
358
+ const traceDefault = path => {
359
+ const mod = modules.get(path);
360
+ if (!mod) return null;
361
+ const def = mod.exports.default_;
362
+ if (!def) return null;
363
+ if (typeof def === 'string') {
364
+ const pathRenames = renames.get(path) || {};
365
+ if (pathRenames[def]) return pathRenames[def];
366
+ const defImp = mod.imports.find(i => i.default_ === def);
367
+ if (defImp) return traceDefault(defImp.resolved);
368
+ return def;
369
+ }
370
+ return '__default';
371
+ };
372
+
373
+ // Transform each module
374
+ const chunks = [];
375
+ for (const path of order) {
376
+ const mod = modules.get(path);
377
+ const pathRenames = renames.get(path) || {};
378
+
379
+ let ast = clone(mod.ast);
380
+ for (const [old, neu] of Object.entries(pathRenames)) {
381
+ renameId(ast, old, neu);
382
+ }
383
+
384
+ for (const imp of mod.imports) {
385
+ const dep = modules.get(imp.resolved);
386
+ if (!dep) continue;
387
+ const depRenames = renames.get(imp.resolved) || {};
388
+
389
+ if (imp.named) {
390
+ for (const { name, alias } of imp.named) {
391
+ // `name` is the exported name, look up what local name it maps to in the dep
392
+ const localName = dep.exports.named[name] || name;
393
+ // Check if that local name was renamed in the dep
394
+ const resolved = depRenames[localName] || localName;
395
+ if (alias !== resolved) renameId(ast, alias, resolved);
396
+ }
397
+ }
398
+
399
+ if (imp.default_) {
400
+ const resolved = traceDefault(imp.resolved);
401
+ if (resolved && imp.default_ !== resolved) {
402
+ renameId(ast, imp.default_, resolved);
403
+ }
404
+ }
405
+
406
+ if (imp.namespace) {
407
+ walk(ast, node => {
408
+ if (Array.isArray(node) && node[0] === '.' && node[1] === imp.namespace) {
409
+ const prop = node[2];
410
+ if (typeof prop === 'string' && dep.exports.named[prop]) {
411
+ const resolved = depRenames[dep.exports.named[prop]] || dep.exports.named[prop];
412
+ node.length = 0;
413
+ node.push(resolved);
414
+ }
415
+ }
416
+ });
417
+ }
418
+ }
419
+
420
+ const { ast: stripped } = stripModuleSyntax(ast);
421
+
422
+ if (stripped) {
423
+ const code = codegen(stripped);
424
+ if (code.trim()) {
425
+ chunks.push(`// === ${path} ===\n${code}`);
426
+ }
427
+ }
428
+ }
429
+
430
+ // Generate exports
431
+ const entryMod = modules.get(entry);
432
+ const entryRenames = renames.get(entry) || {};
433
+ const exportLines = [];
434
+
435
+ // Resolve all named exports including from re-exports
436
+ const resolveExports = (path, seen = new Set()) => {
437
+ if (seen.has(path)) return {};
438
+ seen.add(path);
439
+ const mod = modules.get(path);
440
+ if (!mod) return {};
441
+ const pathRenames = renames.get(path) || {};
442
+ const result = {};
443
+
444
+ // Direct named exports
445
+ for (const [exp, local] of Object.entries(mod.exports.named)) {
446
+ result[exp] = pathRenames[local] || local;
447
+ }
448
+
449
+ // Re-exports
450
+ for (const re of mod.exports.reexports) {
451
+ const depRenames = renames.get(re.resolved) || {};
452
+ if (re.star) {
453
+ // export * from './x' - get all exports from that module
454
+ const depExports = resolveExports(re.resolved, seen);
455
+ for (const [exp, resolved] of Object.entries(depExports)) {
456
+ if (!(exp in result)) result[exp] = resolved; // don't override local exports
457
+ }
458
+ } else if (re.names) {
459
+ // export { a, b } from './x'
460
+ const depMod = modules.get(re.resolved);
461
+ if (depMod) {
462
+ for (const { name, alias } of re.names) {
463
+ const local = depMod.exports.named[name] || name;
464
+ result[alias] = depRenames[local] || local;
465
+ }
466
+ }
467
+ }
468
+ }
469
+ return result;
470
+ };
471
+
472
+ const allExports = resolveExports(entry);
473
+ for (const [exp, resolved] of Object.entries(allExports)) {
474
+ exportLines.push(exp === resolved ? exp : `${resolved} as ${exp}`);
475
+ }
476
+
477
+ if (entryMod.exports.default_) {
478
+ const resolved = traceDefault(entry) || '__default';
479
+ exportLines.push(`${resolved} as default`);
480
+ }
481
+
482
+ let result = chunks.join('\n\n');
483
+ if (exportLines.length) {
484
+ result += `\n\nexport { ${exportLines.join(', ')} }`;
485
+ }
486
+
487
+ return result;
488
+ }
489
+
490
+ /** Bundle with Node.js fs */
491
+ export const bundleFile = (entry) => bundle(resolve(entry), path => readFile(path, 'utf-8'));
492
+
493
+ // CLI
494
+ if (typeof process !== 'undefined' && process.argv[1]?.includes('bundle')) {
495
+ const entry = process.argv[2];
496
+ if (!entry) {
497
+ console.error('Usage: node bundle.js <entry>');
498
+ process.exit(1);
499
+ }
500
+ bundleFile(entry)
501
+ .then(result => console.log(result))
502
+ .catch(e => {
503
+ console.error('Error:', e.message);
504
+ console.error(e.stack);
505
+ process.exit(1);
506
+ });
507
+ }