@vesk/compiler 0.2.47 → 0.2.49
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/server-head.d.ts +14 -1
- package/dist/server-head.d.ts.map +1 -1
- package/dist/server-head.js +218 -17
- package/dist/server-render.d.ts.map +1 -1
- package/dist/server-render.js +5 -3
- package/package.json +2 -2
package/dist/server-head.d.ts
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
import type { ComponentIR } from '@vesk/compiler/src/ir';
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Serialize one component's `<Head>` block.
|
|
4
|
+
*
|
|
5
|
+
* `scope` is the component's `__vesk` bindings (imports, runtime names, the
|
|
6
|
+
* sub-components its body destructures). The head is assembled by re-walking
|
|
7
|
+
* the IR and re-evaluating the expressions rather than by capturing the real
|
|
8
|
+
* render, so the re-evaluation needs the same bindings the component had —
|
|
9
|
+
* without them every expression that touched an import threw and was swallowed,
|
|
10
|
+
* and the served document shipped an empty `<title>`/`<meta>` while the page
|
|
11
|
+
* body rendered fine (and the client corrected it on hydration, which is why
|
|
12
|
+
* this only ever showed up in view-source, in crawlers, and after an SPA nav
|
|
13
|
+
* re-applied the server head).
|
|
14
|
+
*/
|
|
15
|
+
export declare function renderHeadHtml(comp: ComponentIR, props?: Record<string, unknown>, scope?: Record<string, unknown>): string;
|
|
3
16
|
export declare function mergeHeadHtml(pageHead: string, layoutHead: string): {
|
|
4
17
|
html: string;
|
|
5
18
|
conflicts: Array<{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-head.d.ts","sourceRoot":"","sources":["../src/server-head.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"server-head.d.ts","sourceRoot":"","sources":["../src/server-head.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAgVzD;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,WAAW,EACjB,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACnC,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAClC,MAAM,CAkBR;AAoFD,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,CAkExI"}
|
package/dist/server-head.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import { StaticNode, TextNode, DynamicBinding, HeadBlock, RuntimeStatement } from '@vesk/compiler/src/ir';
|
|
1
|
+
import { StaticNode, TextNode, DynamicBinding, HeadBlock, RuntimeStatement, TrackDecl } from '@vesk/compiler/src/ir';
|
|
2
2
|
import { tryEvalExpr, escapeHtml } from '@vesk/compiler/src/server-utils';
|
|
3
3
|
import { isWhitespaceChar } from '@vesk/compiler/src/scan';
|
|
4
|
+
import { parse } from '@vesk/compiler/src/parser';
|
|
5
|
+
import { collectTrackedNames, transformTracked, transformTrackedInit } from '@vesk/compiler/src/client-codegen';
|
|
6
|
+
import { walk } from 'zimmerframe';
|
|
4
7
|
function isHtmlNameChar(ch) {
|
|
5
8
|
const code = ch.charCodeAt(0);
|
|
6
9
|
return ((code >= 48 && code <= 57) || (code >= 65 && code <= 90) || (code >= 97 && code <= 122) || ch === '-');
|
|
@@ -11,22 +14,206 @@ function isAttrBoundaryChar(ch) {
|
|
|
11
14
|
function isValueBoundaryChar(ch) {
|
|
12
15
|
return isWhitespaceChar(ch) || ch === '/' || ch === '>';
|
|
13
16
|
}
|
|
14
|
-
function
|
|
17
|
+
function headCell(value) {
|
|
18
|
+
const cell = {
|
|
19
|
+
__vskHeadValue: value,
|
|
20
|
+
toString() { return String(cell.__vskHeadValue); },
|
|
21
|
+
// Present so `+count` / `String(count)` on the cell itself still yields the
|
|
22
|
+
// value; the return type is widened because `valueOf` must return `this`.
|
|
23
|
+
valueOf() { return cell; },
|
|
24
|
+
};
|
|
25
|
+
return cell;
|
|
26
|
+
}
|
|
27
|
+
const headRuntimeShims = () => ({
|
|
28
|
+
track: (init) => headCell(init),
|
|
29
|
+
derived: (fn) => headCell(typeof fn === 'function' ? fn() : fn),
|
|
30
|
+
get: (cell) => cell && typeof cell === 'object' && '__vskHeadValue' in cell
|
|
31
|
+
? cell.__vskHeadValue
|
|
32
|
+
: cell,
|
|
33
|
+
peek: (cell) => cell && typeof cell === 'object' && '__vskHeadValue' in cell
|
|
34
|
+
? cell.__vskHeadValue
|
|
35
|
+
: cell,
|
|
36
|
+
untrack: (fn) => (typeof fn === 'function' ? fn() : fn),
|
|
37
|
+
});
|
|
38
|
+
function headEvalScope(ctx) {
|
|
39
|
+
// The shims come LAST: they must win over the component's real `track`/`get`
|
|
40
|
+
// from `__vesk`, which would otherwise allocate real cells.
|
|
41
|
+
return { ...ctx.scope, ...ctx.locals, ...headRuntimeShims() };
|
|
42
|
+
}
|
|
43
|
+
function evalHeadExpression(expression, ctx) {
|
|
44
|
+
// `{count}` must resolve to the VALUE, not the cell: rewrite tracked reads the
|
|
45
|
+
// same way the client codegen does, then evaluate with the shims bound.
|
|
46
|
+
const code = transformTracked(expression, ctx.tracked);
|
|
47
|
+
return tryEvalExpr(code, ctx.props, headEvalScope(ctx));
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Evaluate `raw` with `props` plus every name in `scope` bound to its value.
|
|
51
|
+
*
|
|
52
|
+
* The head pass re-evaluates a component's top-level declarations outside the
|
|
53
|
+
* component's own call frame, so it has to rebuild that frame. Passing the
|
|
54
|
+
* component's `__vesk` bindings as named parameters is what makes imports
|
|
55
|
+
* visible: a docs page's `const doc = getDoc(props.params.slug)` needs
|
|
56
|
+
* `getDoc`, which lives in `__vesk` (the generated body destructures it from
|
|
57
|
+
* there), and without it the head silently degraded to empty strings.
|
|
58
|
+
*/
|
|
59
|
+
function evalWithScope(raw, props, scope) {
|
|
60
|
+
const names = Object.keys(scope);
|
|
61
|
+
const fn = new Function('props', ...names, 'return (' + raw + ')');
|
|
62
|
+
return fn(props, ...names.map((n) => scope[n]));
|
|
63
|
+
}
|
|
64
|
+
/** Identifier reads of an ESTree expression (not property keys, not `obj.prop`). */
|
|
65
|
+
function identifiersOf(ast, into) {
|
|
66
|
+
if (!ast || typeof ast !== 'object')
|
|
67
|
+
return;
|
|
68
|
+
walk(ast, null, {
|
|
69
|
+
Identifier(n, context) {
|
|
70
|
+
const parent = context.path.at(-1);
|
|
71
|
+
if (parent) {
|
|
72
|
+
if (parent.type === 'MemberExpression' && !parent.computed && parent.property === n)
|
|
73
|
+
return context.next();
|
|
74
|
+
if (parent.type === 'Property' && parent.key === n)
|
|
75
|
+
return context.next();
|
|
76
|
+
}
|
|
77
|
+
into.add(n.name);
|
|
78
|
+
return context.next();
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
/** Every interpolated/attribute expression inside a `<Head>` subtree. */
|
|
83
|
+
function collectHeadExprs(node, out) {
|
|
84
|
+
if (node instanceof StaticNode) {
|
|
85
|
+
for (const child of node.children) {
|
|
86
|
+
if (child instanceof DynamicBinding)
|
|
87
|
+
out.push(child.expression);
|
|
88
|
+
else
|
|
89
|
+
collectHeadExprs(child, out);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/** Top-level declarations of a component: tracked cells and `const` locals. */
|
|
94
|
+
function declaredNames(comp) {
|
|
95
|
+
const out = new Map();
|
|
96
|
+
for (const node of comp.body) {
|
|
97
|
+
if (node instanceof TrackDecl) {
|
|
98
|
+
const cellName = node.rawName || node.name;
|
|
99
|
+
let initAst = null;
|
|
100
|
+
try {
|
|
101
|
+
initAst = parse(node.init);
|
|
102
|
+
}
|
|
103
|
+
catch { /* unparsable init: no deps to follow */ }
|
|
104
|
+
const entry = { kind: 'track', initSrc: node.init, initAst, names: [cellName] };
|
|
105
|
+
out.set(cellName, entry);
|
|
106
|
+
if (node.name !== cellName) {
|
|
107
|
+
out.set(node.name, entry);
|
|
108
|
+
entry.names.push(node.name);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
else if (node instanceof RuntimeStatement && node.ast) {
|
|
112
|
+
const stmt = node.ast;
|
|
113
|
+
if (stmt.type !== 'VariableDeclaration')
|
|
114
|
+
continue;
|
|
115
|
+
for (const decl of stmt.declarations) {
|
|
116
|
+
if (decl.id.type !== 'Identifier' || !decl.init || !node.source)
|
|
117
|
+
continue;
|
|
118
|
+
out.set(decl.id.name, {
|
|
119
|
+
kind: 'const',
|
|
120
|
+
initSrc: node.source.slice(decl.init.start, decl.init.end),
|
|
121
|
+
initAst: decl.init,
|
|
122
|
+
names: [decl.id.name],
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return out;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* The transitive closure of top-level names the `<Head>` block reads.
|
|
131
|
+
*
|
|
132
|
+
* THE head pass must evaluate only what the head uses. Evaluating every
|
|
133
|
+
* top-level declaration (which is what it used to do) means re-running the
|
|
134
|
+
* component's SIDE EFFECTS in the rebuilt frame: a page whose body does
|
|
135
|
+
* `const data = useFetch('/api/fail')` fetched twice per render, because the
|
|
136
|
+
* head pass called `useFetch` a second time now that the runtime names are in
|
|
137
|
+
* scope. Imports and tracked reads are still resolved — just the declarations
|
|
138
|
+
* the head actually needs, plus the ones those depend on.
|
|
139
|
+
*/
|
|
140
|
+
function headNeededNames(comp) {
|
|
141
|
+
const wanted = new Set();
|
|
142
|
+
for (const node of comp.body) {
|
|
143
|
+
if (!(node instanceof HeadBlock))
|
|
144
|
+
continue;
|
|
145
|
+
const exprs = [];
|
|
146
|
+
for (const child of node.children)
|
|
147
|
+
collectHeadExprs(child, exprs);
|
|
148
|
+
for (const e of exprs)
|
|
149
|
+
identifiersOf(e.ast, wanted);
|
|
150
|
+
}
|
|
151
|
+
const declared = declaredNames(comp);
|
|
152
|
+
const needed = new Set();
|
|
153
|
+
const queue = [...wanted].filter((n) => declared.has(n));
|
|
154
|
+
while (queue.length > 0) {
|
|
155
|
+
const name = queue.shift();
|
|
156
|
+
if (needed.has(name))
|
|
157
|
+
continue;
|
|
158
|
+
needed.add(name);
|
|
159
|
+
const entry = declared.get(name);
|
|
160
|
+
if (!entry)
|
|
161
|
+
continue;
|
|
162
|
+
const deps = new Set();
|
|
163
|
+
identifiersOf(entry.initAst, deps);
|
|
164
|
+
for (const d of deps) {
|
|
165
|
+
// A tracked init reads other cells through the runtime, so follow the
|
|
166
|
+
// tracked names the init mentions.
|
|
167
|
+
if (declared.has(d) && !needed.has(d))
|
|
168
|
+
queue.push(d);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return needed;
|
|
172
|
+
}
|
|
173
|
+
function evaluateLocals(comp, props, scope = {}, tracked = new Map(), needed = null) {
|
|
15
174
|
const locals = {};
|
|
175
|
+
const wanted = (name) => needed === null || needed.has(name);
|
|
16
176
|
for (const node of comp.body) {
|
|
177
|
+
if (node instanceof TrackDecl) {
|
|
178
|
+
// `const [count, setCount] = track(0)`: bind every name the declaration
|
|
179
|
+
// introduces to a head cell, so a later local or head expression that
|
|
180
|
+
// reads it sees the current value through the shims.
|
|
181
|
+
const cellName = node.rawName || node.name;
|
|
182
|
+
if (!wanted(cellName) && !wanted(node.name))
|
|
183
|
+
continue;
|
|
184
|
+
const init = transformTrackedInit(node.init, tracked);
|
|
185
|
+
let value;
|
|
186
|
+
try {
|
|
187
|
+
value = evalWithScope(init, props, { ...scope, ...locals, ...headRuntimeShims() });
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
value = undefined;
|
|
191
|
+
}
|
|
192
|
+
const cell = headCell(value);
|
|
193
|
+
if (node.name !== cellName)
|
|
194
|
+
locals[node.name] = cell;
|
|
195
|
+
locals[cellName] = cell;
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
17
198
|
if (node instanceof RuntimeStatement && node.ast) {
|
|
18
199
|
const stmt = node.ast;
|
|
19
200
|
if (stmt.type === 'VariableDeclaration') {
|
|
20
201
|
for (const decl of stmt.declarations) {
|
|
21
202
|
if (decl.id.type === 'Identifier' && decl.init && node.source) {
|
|
22
203
|
const name = decl.id.name;
|
|
204
|
+
if (!wanted(name))
|
|
205
|
+
continue;
|
|
23
206
|
const initSrc = node.source.slice(decl.init.start, decl.init.end);
|
|
24
207
|
try {
|
|
25
|
-
|
|
26
|
-
|
|
208
|
+
locals[name] = evalWithScope(initSrc, props, {
|
|
209
|
+
...scope,
|
|
210
|
+
...locals,
|
|
211
|
+
...headRuntimeShims(),
|
|
212
|
+
});
|
|
27
213
|
}
|
|
28
214
|
catch {
|
|
29
|
-
// expression can't be evaluated — skip
|
|
215
|
+
// expression can't be evaluated — skip (its dependents degrade
|
|
216
|
+
// to empty, exactly as they did before the scope was passed)
|
|
30
217
|
}
|
|
31
218
|
}
|
|
32
219
|
}
|
|
@@ -35,7 +222,7 @@ function evaluateLocals(comp, props) {
|
|
|
35
222
|
}
|
|
36
223
|
return locals;
|
|
37
224
|
}
|
|
38
|
-
function headElementKey(node,
|
|
225
|
+
function headElementKey(node, ctx) {
|
|
39
226
|
if (!(node instanceof StaticNode))
|
|
40
227
|
return null;
|
|
41
228
|
const tag = node.tag;
|
|
@@ -47,7 +234,7 @@ function headElementKey(node, props, locals) {
|
|
|
47
234
|
for (const child of node.children) {
|
|
48
235
|
if (child instanceof DynamicBinding && child.kind === 'attribute' && child.target && child.target !== 'ref') {
|
|
49
236
|
try {
|
|
50
|
-
attrMap.set(child.target, String(
|
|
237
|
+
attrMap.set(child.target, String(evalHeadExpression(child.expression, ctx)));
|
|
51
238
|
}
|
|
52
239
|
catch { /* skip */ }
|
|
53
240
|
}
|
|
@@ -88,14 +275,13 @@ function headIsReconcilable(tag, attrMap) {
|
|
|
88
275
|
return false;
|
|
89
276
|
return true;
|
|
90
277
|
}
|
|
91
|
-
function irNodeToHeadHtml(node,
|
|
278
|
+
function irNodeToHeadHtml(node, ctx, reconcileable = false) {
|
|
92
279
|
if (node instanceof StaticNode) {
|
|
93
280
|
const attrMap = new Map(node.attributes.map((a) => [a.name, a.value]));
|
|
94
281
|
for (const child of node.children) {
|
|
95
282
|
if (child instanceof DynamicBinding && child.kind === 'attribute' && child.target && child.target !== 'ref') {
|
|
96
283
|
try {
|
|
97
|
-
|
|
98
|
-
attrMap.set(child.target, String(val));
|
|
284
|
+
attrMap.set(child.target, String(evalHeadExpression(child.expression, ctx)));
|
|
99
285
|
}
|
|
100
286
|
catch { /* skip */ }
|
|
101
287
|
}
|
|
@@ -108,7 +294,7 @@ function irNodeToHeadHtml(node, props, locals = {}, reconcileable = false) {
|
|
|
108
294
|
return `<${node.tag}${attrs}${marker} />`;
|
|
109
295
|
const inner = node.children
|
|
110
296
|
.filter((c) => !(c instanceof DynamicBinding && c.kind === 'attribute' && c.target !== 'ref'))
|
|
111
|
-
.map((c) => irNodeToHeadHtml(c,
|
|
297
|
+
.map((c) => irNodeToHeadHtml(c, ctx, false))
|
|
112
298
|
.join('');
|
|
113
299
|
return `<${node.tag}${attrs}${marker}>${inner}</${node.tag}>`;
|
|
114
300
|
}
|
|
@@ -116,8 +302,7 @@ function irNodeToHeadHtml(node, props, locals = {}, reconcileable = false) {
|
|
|
116
302
|
return node.value;
|
|
117
303
|
if (node instanceof DynamicBinding) {
|
|
118
304
|
try {
|
|
119
|
-
|
|
120
|
-
return escapeHtml(String(val));
|
|
305
|
+
return escapeHtml(String(evalHeadExpression(node.expression, ctx)));
|
|
121
306
|
}
|
|
122
307
|
catch {
|
|
123
308
|
return '';
|
|
@@ -125,19 +310,35 @@ function irNodeToHeadHtml(node, props, locals = {}, reconcileable = false) {
|
|
|
125
310
|
}
|
|
126
311
|
return '';
|
|
127
312
|
}
|
|
128
|
-
|
|
129
|
-
|
|
313
|
+
/**
|
|
314
|
+
* Serialize one component's `<Head>` block.
|
|
315
|
+
*
|
|
316
|
+
* `scope` is the component's `__vesk` bindings (imports, runtime names, the
|
|
317
|
+
* sub-components its body destructures). The head is assembled by re-walking
|
|
318
|
+
* the IR and re-evaluating the expressions rather than by capturing the real
|
|
319
|
+
* render, so the re-evaluation needs the same bindings the component had —
|
|
320
|
+
* without them every expression that touched an import threw and was swallowed,
|
|
321
|
+
* and the served document shipped an empty `<title>`/`<meta>` while the page
|
|
322
|
+
* body rendered fine (and the client corrected it on hydration, which is why
|
|
323
|
+
* this only ever showed up in view-source, in crawlers, and after an SPA nav
|
|
324
|
+
* re-applied the server head).
|
|
325
|
+
*/
|
|
326
|
+
export function renderHeadHtml(comp, props = {}, scope = {}) {
|
|
327
|
+
const tracked = collectTrackedNames(comp.body);
|
|
328
|
+
const needed = headNeededNames(comp);
|
|
329
|
+
const locals = evaluateLocals(comp, props, scope, tracked, needed);
|
|
330
|
+
const ctx = { props, locals, tracked, scope };
|
|
130
331
|
const seen = new Set();
|
|
131
332
|
const parts = [];
|
|
132
333
|
for (const node of comp.body) {
|
|
133
334
|
if (node instanceof HeadBlock) {
|
|
134
335
|
for (const child of node.children) {
|
|
135
|
-
const key = headElementKey(child,
|
|
336
|
+
const key = headElementKey(child, ctx);
|
|
136
337
|
if (key !== null && seen.has(key))
|
|
137
338
|
continue;
|
|
138
339
|
if (key !== null)
|
|
139
340
|
seen.add(key);
|
|
140
|
-
parts.push(irNodeToHeadHtml(child,
|
|
341
|
+
parts.push(irNodeToHeadHtml(child, ctx, true));
|
|
141
342
|
}
|
|
142
343
|
}
|
|
143
344
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-render.d.ts","sourceRoot":"","sources":["../src/server-render.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,SAAS,EAAE,eAAe,EAAqB,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAuCvJ,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,iBAAiB,CAKhG;AA6DD,wBAAgB,MAAM,CACpB,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,MAAM,EACrB,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACnC,QAAQ,GAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAa,EAC3C,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACpC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA0C1B;AAED,wBAAgB,UAAU,CACxB,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,MAAM,EACrB,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACnC,QAAQ,GAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAa,EAC3C,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACpC,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC,
|
|
1
|
+
{"version":3,"file":"server-render.d.ts","sourceRoot":"","sources":["../src/server-render.ts"],"names":[],"mappings":"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"}
|
package/dist/server-render.js
CHANGED
|
@@ -187,7 +187,9 @@ export function renderPage(source, componentName, props = {}, registry = new Map
|
|
|
187
187
|
}
|
|
188
188
|
return {
|
|
189
189
|
body: bodyHtml,
|
|
190
|
-
|
|
190
|
+
// The component's own scope, so head expressions that read an import
|
|
191
|
+
// resolve (see renderHeadHtml).
|
|
192
|
+
head: renderHeadHtml(targetComp, ssrProps, scopedVesk(renderFn, __vesk)),
|
|
191
193
|
props: ssrProps
|
|
192
194
|
};
|
|
193
195
|
})();
|
|
@@ -199,7 +201,7 @@ export function renderPage(source, componentName, props = {}, registry = new Map
|
|
|
199
201
|
finally {
|
|
200
202
|
delete globalThis.__vsk_ssr;
|
|
201
203
|
}
|
|
202
|
-
const headHtml = targetComp ? renderHeadHtml(targetComp, ssrProps) : '';
|
|
204
|
+
const headHtml = targetComp ? renderHeadHtml(targetComp, ssrProps, scopedVesk(renderFn, __vesk)) : '';
|
|
203
205
|
const pending = globalThis[`__vsk_ssr_promises_${renderToken}`];
|
|
204
206
|
if (pending && pending.length > 0) {
|
|
205
207
|
// A sync component still started server resources (useFetch): settle them
|
|
@@ -616,7 +618,7 @@ export function renderPageStream(source, componentName, props = {}, registry = n
|
|
|
616
618
|
}
|
|
617
619
|
let streamHeadHtml = null;
|
|
618
620
|
if (targetComp) {
|
|
619
|
-
let headHtml = renderHeadHtml(targetComp, ssrProps);
|
|
621
|
+
let headHtml = renderHeadHtml(targetComp, ssrProps, scopedVesk(renderFn, __vesk));
|
|
620
622
|
if (options.pageHead) {
|
|
621
623
|
const merged = mergeHeadHtml(options.pageHead, headHtml);
|
|
622
624
|
headHtml = merged.html;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vesk/compiler",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.49",
|
|
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.49",
|
|
24
24
|
"acorn": "^8.15.0",
|
|
25
25
|
"esrap": "^2.3.0",
|
|
26
26
|
"is-reference": "^3.0.0",
|