@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.
package/dist/client-codegen.d.ts
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/client-codegen.js
CHANGED
|
@@ -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
|
|
387
|
-
ctx.push(`
|
|
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
|
-
|
|
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
|
|
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,
|
|
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"}
|
package/dist/server-jsgen.js
CHANGED
|
@@ -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
|
-
|
|
51
|
+
// The JSX parser entity-decodes text (`<style>` -> `<style>`), so the
|
|
52
|
+
// decoded value must be re-escaped for HTML text context. Emitting it raw
|
|
53
|
+
// would turn <` 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
|
-
|
|
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.
|
|
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.
|
|
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",
|