@vesk/compiler 0.2.28 → 0.2.30

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.
@@ -48,7 +48,9 @@ export declare function compileClient(source: string, _componentName: string | n
48
48
  * `resolveComponentName`, so callers don't need a second full parse just
49
49
  * to learn the name.
50
50
  */
51
- export declare function compileClientBoth(source: string, _componentName: string | null, sourcePath?: string): {
51
+ export declare function compileClientBoth(source: string, _componentName: string | null, sourcePath?: string, opts?: {
52
+ skipHyd?: boolean;
53
+ }): {
52
54
  comp: string;
53
55
  hyd: string;
54
56
  name: string | null;
@@ -1 +1 @@
1
- {"version":3,"file":"client-codegen.d.ts","sourceRoot":"","sources":["../src/client-codegen.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAuB,MAAM,uBAAuB,CAAC;AACzE,OAAO,EAGL,cAAc,EAQd,gBAAgB,EASjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAyCxD,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,SAAS,GAAG,MAAM,CAAC;CAC1B;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAI1D;AAiDD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,gBAAgB,GAAG,cAAc,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,MAAM,CAwGlI;AAID,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAU5E;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,MAAM,CAY5F;AAoBD,qDAAqD;AACrD,MAAM,MAAM,SAAS,GAAG;IAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtC;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,SAAS,CAQtE;AAmjDD,iBAAS,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAe3C;AAgID,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAO9C;AAqED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,GAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,eAAe,CAAC,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,aAAa,CAAC,EAAE,SAAS,CAAA;CAAO,GAAG,MAAM,CAO9O;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,IAAI,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAwBxJ;AAED,OAAO,EAAE,aAAa,IAAI,OAAO,EAAE,UAAU,EAAE,CAAC"}
1
+ {"version":3,"file":"client-codegen.d.ts","sourceRoot":"","sources":["../src/client-codegen.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAuB,MAAM,uBAAuB,CAAC;AACzE,OAAO,EAGL,cAAc,EAQd,gBAAgB,EASjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAyCxD,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,SAAS,GAAG,MAAM,CAAC;CAC1B;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAI1D;AAiDD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,gBAAgB,GAAG,cAAc,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,MAAM,CAwGlI;AAID,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAU5E;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,MAAM,CAY5F;AAoBD,qDAAqD;AACrD,MAAM,MAAM,SAAS,GAAG;IAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtC;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,SAAS,CAQtE;AAknDD,iBAAS,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAe3C;AAgID,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAO9C;AAqED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,GAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,eAAe,CAAC,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,aAAa,CAAC,EAAE,SAAS,CAAA;CAAO,GAAG,MAAM,CAO9O;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,MAAM,GAAG,IAAI,EAC7B,UAAU,CAAC,EAAE,MAAM,EACnB,IAAI,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GAC3B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CA0BpD;AAED,OAAO,EAAE,aAAa,IAAI,OAAO,EAAE,UAAU,EAAE,CAAC"}
@@ -383,8 +383,9 @@ function emitNode(ctx, node, tracked, effectsVar, parentVar, compPrefix = '__com
383
383
  if (ctx.hydrate) {
384
384
  ctx.push(`if (props.children !== undefined && props.children !== null) {`);
385
385
  ctx.push(` if (typeof props.children === 'function') {`);
386
- ctx.push(` const __child = props.children(${ctx.walker});`);
387
- ctx.push(` if (__child && typeof __child.then === 'function') __pendingChild = __child.then(() => $mount || $root);`);
386
+ ctx.push(` const __slot = createLayoutSlot(${ctx.walker}, ${parentVar});`);
387
+ ctx.push(` const __child = props.children(__slot.walker);`);
388
+ ctx.push(` if (__child && typeof __child.then === 'function') __pendingChild = __slot.track(__child);`);
388
389
  ctx.push(` } else {`);
389
390
  ctx.push(` ${parentVar}.appendChild(props.children);`);
390
391
  ctx.push(` }`);
@@ -493,7 +494,77 @@ function emitStatic(ctx, node, tracked, effectsVar) {
493
494
  let residueBefore = 0;
494
495
  for (const child of children) {
495
496
  let childVar;
496
- if (singleReactiveText && child instanceof DynamicBinding && child.kind === 'text') {
497
+ // Hydrate mode: pure static subtree children of a claimed element are already
498
+ // captured in __vsk_ssrEls. Retrieve them instead of claiming via nextElement
499
+ // (which would miss because SSR emits no marker for static children of dynamic parents).
500
+ const isPureStaticChild = ctx.hydrate && child instanceof StaticNode && isStaticIR(child.children);
501
+ if (isPureStaticChild) {
502
+ const ssrEl = `${el}.__vsk_ssrEls[${residueBefore}]`;
503
+ childVar = ssrEl;
504
+ // Process dynamic attributes on the retrieved SSR element
505
+ const dynAttrs = [];
506
+ const staticChildren = [];
507
+ for (const c of child.children) {
508
+ if (c instanceof DynamicBinding && c.kind === 'attribute') {
509
+ dynAttrs.push(c);
510
+ }
511
+ else {
512
+ staticChildren.push(c);
513
+ }
514
+ }
515
+ for (const attr of dynAttrs) {
516
+ const target = attr.target || '';
517
+ const isEvent = target.startsWith('on') && target.length > 2;
518
+ if (target === 'ref') {
519
+ const expr = transformTracked(attr.expression, tracked);
520
+ ctx.push(`(${expr})(${ssrEl});`);
521
+ }
522
+ else if (isEvent) {
523
+ const eventName = target.slice(2).toLowerCase();
524
+ const handler = transformTracked(attr.expression, tracked);
525
+ if (NON_BUBBLING_EVENTS.has(eventName)) {
526
+ ctx.directEvents.add(eventName);
527
+ ctx.push(`${ssrEl}.addEventListener(${JSON.stringify(eventName)}, ${handler});`);
528
+ }
529
+ else {
530
+ const prop = `__evh_${eventName}`;
531
+ ctx.delegatedEvents.add(eventName);
532
+ ctx.push(`${ssrEl}.${prop} = ${handler};`);
533
+ }
534
+ ctx.push(`${ssrEl}.setAttribute('data-vsk-ev', '');`);
535
+ }
536
+ else if (target === 'style') {
537
+ const expr = transformTracked(attr.expression, tracked);
538
+ const eff = `effect(() => { applyStyle(${ssrEl}, ${expr}); })`;
539
+ if (effectsVar) {
540
+ ctx.push(`${effectsVar}.push(${eff});`);
541
+ }
542
+ else {
543
+ ctx.effects.push(`${eff};`);
544
+ }
545
+ }
546
+ else {
547
+ const expr = transformTracked(attr.expression, tracked);
548
+ const useProp = PROPERTY_ATTRS[node.tag]?.has(target);
549
+ const eff = useProp
550
+ ? `effect(() => { ${ssrEl}.${target} = ${expr}; })`
551
+ : `effect(() => { const __v = ${expr}; if (__v != null && __v !== false) ${ssrEl}.setAttribute(${JSON.stringify(target)}, __v === true ? 'true' : String(__v)); })`;
552
+ if (effectsVar) {
553
+ ctx.push(`${effectsVar}.push(${eff});`);
554
+ }
555
+ else {
556
+ ctx.effects.push(`${eff};`);
557
+ }
558
+ }
559
+ }
560
+ // The subtree is fully static (isStaticIR), so it carries no dynamic
561
+ // attributes, events, or text — every descendant is already present in
562
+ // the SSR element. Re-emitting fresh text nodes here would append
563
+ // duplicates after the SSR text (the SSR <h1> "compiler is the
564
+ // product." regression). Keep the element as-is and don't recurse.
565
+ childVar = ssrEl;
566
+ }
567
+ else if (singleReactiveText && child instanceof DynamicBinding && child.kind === 'text') {
497
568
  childVar = emitDynamicBinding(ctx, child, tracked, effectsVar, `${el}.__vsk_ssrText`);
498
569
  }
499
570
  else {
@@ -2180,7 +2251,7 @@ function emitClientFromIR(ir, options) {
2180
2251
  if (options.hydrate && ir.components.some(c => hasKeyedMap(c.body)))
2181
2252
  runtimeNames.push('reconcileHydrated');
2182
2253
  if (options.hydrate) {
2183
- const hydrateNames = ['hydrate', 'hydrateViewport', 'hydrateIdle', 'hydrateOnInteraction', 'needsHydration', 'createHydrateWalker', 'collectVskMarkers', 'reactiveProps'];
2254
+ const hydrateNames = ['hydrate', 'hydrateViewport', 'hydrateIdle', 'hydrateOnInteraction', 'needsHydration', 'createHydrateWalker', 'collectVskMarkers', 'reactiveProps', 'createLayoutSlot'];
2184
2255
  for (const name of hydrateNames) {
2185
2256
  if (!runtimeNames.includes(name))
2186
2257
  runtimeNames.push(name);
@@ -2227,14 +2298,14 @@ export function compileClient(source, _componentName, options = {}) {
2227
2298
  * `resolveComponentName`, so callers don't need a second full parse just
2228
2299
  * to learn the name.
2229
2300
  */
2230
- export function compileClientBoth(source, _componentName, sourcePath) {
2301
+ export function compileClientBoth(source, _componentName, sourcePath, opts) {
2231
2302
  const ast = parse(source, sourcePath ? { filename: sourcePath } : {});
2232
2303
  // Downstream type-stripping mutates AST nodes in place (stripTsTypes),
2233
2304
  // so each emit mode needs its own tree. Cloning is far cheaper than the
2234
2305
  // second full acorn+TS parse this replaces.
2235
- const hydAst = structuredClone(ast);
2306
+ const hydAst = opts?.skipHyd ? null : structuredClone(ast);
2236
2307
  const ir = generateIR(ast, source, sourcePath);
2237
- const irHyd = generateIR(hydAst, source, sourcePath);
2308
+ const irHyd = opts?.skipHyd ? null : generateIR(hydAst, source, sourcePath);
2238
2309
  const defaultComp = ir.components.find((c) => c.defaultExport);
2239
2310
  let name = null;
2240
2311
  if (defaultComp)
@@ -2249,8 +2320,11 @@ export function compileClientBoth(source, _componentName, sourcePath) {
2249
2320
  // Comp and hyd land in the same scoped file block, so they share one
2250
2321
  // allocator: hyd continues after the names comp already consumed.
2251
2322
  const alloc = nameAllocFor(sourcePath);
2323
+ const comp = emitClientFromIR(ir, { forceClient: true, nameAllocator: alloc });
2324
+ if (opts?.skipHyd)
2325
+ return { comp, hyd: '', name };
2252
2326
  return {
2253
- comp: emitClientFromIR(ir, { forceClient: true, nameAllocator: alloc }),
2327
+ comp,
2254
2328
  hyd: emitClientFromIR(irHyd, { forceClient: true, hydrate: true, includeTopLevel: false, nameAllocator: alloc }),
2255
2329
  name,
2256
2330
  };
@@ -1 +1 @@
1
- {"version":3,"file":"server-jsgen.d.ts","sourceRoot":"","sources":["../src/server-jsgen.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAOzE,OAAO,EAAkG,KAAK,WAAW,EAAE,MAAM,mCAAmC,CAAC;AAmDrK,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,EAAE,OAAO,GAAE,OAAe,EAAE,OAAO,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,MAAM,CAqDjJ;AAkXD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,WAAW,EAAE,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,CAqD1F;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,iBAAiB,EAAE,CA4BzE;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,GAAG,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAYhG"}
1
+ {"version":3,"file":"server-jsgen.d.ts","sourceRoot":"","sources":["../src/server-jsgen.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAOzE,OAAO,EAAkG,KAAK,WAAW,EAAE,MAAM,mCAAmC,CAAC;AAmDrK,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,EAAE,OAAO,GAAE,OAAe,EAAE,OAAO,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,MAAM,CA2DjJ;AAkXD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,WAAW,EAAE,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,CAqD1F;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,iBAAiB,EAAE,CA4BzE;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,GAAG,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAYhG"}
@@ -48,7 +48,10 @@ export function irNodeToJS(node, importedNames, isAsync = false, tracked) {
48
48
  if (node instanceof TextNode) {
49
49
  if (!node.value)
50
50
  return '';
51
- return `__out.push(${JSON.stringify(node.value)});`;
51
+ // The JSX parser entity-decodes text (`&lt;style&gt;` -> `<style>`), so the
52
+ // decoded value must be re-escaped for HTML text context. Emitting it raw
53
+ // would turn &lt;` escapes into real start tags and break the parse.
54
+ return `__out.push(${JSON.stringify(escapeHtml(node.value))});`;
52
55
  }
53
56
  if (node instanceof DynamicBinding)
54
57
  return dynamicBindingToJS(node, tracked);
@@ -105,8 +108,10 @@ export function irNodeToJS(node, importedNames, isAsync = false, tracked) {
105
108
  }
106
109
  if (node instanceof RuntimeStatement)
107
110
  return semicolonizeStatement(transformTracked(node, tracked || new Map()));
108
- if (node instanceof SlotNode)
109
- return `__out.push(props.children || '');`;
111
+ if (node instanceof SlotNode) {
112
+ const sid = `s${nextVskId()}`;
113
+ return `__out.push('<!--vsk-slot:${sid}-->');__out.push(props.children || '');__out.push('<!--vsk-slot-end:${sid}-->');`;
114
+ }
110
115
  if (node instanceof PropSlotRender)
111
116
  return `__out.push(props.${node.propName} || '');`;
112
117
  if (node instanceof PropSlot)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vesk/compiler",
3
- "version": "0.2.28",
3
+ "version": "0.2.30",
4
4
  "type": "module",
5
5
  "description": "Vesk compiler — lexer, parser, semantic analysis, IR, codegen",
6
6
  "main": "./dist/index.js",
@@ -20,7 +20,7 @@
20
20
  },
21
21
  "dependencies": {
22
22
  "@sveltejs/acorn-typescript": "^1.0.0",
23
- "@vesk/types": "^0.2.28",
23
+ "@vesk/types": "^0.2.30",
24
24
  "acorn": "^8.15.0",
25
25
  "esrap": "^2.3.0",
26
26
  "is-reference": "^3.0.0",