ripple 0.2.189 → 0.2.190

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.
@@ -8,7 +8,6 @@
8
8
  VisitorClientContext,
9
9
  TransformClientState,
10
10
  ScopeInterface,
11
- Visitor,
12
11
  Visitors
13
12
  } from '#compiler';
14
13
  */
@@ -52,7 +51,12 @@ import {
52
51
  determine_namespace_for_children,
53
52
  index_to_key,
54
53
  } from '../../../utils.js';
55
- import { obfuscate_imported } from '../../../import-utils.js';
54
+ import {
55
+ CSS_HASH_IDENTIFIER,
56
+ STYLE_IDENTIFIER,
57
+ SERVER_IDENTIFIER,
58
+ obfuscate_identifier,
59
+ } from '../../../identifier-utils.js';
56
60
  import is_reference from 'is-reference';
57
61
  import { object } from '../../../../utils/ast.js';
58
62
  import { render_stylesheets } from '../stylesheet.js';
@@ -316,7 +320,7 @@ function visit_title_element(node, context) {
316
320
  * @returns {string}
317
321
  */
318
322
  function set_hidden_import_from_ripple(name, context) {
319
- name = obfuscate_imported(name);
323
+ name = obfuscate_identifier(name);
320
324
  if (!context.state.imports.has(`import { ${name} } from 'ripple/compiler/internal/import'`)) {
321
325
  context.state.imports.add(`import { ${name} } from 'ripple/compiler/internal/import'`);
322
326
  }
@@ -359,7 +363,7 @@ const visitors = {
359
363
  name: capitalized_name,
360
364
  metadata: {
361
365
  ...node.metadata,
362
- original_name: node.name,
366
+ source_name: node.name,
363
367
  is_capitalized: true,
364
368
  },
365
369
  };
@@ -395,12 +399,44 @@ const visitors = {
395
399
  },
396
400
 
397
401
  ServerIdentifier(node, context) {
398
- return b.id('_$_server_$_');
402
+ const id = b.id(SERVER_IDENTIFIER);
403
+ id.metadata.source_name = '#server';
404
+ id.loc = node.loc;
405
+ return id;
406
+ },
407
+
408
+ StyleIdentifier(node, context) {
409
+ const id = b.id(STYLE_IDENTIFIER);
410
+ id.metadata.source_name = '#style';
411
+ id.loc = node.loc;
412
+ return id;
399
413
  },
400
414
 
401
- /** @type {Visitor<AST.ImportDeclaration, TransformClientState, AST.Node>} */
402
415
  ImportDeclaration(node, context) {
403
- if (!context.state.to_ts && node.importKind === 'type') {
416
+ const { state } = context;
417
+
418
+ if (!state.to_ts && node.importKind === 'type') {
419
+ return b.empty;
420
+ }
421
+
422
+ if (state.to_ts && state.inside_server_block) {
423
+ /** @type {AST.VariableDeclaration[]} */
424
+ const locals = state.server_block_locals;
425
+ for (const spec of node.specifiers) {
426
+ const original_name = spec.local.name;
427
+ const name = obfuscate_identifier(original_name);
428
+ if (
429
+ spec.type !== 'ImportSpecifier' ||
430
+ (spec.imported && /** @type {AST.Identifier} */ (spec.imported).name !== spec.local.name)
431
+ ) {
432
+ spec.local.name = name;
433
+ } else {
434
+ spec.local = b.id(name);
435
+ }
436
+ spec.local.metadata.source_name = original_name;
437
+ locals.push(b.const(original_name, b.id(name)));
438
+ }
439
+ state.imports.add(node);
404
440
  return b.empty;
405
441
  }
406
442
 
@@ -408,8 +444,7 @@ const visitors = {
408
444
  ...node,
409
445
  specifiers: node.specifiers
410
446
  .filter(
411
- (spec) =>
412
- context.state.to_ts || /** @type {AST.ImportSpecifier} */ (spec).importKind !== 'type',
447
+ (spec) => state.to_ts || /** @type {AST.ImportSpecifier} */ (spec).importKind !== 'type',
413
448
  )
414
449
  .map((spec) => context.visit(spec)),
415
450
  });
@@ -422,7 +457,6 @@ const visitors = {
422
457
  return context.visit(/** @type {AST.Expression} */ (node.expression));
423
458
  },
424
459
 
425
- /** @type {Visitor<AST.CallExpression, TransformClientState, AST.Node>} */
426
460
  CallExpression(node, context) {
427
461
  if (!context.state.to_ts) {
428
462
  delete node.typeArguments;
@@ -543,7 +577,7 @@ const visitors = {
543
577
  const calleeId = b.id(alias);
544
578
  calleeId.loc = callee.loc;
545
579
  calleeId.metadata = {
546
- tracked_shorthand: callee.type === 'TrackedMapExpression' ? '#Map' : '#Set',
580
+ source_name: callee.type === 'TrackedMapExpression' ? '#Map' : '#Set',
547
581
  path: [...context.path],
548
582
  };
549
583
  /** @type {AST.NewExpression} */
@@ -648,7 +682,6 @@ const visitors = {
648
682
  return b.call('_$_.get', /** @type {AST.Expression} */ (context.visit(node.argument)));
649
683
  },
650
684
 
651
- /** @type {Visitor<AST.MemberExpression, TransformClientState, AST.Node>} */
652
685
  MemberExpression(node, context) {
653
686
  if (context.state.metadata?.tracking === false) {
654
687
  context.state.metadata.tracking = true;
@@ -712,7 +745,6 @@ const visitors = {
712
745
  return context.next();
713
746
  },
714
747
 
715
- /** @type {Visitor<AST.VariableDeclarator, TransformClientState, AST.Node>} */
716
748
  VariableDeclarator(node, context) {
717
749
  // In TypeScript mode, capitalize identifiers that are used as dynamic components
718
750
  if (context.state.to_ts) {
@@ -732,7 +764,7 @@ const visitors = {
732
764
  name: capitalized_name,
733
765
  metadata: {
734
766
  ...pattern.metadata,
735
- original_name: pattern.name,
767
+ source_name: pattern.name,
736
768
  is_capitalized: true,
737
769
  },
738
770
  };
@@ -1588,6 +1620,37 @@ const visitors = {
1588
1620
  let prop_statements;
1589
1621
  const metadata = { await: false };
1590
1622
 
1623
+ /** @type {AST.Statement[]} */
1624
+ const style_statements = [];
1625
+
1626
+ /** @type {'const' | 'var'} */
1627
+ let var_method_type = 'var';
1628
+ if (context.state.to_ts) {
1629
+ var_method_type = 'const';
1630
+ }
1631
+
1632
+ if (node.css !== null && node.metadata.styleIdentifierPresent) {
1633
+ /** @type {AST.Property[]} */
1634
+ const properties = [];
1635
+ if (node.metadata.topScopedClasses && node.metadata.topScopedClasses.size > 0) {
1636
+ const hash = b[var_method_type](b.id(CSS_HASH_IDENTIFIER), b.literal(node.css.hash));
1637
+ style_statements.push(hash);
1638
+ for (const [className] of node.metadata.topScopedClasses) {
1639
+ properties.push(
1640
+ b.prop(
1641
+ 'init',
1642
+ b.key(className),
1643
+ b.template(
1644
+ [b.quasi('', false), b.quasi(` ${className}`, true)],
1645
+ [b.id(CSS_HASH_IDENTIFIER)],
1646
+ ),
1647
+ ),
1648
+ );
1649
+ }
1650
+ }
1651
+ style_statements.push(b[var_method_type](b.id(STYLE_IDENTIFIER), b.object(properties)));
1652
+ }
1653
+
1591
1654
  if (context.state.to_ts) {
1592
1655
  const body_statements = [
1593
1656
  ...transform_body(node.body, {
@@ -1602,7 +1665,7 @@ const visitors = {
1602
1665
  (param) =>
1603
1666
  /** @type {AST.Pattern} */ (context.visit(param, { ...context.state, metadata })),
1604
1667
  ),
1605
- b.block(body_statements),
1668
+ b.block([...style_statements, ...body_statements]),
1606
1669
  );
1607
1670
  // Mark that this function was originally a component
1608
1671
  func.metadata = /** @type {AST.FunctionExpression['metadata']} */ ({
@@ -1645,6 +1708,7 @@ const visitors = {
1645
1708
  ? [b.id('__anchor'), props, b.id('__block')]
1646
1709
  : [b.id('__anchor'), b.id('_'), b.id('__block')],
1647
1710
  b.block([
1711
+ ...style_statements,
1648
1712
  ...(prop_statements ?? []),
1649
1713
  ...(metadata.await
1650
1714
  ? [b.stmt(b.call('_$_.async', b.thunk(b.block(body_statements), true)))]
@@ -1998,6 +2062,17 @@ const visitors = {
1998
2062
  return b.empty;
1999
2063
  }
2000
2064
 
2065
+ if (context.state.to_ts && context.state.inside_server_block) {
2066
+ const declaration = node.declaration;
2067
+
2068
+ if (declaration && declaration.type === 'FunctionDeclaration') {
2069
+ return context.visit(declaration);
2070
+ } else {
2071
+ // TODO
2072
+ throw new Error('Not implemented');
2073
+ }
2074
+ }
2075
+
2001
2076
  return context.next();
2002
2077
  },
2003
2078
 
@@ -2101,15 +2176,64 @@ const visitors = {
2101
2176
  },
2102
2177
 
2103
2178
  ServerBlock(node, context) {
2179
+ if (context.state.to_ts) {
2180
+ // Convert Imports inside ServerBlock to local variables
2181
+ // ImportDeclaration() visitor will add imports to the top of the module
2182
+ /** @type {AST.VariableDeclaration[]} */
2183
+ const server_block_locals = [];
2184
+
2185
+ const block = /** @type {AST.BlockStatement} */ (
2186
+ context.visit(node.body, {
2187
+ ...context.state,
2188
+ inside_server_block: true,
2189
+ server_block_locals,
2190
+ })
2191
+ );
2192
+
2193
+ /** @type {AST.Property[]} */
2194
+ const properties = [];
2195
+
2196
+ // Extract and preserve original function declarations
2197
+ for (const stmt of node.body.body) {
2198
+ if (
2199
+ stmt.type === 'ExportNamedDeclaration' &&
2200
+ stmt.declaration?.type === 'FunctionDeclaration' &&
2201
+ stmt.declaration.id
2202
+ ) {
2203
+ // create new nodes to avoid same node.loc issue
2204
+ // that would result in double definitions
2205
+ const id = b.id(stmt.declaration.id.name);
2206
+ properties.push(b.prop('init', id, id, false, true));
2207
+ }
2208
+ }
2209
+
2210
+ const value = b.call(
2211
+ b.thunk(b.block([...server_block_locals, ...block.body, b.return(b.object(properties))])),
2212
+ );
2213
+ value.loc = node.loc;
2214
+
2215
+ const server_identifier = b.id(SERVER_IDENTIFIER);
2216
+ server_identifier.loc = node.loc;
2217
+ // Add source_name to properly map longer generated back to '#server'
2218
+ server_identifier.metadata.source_name = '#server';
2219
+
2220
+ const server_const = b.const(server_identifier, value);
2221
+ server_const.loc = node.loc;
2222
+
2223
+ return server_const;
2224
+ }
2225
+
2104
2226
  const exports = node.metadata.exports;
2105
2227
 
2106
- if (exports.length === 0) {
2228
+ if (!context.state.serverIdentifierPresent) {
2229
+ // no point printing the client-side block if #server.func is not used
2107
2230
  return b.empty;
2108
2231
  }
2232
+
2109
2233
  const file_path = context.state.filename;
2110
2234
 
2111
- return b.const(
2112
- '_$_server_$_',
2235
+ return b.var(
2236
+ SERVER_IDENTIFIER,
2113
2237
  b.object(
2114
2238
  /** @type {AST.ServerBlock['metadata']['exports']} */ (exports).map((name) => {
2115
2239
  const func_path = file_path + '#' + name;
@@ -2130,7 +2254,6 @@ const visitors = {
2130
2254
  );
2131
2255
  },
2132
2256
 
2133
- /** @type {Visitor<AST.Program, TransformClientState, AST.Node>} */
2134
2257
  Program(node, context) {
2135
2258
  /** @type {Array<AST.Statement | AST.Directive | AST.ModuleDeclaration>} */
2136
2259
  const statements = [];
@@ -2318,9 +2441,9 @@ function transform_ts_child(node, context) {
2318
2441
  end: node.id.loc.end,
2319
2442
  };
2320
2443
  // Add metadata if this was capitalized
2321
- if (node.metadata?.ts_name && node.metadata?.original_name) {
2444
+ if (node.metadata?.ts_name && node.metadata?.source_name) {
2322
2445
  opening_name_element.metadata = {
2323
- original_name: node.metadata.original_name,
2446
+ source_name: node.metadata.source_name,
2324
2447
  is_capitalized: true,
2325
2448
  path: [...node.metadata.path],
2326
2449
  };
@@ -2357,14 +2480,14 @@ function transform_ts_child(node, context) {
2357
2480
  column:
2358
2481
  closing_tag_start +
2359
2482
  3 +
2360
- (node.metadata?.original_name?.length ||
2483
+ (node.metadata?.source_name?.length ||
2361
2484
  /** @type {string} */ (type_expression).length),
2362
2485
  },
2363
2486
  };
2364
2487
  // Add metadata if this was capitalized
2365
- if (node.metadata?.ts_name && node.metadata?.original_name) {
2488
+ if (node.metadata?.ts_name && node.metadata?.source_name) {
2366
2489
  closing_name_element.metadata = {
2367
- original_name: node.metadata.original_name,
2490
+ source_name: node.metadata.source_name,
2368
2491
  is_capitalized: true,
2369
2492
  path: [...node.metadata.path],
2370
2493
  };
@@ -2399,8 +2522,7 @@ function transform_ts_child(node, context) {
2399
2522
  // - '<' is at node.loc.end.column - type_expression.length - 3
2400
2523
  // - '>' is at node.loc.end.column - 1
2401
2524
  const tag_name_length = node.id.tracked
2402
- ? (node.metadata?.original_name?.length || /** @type {string} */ (type_expression).length) +
2403
- 1 // +1 for '@'
2525
+ ? (node.metadata?.source_name?.length || /** @type {string} */ (type_expression).length) + 1 // +1 for '@'
2404
2526
  : /** @type {string} */ (type_expression).length;
2405
2527
 
2406
2528
  jsxElement.closingElement.loc = {
@@ -2416,10 +2538,10 @@ function transform_ts_child(node, context) {
2416
2538
  }
2417
2539
 
2418
2540
  // Preserve metadata from Element node for mapping purposes
2419
- if (node.metadata && (node.metadata.ts_name || node.metadata.original_name)) {
2541
+ if (node.metadata && (node.metadata.ts_name || node.metadata.source_name)) {
2420
2542
  jsxElement.metadata = {
2421
2543
  ts_name: node.metadata.ts_name,
2422
- original_name: node.metadata.original_name,
2544
+ source_name: node.metadata.source_name,
2423
2545
  path: [...node.metadata.path],
2424
2546
  };
2425
2547
  }
@@ -2928,13 +3050,17 @@ function create_tsx_with_typescript_support() {
2928
3050
  * @param {TransformClientContext} context
2929
3051
  */
2930
3052
  const handle_function = (node, context) => {
3053
+ const loc = /** @type {AST.SourceLocation} */ (node.loc);
3054
+
2931
3055
  if (node.async) {
3056
+ context.location(loc.start.line, loc.start.column);
2932
3057
  context.write('async ');
3058
+ context.location(loc.start.line, loc.start.column + 6);
3059
+ context.write('function');
3060
+ } else {
3061
+ context.write('function', node);
2933
3062
  }
2934
- // Write 'function' keyword with node location for source mapping
2935
- // This creates a mapping from the source position (which may have 'component')
2936
- // to the generated 'function' keyword
2937
- context.write('function', node);
3063
+
2938
3064
  if (node.generator) {
2939
3065
  context.write('*');
2940
3066
  }
@@ -3461,6 +3587,9 @@ export function transform_client(filename, source, analysis, to_ts, minify_css)
3461
3587
  flush_node: null,
3462
3588
  scope: analysis.scope,
3463
3589
  scopes: analysis.scopes,
3590
+ inside_server_block: false,
3591
+ serverIdentifierPresent: analysis.metadata.serverIdentifierPresent,
3592
+ server_block_locals: [],
3464
3593
  stylesheets: [],
3465
3594
  to_ts,
3466
3595
  filename,
@@ -3482,7 +3611,11 @@ export function transform_client(filename, source, analysis, to_ts, minify_css)
3482
3611
  }
3483
3612
 
3484
3613
  for (const import_node of state.imports) {
3485
- program.body.unshift(b.stmt(b.id(import_node)));
3614
+ if (typeof import_node === 'string') {
3615
+ program.body.unshift(b.stmt(b.id(import_node)));
3616
+ } else {
3617
+ program.body.unshift(import_node);
3618
+ }
3486
3619
  }
3487
3620
 
3488
3621
  if (state.events.size > 0) {
@@ -354,18 +354,11 @@ export function convert_source_map_to_mappings(
354
354
  // Only create mappings for identifiers with location info (from source)
355
355
  // Synthesized identifiers (created by builders) don't have .loc and are skipped
356
356
  if (node.name && node.loc) {
357
- // Check if this identifier has tracked_shorthand metadata (e.g., TrackedMap -> #Map)
358
- if (node.metadata?.tracked_shorthand) {
357
+ // Check if this identifier was changed in metadata (e.g., #Map -> TrackedMap)
358
+ // Or if it was capitalized during transformation
359
+ if (node.metadata?.source_name) {
359
360
  tokens.push({
360
- source: node.metadata.tracked_shorthand,
361
- generated: node.name,
362
- loc: node.loc,
363
- });
364
- } else if (node.metadata?.is_capitalized) {
365
- // This identifier was capitalized during transformation
366
- // Map the original lowercase name to the capitalized generated name
367
- tokens.push({
368
- source: node.metadata.original_name,
361
+ source: node.metadata.source_name,
369
362
  generated: node.name,
370
363
  loc: node.loc,
371
364
  });
@@ -391,7 +384,7 @@ export function convert_source_map_to_mappings(
391
384
  if (node.loc && node.name) {
392
385
  if (node.metadata?.is_capitalized) {
393
386
  tokens.push({
394
- source: node.metadata.original_name,
387
+ source: node.metadata.source_name,
395
388
  generated: node.name,
396
389
  loc: node.loc,
397
390
  });
@@ -657,7 +650,7 @@ export function convert_source_map_to_mappings(
657
650
  );
658
651
  if (closingNameNode.metadata?.is_capitalized) {
659
652
  tokens.push({
660
- source: closingNameNode.metadata.original_name,
653
+ source: closingNameNode.metadata.source_name,
661
654
  generated: closingNameNode.name,
662
655
  loc: closingNameNode.loc,
663
656
  });
@@ -679,16 +672,35 @@ export function convert_source_map_to_mappings(
679
672
  // Add function/component keyword token
680
673
  if (node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression') {
681
674
  const node_fn = /** @type (typeof node) & AST.NodeWithLocation */ (node);
682
- const source_keyword = node.metadata?.was_component ? 'component' : 'function';
683
- // Add token for the keyword - esrap already mapped it via context.write('function', node)
675
+ const source_func_keyword = node.metadata?.was_component ? 'component' : 'function';
676
+ let start_col = node_fn.loc.start.column;
677
+ const async_keyword = 'async';
678
+
679
+ // We explicitly mapped async and function in esrap
680
+ if (node_fn.async) {
681
+ tokens.push({
682
+ source: async_keyword,
683
+ generated: async_keyword,
684
+ loc: {
685
+ start: { line: node_fn.loc.start.line, column: start_col },
686
+ end: {
687
+ line: node_fn.loc.start.line,
688
+ column: start_col + async_keyword.length,
689
+ },
690
+ },
691
+ });
692
+
693
+ start_col += async_keyword.length + 1; // +1 for space
694
+ }
695
+
684
696
  tokens.push({
685
- source: source_keyword,
697
+ source: source_func_keyword,
686
698
  generated: 'function',
687
699
  loc: {
688
- start: { line: node_fn.loc.start.line, column: node_fn.loc.start.column },
700
+ start: { line: node_fn.loc.start.line, column: start_col },
689
701
  end: {
690
702
  line: node_fn.loc.start.line,
691
- column: node_fn.loc.start.column + source_keyword.length,
703
+ column: start_col + source_func_keyword.length,
692
704
  },
693
705
  },
694
706
  });
@@ -887,7 +899,27 @@ export function convert_source_map_to_mappings(
887
899
  if (node.object) {
888
900
  visit(node.object);
889
901
  }
890
- if (!node.computed && node.property) {
902
+ if (node.computed && node.property && node.loc) {
903
+ // Need to cover the whole computed property ['something'] or obj[expr]:
904
+ // Add a mapping for the closing bracket ']'
905
+ // ESRap sourcemap includes the opening bracket '[' in the property loc,
906
+ // but for the closing bracket it also includes what comes after it
907
+ // so we never get the mapping that covers just the computed property.
908
+ tokens.push({
909
+ source: ']',
910
+ generated: ']',
911
+ loc: {
912
+ start: {
913
+ line: node.loc.end.line,
914
+ column: node.loc.end.column - 1,
915
+ },
916
+ end: node.loc.end,
917
+ },
918
+ });
919
+
920
+ // Also visit the property for its own mapping
921
+ visit(node.property);
922
+ } else if (!node.computed && node.property) {
891
923
  visit(node.property);
892
924
  }
893
925
  return;
@@ -30,6 +30,11 @@ import { escape } from '../../../../utils/escaping.js';
30
30
  import { is_event_attribute } from '../../../../utils/events.js';
31
31
  import { render_stylesheets } from '../stylesheet.js';
32
32
  import { createHash } from 'node:crypto';
33
+ import {
34
+ STYLE_IDENTIFIER,
35
+ CSS_HASH_IDENTIFIER,
36
+ obfuscate_identifier,
37
+ } from '../../../identifier-utils.js';
33
38
 
34
39
  /**
35
40
  * @param {AST.Node[]} children
@@ -127,22 +132,47 @@ const visitors = {
127
132
  }
128
133
 
129
134
  const metadata = { await: false };
130
- const body_statements = [
135
+
136
+ /** @type {AST.Statement[]} */
137
+ const body_statements = [];
138
+
139
+ if (node.css !== null) {
140
+ const hash_id = b.id(CSS_HASH_IDENTIFIER);
141
+ const hash = b.var(hash_id, b.literal(node.css.hash));
142
+ context.state.stylesheets.push(node.css);
143
+
144
+ // Register CSS hash during rendering
145
+ body_statements.push(
146
+ hash,
147
+ b.stmt(b.call(b.member(b.id('__output'), b.id('register_css')), hash_id)),
148
+ );
149
+
150
+ if (node.metadata.styleIdentifierPresent) {
151
+ /** @type {AST.Property[]} */
152
+ const properties = [];
153
+ if (node.metadata.topScopedClasses && node.metadata.topScopedClasses.size > 0) {
154
+ for (const [className] of node.metadata.topScopedClasses) {
155
+ properties.push(
156
+ b.prop(
157
+ 'init',
158
+ b.key(className),
159
+ b.template([b.quasi('', false), b.quasi(` ${className}`, true)], [hash_id]),
160
+ ),
161
+ );
162
+ }
163
+ }
164
+ body_statements.push(b.var(b.id(STYLE_IDENTIFIER), b.object(properties)));
165
+ }
166
+ }
167
+
168
+ body_statements.push(
131
169
  b.stmt(b.call('_$_.push_component')),
132
170
  ...transform_body(node.body, {
133
171
  ...context,
134
172
  state: { ...context.state, component: node, metadata },
135
173
  }),
136
174
  b.stmt(b.call('_$_.pop_component')),
137
- ];
138
-
139
- if (node.css !== null && node.css) {
140
- context.state.stylesheets.push(node.css);
141
- // Register CSS hash during rendering
142
- body_statements.unshift(
143
- b.stmt(b.call(b.member(b.id('__output'), b.id('register_css')), b.literal(node.css.hash))),
144
- );
145
- }
175
+ );
146
176
 
147
177
  let component_fn = b.function(
148
178
  node.id,
@@ -617,6 +647,7 @@ const visitors = {
617
647
  }
618
648
  }
619
649
  },
650
+
620
651
  SwitchStatement(node, context) {
621
652
  if (!is_inside_component(context)) {
622
653
  return context.next();
@@ -811,9 +842,31 @@ const visitors = {
811
842
  return b.id('_$_server_$_');
812
843
  },
813
844
 
814
- /** @type {Visitor<AST.ImportDeclaration, TransformServerState, AST.Node>} */
845
+ StyleIdentifier(node, context) {
846
+ return b.id(STYLE_IDENTIFIER);
847
+ },
848
+
815
849
  ImportDeclaration(node, context) {
816
- if (!context.state.to_ts && node.importKind === 'type') {
850
+ const { state } = context;
851
+
852
+ if (!state.to_ts && node.importKind === 'type') {
853
+ return b.empty;
854
+ }
855
+
856
+ if (state.inside_server_block) {
857
+ if (!node.specifiers.length) {
858
+ return b.empty;
859
+ }
860
+
861
+ /** @type {AST.VariableDeclaration[]} */
862
+ const locals = state.server_block_locals;
863
+ for (const spec of node.specifiers) {
864
+ const original_name = spec.local.name;
865
+ const name = obfuscate_identifier(original_name);
866
+ spec.local = b.id(name);
867
+ locals.push(b.const(original_name, b.id(name)));
868
+ }
869
+ state.imports.add(node);
817
870
  return b.empty;
818
871
  }
819
872
 
@@ -915,8 +968,7 @@ const visitors = {
915
968
 
916
969
  AwaitExpression(node, context) {
917
970
  const { state } = context;
918
- state.scope.server_block = true;
919
- state.inside_server_block = true;
971
+
920
972
  if (state.to_ts) {
921
973
  return context.next();
922
974
  }
@@ -1007,13 +1059,27 @@ const visitors = {
1007
1059
  ServerBlock(node, context) {
1008
1060
  const exports = node.metadata.exports;
1009
1061
 
1062
+ // Convert Imports inside ServerBlock to local variables
1063
+ // ImportDeclaration() visitor will add imports to the top of the module
1064
+ /** @type {AST.VariableDeclaration[]} */
1065
+ const server_block_locals = [];
1066
+
1067
+ const block = /** @type {AST.BlockStatement} */ (
1068
+ context.visit(node.body, {
1069
+ ...context.state,
1070
+ inside_server_block: true,
1071
+ server_block_locals,
1072
+ })
1073
+ );
1074
+
1010
1075
  if (exports.length === 0) {
1011
- return context.visit(node.body);
1076
+ return {
1077
+ ...block,
1078
+ body: [...server_block_locals, ...block.body],
1079
+ };
1012
1080
  }
1081
+
1013
1082
  const file_path = context.state.filename;
1014
- const block = /** @type {AST.BlockStatement} */ (
1015
- context.visit(node.body, { ...context.state, inside_server_block: true })
1016
- );
1017
1083
  const rpc_modules = globalThis.rpc_modules;
1018
1084
 
1019
1085
  if (rpc_modules) {
@@ -1032,6 +1098,7 @@ const visitors = {
1032
1098
  b.thunk(
1033
1099
  b.block([
1034
1100
  b.var('_$_server_$_', b.object([])),
1101
+ ...server_block_locals,
1035
1102
  ...block.body,
1036
1103
  b.return(b.id('_$_server_$_')),
1037
1104
  ]),
@@ -1059,9 +1126,11 @@ export function transform_server(filename, source, analysis, minify_css) {
1059
1126
  init: null,
1060
1127
  scope: analysis.scope,
1061
1128
  scopes: analysis.scopes,
1129
+ serverIdentifierPresent: analysis.metadata.serverIdentifierPresent,
1062
1130
  stylesheets: [],
1063
1131
  component_metadata,
1064
1132
  inside_server_block: false,
1133
+ server_block_locals: [],
1065
1134
  filename,
1066
1135
  namespace: 'html',
1067
1136
  // TODO: should we remove all `to_ts` usages we use the client rendering for that?
@@ -1071,7 +1140,7 @@ export function transform_server(filename, source, analysis, minify_css) {
1071
1140
 
1072
1141
  state.imports.add(`import * as _$_ from 'ripple/internal/server'`);
1073
1142
 
1074
- const program = walk(analysis.ast, { ...state }, visitors);
1143
+ const program = /** @type {AST.Program} */ (walk(analysis.ast, { ...state }, visitors));
1075
1144
 
1076
1145
  const css = render_stylesheets(state.stylesheets, minify_css);
1077
1146
 
@@ -1090,7 +1159,11 @@ export function transform_server(filename, source, analysis, minify_css) {
1090
1159
 
1091
1160
  // Add async property to component functions
1092
1161
  for (const import_node of state.imports) {
1093
- /** @type {AST.Program} */ (program).body.unshift(b.stmt(b.id(import_node)));
1162
+ if (typeof import_node === 'string') {
1163
+ program.body.unshift(b.stmt(b.id(import_node)));
1164
+ } else {
1165
+ program.body.unshift(import_node);
1166
+ }
1094
1167
  }
1095
1168
 
1096
1169
  const js = print(program, /** @type {Visitors<AST.Node, TransformServerState>} */ (ts()), {