@vesk/compiler 0.2.50-canary.10 → 0.2.50-canary.11
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.
|
@@ -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;
|
|
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;AA4DrK,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,CA4EjJ;AAyZD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,WAAW,EAAE,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,CAwD1F;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB;AAuFD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,iBAAiB,EAAE,CAkCzE;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
|
@@ -3,6 +3,13 @@ import { isStaticIR, collectTrackedNames, transformTracked, transformTrackedInit
|
|
|
3
3
|
import { unwrapTrackCall, stripTrackGeneric, hasTopLevelComma, skipWhitespace, findBalancedEnd, startsWithIdentifier, callsIdentifier } from '@vesk/compiler/src/scan';
|
|
4
4
|
import { escapeHtml, indent, exprJS, extractTopLevelNames, extractRuntimeNames, buildParamInit, __vskHydrate, __vskMarkerless, __vskImportedNames, setVskImportedNames, setVskForceClaim, takeVskForceClaim, nextVskId, } from '@vesk/compiler/src/server-utils';
|
|
5
5
|
import { localValueImportNames } from '@vesk/compiler/src/module-imports';
|
|
6
|
+
import { lowerJsxInExpression } from '@vesk/compiler/src/server-jsx-lowering';
|
|
7
|
+
import { print } from 'esrap';
|
|
8
|
+
import tsPrinter from 'esrap/languages/ts';
|
|
9
|
+
/** Print an already-lowered AST with the plain TS printer (no JSX remains). */
|
|
10
|
+
function printLoweredTs(ast) {
|
|
11
|
+
return print(ast, tsPrinter()).code;
|
|
12
|
+
}
|
|
6
13
|
import { vskImportValueNames } from '@vesk/compiler/src/vsk-imports';
|
|
7
14
|
// Hydrate-mode component-boundary wrapper. Each server-rendered component call
|
|
8
15
|
// is preceded by a `<!--vsk-->` marker and a single container element: the
|
|
@@ -107,8 +114,17 @@ export function irNodeToJS(node, importedNames, isAsync = false, tracked) {
|
|
|
107
114
|
`})();`,
|
|
108
115
|
].join('\n');
|
|
109
116
|
}
|
|
110
|
-
if (node instanceof RuntimeStatement)
|
|
117
|
+
if (node instanceof RuntimeStatement) {
|
|
118
|
+
const stmt = node;
|
|
119
|
+
const lowered = stmt.ast ? lowerJsxInExpression(stmt.ast) : null;
|
|
120
|
+
if (lowered && lowered !== stmt.ast && (tracked?.size ?? 0) === 0) {
|
|
121
|
+
return semicolonizeStatement(printLoweredTs(lowered));
|
|
122
|
+
}
|
|
123
|
+
if (lowered && lowered !== stmt.ast) {
|
|
124
|
+
return semicolonizeStatement(transformTracked({ ...node, ast: lowered }, tracked || new Map()));
|
|
125
|
+
}
|
|
111
126
|
return semicolonizeStatement(transformTracked(node, tracked || new Map()));
|
|
127
|
+
}
|
|
112
128
|
if (node instanceof SlotNode) {
|
|
113
129
|
// Markerless mode drops the slot boundary comments: SSR output is plain
|
|
114
130
|
// HTML and slot claims scope structurally (createLayoutSlot subject to the
|
|
@@ -133,7 +149,21 @@ function isEvent(target) {
|
|
|
133
149
|
return !!target && target.startsWith('on') && target.length > 2;
|
|
134
150
|
}
|
|
135
151
|
function exprJSX(node, tracked) {
|
|
136
|
-
|
|
152
|
+
// A function-valued child — `<For each={items}>{(item) => <li/>}</For>` —
|
|
153
|
+
// carries JSX inside an EXPRESSION, and the server has no IR node for it, so
|
|
154
|
+
// it used to be printed verbatim and the render threw "Unexpected token '<'".
|
|
155
|
+
// Lower it to the string the body emitter would have produced. Client output
|
|
156
|
+
// is untouched (a chunk goes through esbuild's tsx loader, so JSX is legal
|
|
157
|
+
// there).
|
|
158
|
+
const lowered = node.ast ? lowerJsxInExpression(node.ast) : null;
|
|
159
|
+
const didLower = lowered !== node.ast;
|
|
160
|
+
if (didLower && (tracked?.size ?? 0) === 0) {
|
|
161
|
+
// `transformTracked` returns the RAW source when there is nothing to
|
|
162
|
+
// rewrite, which would throw the lowering away. With no tracked names there
|
|
163
|
+
// are no rewrites to lose.
|
|
164
|
+
return exprJS(printLoweredTs(lowered));
|
|
165
|
+
}
|
|
166
|
+
return exprJS(transformTracked({ ...node, ast: lowered }, tracked || new Map()));
|
|
137
167
|
}
|
|
138
168
|
function staticNodeToJS(node, isAsync = false, tracked) {
|
|
139
169
|
const lines = [];
|
|
@@ -461,19 +491,35 @@ function componentCallToJS(node, importedNames, isAsync = false, tracked) {
|
|
|
461
491
|
}
|
|
462
492
|
}
|
|
463
493
|
if (regularChildren.length > 0) {
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
494
|
+
// A single function-valued child (`<For each={items}>{(item) => …}</For>`,
|
|
495
|
+
// or a reference to one) must stay a FUNCTION: `For` calls it per item.
|
|
496
|
+
// Wrapping it in the `(() => { const __out = []; … })()` text emitter made it
|
|
497
|
+
// return undefined, so For rendered an empty list.
|
|
498
|
+
const fnType = regularChildren.length === 1 && regularChildren[0] instanceof DynamicBinding && regularChildren[0].kind === 'text'
|
|
499
|
+
? regularChildren[0].expression.ast?.type
|
|
500
|
+
: null;
|
|
501
|
+
const isFnChild = fnType === 'ArrowFunctionExpression'
|
|
502
|
+
|| fnType === 'FunctionExpression'
|
|
503
|
+
|| fnType === 'Identifier'
|
|
504
|
+
|| fnType === 'MemberExpression';
|
|
505
|
+
if (isFnChild) {
|
|
506
|
+
propsEntries.push(`children: ${exprJSX(regularChildren[0].expression, tracked)}`);
|
|
469
507
|
}
|
|
470
|
-
|
|
471
|
-
const
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
508
|
+
else {
|
|
509
|
+
const childLines = [];
|
|
510
|
+
for (const child of regularChildren) {
|
|
511
|
+
const code = irNodeToJS(child, importedNames, isAsync, tracked);
|
|
512
|
+
if (code)
|
|
513
|
+
childLines.push(code);
|
|
514
|
+
}
|
|
515
|
+
if (childLines.length > 0) {
|
|
516
|
+
const childrenVar = `__ch${nextVskId()}`;
|
|
517
|
+
propsEntries.push(`children: ${childrenVar}`);
|
|
518
|
+
lines.push(`const ${childrenVar} = ${isAsync ? 'await (async ' : '('}() => {`);
|
|
519
|
+
lines.push(`const __out = [];`);
|
|
520
|
+
lines.push(indent(childLines.join('\n')));
|
|
521
|
+
lines.push(`return __out.join(''); })();`);
|
|
522
|
+
}
|
|
477
523
|
}
|
|
478
524
|
}
|
|
479
525
|
const propsObj = `{ ${propsEntries.join(', ')} }`;
|
|
@@ -503,10 +549,10 @@ function componentCallToJS(node, importedNames, isAsync = false, tracked) {
|
|
|
503
549
|
}
|
|
504
550
|
const callExpr = `${awaitKw}${calleeVar}(${propsObj}, __registry, (${calleeVar}.__veskScope || __vesk))`;
|
|
505
551
|
if (__vskHydrate && !__vskMarkerless) {
|
|
506
|
-
lines.push(`__out.push(${JSON.stringify(componentMarker(compName))} + (${callExpr}
|
|
552
|
+
lines.push(`__out.push(${JSON.stringify(componentMarker(compName))} + __flat(${callExpr}));`);
|
|
507
553
|
}
|
|
508
554
|
else {
|
|
509
|
-
lines.push(`__out.push(${callExpr}
|
|
555
|
+
lines.push(`__out.push(__flat(${callExpr}));`);
|
|
510
556
|
}
|
|
511
557
|
return lines.join('\n');
|
|
512
558
|
}
|
|
@@ -524,6 +570,9 @@ export function generateFunctionBody(comp, importedNames) {
|
|
|
524
570
|
lines.push(`const __styleText = (v) => { if (typeof v === 'string') return v; if (v && typeof v === 'object') { let s = ''; for (const k in v) { const x = v[k]; if (x == null || x === false) continue; s += k.replace(/[A-Z]/g, (m) => '-' + m.toLowerCase()) + ':' + x + ';'; } return s; } return String(v); };`);
|
|
525
571
|
lines.push(`const __attr = (n, v) => (v == null || v === false) ? '' : ' ' + n + '="' + __escape(n === 'style' ? __styleText(v) : String(v)) + '"';`);
|
|
526
572
|
lines.push(`const raw = (s) => s == null ? '' : String(s);`);
|
|
573
|
+
// A component may return an ARRAY of strings (`<For>` maps its children), and
|
|
574
|
+
// `Array#toString` joins the rows with commas — `<li>a</li>,<li>b</li>`.
|
|
575
|
+
lines.push(`const __flat = (v) => Array.isArray(v) ? v.join('') : (v == null ? '' : String(v));`);
|
|
527
576
|
lines.push(`const __tk = globalThis.__vsk_ssr_token || '';`);
|
|
528
577
|
if (asyncMode) {
|
|
529
578
|
lines.push(`const __pk = __tk ? '__vsk_ssr_promises_' + __tk : '__vsk_ssr_promises';`);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** True when `node` contains JSX anywhere. */
|
|
2
|
+
export declare function containsJsx(node: unknown, depth?: number): boolean;
|
|
3
|
+
declare const RAW = "__raw";
|
|
4
|
+
/**
|
|
5
|
+
* JSX text semantics, matching what the compiler emits for StaticNode text:
|
|
6
|
+
* lines are trimmed, blank lines vanish, and interior newlines collapse to a
|
|
7
|
+
* single space. Getting this wrong is visible as stray whitespace in SSR HTML.
|
|
8
|
+
*/
|
|
9
|
+
export declare function normalizeJsxText(value: string): string;
|
|
10
|
+
/**
|
|
11
|
+
* Rewrite every JSX node inside `ast` into the string it would have rendered.
|
|
12
|
+
* Returns the original node when there is no JSX, so the common path is free.
|
|
13
|
+
*/
|
|
14
|
+
export declare function lowerJsxInExpression(ast: unknown): unknown;
|
|
15
|
+
export { RAW as RAW_HELPER };
|
|
16
|
+
//# sourceMappingURL=server-jsx-lowering.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-jsx-lowering.d.ts","sourceRoot":"","sources":["../src/server-jsx-lowering.ts"],"names":[],"mappings":"AAwBA,8CAA8C;AAC9C,wBAAgB,WAAW,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,SAAI,GAAG,OAAO,CAa7D;AAKD,QAAA,MAAM,GAAG,UAAU,CAAC;AAEpB;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAiBtD;AAiLD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAG1D;AAED,OAAO,EAAE,GAAG,IAAI,UAAU,EAAE,CAAC"}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/** True when `node` contains JSX anywhere. */
|
|
2
|
+
export function containsJsx(node, depth = 0) {
|
|
3
|
+
if (!node || typeof node !== 'object' || depth > 40)
|
|
4
|
+
return false;
|
|
5
|
+
if (Array.isArray(node)) {
|
|
6
|
+
for (const item of node)
|
|
7
|
+
if (containsJsx(item, depth + 1))
|
|
8
|
+
return true;
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
const type = node.type;
|
|
12
|
+
if (type === 'JSXElement' || type === 'JSXFragment')
|
|
13
|
+
return true;
|
|
14
|
+
for (const key of Object.keys(node)) {
|
|
15
|
+
const value = node[key];
|
|
16
|
+
if (value && typeof value === 'object' && containsJsx(value, depth + 1))
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
/** The helper names the generated server body already defines. */
|
|
22
|
+
const ESCAPE = '__escape';
|
|
23
|
+
const ATTR = '__attr';
|
|
24
|
+
const RAW = '__raw';
|
|
25
|
+
/**
|
|
26
|
+
* JSX text semantics, matching what the compiler emits for StaticNode text:
|
|
27
|
+
* lines are trimmed, blank lines vanish, and interior newlines collapse to a
|
|
28
|
+
* single space. Getting this wrong is visible as stray whitespace in SSR HTML.
|
|
29
|
+
*/
|
|
30
|
+
export function normalizeJsxText(value) {
|
|
31
|
+
const lines = value.split(/\r\n|\n|\r/);
|
|
32
|
+
let lastNonEmpty = -1;
|
|
33
|
+
for (let i = 0; i < lines.length; i++) {
|
|
34
|
+
if (/[^ \t]/.test(lines[i]))
|
|
35
|
+
lastNonEmpty = i;
|
|
36
|
+
}
|
|
37
|
+
let out = '';
|
|
38
|
+
for (let i = 0; i < lines.length; i++) {
|
|
39
|
+
let line = lines[i].replace(/\t/g, ' ');
|
|
40
|
+
if (i !== 0)
|
|
41
|
+
line = line.replace(/^ +/, '');
|
|
42
|
+
if (i !== lines.length - 1)
|
|
43
|
+
line = line.replace(/ +$/, '');
|
|
44
|
+
if (line) {
|
|
45
|
+
if (i !== lastNonEmpty)
|
|
46
|
+
line += ' ';
|
|
47
|
+
out += line;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
function lower(node, ctx) {
|
|
53
|
+
if (Array.isArray(node))
|
|
54
|
+
return node.map((n) => lower(n, ctx));
|
|
55
|
+
if (!node || typeof node !== 'object')
|
|
56
|
+
return node;
|
|
57
|
+
const n = node;
|
|
58
|
+
if (n.type === 'JSXElement') {
|
|
59
|
+
const opening = n.openingElement;
|
|
60
|
+
const name = jsxTagName(opening.name);
|
|
61
|
+
const attrs = lowerAttributes(opening.attributes, ctx);
|
|
62
|
+
const children = lowerChildren(n.children, ctx);
|
|
63
|
+
// statics[0] opens the tag, attributes interleave, then '>', then children,
|
|
64
|
+
// then the closing tag. Getting the '>' placement wrong silently produced
|
|
65
|
+
// `<lia</li>` — a tag that never closes and swallows the next element.
|
|
66
|
+
const statics = [`<${name}`, ...attrs.statics.slice(1)];
|
|
67
|
+
statics[statics.length - 1] += '>';
|
|
68
|
+
const quasis = [...statics, `</${name}>`];
|
|
69
|
+
const expressions = [...attrs.exprs, ...children];
|
|
70
|
+
return template(quasis, expressions);
|
|
71
|
+
}
|
|
72
|
+
if (n.type === 'JSXFragment') {
|
|
73
|
+
return template(['', ''], lowerChildren(n.children, ctx));
|
|
74
|
+
}
|
|
75
|
+
// Any other node: rebuild it with lowered children.
|
|
76
|
+
const out = {};
|
|
77
|
+
for (const key of Object.keys(n)) {
|
|
78
|
+
if (key === 'type' || key === 'start' || key === 'end' || key === 'loc' || key === 'range') {
|
|
79
|
+
out[key] = n[key];
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
const value = n[key];
|
|
83
|
+
out[key] = value && typeof value === 'object' ? lower(value, ctx) : value;
|
|
84
|
+
}
|
|
85
|
+
return out;
|
|
86
|
+
}
|
|
87
|
+
function jsxTagName(nameNode) {
|
|
88
|
+
const n = nameNode;
|
|
89
|
+
if (!n)
|
|
90
|
+
return 'div';
|
|
91
|
+
if (n.type === 'JSXIdentifier')
|
|
92
|
+
return String(n.name);
|
|
93
|
+
if (n.type === 'JSXMemberExpression') {
|
|
94
|
+
const object = jsxTagName(n.object);
|
|
95
|
+
const property = n.property;
|
|
96
|
+
return `${object}.${String(property?.name)}`;
|
|
97
|
+
}
|
|
98
|
+
if (n.type === 'JSXNamespacedName') {
|
|
99
|
+
const ns = n.namespace;
|
|
100
|
+
const name = n.name;
|
|
101
|
+
return `${String(ns?.name)}:${String(name?.name)}`;
|
|
102
|
+
}
|
|
103
|
+
return 'div';
|
|
104
|
+
}
|
|
105
|
+
function lowerAttributes(attrs, ctx) {
|
|
106
|
+
const statics = [''];
|
|
107
|
+
const exprs = [];
|
|
108
|
+
const push = (text, expr) => {
|
|
109
|
+
if (expr === undefined) {
|
|
110
|
+
statics[statics.length - 1] += text;
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
statics[statics.length - 1] += text;
|
|
114
|
+
statics.push('');
|
|
115
|
+
exprs.push(expr);
|
|
116
|
+
};
|
|
117
|
+
for (const attr of attrs || []) {
|
|
118
|
+
const a = attr;
|
|
119
|
+
if (a.type === 'JSXSpreadAttribute') {
|
|
120
|
+
// A spread inside a function-valued child is not lowered; dropping it
|
|
121
|
+
// would be silently wrong, so it is reported instead.
|
|
122
|
+
console.error('[vesk] a spread attribute in a JSX expression is not supported yet (dropped):', String(a.argument ?? '').slice(0, 60));
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
const name = jsxAttrName(a.name);
|
|
126
|
+
const value = a.value;
|
|
127
|
+
if (value === null || value === undefined) {
|
|
128
|
+
push(` ${name}`);
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (value.type === 'StringLiteral') {
|
|
132
|
+
push(` ${name}="${escapeAttrText(String(value.value))}"`);
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (value.type === 'JSXExpressionContainer') {
|
|
136
|
+
const inner = value.expression;
|
|
137
|
+
if (!inner || inner.type === 'JSXEmptyExpression') {
|
|
138
|
+
push(` ${name}="true"`);
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
push(` ${name}="`, callNode(ATTR, [literal(name), coerceString(lower(inner, ctx))]));
|
|
142
|
+
push('"');
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
push(` ${name}="`, callNode(ATTR, [literal(name), coerceString(lower(value, ctx))]));
|
|
146
|
+
push('"');
|
|
147
|
+
}
|
|
148
|
+
return { statics, exprs };
|
|
149
|
+
}
|
|
150
|
+
function jsxAttrName(nameNode) {
|
|
151
|
+
const n = nameNode;
|
|
152
|
+
if (!n)
|
|
153
|
+
return 'data';
|
|
154
|
+
if (n.type === 'JSXIdentifier')
|
|
155
|
+
return String(n.name);
|
|
156
|
+
return jsxTagName(n);
|
|
157
|
+
}
|
|
158
|
+
function lowerChildren(children, ctx) {
|
|
159
|
+
const out = [];
|
|
160
|
+
for (const child of children || []) {
|
|
161
|
+
const c = child;
|
|
162
|
+
if (c.type === 'JSXText') {
|
|
163
|
+
const text = normalizeJsxText(String(c.value ?? ''));
|
|
164
|
+
if (text)
|
|
165
|
+
out.push(literal(text));
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
if (c.type === 'JSXExpressionContainer') {
|
|
169
|
+
const inner = c.expression;
|
|
170
|
+
if (!inner || inner.type === 'JSXEmptyExpression')
|
|
171
|
+
continue;
|
|
172
|
+
const lowered = lower(inner, ctx);
|
|
173
|
+
out.push(coerceString(lowered));
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
const lowered = lower(c, ctx);
|
|
177
|
+
out.push(coerceString(lowered));
|
|
178
|
+
}
|
|
179
|
+
return out;
|
|
180
|
+
}
|
|
181
|
+
/** `{v}` in HTML output must be escaped, exactly as the body emitter does. */
|
|
182
|
+
function coerceString(node) {
|
|
183
|
+
if (!node || typeof node !== 'object')
|
|
184
|
+
return callNode(ESCAPE, [literal(String(node))]);
|
|
185
|
+
const n = node;
|
|
186
|
+
// Already a template literal we built: its parts are static text, but any
|
|
187
|
+
// expression inside came from a nested container that escaped itself.
|
|
188
|
+
if (n.type === 'TemplateLiteral')
|
|
189
|
+
return n;
|
|
190
|
+
if (n.type === 'Literal' || n.type === 'StringLiteral')
|
|
191
|
+
return callNode(ESCAPE, [node]);
|
|
192
|
+
return callNode(ESCAPE, [callNode('String', [node])]);
|
|
193
|
+
}
|
|
194
|
+
/** Assemble a template literal: `statics.length === expressions.length + 1`. */
|
|
195
|
+
function template(statics, expressions) {
|
|
196
|
+
const quasis = statics.map((raw, i) => ({
|
|
197
|
+
type: 'TemplateElement',
|
|
198
|
+
value: { raw },
|
|
199
|
+
tail: i === statics.length - 1,
|
|
200
|
+
}));
|
|
201
|
+
return { type: 'TemplateLiteral', quasis, expressions };
|
|
202
|
+
}
|
|
203
|
+
function literal(value) {
|
|
204
|
+
return { type: 'Literal', value };
|
|
205
|
+
}
|
|
206
|
+
function callNode(callee, args) {
|
|
207
|
+
return {
|
|
208
|
+
type: 'CallExpression',
|
|
209
|
+
callee: { type: 'Identifier', name: callee },
|
|
210
|
+
arguments: args,
|
|
211
|
+
optional: false,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
function escapeAttrText(value) {
|
|
215
|
+
return value.split('&').join('&').split('<').join('<').split('>').join('>').split('"').join('"');
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Rewrite every JSX node inside `ast` into the string it would have rendered.
|
|
219
|
+
* Returns the original node when there is no JSX, so the common path is free.
|
|
220
|
+
*/
|
|
221
|
+
export function lowerJsxInExpression(ast) {
|
|
222
|
+
if (!containsJsx(ast))
|
|
223
|
+
return ast;
|
|
224
|
+
return lower(ast, { track: false });
|
|
225
|
+
}
|
|
226
|
+
export { RAW as RAW_HELPER };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vesk/compiler",
|
|
3
|
-
"version": "0.2.50-canary.
|
|
3
|
+
"version": "0.2.50-canary.11",
|
|
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.50-canary.
|
|
23
|
+
"@vesk/types": "^0.2.50-canary.11",
|
|
24
24
|
"acorn": "^8.15.0",
|
|
25
25
|
"esrap": "^2.3.0",
|
|
26
26
|
"is-reference": "^3.0.0",
|