ripple 0.3.13 → 0.3.15
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/CHANGELOG.md +35 -0
- package/package.json +5 -30
- package/src/runtime/array.js +38 -38
- package/src/runtime/create-subscriber.js +2 -2
- package/src/runtime/internal/client/bindings.js +4 -6
- package/src/runtime/internal/client/events.js +8 -3
- package/src/runtime/internal/client/hmr.js +5 -17
- package/src/runtime/internal/client/runtime.js +1 -0
- package/src/runtime/internal/server/blocks.js +7 -9
- package/src/runtime/internal/server/index.js +14 -22
- package/src/runtime/media-query.js +34 -33
- package/src/runtime/object.js +7 -10
- package/src/runtime/proxy.js +2 -3
- package/src/runtime/reactive-value.js +23 -21
- package/src/utils/ast.js +1 -1
- package/src/utils/attributes.js +43 -0
- package/src/utils/builders.js +2 -2
- package/tests/client/basic/basic.components.test.rsrx +103 -1
- package/tests/client/basic/basic.errors.test.rsrx +1 -1
- package/tests/client/basic/basic.styling.test.rsrx +1 -1
- package/tests/client/compiler/compiler.assignments.test.rsrx +1 -1
- package/tests/client/compiler/compiler.attributes.test.rsrx +1 -1
- package/tests/client/compiler/compiler.basic.test.rsrx +51 -14
- package/tests/client/compiler/compiler.tracked-access.test.rsrx +1 -1
- package/tests/client/compiler/compiler.try-in-function.test.rsrx +1 -1
- package/tests/client/compiler/compiler.typescript.test.rsrx +1 -1
- package/tests/client/css/global-additional-cases.test.rsrx +1 -1
- package/tests/client/css/global-advanced-selectors.test.rsrx +1 -1
- package/tests/client/css/global-at-rules.test.rsrx +1 -1
- package/tests/client/css/global-basic.test.rsrx +1 -1
- package/tests/client/css/global-classes-ids.test.rsrx +1 -1
- package/tests/client/css/global-combinators.test.rsrx +1 -1
- package/tests/client/css/global-complex-nesting.test.rsrx +1 -1
- package/tests/client/css/global-edge-cases.test.rsrx +1 -1
- package/tests/client/css/global-keyframes.test.rsrx +1 -1
- package/tests/client/css/global-nested.test.rsrx +1 -1
- package/tests/client/css/global-pseudo.test.rsrx +1 -1
- package/tests/client/css/global-scoping.test.rsrx +1 -1
- package/tests/client/css/style-identifier.test.rsrx +1 -1
- package/tests/client/return.test.rsrx +1 -1
- package/tests/hydration/build-components.js +1 -1
- package/tests/server/basic.components.test.rsrx +114 -0
- package/tests/server/compiler.test.rsrx +38 -1
- package/tests/server/style-identifier.test.rsrx +1 -1
- package/tests/setup-server.js +1 -1
- package/tests/utils/compiler-compat-config.test.js +1 -1
- package/types/index.d.ts +1 -1
- package/src/compiler/comment-utils.js +0 -91
- package/src/compiler/errors.js +0 -77
- package/src/compiler/identifier-utils.js +0 -80
- package/src/compiler/index.d.ts +0 -127
- package/src/compiler/index.js +0 -89
- package/src/compiler/phases/1-parse/index.js +0 -3007
- package/src/compiler/phases/1-parse/style.js +0 -704
- package/src/compiler/phases/2-analyze/css-analyze.js +0 -160
- package/src/compiler/phases/2-analyze/index.js +0 -2208
- package/src/compiler/phases/2-analyze/prune.js +0 -1131
- package/src/compiler/phases/2-analyze/validation.js +0 -168
- package/src/compiler/phases/3-transform/client/index.js +0 -5264
- package/src/compiler/phases/3-transform/segments.js +0 -2125
- package/src/compiler/phases/3-transform/server/index.js +0 -1749
- package/src/compiler/phases/3-transform/stylesheet.js +0 -545
- package/src/compiler/scope.js +0 -476
- package/src/compiler/source-map-utils.js +0 -358
- package/src/compiler/types/acorn.d.ts +0 -11
- package/src/compiler/types/estree-jsx.d.ts +0 -11
- package/src/compiler/types/estree.d.ts +0 -11
- package/src/compiler/types/index.d.ts +0 -1411
- package/src/compiler/types/parse.d.ts +0 -1723
- package/src/compiler/utils.js +0 -1258
|
@@ -1,1749 +0,0 @@
|
|
|
1
|
-
/** @import * as AST from 'estree'; */
|
|
2
|
-
/** @import { RawSourceMap } from 'source-map'; */
|
|
3
|
-
/**
|
|
4
|
-
@import {
|
|
5
|
-
TransformServerContext,
|
|
6
|
-
TransformServerState,
|
|
7
|
-
Visitors,
|
|
8
|
-
AnalysisResult,
|
|
9
|
-
ScopeInterface,
|
|
10
|
-
} from '#compiler' */
|
|
11
|
-
|
|
12
|
-
import * as b from '../../../../utils/builders.js';
|
|
13
|
-
import { walk } from 'zimmerframe';
|
|
14
|
-
import ts from 'esrap/languages/ts';
|
|
15
|
-
import path from 'node:path';
|
|
16
|
-
import { print } from 'esrap';
|
|
17
|
-
import is_reference from 'is-reference';
|
|
18
|
-
import {
|
|
19
|
-
determine_namespace_for_children,
|
|
20
|
-
escape_html,
|
|
21
|
-
is_boolean_attribute,
|
|
22
|
-
is_element_dom_element,
|
|
23
|
-
is_inside_component,
|
|
24
|
-
is_void_element,
|
|
25
|
-
normalize_children,
|
|
26
|
-
is_children_template_expression,
|
|
27
|
-
is_binding_function,
|
|
28
|
-
is_element_dynamic,
|
|
29
|
-
is_ripple_track_call,
|
|
30
|
-
is_ripple_import,
|
|
31
|
-
replace_lazy_param_pattern,
|
|
32
|
-
hash,
|
|
33
|
-
flatten_switch_consequent,
|
|
34
|
-
get_ripple_namespace_call_name,
|
|
35
|
-
strip_class_typescript_syntax,
|
|
36
|
-
jsx_to_ripple_node,
|
|
37
|
-
} from '../../../utils.js';
|
|
38
|
-
import { escape } from '../../../../utils/escaping.js';
|
|
39
|
-
import { is_event_attribute } from '../../../../utils/events.js';
|
|
40
|
-
import { render_stylesheets } from '../stylesheet.js';
|
|
41
|
-
import { createHash } from 'node:crypto';
|
|
42
|
-
import {
|
|
43
|
-
STYLE_IDENTIFIER,
|
|
44
|
-
CSS_HASH_IDENTIFIER,
|
|
45
|
-
obfuscate_identifier,
|
|
46
|
-
} from '../../../identifier-utils.js';
|
|
47
|
-
import { BLOCK_CLOSE, BLOCK_OPEN } from '../../../../constants.js';
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Checks if a node is template or control-flow content that should be wrapped when return flags are active
|
|
51
|
-
* @param {AST.Node} node
|
|
52
|
-
* @returns {boolean}
|
|
53
|
-
*/
|
|
54
|
-
function is_template_or_control_flow(node) {
|
|
55
|
-
return (
|
|
56
|
-
node.type === 'Element' ||
|
|
57
|
-
node.type === 'RippleExpression' ||
|
|
58
|
-
node.type === 'Text' ||
|
|
59
|
-
node.type === 'Html' ||
|
|
60
|
-
node.type === 'Tsx' ||
|
|
61
|
-
node.type === 'TsxCompat' ||
|
|
62
|
-
node.type === 'IfStatement' ||
|
|
63
|
-
node.type === 'ForOfStatement' ||
|
|
64
|
-
node.type === 'TryStatement' ||
|
|
65
|
-
node.type === 'SwitchStatement'
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* @param {AST.Node} node
|
|
71
|
-
* @returns {boolean}
|
|
72
|
-
*/
|
|
73
|
-
function should_wrap_node_in_regular_block(node) {
|
|
74
|
-
return is_template_or_control_flow(node) && node.type !== 'TryStatement';
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* @param {AST.Node} node
|
|
79
|
-
* @returns {boolean}
|
|
80
|
-
*/
|
|
81
|
-
function is_head_element(node) {
|
|
82
|
-
return node.type === 'Element' && node.id.type === 'Identifier' && node.id.name === 'head';
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Builds a negated AND condition from return flag names: !__r_1 && !__r_2 && ...
|
|
87
|
-
* @param {string[]} flags
|
|
88
|
-
* @returns {AST.Expression}
|
|
89
|
-
*/
|
|
90
|
-
function build_return_guard(flags) {
|
|
91
|
-
/** @type {AST.Expression} */
|
|
92
|
-
let condition = b.unary('!', b.id(flags[0]));
|
|
93
|
-
for (let i = 1; i < flags.length; i++) {
|
|
94
|
-
condition = b.logical('&&', condition, b.unary('!', b.id(flags[i])));
|
|
95
|
-
}
|
|
96
|
-
return condition;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Collects all unique return statements from the direct children of a body
|
|
101
|
-
* @param {AST.Node[]} children
|
|
102
|
-
* @returns {AST.ReturnStatement[]}
|
|
103
|
-
*/
|
|
104
|
-
function collect_returns_from_children(children) {
|
|
105
|
-
/** @type {AST.ReturnStatement[]} */
|
|
106
|
-
const returns = [];
|
|
107
|
-
const seen = new Set();
|
|
108
|
-
for (const node of children) {
|
|
109
|
-
if (node.type === 'ReturnStatement') {
|
|
110
|
-
if (!seen.has(node)) {
|
|
111
|
-
seen.add(node);
|
|
112
|
-
returns.push(node);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
if (node.metadata?.returns) {
|
|
116
|
-
for (const ret of node.metadata.returns) {
|
|
117
|
-
if (!seen.has(ret)) {
|
|
118
|
-
seen.add(ret);
|
|
119
|
-
returns.push(ret);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
return returns;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* @param {AST.Node[]} children
|
|
129
|
-
* @param {TransformServerContext} context
|
|
130
|
-
*/
|
|
131
|
-
function transform_children(children, context) {
|
|
132
|
-
const { visit, state } = context;
|
|
133
|
-
const normalized = normalize_children(children, context);
|
|
134
|
-
const should_wrap_in_regular_block =
|
|
135
|
-
state.component !== undefined && !state.skip_regular_blocks && !state.in_regular_block;
|
|
136
|
-
|
|
137
|
-
const all_returns = collect_returns_from_children(normalized);
|
|
138
|
-
/** @type {Map<AST.ReturnStatement, { name: string, tracked: boolean }>} */
|
|
139
|
-
const return_flags = new Map([...(state.return_flags || [])]);
|
|
140
|
-
/** @type {AST.ReturnStatement[]} */
|
|
141
|
-
const new_returns = [];
|
|
142
|
-
for (const ret of all_returns) {
|
|
143
|
-
if (!return_flags.has(ret)) {
|
|
144
|
-
return_flags.set(ret, { name: state.scope.generate('__r'), tracked: false });
|
|
145
|
-
new_returns.push(ret);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
for (const ret of new_returns) {
|
|
150
|
-
const info = /** @type {{ name: string, tracked: boolean }} */ (return_flags.get(ret));
|
|
151
|
-
state.init?.push(b.var(b.id(info.name), b.false));
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// Track accumulated return flags as we process children
|
|
155
|
-
/** @type {string[]} */
|
|
156
|
-
let accumulated_flags = [];
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* @param {AST.ReturnStatement[] | undefined} returns
|
|
160
|
-
*/
|
|
161
|
-
const push_return_flags = (returns) => {
|
|
162
|
-
if (!returns) return;
|
|
163
|
-
for (const ret of returns) {
|
|
164
|
-
const info = return_flags.get(ret);
|
|
165
|
-
if (info && !accumulated_flags.includes(info.name)) {
|
|
166
|
-
accumulated_flags.push(info.name);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* @param {AST.Statement[]} statements
|
|
173
|
-
* @returns {AST.Statement[]}
|
|
174
|
-
*/
|
|
175
|
-
const wrap_regular_block = (statements) => {
|
|
176
|
-
if (!should_wrap_in_regular_block || statements.length === 0) {
|
|
177
|
-
return statements;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
return [b.stmt(b.call('_$_.regular_block', b.arrow([], b.block(statements))))];
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
/** @param {AST.Node} node */
|
|
184
|
-
const process_node = (node, local_state = state) => {
|
|
185
|
-
if (node.type === 'BreakStatement') {
|
|
186
|
-
state.init?.push(b.break);
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
189
|
-
if (
|
|
190
|
-
node.type === 'VariableDeclaration' ||
|
|
191
|
-
node.type === 'ExpressionStatement' ||
|
|
192
|
-
node.type === 'ThrowStatement' ||
|
|
193
|
-
node.type === 'FunctionDeclaration' ||
|
|
194
|
-
node.type === 'DebuggerStatement' ||
|
|
195
|
-
node.type === 'ClassDeclaration' ||
|
|
196
|
-
node.type === 'TSTypeAliasDeclaration' ||
|
|
197
|
-
node.type === 'TSInterfaceDeclaration' ||
|
|
198
|
-
node.type === 'ReturnStatement' ||
|
|
199
|
-
node.type === 'Component'
|
|
200
|
-
) {
|
|
201
|
-
state.init?.push(
|
|
202
|
-
/** @type {AST.Statement} */ (visit(node, { ...local_state, return_flags })),
|
|
203
|
-
);
|
|
204
|
-
if (node.type === 'ReturnStatement') {
|
|
205
|
-
const info = return_flags.get(node);
|
|
206
|
-
if (info && !accumulated_flags.includes(info.name)) {
|
|
207
|
-
accumulated_flags.push(info.name);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
} else {
|
|
211
|
-
visit(node, { ...local_state, return_flags, template_child: true });
|
|
212
|
-
}
|
|
213
|
-
};
|
|
214
|
-
|
|
215
|
-
/** @type {AST.Node[]} */
|
|
216
|
-
let pending_group = [];
|
|
217
|
-
/** @type {string[]} */
|
|
218
|
-
let pending_guard_flags = [];
|
|
219
|
-
|
|
220
|
-
const flush_pending_group = () => {
|
|
221
|
-
if (pending_group.length === 0) return;
|
|
222
|
-
|
|
223
|
-
const group = pending_group;
|
|
224
|
-
const guard_flags = pending_guard_flags;
|
|
225
|
-
pending_group = [];
|
|
226
|
-
pending_guard_flags = [];
|
|
227
|
-
|
|
228
|
-
/** @type {AST.Statement[]} */
|
|
229
|
-
const wrapped = [];
|
|
230
|
-
const saved_init = state.init;
|
|
231
|
-
state.init = wrapped;
|
|
232
|
-
|
|
233
|
-
for (const group_node of group) {
|
|
234
|
-
process_node(group_node, { ...state, init: wrapped, in_regular_block: true });
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
state.init = saved_init;
|
|
238
|
-
if (wrapped.length === 0) return;
|
|
239
|
-
|
|
240
|
-
const guard = build_return_guard(guard_flags);
|
|
241
|
-
state.init?.push(
|
|
242
|
-
...wrap_regular_block([
|
|
243
|
-
b.stmt(b.call(b.id('_$_.output_push'), b.literal(BLOCK_OPEN))),
|
|
244
|
-
b.if(guard, b.block(wrapped)),
|
|
245
|
-
b.stmt(b.call(b.id('_$_.output_push'), b.literal(BLOCK_CLOSE))),
|
|
246
|
-
]),
|
|
247
|
-
);
|
|
248
|
-
};
|
|
249
|
-
|
|
250
|
-
/**
|
|
251
|
-
* @param {AST.Node} node
|
|
252
|
-
* @returns {void}
|
|
253
|
-
*/
|
|
254
|
-
const process_wrapped_template_or_control_flow = (node) => {
|
|
255
|
-
/** @type {AST.Statement[]} */
|
|
256
|
-
const wrapped = [];
|
|
257
|
-
const saved_init = state.init;
|
|
258
|
-
state.init = wrapped;
|
|
259
|
-
process_node(node, { ...state, init: wrapped, in_regular_block: true });
|
|
260
|
-
state.init = saved_init;
|
|
261
|
-
|
|
262
|
-
if (wrapped.length === 0) {
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
state.init?.push(...wrap_regular_block(wrapped));
|
|
267
|
-
};
|
|
268
|
-
|
|
269
|
-
for (let idx = 0; idx < normalized.length; idx++) {
|
|
270
|
-
const node = normalized[idx];
|
|
271
|
-
|
|
272
|
-
if (is_head_element(node)) {
|
|
273
|
-
flush_pending_group();
|
|
274
|
-
continue;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
if (accumulated_flags.length > 0 && should_wrap_node_in_regular_block(node)) {
|
|
278
|
-
if (pending_group.length === 0) {
|
|
279
|
-
pending_guard_flags = [...accumulated_flags];
|
|
280
|
-
}
|
|
281
|
-
pending_group.push(node);
|
|
282
|
-
|
|
283
|
-
if (node.metadata?.has_return && node.metadata.returns) {
|
|
284
|
-
flush_pending_group();
|
|
285
|
-
push_return_flags(node.metadata.returns);
|
|
286
|
-
}
|
|
287
|
-
continue;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
flush_pending_group();
|
|
291
|
-
|
|
292
|
-
if (should_wrap_node_in_regular_block(node)) {
|
|
293
|
-
process_wrapped_template_or_control_flow(node);
|
|
294
|
-
} else {
|
|
295
|
-
process_node(node);
|
|
296
|
-
}
|
|
297
|
-
push_return_flags(node.metadata?.has_return ? node.metadata.returns : undefined);
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
flush_pending_group();
|
|
301
|
-
|
|
302
|
-
const head_elements = /** @type {AST.Element[]} */ (
|
|
303
|
-
children.filter((node) => is_head_element(node))
|
|
304
|
-
);
|
|
305
|
-
|
|
306
|
-
if (head_elements.length) {
|
|
307
|
-
state.init?.push(b.stmt(b.call(b.id('_$_.set_output_target'), b.literal('head'))));
|
|
308
|
-
for (let i = 0; i < head_elements.length; i++) {
|
|
309
|
-
const head_element = head_elements[i];
|
|
310
|
-
// Generate a hash for this head element to match client-side hydration
|
|
311
|
-
// Use both filename and index to ensure uniqueness
|
|
312
|
-
const hash_source = `${context.state.filename}:head:${i}:${head_element.start ?? 0}`;
|
|
313
|
-
const hash_value = hash(hash_source);
|
|
314
|
-
|
|
315
|
-
// Emit hydration marker comment with hash
|
|
316
|
-
state.init?.push(b.stmt(b.call(b.id('_$_.output_push'), b.literal(`<!--${hash_value}-->`))));
|
|
317
|
-
|
|
318
|
-
transform_children(head_element.children, {
|
|
319
|
-
...context,
|
|
320
|
-
state: { ...state, skip_regular_blocks: true },
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
// No closing marker needed for head elements - the hash is sufficient
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
state.init?.push(b.stmt(b.call(b.id('_$_.set_output_target'), b.literal(null))));
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
/**
|
|
331
|
-
* @param {AST.Node[]} body
|
|
332
|
-
* @param {TransformServerContext} context
|
|
333
|
-
* @returns {AST.Statement[]}
|
|
334
|
-
*/
|
|
335
|
-
function transform_body(body, context) {
|
|
336
|
-
const { state } = context;
|
|
337
|
-
/** @type {TransformServerState} */
|
|
338
|
-
const body_state = {
|
|
339
|
-
...state,
|
|
340
|
-
init: [],
|
|
341
|
-
metadata: state.metadata,
|
|
342
|
-
};
|
|
343
|
-
|
|
344
|
-
transform_children(body, { ...context, state: body_state });
|
|
345
|
-
|
|
346
|
-
return /** @type {AST.Statement[]} */ (body_state.init);
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
/** @type {Visitors<AST.Node, TransformServerState>} */
|
|
350
|
-
const visitors = {
|
|
351
|
-
_: (node, { next, state }) => {
|
|
352
|
-
const scope = state.scopes.get(node);
|
|
353
|
-
|
|
354
|
-
if (scope && scope !== state.scope) {
|
|
355
|
-
return next({ ...state, scope });
|
|
356
|
-
} else {
|
|
357
|
-
return next();
|
|
358
|
-
}
|
|
359
|
-
},
|
|
360
|
-
|
|
361
|
-
Identifier(node, context) {
|
|
362
|
-
const parent = /** @type {AST.Node} */ (context.path.at(-1));
|
|
363
|
-
|
|
364
|
-
if (is_reference(node, parent)) {
|
|
365
|
-
// Apply lazy destructuring binding transforms only
|
|
366
|
-
const binding = context.state.scope?.get(node.name);
|
|
367
|
-
if (
|
|
368
|
-
binding?.transform?.read &&
|
|
369
|
-
binding.node !== node &&
|
|
370
|
-
(binding.kind === 'lazy' || binding.kind === 'lazy_fallback')
|
|
371
|
-
) {
|
|
372
|
-
return binding.transform.read(node);
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
return node;
|
|
376
|
-
}
|
|
377
|
-
},
|
|
378
|
-
|
|
379
|
-
Component(node, context) {
|
|
380
|
-
/** @type {AST.Pattern | null} */
|
|
381
|
-
let props_param_output = null;
|
|
382
|
-
|
|
383
|
-
if (node.params.length > 0) {
|
|
384
|
-
let props_param = node.params[0];
|
|
385
|
-
|
|
386
|
-
if (props_param.type === 'Identifier') {
|
|
387
|
-
delete props_param.typeAnnotation;
|
|
388
|
-
props_param_output = props_param;
|
|
389
|
-
} else if (props_param.type === 'ObjectPattern' || props_param.type === 'ArrayPattern') {
|
|
390
|
-
delete props_param.typeAnnotation;
|
|
391
|
-
if (props_param.lazy) {
|
|
392
|
-
// Lazy destructuring: use __props identifier, bindings resolved via transforms
|
|
393
|
-
props_param_output = b.id('__props');
|
|
394
|
-
} else {
|
|
395
|
-
props_param_output = replace_lazy_param_pattern(props_param);
|
|
396
|
-
}
|
|
397
|
-
} else {
|
|
398
|
-
props_param_output = props_param;
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
/** @type {AST.Statement[]} */
|
|
403
|
-
const body_statements = [];
|
|
404
|
-
|
|
405
|
-
if (node.css !== null) {
|
|
406
|
-
const hash_id = b.id(CSS_HASH_IDENTIFIER);
|
|
407
|
-
const hash = b.var(hash_id, b.literal(node.css.hash));
|
|
408
|
-
context.state.stylesheets.push(node.css);
|
|
409
|
-
|
|
410
|
-
// Register CSS hash during rendering
|
|
411
|
-
body_statements.push(hash, b.stmt(b.call(b.id('_$_.output_register_css'), hash_id)));
|
|
412
|
-
|
|
413
|
-
if (node.metadata.styleIdentifierPresent) {
|
|
414
|
-
/** @type {AST.Property[]} */
|
|
415
|
-
const properties = [];
|
|
416
|
-
if (node.metadata.topScopedClasses && node.metadata.topScopedClasses.size > 0) {
|
|
417
|
-
for (const [className] of node.metadata.topScopedClasses) {
|
|
418
|
-
properties.push(
|
|
419
|
-
b.prop(
|
|
420
|
-
'init',
|
|
421
|
-
b.key(className),
|
|
422
|
-
b.template([b.quasi('', false), b.quasi(` ${className}`, true)], [hash_id]),
|
|
423
|
-
),
|
|
424
|
-
);
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
body_statements.push(b.var(b.id(STYLE_IDENTIFIER), b.object(properties)));
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
body_statements.push(
|
|
432
|
-
b.stmt(b.call('_$_.push_component')),
|
|
433
|
-
...transform_body(node.body, {
|
|
434
|
-
...context,
|
|
435
|
-
state: {
|
|
436
|
-
...context.state,
|
|
437
|
-
component: node,
|
|
438
|
-
applyParentCssScope:
|
|
439
|
-
node.id?.name === 'render_children' ? context.state.applyParentCssScope : undefined,
|
|
440
|
-
},
|
|
441
|
-
}),
|
|
442
|
-
b.stmt(b.call('_$_.pop_component')),
|
|
443
|
-
);
|
|
444
|
-
|
|
445
|
-
let component_fn = b.function(
|
|
446
|
-
node.id,
|
|
447
|
-
props_param_output ? [props_param_output] : [],
|
|
448
|
-
b.block(body_statements),
|
|
449
|
-
);
|
|
450
|
-
|
|
451
|
-
// Anonymous components return a FunctionExpression
|
|
452
|
-
if (!node.id) {
|
|
453
|
-
return component_fn;
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
// Named components return a FunctionDeclaration
|
|
457
|
-
const declaration = b.function_declaration(node.id, component_fn.params, component_fn.body);
|
|
458
|
-
|
|
459
|
-
return declaration;
|
|
460
|
-
},
|
|
461
|
-
|
|
462
|
-
CallExpression(node, context) {
|
|
463
|
-
const { state } = context;
|
|
464
|
-
|
|
465
|
-
if (!state.to_ts) {
|
|
466
|
-
delete node.typeArguments;
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
const callee = node.callee;
|
|
470
|
-
|
|
471
|
-
// Handle direct calls to ripple-imported functions: effect(), untrack(), RippleArray(), etc.
|
|
472
|
-
if (callee.type === 'Identifier' && is_ripple_import(callee, context)) {
|
|
473
|
-
const ripple_runtime_method = get_ripple_namespace_call_name(callee.name);
|
|
474
|
-
if (ripple_runtime_method !== null) {
|
|
475
|
-
return {
|
|
476
|
-
...node,
|
|
477
|
-
callee: b.member(b.id('_$_'), b.id(ripple_runtime_method)),
|
|
478
|
-
arguments: /** @type {(AST.Expression | AST.SpreadElement)[]} */ ([
|
|
479
|
-
...node.arguments.map((arg) => context.visit(arg)),
|
|
480
|
-
]),
|
|
481
|
-
};
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
const track_call_name = is_ripple_track_call(callee, context);
|
|
486
|
-
if (track_call_name) {
|
|
487
|
-
const track_method_name = track_call_name === 'trackAsync' ? 'track_async' : 'track';
|
|
488
|
-
|
|
489
|
-
return {
|
|
490
|
-
...node,
|
|
491
|
-
callee: b.member(b.id('_$_'), b.id(track_method_name)),
|
|
492
|
-
arguments: /** @type {(AST.Expression | AST.SpreadElement)[]} */ (
|
|
493
|
-
node.arguments.map((arg) => context.visit(arg))
|
|
494
|
-
),
|
|
495
|
-
};
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
// Handle member calls on ripple imports, like RippleArray.from()
|
|
499
|
-
if (
|
|
500
|
-
callee.type === 'MemberExpression' &&
|
|
501
|
-
callee.object.type === 'Identifier' &&
|
|
502
|
-
callee.property.type === 'Identifier' &&
|
|
503
|
-
is_ripple_import(callee, context)
|
|
504
|
-
) {
|
|
505
|
-
const object = callee.object;
|
|
506
|
-
const property = callee.property;
|
|
507
|
-
const method_name = get_ripple_namespace_call_name(object.name);
|
|
508
|
-
if (method_name !== null) {
|
|
509
|
-
return b.member(
|
|
510
|
-
b.id('_$_'),
|
|
511
|
-
b.member(
|
|
512
|
-
b.id(method_name),
|
|
513
|
-
b.call(
|
|
514
|
-
b.id(property.name),
|
|
515
|
-
.../** @type {(AST.Expression | AST.SpreadElement)[]} */ (
|
|
516
|
-
node.arguments.map((arg) => context.visit(arg))
|
|
517
|
-
),
|
|
518
|
-
),
|
|
519
|
-
),
|
|
520
|
-
);
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
return context.next();
|
|
525
|
-
},
|
|
526
|
-
|
|
527
|
-
NewExpression(node, context) {
|
|
528
|
-
const callee = node.callee;
|
|
529
|
-
|
|
530
|
-
if (!context.state.to_ts) {
|
|
531
|
-
delete node.typeArguments;
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
// Transform `new RippleArray(...)`, `new RippleMap(...)`, etc. imported from 'ripple'
|
|
535
|
-
if (callee.type === 'Identifier' && is_ripple_import(callee, context)) {
|
|
536
|
-
const ripple_runtime_method = get_ripple_namespace_call_name(callee.name);
|
|
537
|
-
if (ripple_runtime_method !== null) {
|
|
538
|
-
return b.call(
|
|
539
|
-
'_$_.' + ripple_runtime_method,
|
|
540
|
-
.../** @type {(AST.Expression | AST.SpreadElement)[]} */ (
|
|
541
|
-
node.arguments.map((arg) => context.visit(arg))
|
|
542
|
-
),
|
|
543
|
-
);
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
return context.next();
|
|
548
|
-
},
|
|
549
|
-
|
|
550
|
-
PropertyDefinition(node, context) {
|
|
551
|
-
if (!context.state.to_ts) {
|
|
552
|
-
delete node.typeAnnotation;
|
|
553
|
-
}
|
|
554
|
-
return context.next();
|
|
555
|
-
},
|
|
556
|
-
|
|
557
|
-
ClassDeclaration(node, context) {
|
|
558
|
-
if (!context.state.to_ts) {
|
|
559
|
-
strip_class_typescript_syntax(node, context);
|
|
560
|
-
}
|
|
561
|
-
return context.next();
|
|
562
|
-
},
|
|
563
|
-
|
|
564
|
-
ClassExpression(node, context) {
|
|
565
|
-
if (!context.state.to_ts) {
|
|
566
|
-
strip_class_typescript_syntax(node, context);
|
|
567
|
-
}
|
|
568
|
-
return context.next();
|
|
569
|
-
},
|
|
570
|
-
|
|
571
|
-
FunctionDeclaration(node, context) {
|
|
572
|
-
if (!context.state.to_ts) {
|
|
573
|
-
delete node.returnType;
|
|
574
|
-
delete node.typeParameters;
|
|
575
|
-
for (let i = 0; i < node.params.length; i++) {
|
|
576
|
-
const param = node.params[i];
|
|
577
|
-
delete param.typeAnnotation;
|
|
578
|
-
// Handle AssignmentPattern (parameters with default values)
|
|
579
|
-
if (param.type === 'AssignmentPattern' && param.left) {
|
|
580
|
-
delete param.left.typeAnnotation;
|
|
581
|
-
}
|
|
582
|
-
// Replace lazy destructuring params with generated identifiers
|
|
583
|
-
const pattern = param.type === 'AssignmentPattern' ? param.left : param;
|
|
584
|
-
if (pattern.type === 'ObjectPattern' || pattern.type === 'ArrayPattern') {
|
|
585
|
-
const transformed_pattern = replace_lazy_param_pattern(pattern);
|
|
586
|
-
node.params[i] =
|
|
587
|
-
param.type === 'AssignmentPattern'
|
|
588
|
-
? /** @type {AST.AssignmentPattern} */ ({ ...param, left: transformed_pattern })
|
|
589
|
-
: transformed_pattern;
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
return context.next();
|
|
594
|
-
},
|
|
595
|
-
|
|
596
|
-
FunctionExpression(node, context) {
|
|
597
|
-
if (!context.state.to_ts) {
|
|
598
|
-
delete node.returnType;
|
|
599
|
-
delete node.typeParameters;
|
|
600
|
-
for (let i = 0; i < node.params.length; i++) {
|
|
601
|
-
const param = node.params[i];
|
|
602
|
-
delete param.typeAnnotation;
|
|
603
|
-
// Handle AssignmentPattern (parameters with default values)
|
|
604
|
-
if (param.type === 'AssignmentPattern' && param.left) {
|
|
605
|
-
delete param.left.typeAnnotation;
|
|
606
|
-
}
|
|
607
|
-
// Replace lazy destructuring params with generated identifiers
|
|
608
|
-
const pattern = param.type === 'AssignmentPattern' ? param.left : param;
|
|
609
|
-
if (pattern.type === 'ObjectPattern' || pattern.type === 'ArrayPattern') {
|
|
610
|
-
const transformed_pattern = replace_lazy_param_pattern(pattern);
|
|
611
|
-
node.params[i] =
|
|
612
|
-
param.type === 'AssignmentPattern'
|
|
613
|
-
? /** @type {AST.AssignmentPattern} */ ({ ...param, left: transformed_pattern })
|
|
614
|
-
: transformed_pattern;
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
return context.next();
|
|
619
|
-
},
|
|
620
|
-
|
|
621
|
-
BlockStatement(node, context) {
|
|
622
|
-
/** @type {AST.Statement[]} */
|
|
623
|
-
const statements = [];
|
|
624
|
-
|
|
625
|
-
for (const statement of node.body) {
|
|
626
|
-
statements.push(/** @type {AST.Statement} */ (context.visit(statement)));
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
return b.block(statements);
|
|
630
|
-
},
|
|
631
|
-
|
|
632
|
-
ArrowFunctionExpression(node, context) {
|
|
633
|
-
delete node.returnType;
|
|
634
|
-
delete node.typeParameters;
|
|
635
|
-
for (let i = 0; i < node.params.length; i++) {
|
|
636
|
-
const param = node.params[i];
|
|
637
|
-
delete param.typeAnnotation;
|
|
638
|
-
// Handle AssignmentPattern (parameters with default values)
|
|
639
|
-
if (param.type === 'AssignmentPattern' && param.left) {
|
|
640
|
-
delete param.left.typeAnnotation;
|
|
641
|
-
}
|
|
642
|
-
// Replace lazy destructuring params with generated identifiers
|
|
643
|
-
const pattern = param.type === 'AssignmentPattern' ? param.left : param;
|
|
644
|
-
if (pattern.type === 'ObjectPattern' || pattern.type === 'ArrayPattern') {
|
|
645
|
-
const transformed_pattern = replace_lazy_param_pattern(pattern);
|
|
646
|
-
node.params[i] =
|
|
647
|
-
param.type === 'AssignmentPattern'
|
|
648
|
-
? /** @type {AST.AssignmentPattern} */ ({ ...param, left: transformed_pattern })
|
|
649
|
-
: transformed_pattern;
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
return context.next();
|
|
654
|
-
},
|
|
655
|
-
|
|
656
|
-
TSAsExpression(node, context) {
|
|
657
|
-
if (!context.state.to_ts) {
|
|
658
|
-
return context.visit(node.expression);
|
|
659
|
-
}
|
|
660
|
-
return context.next();
|
|
661
|
-
},
|
|
662
|
-
|
|
663
|
-
TSInstantiationExpression(node, context) {
|
|
664
|
-
if (!context.state.to_ts) {
|
|
665
|
-
// In JavaScript, just return the expression wrapped in parentheses
|
|
666
|
-
return b.sequence(/** @type {AST.Expression[]} */ ([context.visit(node.expression)]));
|
|
667
|
-
}
|
|
668
|
-
return context.next();
|
|
669
|
-
},
|
|
670
|
-
|
|
671
|
-
TSTypeAliasDeclaration(_, context) {
|
|
672
|
-
if (!context.state.to_ts) {
|
|
673
|
-
return b.empty;
|
|
674
|
-
}
|
|
675
|
-
context.next();
|
|
676
|
-
},
|
|
677
|
-
|
|
678
|
-
TSInterfaceDeclaration(_, context) {
|
|
679
|
-
if (!context.state.to_ts) {
|
|
680
|
-
return b.empty;
|
|
681
|
-
}
|
|
682
|
-
context.next();
|
|
683
|
-
},
|
|
684
|
-
|
|
685
|
-
ExportNamedDeclaration(node, context) {
|
|
686
|
-
if (!context.state.to_ts && node.exportKind === 'type') {
|
|
687
|
-
return b.empty;
|
|
688
|
-
}
|
|
689
|
-
if (!context.state.ancestor_server_block) {
|
|
690
|
-
return context.next();
|
|
691
|
-
}
|
|
692
|
-
const declaration = node.declaration;
|
|
693
|
-
/** @type {AST.Statement[]} */
|
|
694
|
-
const statements = [];
|
|
695
|
-
|
|
696
|
-
if (declaration && declaration.type === 'FunctionDeclaration') {
|
|
697
|
-
const name = declaration.id.name;
|
|
698
|
-
if (context.state.server_exported_names.includes(name)) {
|
|
699
|
-
return b.empty;
|
|
700
|
-
}
|
|
701
|
-
context.state.server_exported_names.push(name);
|
|
702
|
-
return b.stmt(
|
|
703
|
-
b.assignment(
|
|
704
|
-
'=',
|
|
705
|
-
b.member(b.id('_$_server_$_'), b.id(name)),
|
|
706
|
-
/** @type {AST.Expression} */
|
|
707
|
-
(context.visit(declaration)),
|
|
708
|
-
),
|
|
709
|
-
);
|
|
710
|
-
} else if (declaration && declaration.type === 'VariableDeclaration') {
|
|
711
|
-
for (const decl of declaration.declarations) {
|
|
712
|
-
if (decl.init !== undefined && decl.init !== null) {
|
|
713
|
-
if (decl.id.type === 'Identifier') {
|
|
714
|
-
const name = decl.id.name;
|
|
715
|
-
if (
|
|
716
|
-
decl.init.type === 'FunctionExpression' ||
|
|
717
|
-
decl.init.type === 'ArrowFunctionExpression'
|
|
718
|
-
) {
|
|
719
|
-
if (context.state.server_exported_names.includes(name)) {
|
|
720
|
-
continue;
|
|
721
|
-
}
|
|
722
|
-
context.state.server_exported_names.push(name);
|
|
723
|
-
statements.push(
|
|
724
|
-
b.stmt(
|
|
725
|
-
b.assignment(
|
|
726
|
-
'=',
|
|
727
|
-
b.member(b.id('_$_server_$_'), b.id(name)),
|
|
728
|
-
/** @type {AST.Expression} */
|
|
729
|
-
(context.visit(decl.init)),
|
|
730
|
-
),
|
|
731
|
-
),
|
|
732
|
-
);
|
|
733
|
-
} else if (decl.init.type === 'Identifier') {
|
|
734
|
-
if (context.state.server_exported_names.includes(name)) {
|
|
735
|
-
continue;
|
|
736
|
-
}
|
|
737
|
-
context.state.server_exported_names.push(name);
|
|
738
|
-
|
|
739
|
-
statements.push(
|
|
740
|
-
b.stmt(
|
|
741
|
-
b.assignment(
|
|
742
|
-
'=',
|
|
743
|
-
b.member(b.id('_$_server_$_'), b.id(name)),
|
|
744
|
-
b.id(decl.init.name),
|
|
745
|
-
),
|
|
746
|
-
),
|
|
747
|
-
);
|
|
748
|
-
} else {
|
|
749
|
-
// TODO allow exporting variables that are not functions
|
|
750
|
-
throw new Error('Not implemented');
|
|
751
|
-
}
|
|
752
|
-
} else {
|
|
753
|
-
// TODO allow exporting variables that are not functions
|
|
754
|
-
throw new Error('Not implemented');
|
|
755
|
-
}
|
|
756
|
-
} else {
|
|
757
|
-
// TODO allow exporting uninitialized variables
|
|
758
|
-
throw new Error('Not implemented');
|
|
759
|
-
}
|
|
760
|
-
// TODO: allow exporting consts when hydration is supported
|
|
761
|
-
}
|
|
762
|
-
} else if (node.specifiers) {
|
|
763
|
-
for (const specifier of node.specifiers) {
|
|
764
|
-
const name = /** @type {AST.Identifier} */ (specifier.local).name;
|
|
765
|
-
if (context.state.server_exported_names.includes(name)) {
|
|
766
|
-
continue;
|
|
767
|
-
}
|
|
768
|
-
context.state.server_exported_names.push(name);
|
|
769
|
-
|
|
770
|
-
const binding = context.state.scope.get(name);
|
|
771
|
-
|
|
772
|
-
if (!binding || !is_binding_function(binding, context.state.scope)) {
|
|
773
|
-
continue;
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
statements.push(
|
|
777
|
-
b.stmt(b.assignment('=', b.member(b.id('_$_server_$_'), b.id(name)), specifier.local)),
|
|
778
|
-
);
|
|
779
|
-
}
|
|
780
|
-
} else {
|
|
781
|
-
// TODO
|
|
782
|
-
throw new Error('Not implemented');
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
return statements.length ? b.block(statements) : b.empty;
|
|
786
|
-
},
|
|
787
|
-
|
|
788
|
-
ExpressionStatement(node, context) {
|
|
789
|
-
// Handle standalone lazy destructuring: &[data] = track(0); → const lazy0 = track(0);
|
|
790
|
-
if (
|
|
791
|
-
node.expression.type === 'AssignmentExpression' &&
|
|
792
|
-
(node.expression.left.type === 'ObjectPattern' ||
|
|
793
|
-
node.expression.left.type === 'ArrayPattern') &&
|
|
794
|
-
node.expression.left.lazy &&
|
|
795
|
-
node.expression.left.metadata?.lazy_id
|
|
796
|
-
) {
|
|
797
|
-
const right = /** @type {AST.Expression} */ (context.visit(node.expression.right));
|
|
798
|
-
return b.const(b.id(node.expression.left.metadata.lazy_id), right);
|
|
799
|
-
}
|
|
800
|
-
return context.next();
|
|
801
|
-
},
|
|
802
|
-
|
|
803
|
-
VariableDeclaration(node, context) {
|
|
804
|
-
for (const declarator of node.declarations) {
|
|
805
|
-
if (!context.state.to_ts) {
|
|
806
|
-
delete declarator.id.typeAnnotation;
|
|
807
|
-
|
|
808
|
-
// Replace lazy destructuring patterns with the generated identifier
|
|
809
|
-
if (
|
|
810
|
-
(declarator.id.type === 'ObjectPattern' || declarator.id.type === 'ArrayPattern') &&
|
|
811
|
-
declarator.id.lazy &&
|
|
812
|
-
declarator.id.metadata?.lazy_id
|
|
813
|
-
) {
|
|
814
|
-
declarator.id = b.id(declarator.id.metadata.lazy_id);
|
|
815
|
-
}
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
return context.next();
|
|
820
|
-
},
|
|
821
|
-
|
|
822
|
-
Element(node, context) {
|
|
823
|
-
const { state, visit } = context;
|
|
824
|
-
|
|
825
|
-
const dynamic_name = state.dynamicElementName;
|
|
826
|
-
if (dynamic_name) {
|
|
827
|
-
state.dynamicElementName = undefined;
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
const is_dom_element = !!dynamic_name || is_element_dom_element(node);
|
|
831
|
-
const is_spreading = node.attributes.some((attr) => attr.type === 'SpreadAttribute');
|
|
832
|
-
/** @type {(AST.Property | AST.SpreadElement)[] | null} */
|
|
833
|
-
const spread_attributes = is_spreading ? [] : null;
|
|
834
|
-
const child_namespace =
|
|
835
|
-
!dynamic_name && is_dom_element
|
|
836
|
-
? determine_namespace_for_children(
|
|
837
|
-
/** @type {AST.Identifier} */ (node.id).name,
|
|
838
|
-
state.namespace,
|
|
839
|
-
)
|
|
840
|
-
: state.namespace;
|
|
841
|
-
|
|
842
|
-
if (is_dom_element) {
|
|
843
|
-
const is_void = dynamic_name
|
|
844
|
-
? false
|
|
845
|
-
: is_void_element(/** @type {AST.Identifier} */ (node.id).name);
|
|
846
|
-
const use_self_closing_syntax = node.selfClosing && (is_void || !!dynamic_name);
|
|
847
|
-
const tag_name = dynamic_name
|
|
848
|
-
? dynamic_name
|
|
849
|
-
: b.literal(/** @type {AST.Identifier} */ (node.id).name);
|
|
850
|
-
/** @type {AST.CSS.StyleSheet['hash'] | null} */
|
|
851
|
-
const scoping_hash =
|
|
852
|
-
state.applyParentCssScope ??
|
|
853
|
-
(node.metadata.scoped && state.component?.css
|
|
854
|
-
? /** @type {AST.CSS.StyleSheet} */ (state.component?.css).hash
|
|
855
|
-
: null);
|
|
856
|
-
|
|
857
|
-
state.init?.push(
|
|
858
|
-
b.stmt(
|
|
859
|
-
b.call(
|
|
860
|
-
b.id('_$_.output_push'),
|
|
861
|
-
dynamic_name
|
|
862
|
-
? b.template([b.quasi('<', false), b.quasi('', false)], [tag_name])
|
|
863
|
-
: b.literal('<' + /** @type {AST.Literal} */ (tag_name).value),
|
|
864
|
-
),
|
|
865
|
-
),
|
|
866
|
-
);
|
|
867
|
-
let class_attribute = null;
|
|
868
|
-
|
|
869
|
-
/**
|
|
870
|
-
* @param {string} name
|
|
871
|
-
* @param {string | number | bigint | boolean | RegExp | null | undefined} value
|
|
872
|
-
* @param {'push' | 'unshift'} [spread_method]
|
|
873
|
-
*/
|
|
874
|
-
const handle_static_attr = (name, value, spread_method = 'push') => {
|
|
875
|
-
if (is_spreading) {
|
|
876
|
-
// For spread attributes, store just the actual value, not the full attribute string
|
|
877
|
-
const actual_value =
|
|
878
|
-
is_boolean_attribute(name) && value === true
|
|
879
|
-
? b.literal(true)
|
|
880
|
-
: b.literal(value === true ? '' : value);
|
|
881
|
-
|
|
882
|
-
// spread_attributes cannot be null based on is_spreading === true
|
|
883
|
-
/** @type {(AST.Property | AST.SpreadElement)[]} */ (spread_attributes)[spread_method](
|
|
884
|
-
b.prop('init', b.literal(name), actual_value),
|
|
885
|
-
);
|
|
886
|
-
} else {
|
|
887
|
-
const attr_str = ` ${name}${
|
|
888
|
-
is_boolean_attribute(name) && value === true
|
|
889
|
-
? ''
|
|
890
|
-
: `="${value === true ? '' : escape_html(value, true)}"`
|
|
891
|
-
}`;
|
|
892
|
-
|
|
893
|
-
state.init?.push(b.stmt(b.call(b.id('_$_.output_push'), b.literal(attr_str))));
|
|
894
|
-
}
|
|
895
|
-
};
|
|
896
|
-
|
|
897
|
-
for (const attr of node.attributes) {
|
|
898
|
-
if (attr.type === 'Attribute') {
|
|
899
|
-
if (attr.name.type === 'Identifier') {
|
|
900
|
-
const name = attr.name.name;
|
|
901
|
-
|
|
902
|
-
if (attr.value === null) {
|
|
903
|
-
handle_static_attr(name, true);
|
|
904
|
-
continue;
|
|
905
|
-
}
|
|
906
|
-
|
|
907
|
-
if (attr.value.type === 'Literal' && name !== 'class') {
|
|
908
|
-
handle_static_attr(name, attr.value.value);
|
|
909
|
-
continue;
|
|
910
|
-
}
|
|
911
|
-
|
|
912
|
-
if (name === 'class') {
|
|
913
|
-
class_attribute = attr;
|
|
914
|
-
|
|
915
|
-
continue;
|
|
916
|
-
}
|
|
917
|
-
|
|
918
|
-
if (is_event_attribute(name)) {
|
|
919
|
-
continue;
|
|
920
|
-
}
|
|
921
|
-
const metadata = { tracking: false };
|
|
922
|
-
const expression = /** @type {AST.Expression} */ (
|
|
923
|
-
visit(attr.value, { ...state, metadata })
|
|
924
|
-
);
|
|
925
|
-
|
|
926
|
-
state.init?.push(
|
|
927
|
-
b.stmt(
|
|
928
|
-
b.call(
|
|
929
|
-
b.id('_$_.output_push'),
|
|
930
|
-
b.call(
|
|
931
|
-
'_$_.attr',
|
|
932
|
-
b.literal(name),
|
|
933
|
-
expression,
|
|
934
|
-
b.literal(is_boolean_attribute(name)),
|
|
935
|
-
),
|
|
936
|
-
),
|
|
937
|
-
),
|
|
938
|
-
);
|
|
939
|
-
}
|
|
940
|
-
} else if (attr.type === 'SpreadAttribute') {
|
|
941
|
-
spread_attributes?.push(
|
|
942
|
-
b.spread(/** @type {AST.Expression} */ (visit(attr.argument, state))),
|
|
943
|
-
);
|
|
944
|
-
}
|
|
945
|
-
}
|
|
946
|
-
|
|
947
|
-
if (class_attribute !== null) {
|
|
948
|
-
const attr_value = /** @type {AST.Expression} */ (class_attribute.value);
|
|
949
|
-
if (attr_value.type === 'Literal') {
|
|
950
|
-
let value = attr_value.value;
|
|
951
|
-
|
|
952
|
-
if (scoping_hash) {
|
|
953
|
-
value = `${scoping_hash} ${value}`;
|
|
954
|
-
}
|
|
955
|
-
|
|
956
|
-
handle_static_attr(class_attribute.name.name, value);
|
|
957
|
-
} else {
|
|
958
|
-
const metadata = { tracking: false };
|
|
959
|
-
let expression = /** @type {AST.Expression} */ (
|
|
960
|
-
visit(attr_value, { ...state, metadata })
|
|
961
|
-
);
|
|
962
|
-
|
|
963
|
-
if (scoping_hash) {
|
|
964
|
-
// Pass array to clsx so it can handle objects properly
|
|
965
|
-
expression = b.array([expression, b.literal(scoping_hash)]);
|
|
966
|
-
}
|
|
967
|
-
|
|
968
|
-
state.init?.push(
|
|
969
|
-
b.stmt(
|
|
970
|
-
b.call(b.id('_$_.output_push'), b.call('_$_.attr', b.literal('class'), expression)),
|
|
971
|
-
),
|
|
972
|
-
);
|
|
973
|
-
}
|
|
974
|
-
} else if (scoping_hash) {
|
|
975
|
-
handle_static_attr('class', scoping_hash, is_spreading ? 'unshift' : 'push');
|
|
976
|
-
}
|
|
977
|
-
|
|
978
|
-
if (spread_attributes !== null && spread_attributes.length > 0) {
|
|
979
|
-
state.init?.push(
|
|
980
|
-
b.stmt(
|
|
981
|
-
b.call(
|
|
982
|
-
b.id('_$_.output_push'),
|
|
983
|
-
b.call(
|
|
984
|
-
'_$_.spread_attrs',
|
|
985
|
-
b.object(spread_attributes),
|
|
986
|
-
scoping_hash ? b.literal(scoping_hash) : undefined,
|
|
987
|
-
),
|
|
988
|
-
),
|
|
989
|
-
),
|
|
990
|
-
);
|
|
991
|
-
}
|
|
992
|
-
|
|
993
|
-
state.init?.push(
|
|
994
|
-
b.stmt(b.call(b.id('_$_.output_push'), b.literal(use_self_closing_syntax ? ' />' : '>'))),
|
|
995
|
-
);
|
|
996
|
-
|
|
997
|
-
// In dev mode, emit push_element for runtime nesting validation
|
|
998
|
-
if (state.dev && !dynamic_name) {
|
|
999
|
-
const element_name = /** @type {AST.Identifier} */ (node.id).name;
|
|
1000
|
-
const loc = node.loc;
|
|
1001
|
-
state.init?.push(
|
|
1002
|
-
b.stmt(
|
|
1003
|
-
b.call(
|
|
1004
|
-
'_$_.push_element',
|
|
1005
|
-
b.literal(element_name),
|
|
1006
|
-
b.literal(state.filename),
|
|
1007
|
-
b.literal(loc?.start.line ?? 0),
|
|
1008
|
-
b.literal(loc?.start.column ?? 0),
|
|
1009
|
-
),
|
|
1010
|
-
),
|
|
1011
|
-
);
|
|
1012
|
-
}
|
|
1013
|
-
|
|
1014
|
-
if (!is_void) {
|
|
1015
|
-
/** @type {AST.Statement[]} */
|
|
1016
|
-
const init = [];
|
|
1017
|
-
transform_children(
|
|
1018
|
-
node.children,
|
|
1019
|
-
/** @type {TransformServerContext} */ ({
|
|
1020
|
-
visit,
|
|
1021
|
-
state: {
|
|
1022
|
-
...state,
|
|
1023
|
-
init,
|
|
1024
|
-
...(state.applyParentCssScope ||
|
|
1025
|
-
(dynamic_name && node.metadata.scoped && state.component?.css)
|
|
1026
|
-
? {
|
|
1027
|
-
applyParentCssScope:
|
|
1028
|
-
state.applyParentCssScope ||
|
|
1029
|
-
/** @type {AST.CSS.StyleSheet} */ (state.component?.css).hash,
|
|
1030
|
-
}
|
|
1031
|
-
: {}),
|
|
1032
|
-
},
|
|
1033
|
-
}),
|
|
1034
|
-
);
|
|
1035
|
-
|
|
1036
|
-
if (init.length > 0) {
|
|
1037
|
-
state.init?.push(b.block(init));
|
|
1038
|
-
}
|
|
1039
|
-
|
|
1040
|
-
if (!use_self_closing_syntax) {
|
|
1041
|
-
state.init?.push(
|
|
1042
|
-
b.stmt(
|
|
1043
|
-
b.call(
|
|
1044
|
-
b.id('_$_.output_push'),
|
|
1045
|
-
dynamic_name
|
|
1046
|
-
? b.template([b.quasi('</', false), b.quasi('>', false)], [tag_name])
|
|
1047
|
-
: b.literal('</' + /** @type {AST.Literal} */ (tag_name).value + '>'),
|
|
1048
|
-
),
|
|
1049
|
-
),
|
|
1050
|
-
);
|
|
1051
|
-
}
|
|
1052
|
-
}
|
|
1053
|
-
|
|
1054
|
-
// In dev mode, emit pop_element after the element is fully rendered
|
|
1055
|
-
if (state.dev && !dynamic_name) {
|
|
1056
|
-
state.init?.push(b.stmt(b.call('_$_.pop_element')));
|
|
1057
|
-
}
|
|
1058
|
-
} else {
|
|
1059
|
-
/** @type {(AST.Property | AST.SpreadElement)[]} */
|
|
1060
|
-
const props = [];
|
|
1061
|
-
/** @type {AST.Property | null} */
|
|
1062
|
-
let children_prop = null;
|
|
1063
|
-
|
|
1064
|
-
const apply_parent_css_scope = state.applyParentCssScope;
|
|
1065
|
-
|
|
1066
|
-
for (const attr of node.attributes) {
|
|
1067
|
-
if (attr.type === 'Attribute') {
|
|
1068
|
-
if (attr.name.type === 'Identifier') {
|
|
1069
|
-
const metadata = { tracking: false };
|
|
1070
|
-
let property =
|
|
1071
|
-
attr.value === null
|
|
1072
|
-
? b.literal(true)
|
|
1073
|
-
: /** @type {AST.Expression} */ (
|
|
1074
|
-
visit(/** @type {AST.Expression} */ (attr.value), {
|
|
1075
|
-
...state,
|
|
1076
|
-
metadata,
|
|
1077
|
-
})
|
|
1078
|
-
);
|
|
1079
|
-
|
|
1080
|
-
if (attr.name.name === 'children') {
|
|
1081
|
-
children_prop = b.prop(
|
|
1082
|
-
'init',
|
|
1083
|
-
b.id('children'),
|
|
1084
|
-
b.call('_$_.normalize_children', property),
|
|
1085
|
-
);
|
|
1086
|
-
continue;
|
|
1087
|
-
}
|
|
1088
|
-
|
|
1089
|
-
props.push(b.prop('init', b.key(attr.name.name), property));
|
|
1090
|
-
}
|
|
1091
|
-
} else if (attr.type === 'SpreadAttribute') {
|
|
1092
|
-
props.push(
|
|
1093
|
-
b.spread(
|
|
1094
|
-
/** @type {AST.Expression} */ (
|
|
1095
|
-
visit(attr.argument, { ...state, metadata: { ...state.metadata } })
|
|
1096
|
-
),
|
|
1097
|
-
),
|
|
1098
|
-
);
|
|
1099
|
-
}
|
|
1100
|
-
}
|
|
1101
|
-
|
|
1102
|
-
const children_filtered = node.children.filter(
|
|
1103
|
-
(child) => child.type !== 'EmptyStatement' && child.type !== 'Component',
|
|
1104
|
-
);
|
|
1105
|
-
|
|
1106
|
-
if (children_filtered.length > 0) {
|
|
1107
|
-
const component_scope = /** @type {ScopeInterface} */ (context.state.scopes.get(node));
|
|
1108
|
-
const children = b.call(
|
|
1109
|
-
'_$_.ripple_element',
|
|
1110
|
-
/** @type {AST.Expression} */ (
|
|
1111
|
-
visit(b.component(b.id('render_children'), [], children_filtered), {
|
|
1112
|
-
...context.state,
|
|
1113
|
-
...(apply_parent_css_scope ||
|
|
1114
|
-
(is_element_dynamic(node) && node.metadata.scoped && state.component?.css)
|
|
1115
|
-
? {
|
|
1116
|
-
applyParentCssScope:
|
|
1117
|
-
apply_parent_css_scope ||
|
|
1118
|
-
/** @type {AST.CSS.StyleSheet} */ (state.component?.css).hash,
|
|
1119
|
-
}
|
|
1120
|
-
: {}),
|
|
1121
|
-
scope: component_scope,
|
|
1122
|
-
namespace: child_namespace,
|
|
1123
|
-
})
|
|
1124
|
-
),
|
|
1125
|
-
);
|
|
1126
|
-
|
|
1127
|
-
if (children_prop) {
|
|
1128
|
-
children_prop.value = b.logical(
|
|
1129
|
-
'??',
|
|
1130
|
-
/** @type {AST.Expression} */ (children_prop.value),
|
|
1131
|
-
children,
|
|
1132
|
-
);
|
|
1133
|
-
} else {
|
|
1134
|
-
children_prop = b.prop('init', b.id('children'), children);
|
|
1135
|
-
}
|
|
1136
|
-
}
|
|
1137
|
-
|
|
1138
|
-
if (children_prop) {
|
|
1139
|
-
props.push(children_prop);
|
|
1140
|
-
}
|
|
1141
|
-
|
|
1142
|
-
const args = [b.object(props)];
|
|
1143
|
-
|
|
1144
|
-
// Check if this is a locally defined component
|
|
1145
|
-
const component_name = node.id.type === 'Identifier' ? node.id.name : null;
|
|
1146
|
-
const local_metadata = component_name
|
|
1147
|
-
? state.component_metadata.find((m) => m.id === component_name)
|
|
1148
|
-
: null;
|
|
1149
|
-
const comp_id = b.id('comp');
|
|
1150
|
-
const args_id = b.id('args');
|
|
1151
|
-
const comp_call = b.call(comp_id, b.spread(args_id));
|
|
1152
|
-
const comp_call_statement = b.stmt(comp_call);
|
|
1153
|
-
|
|
1154
|
-
/** @type {AST.Statement[]} */
|
|
1155
|
-
const init = [];
|
|
1156
|
-
const visited_id = /** @type {AST.Expression} */ (visit(node.id, state));
|
|
1157
|
-
/** @type {AST.Statement[]} */
|
|
1158
|
-
const statements = [
|
|
1159
|
-
b.const(comp_id, is_element_dynamic(node) ? b.call('_$_.get', visited_id) : visited_id),
|
|
1160
|
-
b.const(args_id, b.array(args)),
|
|
1161
|
-
];
|
|
1162
|
-
|
|
1163
|
-
if (local_metadata) {
|
|
1164
|
-
statements.push(comp_call_statement);
|
|
1165
|
-
} else if (!is_element_dynamic(node)) {
|
|
1166
|
-
// imported or children
|
|
1167
|
-
statements.push(b.if(comp_id, b.block([comp_call_statement])));
|
|
1168
|
-
} else {
|
|
1169
|
-
// if it's a dynamic element, build the element output
|
|
1170
|
-
// and store the results in the `init` array
|
|
1171
|
-
visit(
|
|
1172
|
-
node,
|
|
1173
|
-
/** @type {TransformServerState} */ ({
|
|
1174
|
-
...state,
|
|
1175
|
-
dynamicElementName: b.template([b.quasi('', false), b.quasi('', false)], [comp_id]),
|
|
1176
|
-
init,
|
|
1177
|
-
}),
|
|
1178
|
-
);
|
|
1179
|
-
|
|
1180
|
-
statements.push(b.stmt(b.call(b.id('_$_.output_push'), b.literal(BLOCK_OPEN))));
|
|
1181
|
-
|
|
1182
|
-
statements.push(
|
|
1183
|
-
b.if(
|
|
1184
|
-
b.binary('===', b.unary('typeof', comp_id), b.literal('function')),
|
|
1185
|
-
b.block([comp_call_statement]),
|
|
1186
|
-
// make sure that falsy values for dynamic element or component don't get rendered
|
|
1187
|
-
b.if(comp_id, b.block(init)),
|
|
1188
|
-
),
|
|
1189
|
-
);
|
|
1190
|
-
|
|
1191
|
-
statements.push(b.stmt(b.call(b.id('_$_.output_push'), b.literal(BLOCK_CLOSE))));
|
|
1192
|
-
}
|
|
1193
|
-
|
|
1194
|
-
state.init?.push(b.block(statements));
|
|
1195
|
-
}
|
|
1196
|
-
},
|
|
1197
|
-
|
|
1198
|
-
SwitchStatement(node, context) {
|
|
1199
|
-
if (!is_inside_component(context)) {
|
|
1200
|
-
return context.next();
|
|
1201
|
-
}
|
|
1202
|
-
|
|
1203
|
-
const cases = [];
|
|
1204
|
-
|
|
1205
|
-
for (const switch_case of node.cases) {
|
|
1206
|
-
const case_body = [];
|
|
1207
|
-
|
|
1208
|
-
if (switch_case.consequent.length !== 0) {
|
|
1209
|
-
const flattened_consequent = flatten_switch_consequent(switch_case.consequent);
|
|
1210
|
-
const consequent_scope =
|
|
1211
|
-
context.state.scopes.get(switch_case.consequent) || context.state.scope;
|
|
1212
|
-
const consequent = b.block(
|
|
1213
|
-
transform_body(flattened_consequent, {
|
|
1214
|
-
...context,
|
|
1215
|
-
state: { ...context.state, scope: consequent_scope },
|
|
1216
|
-
}),
|
|
1217
|
-
);
|
|
1218
|
-
case_body.push(...consequent.body);
|
|
1219
|
-
}
|
|
1220
|
-
|
|
1221
|
-
cases.push(
|
|
1222
|
-
b.switch_case(
|
|
1223
|
-
switch_case.test ? /** @type {AST.Expression} */ (context.visit(switch_case.test)) : null,
|
|
1224
|
-
case_body,
|
|
1225
|
-
),
|
|
1226
|
-
);
|
|
1227
|
-
}
|
|
1228
|
-
|
|
1229
|
-
context.state.init?.push(b.stmt(b.call(b.id('_$_.output_push'), b.literal(BLOCK_OPEN))));
|
|
1230
|
-
|
|
1231
|
-
context.state.init?.push(
|
|
1232
|
-
b.switch(/** @type {AST.Expression} */ (context.visit(node.discriminant)), cases),
|
|
1233
|
-
);
|
|
1234
|
-
|
|
1235
|
-
context.state.init?.push(b.stmt(b.call(b.id('_$_.output_push'), b.literal(BLOCK_CLOSE))));
|
|
1236
|
-
},
|
|
1237
|
-
|
|
1238
|
-
ForOfStatement(node, context) {
|
|
1239
|
-
if (!is_inside_component(context)) {
|
|
1240
|
-
context.next();
|
|
1241
|
-
return;
|
|
1242
|
-
}
|
|
1243
|
-
const body_scope = context.state.scopes.get(node.body);
|
|
1244
|
-
|
|
1245
|
-
context.state.init?.push(b.stmt(b.call(b.id('_$_.output_push'), b.literal(BLOCK_OPEN))));
|
|
1246
|
-
|
|
1247
|
-
const body = transform_body(/** @type {AST.BlockStatement} */ (node.body).body, {
|
|
1248
|
-
...context,
|
|
1249
|
-
state: { ...context.state, scope: /** @type {ScopeInterface} */ (body_scope) },
|
|
1250
|
-
});
|
|
1251
|
-
|
|
1252
|
-
if (node.index) {
|
|
1253
|
-
context.state.init?.push(b.var(node.index, b.literal(0)));
|
|
1254
|
-
body.push(b.stmt(b.update('++', node.index)));
|
|
1255
|
-
}
|
|
1256
|
-
|
|
1257
|
-
context.state.init?.push(
|
|
1258
|
-
b.for_of(
|
|
1259
|
-
/** @type {AST.VariableDeclaration} */ (context.visit(node.left)),
|
|
1260
|
-
/** @type {AST.Expression} */
|
|
1261
|
-
(context.visit(node.right)),
|
|
1262
|
-
b.block(body),
|
|
1263
|
-
),
|
|
1264
|
-
);
|
|
1265
|
-
|
|
1266
|
-
context.state.init?.push(b.stmt(b.call(b.id('_$_.output_push'), b.literal(BLOCK_CLOSE))));
|
|
1267
|
-
},
|
|
1268
|
-
|
|
1269
|
-
IfStatement(node, context) {
|
|
1270
|
-
if (!is_inside_component(context)) {
|
|
1271
|
-
context.next();
|
|
1272
|
-
return;
|
|
1273
|
-
}
|
|
1274
|
-
|
|
1275
|
-
const consequent_body =
|
|
1276
|
-
node.consequent.type === 'BlockStatement' ? node.consequent.body : [node.consequent];
|
|
1277
|
-
|
|
1278
|
-
const consequent = b.block(
|
|
1279
|
-
transform_body(consequent_body, {
|
|
1280
|
-
...context,
|
|
1281
|
-
state: {
|
|
1282
|
-
...context.state,
|
|
1283
|
-
scope: /** @type {ScopeInterface} */ (
|
|
1284
|
-
context.state.scopes.get(node.consequent) || context.state.scope
|
|
1285
|
-
),
|
|
1286
|
-
},
|
|
1287
|
-
}),
|
|
1288
|
-
);
|
|
1289
|
-
|
|
1290
|
-
context.state.init?.push(b.stmt(b.call(b.id('_$_.output_push'), b.literal(BLOCK_OPEN))));
|
|
1291
|
-
|
|
1292
|
-
/** @type {AST.BlockStatement | AST.IfStatement | null} */
|
|
1293
|
-
let alternate = null;
|
|
1294
|
-
if (node.alternate) {
|
|
1295
|
-
const alternate_scope = context.state.scopes.get(node.alternate) || context.state.scope;
|
|
1296
|
-
const alternate_body_nodes =
|
|
1297
|
-
node.alternate.type === 'IfStatement'
|
|
1298
|
-
? [node.alternate]
|
|
1299
|
-
: /** @type {AST.BlockStatement} */ (node.alternate).body;
|
|
1300
|
-
|
|
1301
|
-
alternate = b.block(
|
|
1302
|
-
transform_body(alternate_body_nodes, {
|
|
1303
|
-
...context,
|
|
1304
|
-
state: { ...context.state, scope: alternate_scope },
|
|
1305
|
-
}),
|
|
1306
|
-
);
|
|
1307
|
-
}
|
|
1308
|
-
|
|
1309
|
-
context.state.init?.push(
|
|
1310
|
-
b.if(/** @type {AST.Expression} */ (context.visit(node.test)), consequent, alternate),
|
|
1311
|
-
);
|
|
1312
|
-
|
|
1313
|
-
context.state.init?.push(b.stmt(b.call(b.id('_$_.output_push'), b.literal(BLOCK_CLOSE))));
|
|
1314
|
-
},
|
|
1315
|
-
|
|
1316
|
-
ReturnStatement(node, context) {
|
|
1317
|
-
if (!is_inside_component(context)) {
|
|
1318
|
-
return context.next();
|
|
1319
|
-
}
|
|
1320
|
-
const info = context.state.return_flags?.get(node);
|
|
1321
|
-
if (info) {
|
|
1322
|
-
return b.stmt(b.assignment('=', b.id(info.name), b.true));
|
|
1323
|
-
}
|
|
1324
|
-
return context.next();
|
|
1325
|
-
},
|
|
1326
|
-
|
|
1327
|
-
AssignmentExpression(node, context) {
|
|
1328
|
-
const left = node.left;
|
|
1329
|
-
|
|
1330
|
-
// Handle lazy binding assignments (e.g., a = 5 where a is from let &{a} = obj)
|
|
1331
|
-
if (left.type === 'Identifier') {
|
|
1332
|
-
const binding = context.state.scope?.get(left.name);
|
|
1333
|
-
if (binding?.transform?.assign && binding.node !== left) {
|
|
1334
|
-
let value = /** @type {AST.Expression} */ (context.visit(node.right));
|
|
1335
|
-
|
|
1336
|
-
// For compound operators (+=, -=, *=, /=), expand to read + operation
|
|
1337
|
-
if (node.operator !== '=') {
|
|
1338
|
-
const operator = node.operator.slice(0, -1); // '+=' -> '+'
|
|
1339
|
-
const current = binding.transform.read(left);
|
|
1340
|
-
value = b.binary(/** @type {AST.BinaryOperator} */ (operator), current, value);
|
|
1341
|
-
}
|
|
1342
|
-
|
|
1343
|
-
return binding.transform.assign(left, value);
|
|
1344
|
-
}
|
|
1345
|
-
}
|
|
1346
|
-
|
|
1347
|
-
return context.next();
|
|
1348
|
-
},
|
|
1349
|
-
|
|
1350
|
-
UpdateExpression(node, context) {
|
|
1351
|
-
const argument = node.argument;
|
|
1352
|
-
|
|
1353
|
-
// Handle lazy binding updates (e.g., a++ where a is from let &{a} = obj)
|
|
1354
|
-
if (argument.type === 'Identifier') {
|
|
1355
|
-
const binding = context.state.scope?.get(argument.name);
|
|
1356
|
-
if (binding?.transform?.update && binding.node !== argument) {
|
|
1357
|
-
return binding.transform.update(node);
|
|
1358
|
-
}
|
|
1359
|
-
}
|
|
1360
|
-
},
|
|
1361
|
-
|
|
1362
|
-
ServerIdentifier(node, context) {
|
|
1363
|
-
return b.id('_$_server_$_');
|
|
1364
|
-
},
|
|
1365
|
-
|
|
1366
|
-
StyleIdentifier(node, context) {
|
|
1367
|
-
return b.id(STYLE_IDENTIFIER);
|
|
1368
|
-
},
|
|
1369
|
-
|
|
1370
|
-
ImportDeclaration(node, context) {
|
|
1371
|
-
const { state } = context;
|
|
1372
|
-
|
|
1373
|
-
if (!state.to_ts && node.importKind === 'type') {
|
|
1374
|
-
return b.empty;
|
|
1375
|
-
}
|
|
1376
|
-
|
|
1377
|
-
if (state.ancestor_server_block) {
|
|
1378
|
-
if (!node.specifiers.length) {
|
|
1379
|
-
return b.empty;
|
|
1380
|
-
}
|
|
1381
|
-
|
|
1382
|
-
/** @type {AST.VariableDeclaration[]} */
|
|
1383
|
-
const locals = state.server_block_locals;
|
|
1384
|
-
for (const spec of node.specifiers) {
|
|
1385
|
-
const original_name = spec.local.name;
|
|
1386
|
-
const name = obfuscate_identifier(original_name);
|
|
1387
|
-
spec.local = b.id(name);
|
|
1388
|
-
locals.push(b.const(original_name, b.id(name)));
|
|
1389
|
-
}
|
|
1390
|
-
state.imports.add(node);
|
|
1391
|
-
return b.empty;
|
|
1392
|
-
}
|
|
1393
|
-
|
|
1394
|
-
return /** @type {AST.ImportDeclaration} */ ({
|
|
1395
|
-
...node,
|
|
1396
|
-
specifiers: node.specifiers
|
|
1397
|
-
.filter((spec) => /** @type {AST.ImportSpecifier} */ (spec).importKind !== 'type')
|
|
1398
|
-
.map((spec) => context.visit(spec)),
|
|
1399
|
-
});
|
|
1400
|
-
},
|
|
1401
|
-
|
|
1402
|
-
TryStatement(node, context) {
|
|
1403
|
-
if (!is_inside_component(context)) {
|
|
1404
|
-
return context.next();
|
|
1405
|
-
}
|
|
1406
|
-
|
|
1407
|
-
const has_pending = node.pending !== null;
|
|
1408
|
-
const has_catch = node.handler !== null;
|
|
1409
|
-
|
|
1410
|
-
const body = transform_body(node.block.body, {
|
|
1411
|
-
...context,
|
|
1412
|
-
state: {
|
|
1413
|
-
...context.state,
|
|
1414
|
-
scope: /** @type {ScopeInterface} */ (context.state.scopes.get(node.block)),
|
|
1415
|
-
},
|
|
1416
|
-
});
|
|
1417
|
-
|
|
1418
|
-
// Wrap try_fn body with hydration markers when pending or catch is present
|
|
1419
|
-
const try_fn = b.arrow(
|
|
1420
|
-
[],
|
|
1421
|
-
b.block(
|
|
1422
|
-
has_pending || has_catch
|
|
1423
|
-
? [
|
|
1424
|
-
b.stmt(b.call(b.id('_$_.output_push'), b.literal(BLOCK_OPEN))),
|
|
1425
|
-
...body,
|
|
1426
|
-
b.stmt(b.call(b.id('_$_.output_push'), b.literal(BLOCK_CLOSE))),
|
|
1427
|
-
]
|
|
1428
|
-
: body,
|
|
1429
|
-
),
|
|
1430
|
-
);
|
|
1431
|
-
|
|
1432
|
-
/** @type {AST.Expression} */
|
|
1433
|
-
let catch_fn = b.literal(null);
|
|
1434
|
-
|
|
1435
|
-
const handler = node.handler;
|
|
1436
|
-
if (handler) {
|
|
1437
|
-
if (handler.param) {
|
|
1438
|
-
delete handler.param.typeAnnotation;
|
|
1439
|
-
}
|
|
1440
|
-
|
|
1441
|
-
/** @type {AST.Statement | null} */
|
|
1442
|
-
let reset = null;
|
|
1443
|
-
if (handler.resetParam) {
|
|
1444
|
-
delete handler.resetParam.typeAnnotation;
|
|
1445
|
-
|
|
1446
|
-
reset = b.const(
|
|
1447
|
-
handler.resetParam.type === 'AssignmentPattern'
|
|
1448
|
-
? /** @type {AST.Identifier} */ (handler.resetParam.left).name
|
|
1449
|
-
: /** @type {AST.Identifier} */ (handler.resetParam).name,
|
|
1450
|
-
b.id('_$_.noop'),
|
|
1451
|
-
);
|
|
1452
|
-
}
|
|
1453
|
-
|
|
1454
|
-
catch_fn = b.arrow(
|
|
1455
|
-
[handler.param || b.id('error')],
|
|
1456
|
-
b.block([
|
|
1457
|
-
b.stmt(b.call(b.id('_$_.output_push'), b.literal(BLOCK_OPEN))),
|
|
1458
|
-
...(reset ? [reset] : []),
|
|
1459
|
-
...transform_body(handler.body.body, {
|
|
1460
|
-
...context,
|
|
1461
|
-
state: {
|
|
1462
|
-
...context.state,
|
|
1463
|
-
scope: /** @type {ScopeInterface} */ (context.state.scopes.get(handler.body)),
|
|
1464
|
-
},
|
|
1465
|
-
}),
|
|
1466
|
-
b.stmt(b.call(b.id('_$_.output_push'), b.literal(BLOCK_CLOSE))),
|
|
1467
|
-
]),
|
|
1468
|
-
);
|
|
1469
|
-
}
|
|
1470
|
-
|
|
1471
|
-
const pending_body = node.pending
|
|
1472
|
-
? transform_body(node.pending.body, {
|
|
1473
|
-
...context,
|
|
1474
|
-
state: {
|
|
1475
|
-
...context.state,
|
|
1476
|
-
scope: /** @type {ScopeInterface} */ (context.state.scopes.get(node.pending)),
|
|
1477
|
-
},
|
|
1478
|
-
})
|
|
1479
|
-
: null;
|
|
1480
|
-
|
|
1481
|
-
// Wrap pending_fn body with hydration markers
|
|
1482
|
-
const pending_fn =
|
|
1483
|
-
pending_body !== null
|
|
1484
|
-
? b.arrow(
|
|
1485
|
-
[],
|
|
1486
|
-
b.block([
|
|
1487
|
-
b.stmt(b.call(b.id('_$_.output_push'), b.literal(BLOCK_OPEN))),
|
|
1488
|
-
...pending_body,
|
|
1489
|
-
b.stmt(b.call(b.id('_$_.output_push'), b.literal(BLOCK_CLOSE))),
|
|
1490
|
-
]),
|
|
1491
|
-
)
|
|
1492
|
-
: b.literal(null);
|
|
1493
|
-
|
|
1494
|
-
context.state.init?.push(b.stmt(b.call('_$_.try_block', try_fn, catch_fn, pending_fn)));
|
|
1495
|
-
},
|
|
1496
|
-
|
|
1497
|
-
RippleExpression(node, { visit, state }) {
|
|
1498
|
-
let expression = /** @type {AST.Expression} */ (visit(node.expression, state));
|
|
1499
|
-
const is_children_expression = is_children_template_expression(node.expression, state.scope);
|
|
1500
|
-
|
|
1501
|
-
if (expression.type === 'Literal') {
|
|
1502
|
-
state.init?.push(
|
|
1503
|
-
b.stmt(b.call(b.id('_$_.output_push'), b.literal(escape(expression.value)))),
|
|
1504
|
-
);
|
|
1505
|
-
} else if (is_children_expression) {
|
|
1506
|
-
state.init?.push(b.stmt(b.call('_$_.render_expression', expression)));
|
|
1507
|
-
} else {
|
|
1508
|
-
state.init?.push(b.stmt(b.call(b.id('_$_.output_push'), b.call('_$_.escape', expression))));
|
|
1509
|
-
}
|
|
1510
|
-
},
|
|
1511
|
-
|
|
1512
|
-
Text(node, { visit, state }) {
|
|
1513
|
-
let expression = /** @type {AST.Expression} */ (visit(node.expression, state));
|
|
1514
|
-
|
|
1515
|
-
if (expression.type === 'Literal') {
|
|
1516
|
-
state.init?.push(
|
|
1517
|
-
b.stmt(b.call(b.id('_$_.output_push'), b.literal(escape(expression.value)))),
|
|
1518
|
-
);
|
|
1519
|
-
} else {
|
|
1520
|
-
state.init?.push(b.stmt(b.call(b.id('_$_.output_push'), b.call('_$_.escape', expression))));
|
|
1521
|
-
}
|
|
1522
|
-
},
|
|
1523
|
-
|
|
1524
|
-
Tsx(node, { visit, state }) {
|
|
1525
|
-
const converted_children = node.children
|
|
1526
|
-
.map((child) => jsx_to_ripple_node(/** @type {AST.Node} */ (child)))
|
|
1527
|
-
.flat()
|
|
1528
|
-
.filter((child) => child != null);
|
|
1529
|
-
|
|
1530
|
-
/** @type {AST.Statement[]} */
|
|
1531
|
-
const init = [];
|
|
1532
|
-
transform_children(
|
|
1533
|
-
converted_children,
|
|
1534
|
-
/** @type {TransformServerContext} */ ({
|
|
1535
|
-
visit,
|
|
1536
|
-
state: {
|
|
1537
|
-
...state,
|
|
1538
|
-
init,
|
|
1539
|
-
},
|
|
1540
|
-
}),
|
|
1541
|
-
);
|
|
1542
|
-
|
|
1543
|
-
if (state.template_child) {
|
|
1544
|
-
// Template body: push children statements inline
|
|
1545
|
-
if (init.length > 0) {
|
|
1546
|
-
state.init?.push(b.block(init));
|
|
1547
|
-
}
|
|
1548
|
-
} else {
|
|
1549
|
-
// Expression context: return ripple_element(render_fn)
|
|
1550
|
-
const render_fn = b.function(b.id('render_children'), [], b.block(init));
|
|
1551
|
-
return b.call('_$_.ripple_element', render_fn);
|
|
1552
|
-
}
|
|
1553
|
-
},
|
|
1554
|
-
|
|
1555
|
-
Html(node, { visit, state }) {
|
|
1556
|
-
const expression = /** @type {AST.Expression} */ (visit(node.expression, state));
|
|
1557
|
-
|
|
1558
|
-
// For literal values, compute hash at build time
|
|
1559
|
-
if (expression.type === 'Literal') {
|
|
1560
|
-
const value = String(expression.value ?? '');
|
|
1561
|
-
const hash_value = hash(value);
|
|
1562
|
-
// Push hash comment
|
|
1563
|
-
state.init?.push(b.stmt(b.call(b.id('_$_.output_push'), b.literal(`<!--${hash_value}-->`))));
|
|
1564
|
-
// Push the HTML content
|
|
1565
|
-
state.init?.push(b.stmt(b.call(b.id('_$_.output_push'), b.literal(value))));
|
|
1566
|
-
// Push empty comment as end marker
|
|
1567
|
-
state.init?.push(b.stmt(b.call(b.id('_$_.output_push'), b.literal('<!---->'))));
|
|
1568
|
-
} else {
|
|
1569
|
-
// For dynamic values, compute hash at runtime
|
|
1570
|
-
// Create a variable to store the value
|
|
1571
|
-
const value_id = state.scope?.generate('html_value');
|
|
1572
|
-
if (value_id) {
|
|
1573
|
-
state.init?.push(
|
|
1574
|
-
b.const(value_id, b.call(b.id('String'), b.logical('??', expression, b.literal('')))),
|
|
1575
|
-
);
|
|
1576
|
-
// Compute hash at runtime using _$_.hash and push as comment
|
|
1577
|
-
state.init?.push(
|
|
1578
|
-
b.stmt(
|
|
1579
|
-
b.call(
|
|
1580
|
-
b.id('_$_.output_push'),
|
|
1581
|
-
b.binary(
|
|
1582
|
-
'+',
|
|
1583
|
-
b.binary('+', b.literal('<!--'), b.call('_$_.hash', b.id(value_id))),
|
|
1584
|
-
b.literal('-->'),
|
|
1585
|
-
),
|
|
1586
|
-
),
|
|
1587
|
-
),
|
|
1588
|
-
);
|
|
1589
|
-
// Push the HTML content
|
|
1590
|
-
state.init?.push(b.stmt(b.call(b.id('_$_.output_push'), b.id(value_id))));
|
|
1591
|
-
// Push empty comment as end marker
|
|
1592
|
-
state.init?.push(b.stmt(b.call(b.id('_$_.output_push'), b.literal('<!---->'))));
|
|
1593
|
-
}
|
|
1594
|
-
}
|
|
1595
|
-
},
|
|
1596
|
-
|
|
1597
|
-
ScriptContent(node, context) {
|
|
1598
|
-
context.state.init?.push(b.stmt(b.call(b.id('_$_.output_push'), b.literal(node.content))));
|
|
1599
|
-
},
|
|
1600
|
-
|
|
1601
|
-
ServerBlock(node, context) {
|
|
1602
|
-
const exports = node.metadata.exports;
|
|
1603
|
-
|
|
1604
|
-
// Convert Imports inside ServerBlock to local variables
|
|
1605
|
-
// ImportDeclaration() visitor will add imports to the top of the module
|
|
1606
|
-
/** @type {AST.VariableDeclaration[]} */
|
|
1607
|
-
const server_block_locals = [];
|
|
1608
|
-
|
|
1609
|
-
const block = /** @type {AST.BlockStatement} */ (
|
|
1610
|
-
context.visit(node.body, {
|
|
1611
|
-
...context.state,
|
|
1612
|
-
ancestor_server_block: node,
|
|
1613
|
-
server_block_locals,
|
|
1614
|
-
server_exported_names: [],
|
|
1615
|
-
})
|
|
1616
|
-
);
|
|
1617
|
-
|
|
1618
|
-
if (exports.size === 0) {
|
|
1619
|
-
return {
|
|
1620
|
-
...block,
|
|
1621
|
-
body: [...server_block_locals, ...block.body],
|
|
1622
|
-
};
|
|
1623
|
-
}
|
|
1624
|
-
|
|
1625
|
-
const file_path = context.state.filename;
|
|
1626
|
-
const rpc_modules = globalThis.rpc_modules;
|
|
1627
|
-
|
|
1628
|
-
if (rpc_modules) {
|
|
1629
|
-
for (const name of exports) {
|
|
1630
|
-
const func_path = file_path + '#' + name;
|
|
1631
|
-
// needs to be a sha256 hash of func_path, to avoid leaking file structure
|
|
1632
|
-
const hash = createHash('sha256').update(func_path).digest('hex').slice(0, 8);
|
|
1633
|
-
rpc_modules.set(hash, [file_path, name]);
|
|
1634
|
-
}
|
|
1635
|
-
}
|
|
1636
|
-
|
|
1637
|
-
return b.export(
|
|
1638
|
-
b.const(
|
|
1639
|
-
'_$_server_$_',
|
|
1640
|
-
b.call(
|
|
1641
|
-
b.thunk(
|
|
1642
|
-
b.block([
|
|
1643
|
-
b.var('_$_server_$_', b.object([])),
|
|
1644
|
-
...server_block_locals,
|
|
1645
|
-
...block.body,
|
|
1646
|
-
b.return(b.id('_$_server_$_')),
|
|
1647
|
-
]),
|
|
1648
|
-
),
|
|
1649
|
-
),
|
|
1650
|
-
),
|
|
1651
|
-
);
|
|
1652
|
-
},
|
|
1653
|
-
|
|
1654
|
-
Program(node, context) {
|
|
1655
|
-
// We need a Program visitor to make sure all top level entities are visited
|
|
1656
|
-
// Without it, and without at least one export component
|
|
1657
|
-
// other components are not visited
|
|
1658
|
-
/** @type {Array<AST.Statement | AST.Directive | AST.ModuleDeclaration>} */
|
|
1659
|
-
const statements = [];
|
|
1660
|
-
|
|
1661
|
-
for (const statement of node.body) {
|
|
1662
|
-
statements.push(
|
|
1663
|
-
/** @type {AST.Statement | AST.Directive | AST.ModuleDeclaration} */ (
|
|
1664
|
-
context.visit(statement)
|
|
1665
|
-
),
|
|
1666
|
-
);
|
|
1667
|
-
}
|
|
1668
|
-
|
|
1669
|
-
return { ...node, body: statements };
|
|
1670
|
-
},
|
|
1671
|
-
};
|
|
1672
|
-
|
|
1673
|
-
/**
|
|
1674
|
-
* @param {string} filename
|
|
1675
|
-
* @param {string} source
|
|
1676
|
-
* @param {AnalysisResult} analysis
|
|
1677
|
-
* @param {boolean} minify_css
|
|
1678
|
-
* @param {boolean} [dev]
|
|
1679
|
-
* @returns {{ ast: AST.Program; js: { code: string; map: RawSourceMap | null }; css: string; }}
|
|
1680
|
-
*/
|
|
1681
|
-
export function transform_server(filename, source, analysis, minify_css, dev = false) {
|
|
1682
|
-
// Use component metadata collected during the analyze phase
|
|
1683
|
-
const component_metadata = analysis.component_metadata || [];
|
|
1684
|
-
|
|
1685
|
-
/** @type {TransformServerState} */
|
|
1686
|
-
const state = {
|
|
1687
|
-
imports: new Set(),
|
|
1688
|
-
init: null,
|
|
1689
|
-
scope: analysis.scope,
|
|
1690
|
-
scopes: analysis.scopes,
|
|
1691
|
-
serverIdentifierPresent: analysis.metadata.serverIdentifierPresent,
|
|
1692
|
-
stylesheets: [],
|
|
1693
|
-
component_metadata,
|
|
1694
|
-
ancestor_server_block: undefined,
|
|
1695
|
-
server_block_locals: [],
|
|
1696
|
-
server_exported_names: [],
|
|
1697
|
-
filename,
|
|
1698
|
-
namespace: 'html',
|
|
1699
|
-
// TODO: should we remove all `to_ts` usages we use the client rendering for that?
|
|
1700
|
-
to_ts: false,
|
|
1701
|
-
metadata: {},
|
|
1702
|
-
dev,
|
|
1703
|
-
};
|
|
1704
|
-
|
|
1705
|
-
state.imports.add(`import * as _$_ from 'ripple/internal/server'`);
|
|
1706
|
-
|
|
1707
|
-
const program = /** @type {AST.Program} */ (walk(analysis.ast, { ...state }, visitors));
|
|
1708
|
-
|
|
1709
|
-
const css = render_stylesheets(state.stylesheets, minify_css);
|
|
1710
|
-
|
|
1711
|
-
// Add CSS registration if there are stylesheets
|
|
1712
|
-
if (state.stylesheets.length > 0 && css) {
|
|
1713
|
-
// Register each stylesheet's CSS
|
|
1714
|
-
for (const stylesheet of state.stylesheets) {
|
|
1715
|
-
const css_for_component = render_stylesheets([stylesheet]);
|
|
1716
|
-
/** @type {AST.Program} */ (program).body.push(
|
|
1717
|
-
b.stmt(
|
|
1718
|
-
b.call('_$_.register_css', b.literal(stylesheet.hash), b.literal(css_for_component)),
|
|
1719
|
-
),
|
|
1720
|
-
);
|
|
1721
|
-
}
|
|
1722
|
-
}
|
|
1723
|
-
|
|
1724
|
-
/** @type {AST.Program['body']} */
|
|
1725
|
-
let body = [];
|
|
1726
|
-
|
|
1727
|
-
for (const import_node of state.imports) {
|
|
1728
|
-
if (typeof import_node === 'string') {
|
|
1729
|
-
body.push(b.stmt(b.id(import_node)));
|
|
1730
|
-
} else {
|
|
1731
|
-
body.push(import_node);
|
|
1732
|
-
}
|
|
1733
|
-
}
|
|
1734
|
-
|
|
1735
|
-
body.push(...program.body);
|
|
1736
|
-
|
|
1737
|
-
program.body = body;
|
|
1738
|
-
|
|
1739
|
-
const js = print(program, /** @type {Visitors<AST.Node, TransformServerState>} */ (ts()), {
|
|
1740
|
-
sourceMapContent: source,
|
|
1741
|
-
sourceMapSource: path.basename(filename),
|
|
1742
|
-
});
|
|
1743
|
-
|
|
1744
|
-
return {
|
|
1745
|
-
ast: /** @type {AST.Program} */ (program),
|
|
1746
|
-
js,
|
|
1747
|
-
css,
|
|
1748
|
-
};
|
|
1749
|
-
}
|