@vesk/compiler 0.2.27 → 0.2.29
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 +3 -1
- package/dist/client-codegen.d.ts.map +1 -1
- package/dist/client-codegen.js +82 -8
- package/dist/ir-generator.d.ts.map +1 -1
- package/dist/ir-generator.js +96 -76
- package/dist/server-jsgen.d.ts.map +1 -1
- package/dist/server-jsgen.js +8 -3
- package/package.json +2 -2
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":"ir-generator.d.ts","sourceRoot":"","sources":["../src/ir-generator.ts"],"names":[],"mappings":"AACA,OAAO,EACL,MAAM,EAsBP,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"ir-generator.d.ts","sourceRoot":"","sources":["../src/ir-generator.ts"],"names":[],"mappings":"AACA,OAAO,EACL,MAAM,EAsBP,MAAM,uBAAuB,CAAC;AA0S/B,wBAAgB,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,GAAG,CAAC;IAAC,cAAc,CAAC,EAAE,GAAG,CAAC;IAAC,UAAU,CAAC,EAAE,GAAG,EAAE,CAAA;CAAE,CAAC,GAAG,SAAS,GAAG,IAAI,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAkCnL;AA07BD,wBAAgB,UAAU,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAwN9E"}
|
package/dist/ir-generator.js
CHANGED
|
@@ -5,7 +5,7 @@ import { skipWhitespace, findBalancedEnd, splitTopLevel, startsWithIdentifier, s
|
|
|
5
5
|
import { stripCodeTypes } from '@vesk/compiler/src/strip-ts';
|
|
6
6
|
import { stripTypeImport } from '@vesk/compiler/src/vsk-imports';
|
|
7
7
|
import { importBindingPairs } from '@vesk/compiler/src/module-imports';
|
|
8
|
-
import {
|
|
8
|
+
import { extractImportNames, importModuleTarget } from '@vesk/compiler/src/tokens';
|
|
9
9
|
let __vskAnnotations = [];
|
|
10
10
|
/**
|
|
11
11
|
* Prop names of the component currently being processed that are declared as
|
|
@@ -168,6 +168,92 @@ function estreeCallsFetch(ast) {
|
|
|
168
168
|
}
|
|
169
169
|
return false;
|
|
170
170
|
}
|
|
171
|
+
/**
|
|
172
|
+
* The simple identifier a member chain *ends* in (`a.b.c` → `c`), or null when
|
|
173
|
+
* the object doesn't end in a bare name token — mirrors the tokenizer's "name
|
|
174
|
+
* two tokens before the call paren" rule so `a.b(` and `a?.b(` surface `a`,
|
|
175
|
+
* `a.b.c(` surfaces `b`, while `this.b(`, `a[0].b(` and `a(x).b(` surface
|
|
176
|
+
* nothing.
|
|
177
|
+
*/
|
|
178
|
+
function memberReceiverName(obj) {
|
|
179
|
+
if (!obj || typeof obj !== 'object')
|
|
180
|
+
return null;
|
|
181
|
+
const rec = obj;
|
|
182
|
+
if (rec.type === 'Identifier' && typeof rec.name === 'string')
|
|
183
|
+
return rec.name;
|
|
184
|
+
const prop = rec.type === 'MemberExpression' && !rec.computed ? rec.property : null;
|
|
185
|
+
if (prop && typeof prop === 'object' && prop.type === 'Identifier') {
|
|
186
|
+
const propName = prop.name;
|
|
187
|
+
if (typeof propName === 'string')
|
|
188
|
+
return propName;
|
|
189
|
+
}
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Single-pass ESTree walk collecting every simple call target in `ast` —
|
|
194
|
+
* identifiers invoked directly (`fn(` / `fn<T>(`, constructor calls included)
|
|
195
|
+
* and the receiver name of a bare member call (`a.b(` → `a`, `a.b?.c(` → `b`)
|
|
196
|
+
* — plus every JSX element tag name. Mirrors the semantics of
|
|
197
|
+
* `collectCalledIdentifiers` over the equivalent text, so one walk of the
|
|
198
|
+
* already-parsed tree replaces re-tokenizing each IR raw fragment.
|
|
199
|
+
*/
|
|
200
|
+
function collectCallAndJsxTargets(ast) {
|
|
201
|
+
const targets = new Set();
|
|
202
|
+
const stack = [ast];
|
|
203
|
+
while (stack.length > 0) {
|
|
204
|
+
const candidate = stack.pop();
|
|
205
|
+
if (!candidate || typeof candidate !== 'object')
|
|
206
|
+
continue;
|
|
207
|
+
const node = candidate;
|
|
208
|
+
if (typeof node.type === 'string') {
|
|
209
|
+
if (node.type === 'CallExpression' || node.type === 'NewExpression') {
|
|
210
|
+
const callee = node.callee;
|
|
211
|
+
if (callee && typeof callee === 'object') {
|
|
212
|
+
const c = callee;
|
|
213
|
+
if (c.type === 'Identifier' && typeof c.name === 'string' && node.optional !== true) {
|
|
214
|
+
targets.add(c.name);
|
|
215
|
+
}
|
|
216
|
+
else if (c.type === 'MemberExpression' && !c.computed && node.optional !== true) {
|
|
217
|
+
const receiverName = memberReceiverName(c.object);
|
|
218
|
+
if (receiverName)
|
|
219
|
+
targets.add(receiverName);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
else if (node.type === 'JSXElement') {
|
|
224
|
+
const opening = node.openingElement;
|
|
225
|
+
const tag = opening && typeof opening === 'object' ? opening.name : null;
|
|
226
|
+
if (tag && typeof tag === 'object') {
|
|
227
|
+
const t = tag;
|
|
228
|
+
if (t.type === 'JSXIdentifier' && typeof t.name === 'string') {
|
|
229
|
+
targets.add(t.name);
|
|
230
|
+
}
|
|
231
|
+
else if (t.type === 'JSXMemberExpression') {
|
|
232
|
+
const prop = t.property;
|
|
233
|
+
if (prop && typeof prop === 'object' && prop.type === 'JSXIdentifier') {
|
|
234
|
+
const propName = prop.name;
|
|
235
|
+
if (typeof propName === 'string')
|
|
236
|
+
targets.add(propName);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
for (const key of Object.keys(node)) {
|
|
242
|
+
if (key === 'parent' || key === 'loc' || key === 'start' || key === 'end' || key === 'range')
|
|
243
|
+
continue;
|
|
244
|
+
const value = node[key];
|
|
245
|
+
if (Array.isArray(value)) {
|
|
246
|
+
for (const item of value)
|
|
247
|
+
stack.push(item);
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
stack.push(value);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return targets;
|
|
256
|
+
}
|
|
171
257
|
function componentUsesFetch(nodes) {
|
|
172
258
|
for (const node of nodes) {
|
|
173
259
|
if (node instanceof ServerBlock || node instanceof ClientBlock) {
|
|
@@ -1470,83 +1556,17 @@ export function generateIR(ast, source, filename) {
|
|
|
1470
1556
|
'createResource', 'getAction', 'validateActionInput', 'issuesToFieldMap', 'isFormAction',
|
|
1471
1557
|
'Show', 'For', 'Switch', 'Match',
|
|
1472
1558
|
];
|
|
1559
|
+
// One walk of the already-parsed tree finds every auto-importable call
|
|
1560
|
+
// target and JSX element name. Every IR raw the old per-fragment scan
|
|
1561
|
+
// re-tokenized ("addUsedFrom" over ~15 text slices) is a subtree of this
|
|
1562
|
+
// very same `ast` — nothing is lost, but the acorn tokenizer is invoked once
|
|
1563
|
+
// per file instead of once per fragment.
|
|
1564
|
+
const called = collectCallAndJsxTargets(ast);
|
|
1473
1565
|
const usedFunctions = new Set();
|
|
1474
|
-
const
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
if (called.has(fn))
|
|
1478
|
-
usedFunctions.add(fn);
|
|
1479
|
-
}
|
|
1480
|
-
};
|
|
1481
|
-
for (const code of topLevelCode)
|
|
1482
|
-
addUsedFrom(code);
|
|
1483
|
-
for (const code of [loadFn, staticProps]) {
|
|
1484
|
-
if (code)
|
|
1485
|
-
addUsedFrom(code);
|
|
1486
|
-
}
|
|
1487
|
-
function scanForAutoImport(nodes) {
|
|
1488
|
-
for (const node of nodes) {
|
|
1489
|
-
if (node instanceof RuntimeStatement && node.raw) {
|
|
1490
|
-
addUsedFrom(node.raw);
|
|
1491
|
-
}
|
|
1492
|
-
if (node instanceof TrackDecl && node.init) {
|
|
1493
|
-
addUsedFrom(node.init);
|
|
1494
|
-
}
|
|
1495
|
-
if (node instanceof ForLoop) {
|
|
1496
|
-
if (node.init)
|
|
1497
|
-
addUsedFrom(node.init);
|
|
1498
|
-
if (node.update)
|
|
1499
|
-
addUsedFrom(node.update);
|
|
1500
|
-
if (node.condition && node.condition.raw)
|
|
1501
|
-
addUsedFrom(node.condition.raw);
|
|
1502
|
-
scanForAutoImport(node.bodyTemplate);
|
|
1503
|
-
}
|
|
1504
|
-
if (node instanceof WhileLoop) {
|
|
1505
|
-
if (node.condition && node.condition.raw)
|
|
1506
|
-
addUsedFrom(node.condition.raw);
|
|
1507
|
-
scanForAutoImport(node.bodyTemplate);
|
|
1508
|
-
}
|
|
1509
|
-
if (node instanceof SwitchBlock) {
|
|
1510
|
-
if (node.discriminant && node.discriminant.raw)
|
|
1511
|
-
addUsedFrom(node.discriminant.raw);
|
|
1512
|
-
for (const c of node.cases) {
|
|
1513
|
-
if (c.test && c.test.raw)
|
|
1514
|
-
addUsedFrom(c.test.raw);
|
|
1515
|
-
scanForAutoImport(c.body);
|
|
1516
|
-
}
|
|
1517
|
-
}
|
|
1518
|
-
if (node instanceof TryCatch) {
|
|
1519
|
-
scanForAutoImport(node.bodyTemplate);
|
|
1520
|
-
scanForAutoImport(node.catchBody);
|
|
1521
|
-
}
|
|
1522
|
-
if (node instanceof ComponentCall) {
|
|
1523
|
-
if (autoImportable.includes(node.componentName)) {
|
|
1524
|
-
usedFunctions.add(node.componentName);
|
|
1525
|
-
}
|
|
1526
|
-
for (const prop of node.props) {
|
|
1527
|
-
if (prop.value && prop.value.raw) {
|
|
1528
|
-
addUsedFrom(prop.value.raw);
|
|
1529
|
-
}
|
|
1530
|
-
}
|
|
1531
|
-
scanForAutoImport(node.children);
|
|
1532
|
-
}
|
|
1533
|
-
if (node instanceof MapRegion) {
|
|
1534
|
-
scanForAutoImport(node.bodyTemplate);
|
|
1535
|
-
scanForAutoImport(node.alternateNodes);
|
|
1536
|
-
}
|
|
1537
|
-
if (node instanceof OpaqueDynamicRegion) {
|
|
1538
|
-
scanForAutoImport(node.consequentNodes);
|
|
1539
|
-
scanForAutoImport(node.alternateNodes);
|
|
1540
|
-
}
|
|
1541
|
-
if (node instanceof DynamicBinding && node.expression && node.expression.raw) {
|
|
1542
|
-
addUsedFrom(node.expression.raw);
|
|
1543
|
-
}
|
|
1544
|
-
if (node instanceof StaticNode || node instanceof ServerBlock || node instanceof ClientBlock || node instanceof HeadBlock) {
|
|
1545
|
-
scanForAutoImport(node.children);
|
|
1546
|
-
}
|
|
1547
|
-
}
|
|
1566
|
+
for (const fn of autoImportable) {
|
|
1567
|
+
if (called.has(fn))
|
|
1568
|
+
usedFunctions.add(fn);
|
|
1548
1569
|
}
|
|
1549
|
-
scanForAutoImport(components.flatMap(c => c.body));
|
|
1550
1570
|
if (usedFunctions.size > 0) {
|
|
1551
1571
|
const existing = new Set();
|
|
1552
1572
|
for (const imp of imports) {
|
|
@@ -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.29",
|
|
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.29",
|
|
24
24
|
"acorn": "^8.15.0",
|
|
25
25
|
"esrap": "^2.3.0",
|
|
26
26
|
"is-reference": "^3.0.0",
|