@marko/language-tools 2.5.44 → 2.5.46

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.
@@ -9,13 +9,11 @@ export interface ProgramScope {
9
9
  parent: undefined;
10
10
  hoists: false;
11
11
  bindings: Bindings;
12
- mutatedBindings: undefined | Set<VarBinding>;
13
12
  }
14
13
  export interface TagScope {
15
14
  parent: Scope;
16
15
  hoists: boolean;
17
16
  bindings: undefined | Bindings;
18
- mutatedBindings: undefined | Set<VarBinding>;
19
17
  }
20
18
  export type Binding = VarBinding | ParamBinding | HoistedBinding;
21
19
  export interface VarBinding {
@@ -46,22 +44,26 @@ export declare enum BindingType {
46
44
  param = 1,
47
45
  hoisted = 2
48
46
  }
47
+ export interface Mutation {
48
+ start: number;
49
+ binding: VarBinding;
50
+ }
49
51
  type Bindings = {
50
52
  [name: string]: Binding;
51
53
  };
52
54
  /**
53
55
  * Traverses the Marko tree and analyzes the bindings.
54
56
  */
55
- export declare function crawlProgramScope(parsed: Parsed, scriptParser: ScriptParser): [number, ...number[]] | [...number[], number] | undefined;
57
+ export declare function crawlProgramScope(parsed: Parsed, scriptParser: ScriptParser): [Mutation, ...Mutation[]] | [...Mutation[], Mutation] | undefined;
56
58
  export declare function getProgramBindings(node: Node.Program): {
57
59
  all: Repeated<string>;
58
60
  vars: Repeatable<string>;
59
61
  hoists: Repeatable<string>;
60
62
  } | undefined;
63
+ export declare function getMutatedVars(tag: Node.Tag): Repeatable<VarBinding>;
61
64
  export declare function getHoistSources(body: Node.ParentNode["body"]): Repeatable<string>;
62
- export declare function getMutatedVars(node: Node.ParentNode): Set<VarBinding> | undefined;
63
65
  export declare function isMutatedVar(node: Node.ParentNode, name: string): boolean;
64
- export declare function hasHoists(node: Node.ParentTag): boolean;
66
+ export declare function hasHoists(node: Node.Tag): boolean;
65
67
  export declare function getBoundAttrRange(value: Node.AttrValue): {
66
68
  value: Range;
67
69
  types: undefined | Range;
package/dist/index.js CHANGED
@@ -1089,8 +1089,7 @@ function crawlProgramScope(parsed, scriptParser) {
1089
1089
  const programScope = {
1090
1090
  parent: void 0,
1091
1091
  hoists: false,
1092
- bindings: {},
1093
- mutatedBindings: void 0
1092
+ bindings: {}
1094
1093
  };
1095
1094
  programScope.bindings.input = {
1096
1095
  type: 1 /* param */,
@@ -1140,7 +1139,7 @@ function crawlProgramScope(parsed, scriptParser) {
1140
1139
  }
1141
1140
  }
1142
1141
  if (mutations.length) {
1143
- return mutations.sort((a, b) => a - b);
1142
+ return mutations.sort(byStart);
1144
1143
  }
1145
1144
  function visit(body, parentScope) {
1146
1145
  var _a2;
@@ -1182,8 +1181,7 @@ function crawlProgramScope(parsed, scriptParser) {
1182
1181
  const bodyScope = {
1183
1182
  parent: parentScope,
1184
1183
  hoists: false,
1185
- bindings: {},
1186
- mutatedBindings: void 0
1184
+ bindings: {}
1187
1185
  };
1188
1186
  if (child.params) {
1189
1187
  bodyScope.bindings ??= {};
@@ -1263,9 +1261,6 @@ ${read({
1263
1261
  );
1264
1262
  if (binding) {
1265
1263
  binding.mutated = true;
1266
- (parentScope.mutatedBindings ||= /* @__PURE__ */ new Set()).add(
1267
- binding
1268
- );
1269
1264
  }
1270
1265
  BoundAttrValueRange.set(attr.value, {
1271
1266
  types,
@@ -1358,6 +1353,21 @@ function getProgramBindings(node) {
1358
1353
  };
1359
1354
  }
1360
1355
  }
1356
+ function getMutatedVars(tag) {
1357
+ const { bindings } = Scopes.get(tag.parent.body);
1358
+ let vars;
1359
+ for (const key in bindings) {
1360
+ const binding = bindings[key];
1361
+ if (binding.type == 0 /* var */ && binding.node === tag && binding.mutated) {
1362
+ if (vars) {
1363
+ vars.push(binding);
1364
+ } else {
1365
+ vars = [binding];
1366
+ }
1367
+ }
1368
+ }
1369
+ return vars;
1370
+ }
1361
1371
  function getHoistSources(body) {
1362
1372
  let result;
1363
1373
  if (body) {
@@ -1374,15 +1384,15 @@ function getHoistSources(body) {
1374
1384
  }
1375
1385
  return result;
1376
1386
  }
1377
- function getMutatedVars(node) {
1378
- return Scopes.get(node.body).mutatedBindings;
1379
- }
1380
1387
  function isMutatedVar(node, name) {
1381
- const { mutatedBindings } = Scopes.get(node.body);
1382
- if (mutatedBindings) {
1383
- for (const binding of mutatedBindings) {
1384
- if (binding.name === name) return true;
1388
+ var _a;
1389
+ let scope = Scopes.get(node.body);
1390
+ while (scope) {
1391
+ const binding = (_a = scope.bindings) == null ? void 0 : _a[name];
1392
+ if ((binding == null ? void 0 : binding.type) === 0 /* var */ && binding.mutated) {
1393
+ return true;
1385
1394
  }
1395
+ scope = scope.parent;
1386
1396
  }
1387
1397
  return false;
1388
1398
  }
@@ -1629,12 +1639,11 @@ function trackMutations(node, scope, mutations, parentBlock, parentBlockShadows,
1629
1639
  }
1630
1640
  if (block !== parentBlock && blockMutations.length) {
1631
1641
  for (const { name, start } of blockMutations) {
1632
- if (blockShadows.has(name)) continue;
1642
+ if (start == null || blockShadows.has(name)) continue;
1633
1643
  const binding = resolveWritableVar(scope, name);
1634
1644
  if (binding) {
1635
1645
  binding.mutated = true;
1636
- mutations.push(start);
1637
- (scope.mutatedBindings ||= /* @__PURE__ */ new Set()).add(binding);
1646
+ mutations.push({ start, binding });
1638
1647
  }
1639
1648
  }
1640
1649
  }
@@ -1660,6 +1669,9 @@ function traverse(node, enter) {
1660
1669
  }
1661
1670
  }
1662
1671
  }
1672
+ function byStart(a, b) {
1673
+ return a.start - b.start;
1674
+ }
1663
1675
 
1664
1676
  // src/extractors/script/util/get-component-filename.ts
1665
1677
  var import_fs = __toESM(require("fs"));
@@ -2002,6 +2014,9 @@ var REG_OBJECT_PROPERTY = /^[_$a-z][_$a-z0-9]*$/i;
2002
2014
  var REG_COMMENT_PRAGMA = /\/\/(?:\s*@ts-|\/\s*<)/y;
2003
2015
  var REG_TAG_NAME_IDENTIFIER = /^[A-Z][a-zA-Z0-9_$]+$/;
2004
2016
  var IF_TAG_ALTERNATES = /* @__PURE__ */ new WeakMap();
2017
+ var TAG_ID = /* @__PURE__ */ new WeakMap();
2018
+ var RENDER_VAR = /* @__PURE__ */ new WeakMap();
2019
+ var TEMPLATE_VAR = /* @__PURE__ */ new WeakMap();
2005
2020
  var WROTE_COMMENT = /* @__PURE__ */ new WeakSet();
2006
2021
  var START_OF_FILE = { start: 0, end: 0 };
2007
2022
  var ScriptLang = /* @__PURE__ */ ((ScriptLang2) => {
@@ -2022,14 +2037,11 @@ var ScriptExtractor = class {
2022
2037
  #scriptParser;
2023
2038
  #read;
2024
2039
  #lookup;
2025
- #tagIds = /* @__PURE__ */ new Map();
2026
- #renderIds = /* @__PURE__ */ new Map();
2027
2040
  #scriptLang;
2028
2041
  #ts;
2029
2042
  #runtimeTypes;
2030
- #mutationOffsets;
2043
+ #mutations;
2031
2044
  #tagId = 1;
2032
- #renderId = 1;
2033
2045
  #closeBrackets = [0];
2034
2046
  constructor(opts) {
2035
2047
  const { parsed, lookup, scriptLang } = opts;
@@ -2050,7 +2062,7 @@ var ScriptExtractor = class {
2050
2062
  this.#extractor = new Extractor(parsed);
2051
2063
  this.#scriptParser = new ScriptParser(parsed);
2052
2064
  this.#read = parsed.read.bind(parsed);
2053
- this.#mutationOffsets = crawlProgramScope(this.#parsed, this.#scriptParser);
2065
+ this.#mutations = crawlProgramScope(this.#parsed, this.#scriptParser);
2054
2066
  this.#writeProgram(parsed.program);
2055
2067
  }
2056
2068
  end() {
@@ -2231,7 +2243,7 @@ function ${templateName}() {
2231
2243
  }
2232
2244
  }
2233
2245
  if (body == null ? void 0 : body.content) {
2234
- this.#writeChildren(program, body.content);
2246
+ this.#writeChildren(body.content);
2235
2247
  if (bindings) {
2236
2248
  if (bindings.vars) {
2237
2249
  for (const name of bindings.vars) {
@@ -2255,7 +2267,7 @@ function ${templateName}() {
2255
2267
  `
2256
2268
  );
2257
2269
  if (didReturn) {
2258
- this.#extractor.write(`return ${varLocal("return")}.return;
2270
+ this.#extractor.write(`return ${varLocal("return")};
2259
2271
  }
2260
2272
  `);
2261
2273
  } else {
@@ -2360,7 +2372,7 @@ constructor(_?: Return) {}
2360
2372
  this.#extractor.copy(returned);
2361
2373
  this.#extractor.write(");\n");
2362
2374
  }
2363
- #writeChildren(parent, children, skipRenderId = false) {
2375
+ #writeChildren(children, skipRenderId = false) {
2364
2376
  var _a, _b;
2365
2377
  const last = children.length - 1;
2366
2378
  let returnTag;
@@ -2376,21 +2388,19 @@ constructor(_?: Return) {}
2376
2388
  break;
2377
2389
  case "if": {
2378
2390
  const alternates = IF_TAG_ALTERNATES.get(child);
2379
- let renderId;
2391
+ let didHoist = false;
2380
2392
  if (!skipRenderId) {
2381
- renderId = this.#getRenderId(child);
2382
- if (!renderId && alternates) {
2393
+ didHoist = hasHoists(child);
2394
+ if (!didHoist && alternates) {
2383
2395
  for (const { node } of alternates) {
2384
- if (renderId = this.#getRenderId(node)) {
2385
- this.#renderIds.set(child, renderId);
2386
- this.#renderIds.delete(node);
2396
+ if (didHoist = hasHoists(node)) {
2387
2397
  break;
2388
2398
  }
2389
2399
  }
2390
2400
  }
2391
- if (renderId) {
2401
+ if (didHoist) {
2392
2402
  this.#extractor.write(
2393
- `const ${varLocal("rendered_" + renderId)} = (() => {
2403
+ `const ${this.#getRenderVar(child, true)} = (() => {
2394
2404
  `
2395
2405
  );
2396
2406
  }
@@ -2401,7 +2411,7 @@ constructor(_?: Return) {}
2401
2411
  ).write(") {\n");
2402
2412
  const ifBody = this.#processBody(child);
2403
2413
  if (ifBody == null ? void 0 : ifBody.content) {
2404
- this.#writeChildren(child, ifBody.content, true);
2414
+ this.#writeChildren(ifBody.content, true);
2405
2415
  const scopeExpr = this.#getScopeExpression(child.body);
2406
2416
  if (scopeExpr) {
2407
2417
  this.#extractor.write(`return {
@@ -2425,7 +2435,7 @@ scope: ${scopeExpr}
2425
2435
  }
2426
2436
  const alternateBody = this.#processBody(node);
2427
2437
  if (alternateBody == null ? void 0 : alternateBody.content) {
2428
- this.#writeChildren(node, alternateBody.content, true);
2438
+ this.#writeChildren(alternateBody.content, true);
2429
2439
  const scopeExpr = this.#getScopeExpression(node.body);
2430
2440
  if (scopeExpr) {
2431
2441
  this.#extractor.write(
@@ -2439,21 +2449,20 @@ scope: ${scopeExpr}
2439
2449
  }
2440
2450
  }
2441
2451
  }
2442
- if (needsAlternate && renderId) {
2452
+ if (needsAlternate && didHoist) {
2443
2453
  this.#extractor.write("\n} else {\nreturn undefined;\n}\n");
2444
2454
  } else {
2445
2455
  this.#extractor.write("\n}\n");
2446
2456
  }
2447
- if (renderId) {
2457
+ if (didHoist) {
2448
2458
  this.#extractor.write("\n})();\n");
2449
2459
  }
2450
2460
  break;
2451
2461
  }
2452
2462
  case "for": {
2453
- const renderId = this.#getRenderId(child);
2454
- if (renderId) {
2463
+ if (hasHoists(child)) {
2455
2464
  this.#extractor.write(
2456
- `const ${varLocal("rendered_" + renderId)} = `
2465
+ `const ${this.#getRenderVar(child, true)} = `
2457
2466
  );
2458
2467
  }
2459
2468
  this.#extractor.write(
@@ -2470,7 +2479,7 @@ scope: ${scopeExpr}
2470
2479
  this.#extractor.write("\n) => {\n");
2471
2480
  const body = this.#processBody(child);
2472
2481
  if (body == null ? void 0 : body.content) {
2473
- this.#writeChildren(child, body.content);
2482
+ this.#writeChildren(body.content);
2474
2483
  }
2475
2484
  this.#writeReturn(void 0, (body == null ? void 0 : body.content) && child.body);
2476
2485
  if (body == null ? void 0 : body.content) {
@@ -2486,7 +2495,7 @@ scope: ${scopeExpr}
2486
2495
  ).write("\n) {\n");
2487
2496
  const body = this.#processBody(child);
2488
2497
  if (body == null ? void 0 : body.content) {
2489
- this.#writeChildren(child, body.content);
2498
+ this.#writeChildren(body.content);
2490
2499
  this.#endChildren();
2491
2500
  }
2492
2501
  this.#extractor.write("\n}\n");
@@ -2505,59 +2514,29 @@ scope: ${scopeExpr}
2505
2514
  break;
2506
2515
  }
2507
2516
  }
2508
- const mutatedVars = getMutatedVars(parent);
2509
- if (returnTag || mutatedVars) {
2510
- this.#extractor.write(`var ${varLocal("return")} = {
2511
- `);
2512
- if (returnTag) {
2513
- this.#extractor.write(`return: ${varShared("returnTag")}(`);
2514
- this.#writeTagInputObject(returnTag);
2515
- this.#extractor.write(")");
2516
- if (mutatedVars) {
2517
- this.#extractor.write(",\n");
2518
- }
2519
- }
2520
- if (mutatedVars) {
2521
- this.#extractor.write(`mutate: ${varShared("mutable")}([
2522
- `);
2523
- for (const binding of mutatedVars) {
2524
- this.#extractor.write(
2525
- `${// TODO use a different format to avoid const annotation.
2526
- this.#scriptLang === "js" /* js */ ? "/** @type {const} */" : ""}[${JSON.stringify(binding.name) + (binding.sourceName && binding.sourceName !== binding.name ? `, ${JSON.stringify(binding.sourceName)}` : "")}, ${varLocal(
2527
- "rendered_" + this.#getRenderId(binding.node)
2528
- )}.return${binding.objectPath || ""}]${SEP_COMMA_NEW_LINE}`
2529
- );
2530
- }
2531
- this.#extractor.write(
2532
- `]${this.#scriptLang === "ts" /* ts */ ? " as const" : ""})`
2533
- );
2534
- }
2535
- this.#extractor.write("\n};\n");
2536
- if (mutatedVars) {
2537
- this.#extractor.write(`${varShared("noop")}({
2538
- `);
2539
- for (const binding of mutatedVars) {
2540
- this.#extractor.write(binding.name + SEP_COMMA_NEW_LINE);
2541
- }
2542
- this.#extractor.write("});\n");
2543
- }
2517
+ if (returnTag) {
2518
+ this.#extractor.write(
2519
+ `var ${varLocal("return")} = ${varShared("returnTag")}(`
2520
+ );
2521
+ this.#writeTagInputObject(returnTag);
2522
+ this.#extractor.write(");\n");
2544
2523
  }
2545
2524
  return returnTag !== void 0;
2546
2525
  }
2547
2526
  #writeTag(tag) {
2548
2527
  const tagName = tag.nameText;
2549
- const renderId = this.#getRenderId(tag);
2550
2528
  const def = tagName ? this.#lookup.getTag(tagName) : void 0;
2551
2529
  const importPath = resolveTagImport(this.#filename, def);
2552
2530
  const isHTML = !importPath && (def == null ? void 0 : def.html);
2553
- let tagIdentifier;
2554
2531
  let isTemplate = false;
2532
+ let renderVar;
2533
+ let templateVar;
2555
2534
  if (!def || importPath) {
2556
2535
  const isIdentifier = tagName && REG_TAG_NAME_IDENTIFIER.test(tagName);
2557
2536
  const isMarkoFile = importPath == null ? void 0 : importPath.endsWith(".marko");
2558
2537
  if (isIdentifier || isMarkoFile || !importPath) {
2559
- tagIdentifier = varLocal("tag_" + this.#ensureTagId(tag));
2560
- this.#extractor.write(`const ${tagIdentifier} = (
2538
+ templateVar = this.#getTemplateVar(tag, true);
2539
+ this.#extractor.write(`const ${templateVar} = (
2561
2540
  `);
2562
2541
  if (isIdentifier) {
2563
2542
  if (importPath) {
@@ -2577,22 +2556,23 @@ scope: ${scopeExpr}
2577
2556
  }
2578
2557
  this.#extractor.write("\n);\n");
2579
2558
  } else {
2580
- tagIdentifier = varShared("missingTag");
2559
+ templateVar = varShared("missingTag");
2581
2560
  }
2582
2561
  const attrTagTree = this.#getAttrTagTree(tag);
2583
2562
  if (attrTagTree) {
2584
- this.#writeAttrTagTree(attrTagTree, tagIdentifier);
2563
+ this.#writeAttrTagTree(attrTagTree, templateVar);
2585
2564
  this.#extractor.write(";\n");
2586
2565
  }
2587
2566
  }
2588
- if (renderId) {
2589
- this.#extractor.write(`const ${varLocal("rendered_" + renderId)} = `);
2567
+ if (!isHTML && tag.var || hasHoists(tag)) {
2568
+ renderVar = this.#getRenderVar(tag, true);
2569
+ this.#extractor.write(`const ${renderVar} = `);
2590
2570
  }
2591
2571
  if (isHTML) {
2592
2572
  this.#extractor.write(`${varShared("renderNativeTag")}("`).copy(isEmptyRange2(tag.name) ? tagName : tag.name).write('")');
2593
- } else if (tagIdentifier) {
2573
+ } else if (templateVar) {
2594
2574
  this.#extractor.write(
2595
- `${varShared(isTemplate ? "renderTemplate" : "renderDynamicTag")}(${tagIdentifier})`
2575
+ `${varShared(isTemplate ? "renderTemplate" : "renderDynamicTag")}(${templateVar})`
2596
2576
  );
2597
2577
  } else {
2598
2578
  this.#extractor.write(varShared("missingTag"));
@@ -2604,14 +2584,26 @@ scope: ${scopeExpr}
2604
2584
  }
2605
2585
  this.#writeTagInputObject(tag);
2606
2586
  this.#extractor.write(");\n");
2607
- if (renderId && tag.var) {
2587
+ if (tag.var) {
2608
2588
  this.#extractor.write(`{const `);
2609
2589
  this.#closeBrackets[this.#closeBrackets.length - 1]++;
2610
- this.#copyWithMutationsReplaced(tag.var.value);
2611
- this.#extractor.write(
2612
- ` = ${varLocal("rendered_" + renderId)}.return.${ATTR_UNAMED2};
2590
+ if (renderVar) {
2591
+ const mutatedVars = getMutatedVars(tag);
2592
+ this.#copyWithMutationsReplaced(tag.var.value);
2593
+ this.#extractor.write(` = ${renderVar}.return.${ATTR_UNAMED2};
2594
+ `);
2595
+ if (mutatedVars) {
2596
+ for (const binding of mutatedVars) {
2597
+ this.#extractor.write(
2598
+ `const ${varLocal(`change__${binding.name}`)} = ${varShared("change")}(${JSON.stringify(binding.name) + (binding.sourceName && binding.sourceName !== binding.name ? `, ${JSON.stringify(binding.sourceName)}` : "")}, ${renderVar}.return${binding.objectPath || ""});
2613
2599
  `
2614
- );
2600
+ );
2601
+ }
2602
+ }
2603
+ } else if (isHTML) {
2604
+ this.#extractor.copy(tag.var.value).write(` = ${varShared("el")}(${JSON.stringify(def.name)});
2605
+ `);
2606
+ }
2615
2607
  }
2616
2608
  }
2617
2609
  #endChildren() {
@@ -2710,7 +2702,7 @@ scope: ${scopeExpr}
2710
2702
  `Change"(
2711
2703
  // @ts-ignore
2712
2704
  _${valueLiteral}) {
2713
- ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}`
2705
+ ${isMutatedVar(tag.parent, valueLiteral) ? varLocal(`change__${valueLiteral}.`) : ""}`
2714
2706
  ).copy(boundRange.value).write(`= _${valueLiteral};
2715
2707
  }`);
2716
2708
  } else if (boundRange.member.computed) {
@@ -2826,8 +2818,8 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
2826
2818
  this.#writeTagNameComment(firstAttrTag);
2827
2819
  this.#extractor.write("]: ");
2828
2820
  if (isRepeated) {
2829
- const tagId = this.#tagIds.get(firstAttrTag.owner);
2830
- if (tagId) {
2821
+ const templateVar = this.#getTemplateVar(firstAttrTag.owner);
2822
+ if (templateVar) {
2831
2823
  let accessor = `"${name}"`;
2832
2824
  let curTag = firstAttrTag.parent;
2833
2825
  while (curTag) {
@@ -2839,7 +2831,7 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
2839
2831
  curTag = curTag.parent;
2840
2832
  }
2841
2833
  this.#extractor.write(
2842
- `${varShared("attrTagFor")}(${varLocal("tag_" + tagId)},${accessor})([`
2834
+ `${varShared("attrTagFor")}(${templateVar},${accessor})([`
2843
2835
  );
2844
2836
  } else {
2845
2837
  this.#extractor.write(`${varShared("attrTag")}([`);
@@ -2963,13 +2955,11 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
2963
2955
  if (tag.params || hasBodyContent) {
2964
2956
  this.#extractor.write("[");
2965
2957
  if (this.#interop) {
2966
- const tagId = this.#tagIds.get(
2958
+ const templateVar = this.#getTemplateVar(
2967
2959
  tag.type === 16 /* AttrTag */ ? tag.owner : tag
2968
2960
  );
2969
- if (tagId) {
2970
- this.#extractor.write(
2971
- `${varShared("contentFor")}(${varLocal("tag_" + tagId)})`
2972
- );
2961
+ if (templateVar) {
2962
+ this.#extractor.write(`${varShared("contentFor")}(${templateVar})`);
2973
2963
  } else {
2974
2964
  this.#extractor.write(varShared("content"));
2975
2965
  }
@@ -2990,16 +2980,13 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
2990
2980
  }
2991
2981
  let didReturn = false;
2992
2982
  if (body == null ? void 0 : body.content) {
2993
- didReturn = this.#writeChildren(tag, body.content);
2983
+ didReturn = this.#writeChildren(body.content);
2994
2984
  }
2995
2985
  if (!tag.params) {
2996
2986
  this.#extractor.write(`return () => {
2997
2987
  `);
2998
2988
  }
2999
- this.#writeReturn(
3000
- didReturn ? `${varLocal("return")}.return` : void 0,
3001
- tag.body
3002
- );
2989
+ this.#writeReturn(didReturn ? varLocal("return") : void 0, tag.body);
3003
2990
  if (body == null ? void 0 : body.content) {
3004
2991
  this.#endChildren();
3005
2992
  }
@@ -3024,7 +3011,7 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
3024
3011
  return this.#scriptLang === "ts" /* ts */ ? `${varShared("any")} as ${type}` : `/** @type {${type}} */(${varShared("any")})`;
3025
3012
  }
3026
3013
  #copyWithMutationsReplaced(range) {
3027
- const mutations = this.#mutationOffsets;
3014
+ const mutations = this.#mutations;
3028
3015
  if (!mutations) return this.#extractor.copy(range);
3029
3016
  const len = mutations.length;
3030
3017
  let curOffset = range.start;
@@ -3033,23 +3020,23 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
3033
3020
  let maxIndex = len;
3034
3021
  while (minIndex < maxIndex) {
3035
3022
  const midIndex = minIndex + maxIndex >>> 1;
3036
- if (mutations[midIndex] >= curOffset) {
3023
+ if (mutations[midIndex].start >= curOffset) {
3037
3024
  maxIndex = midIndex;
3038
3025
  } else {
3039
3026
  minIndex = midIndex + 1;
3040
3027
  }
3041
3028
  }
3042
- const nextOffset = maxIndex === len ? range.end : mutations[maxIndex];
3043
- if (nextOffset >= range.end) {
3029
+ const mutation = maxIndex !== len && mutations[maxIndex];
3030
+ if (!mutation || mutation.start >= range.end) {
3044
3031
  this.#extractor.copy({
3045
3032
  start: curOffset,
3046
3033
  end: range.end
3047
3034
  });
3048
3035
  return;
3049
3036
  }
3050
- this.#extractor.copy({ start: curOffset, end: nextOffset });
3051
- this.#extractor.write(`${varLocal("return")}.mutate.`);
3052
- curOffset = nextOffset;
3037
+ this.#extractor.copy({ start: curOffset, end: mutation.start });
3038
+ this.#extractor.write(`${varLocal(`change__${mutation.binding.name}`)}.`);
3039
+ curOffset = mutation.start;
3053
3040
  minIndex = maxIndex + 1;
3054
3041
  } while (true);
3055
3042
  }
@@ -3201,7 +3188,7 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
3201
3188
  if (body) {
3202
3189
  if (body.content) {
3203
3190
  this.#extractor.write("(() => {\n");
3204
- this.#writeChildren(tag, body.content);
3191
+ this.#writeChildren(body.content);
3205
3192
  this.#extractor.write("return ");
3206
3193
  }
3207
3194
  this.#extractor.write("{\n");
@@ -3303,13 +3290,27 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
3303
3290
  return tag.name.expressions[0].value;
3304
3291
  }
3305
3292
  }
3306
- #getRenderId(tag) {
3307
- let renderId = this.#renderIds.get(tag);
3308
- if (!renderId && (tag.var || hasHoists(tag))) {
3309
- renderId = this.#renderId++;
3310
- this.#renderIds.set(tag, renderId);
3293
+ #getRenderVar(tag, declared = false) {
3294
+ let id = RENDER_VAR.get(tag);
3295
+ if (!id && declared) {
3296
+ RENDER_VAR.set(tag, id = varLocal("rendered_" + this.#getTagId(tag)));
3297
+ }
3298
+ return id;
3299
+ }
3300
+ #getTemplateVar(tag, declared = false) {
3301
+ let id = TEMPLATE_VAR.get(tag);
3302
+ if (!id && declared) {
3303
+ TEMPLATE_VAR.set(tag, id = varLocal("tag_" + this.#getTagId(tag)));
3304
+ }
3305
+ return id;
3306
+ }
3307
+ #getTagId(tag) {
3308
+ let id = TAG_ID.get(tag);
3309
+ if (id === void 0) {
3310
+ id = this.#tagId++;
3311
+ TAG_ID.set(tag, id);
3311
3312
  }
3312
- return renderId;
3313
+ return id;
3313
3314
  }
3314
3315
  #getScopeExpression(body) {
3315
3316
  const sources = getHoistSources(body);
@@ -3317,39 +3318,27 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
3317
3318
  return sources ? `{ ${hoists ? `...${hoists}, ` : ""}${sources.join(SEP_COMMA_SPACE)} }` : hoists;
3318
3319
  }
3319
3320
  #getBodyHoistScopeExpression(body) {
3320
- let hoistIds;
3321
+ let hoistVars;
3321
3322
  if (body) {
3322
3323
  for (const child of body) {
3323
- const renderId = child.type === 1 /* Tag */ && this.#renderIds.get(child);
3324
- if (renderId && (!child.var || hasHoists(child))) {
3325
- if (hoistIds) {
3326
- hoistIds.push(renderId);
3327
- } else {
3328
- hoistIds = [renderId];
3324
+ if (child.type === 1 /* Tag */) {
3325
+ const renderVar = this.#getRenderVar(child);
3326
+ if (renderVar && (!child.var || hasHoists(child))) {
3327
+ if (hoistVars) {
3328
+ hoistVars.push(renderVar);
3329
+ } else {
3330
+ hoistVars = [renderVar];
3331
+ }
3329
3332
  }
3330
3333
  }
3331
3334
  }
3332
3335
  }
3333
- if (hoistIds) {
3334
- if (hoistIds.length === 1) {
3335
- return `${varShared("readScope")}(${varLocal("rendered_" + hoistIds[0])})`;
3336
+ if (hoistVars) {
3337
+ if (hoistVars.length === 1) {
3338
+ return `${varShared("readScope")}(${hoistVars[0]})`;
3336
3339
  }
3337
- let result = `${varShared("readScopes")}({ `;
3338
- let sep = "";
3339
- for (const renderId of hoistIds) {
3340
- result += sep + `${varLocal("rendered_" + renderId)}`;
3341
- sep = SEP_COMMA_SPACE;
3342
- }
3343
- return result + " })";
3344
- }
3345
- }
3346
- #ensureTagId(tag) {
3347
- let tagId = this.#tagIds.get(tag);
3348
- if (!tagId) {
3349
- tagId = this.#tagId++;
3350
- this.#tagIds.set(tag, tagId);
3340
+ return `${varShared("readScopes")}({ ${hoistVars.join(SEP_COMMA_SPACE)} })`;
3351
3341
  }
3352
- return tagId;
3353
3342
  }
3354
3343
  #getNamedAttrModifierIndex(attr) {
3355
3344
  const start = attr.name.start + 1;
package/dist/index.mjs CHANGED
@@ -1049,8 +1049,7 @@ function crawlProgramScope(parsed, scriptParser) {
1049
1049
  const programScope = {
1050
1050
  parent: void 0,
1051
1051
  hoists: false,
1052
- bindings: {},
1053
- mutatedBindings: void 0
1052
+ bindings: {}
1054
1053
  };
1055
1054
  programScope.bindings.input = {
1056
1055
  type: 1 /* param */,
@@ -1100,7 +1099,7 @@ function crawlProgramScope(parsed, scriptParser) {
1100
1099
  }
1101
1100
  }
1102
1101
  if (mutations.length) {
1103
- return mutations.sort((a, b) => a - b);
1102
+ return mutations.sort(byStart);
1104
1103
  }
1105
1104
  function visit(body, parentScope) {
1106
1105
  var _a2;
@@ -1142,8 +1141,7 @@ function crawlProgramScope(parsed, scriptParser) {
1142
1141
  const bodyScope = {
1143
1142
  parent: parentScope,
1144
1143
  hoists: false,
1145
- bindings: {},
1146
- mutatedBindings: void 0
1144
+ bindings: {}
1147
1145
  };
1148
1146
  if (child.params) {
1149
1147
  bodyScope.bindings ??= {};
@@ -1223,9 +1221,6 @@ ${read({
1223
1221
  );
1224
1222
  if (binding) {
1225
1223
  binding.mutated = true;
1226
- (parentScope.mutatedBindings ||= /* @__PURE__ */ new Set()).add(
1227
- binding
1228
- );
1229
1224
  }
1230
1225
  BoundAttrValueRange.set(attr.value, {
1231
1226
  types,
@@ -1318,6 +1313,21 @@ function getProgramBindings(node) {
1318
1313
  };
1319
1314
  }
1320
1315
  }
1316
+ function getMutatedVars(tag) {
1317
+ const { bindings } = Scopes.get(tag.parent.body);
1318
+ let vars;
1319
+ for (const key in bindings) {
1320
+ const binding = bindings[key];
1321
+ if (binding.type == 0 /* var */ && binding.node === tag && binding.mutated) {
1322
+ if (vars) {
1323
+ vars.push(binding);
1324
+ } else {
1325
+ vars = [binding];
1326
+ }
1327
+ }
1328
+ }
1329
+ return vars;
1330
+ }
1321
1331
  function getHoistSources(body) {
1322
1332
  let result;
1323
1333
  if (body) {
@@ -1334,15 +1344,15 @@ function getHoistSources(body) {
1334
1344
  }
1335
1345
  return result;
1336
1346
  }
1337
- function getMutatedVars(node) {
1338
- return Scopes.get(node.body).mutatedBindings;
1339
- }
1340
1347
  function isMutatedVar(node, name) {
1341
- const { mutatedBindings } = Scopes.get(node.body);
1342
- if (mutatedBindings) {
1343
- for (const binding of mutatedBindings) {
1344
- if (binding.name === name) return true;
1348
+ var _a;
1349
+ let scope = Scopes.get(node.body);
1350
+ while (scope) {
1351
+ const binding = (_a = scope.bindings) == null ? void 0 : _a[name];
1352
+ if ((binding == null ? void 0 : binding.type) === 0 /* var */ && binding.mutated) {
1353
+ return true;
1345
1354
  }
1355
+ scope = scope.parent;
1346
1356
  }
1347
1357
  return false;
1348
1358
  }
@@ -1589,12 +1599,11 @@ function trackMutations(node, scope, mutations, parentBlock, parentBlockShadows,
1589
1599
  }
1590
1600
  if (block !== parentBlock && blockMutations.length) {
1591
1601
  for (const { name, start } of blockMutations) {
1592
- if (blockShadows.has(name)) continue;
1602
+ if (start == null || blockShadows.has(name)) continue;
1593
1603
  const binding = resolveWritableVar(scope, name);
1594
1604
  if (binding) {
1595
1605
  binding.mutated = true;
1596
- mutations.push(start);
1597
- (scope.mutatedBindings ||= /* @__PURE__ */ new Set()).add(binding);
1606
+ mutations.push({ start, binding });
1598
1607
  }
1599
1608
  }
1600
1609
  }
@@ -1620,6 +1629,9 @@ function traverse(node, enter) {
1620
1629
  }
1621
1630
  }
1622
1631
  }
1632
+ function byStart(a, b) {
1633
+ return a.start - b.start;
1634
+ }
1623
1635
 
1624
1636
  // src/extractors/script/util/get-component-filename.ts
1625
1637
  import fs from "fs";
@@ -1965,6 +1977,9 @@ var REG_OBJECT_PROPERTY = /^[_$a-z][_$a-z0-9]*$/i;
1965
1977
  var REG_COMMENT_PRAGMA = /\/\/(?:\s*@ts-|\/\s*<)/y;
1966
1978
  var REG_TAG_NAME_IDENTIFIER = /^[A-Z][a-zA-Z0-9_$]+$/;
1967
1979
  var IF_TAG_ALTERNATES = /* @__PURE__ */ new WeakMap();
1980
+ var TAG_ID = /* @__PURE__ */ new WeakMap();
1981
+ var RENDER_VAR = /* @__PURE__ */ new WeakMap();
1982
+ var TEMPLATE_VAR = /* @__PURE__ */ new WeakMap();
1968
1983
  var WROTE_COMMENT = /* @__PURE__ */ new WeakSet();
1969
1984
  var START_OF_FILE = { start: 0, end: 0 };
1970
1985
  var ScriptLang = /* @__PURE__ */ ((ScriptLang2) => {
@@ -1985,14 +2000,11 @@ var ScriptExtractor = class {
1985
2000
  #scriptParser;
1986
2001
  #read;
1987
2002
  #lookup;
1988
- #tagIds = /* @__PURE__ */ new Map();
1989
- #renderIds = /* @__PURE__ */ new Map();
1990
2003
  #scriptLang;
1991
2004
  #ts;
1992
2005
  #runtimeTypes;
1993
- #mutationOffsets;
2006
+ #mutations;
1994
2007
  #tagId = 1;
1995
- #renderId = 1;
1996
2008
  #closeBrackets = [0];
1997
2009
  constructor(opts) {
1998
2010
  const { parsed, lookup, scriptLang } = opts;
@@ -2013,7 +2025,7 @@ var ScriptExtractor = class {
2013
2025
  this.#extractor = new Extractor(parsed);
2014
2026
  this.#scriptParser = new ScriptParser(parsed);
2015
2027
  this.#read = parsed.read.bind(parsed);
2016
- this.#mutationOffsets = crawlProgramScope(this.#parsed, this.#scriptParser);
2028
+ this.#mutations = crawlProgramScope(this.#parsed, this.#scriptParser);
2017
2029
  this.#writeProgram(parsed.program);
2018
2030
  }
2019
2031
  end() {
@@ -2194,7 +2206,7 @@ function ${templateName}() {
2194
2206
  }
2195
2207
  }
2196
2208
  if (body == null ? void 0 : body.content) {
2197
- this.#writeChildren(program, body.content);
2209
+ this.#writeChildren(body.content);
2198
2210
  if (bindings) {
2199
2211
  if (bindings.vars) {
2200
2212
  for (const name of bindings.vars) {
@@ -2218,7 +2230,7 @@ function ${templateName}() {
2218
2230
  `
2219
2231
  );
2220
2232
  if (didReturn) {
2221
- this.#extractor.write(`return ${varLocal("return")}.return;
2233
+ this.#extractor.write(`return ${varLocal("return")};
2222
2234
  }
2223
2235
  `);
2224
2236
  } else {
@@ -2323,7 +2335,7 @@ constructor(_?: Return) {}
2323
2335
  this.#extractor.copy(returned);
2324
2336
  this.#extractor.write(");\n");
2325
2337
  }
2326
- #writeChildren(parent, children, skipRenderId = false) {
2338
+ #writeChildren(children, skipRenderId = false) {
2327
2339
  var _a, _b;
2328
2340
  const last = children.length - 1;
2329
2341
  let returnTag;
@@ -2339,21 +2351,19 @@ constructor(_?: Return) {}
2339
2351
  break;
2340
2352
  case "if": {
2341
2353
  const alternates = IF_TAG_ALTERNATES.get(child);
2342
- let renderId;
2354
+ let didHoist = false;
2343
2355
  if (!skipRenderId) {
2344
- renderId = this.#getRenderId(child);
2345
- if (!renderId && alternates) {
2356
+ didHoist = hasHoists(child);
2357
+ if (!didHoist && alternates) {
2346
2358
  for (const { node } of alternates) {
2347
- if (renderId = this.#getRenderId(node)) {
2348
- this.#renderIds.set(child, renderId);
2349
- this.#renderIds.delete(node);
2359
+ if (didHoist = hasHoists(node)) {
2350
2360
  break;
2351
2361
  }
2352
2362
  }
2353
2363
  }
2354
- if (renderId) {
2364
+ if (didHoist) {
2355
2365
  this.#extractor.write(
2356
- `const ${varLocal("rendered_" + renderId)} = (() => {
2366
+ `const ${this.#getRenderVar(child, true)} = (() => {
2357
2367
  `
2358
2368
  );
2359
2369
  }
@@ -2364,7 +2374,7 @@ constructor(_?: Return) {}
2364
2374
  ).write(") {\n");
2365
2375
  const ifBody = this.#processBody(child);
2366
2376
  if (ifBody == null ? void 0 : ifBody.content) {
2367
- this.#writeChildren(child, ifBody.content, true);
2377
+ this.#writeChildren(ifBody.content, true);
2368
2378
  const scopeExpr = this.#getScopeExpression(child.body);
2369
2379
  if (scopeExpr) {
2370
2380
  this.#extractor.write(`return {
@@ -2388,7 +2398,7 @@ scope: ${scopeExpr}
2388
2398
  }
2389
2399
  const alternateBody = this.#processBody(node);
2390
2400
  if (alternateBody == null ? void 0 : alternateBody.content) {
2391
- this.#writeChildren(node, alternateBody.content, true);
2401
+ this.#writeChildren(alternateBody.content, true);
2392
2402
  const scopeExpr = this.#getScopeExpression(node.body);
2393
2403
  if (scopeExpr) {
2394
2404
  this.#extractor.write(
@@ -2402,21 +2412,20 @@ scope: ${scopeExpr}
2402
2412
  }
2403
2413
  }
2404
2414
  }
2405
- if (needsAlternate && renderId) {
2415
+ if (needsAlternate && didHoist) {
2406
2416
  this.#extractor.write("\n} else {\nreturn undefined;\n}\n");
2407
2417
  } else {
2408
2418
  this.#extractor.write("\n}\n");
2409
2419
  }
2410
- if (renderId) {
2420
+ if (didHoist) {
2411
2421
  this.#extractor.write("\n})();\n");
2412
2422
  }
2413
2423
  break;
2414
2424
  }
2415
2425
  case "for": {
2416
- const renderId = this.#getRenderId(child);
2417
- if (renderId) {
2426
+ if (hasHoists(child)) {
2418
2427
  this.#extractor.write(
2419
- `const ${varLocal("rendered_" + renderId)} = `
2428
+ `const ${this.#getRenderVar(child, true)} = `
2420
2429
  );
2421
2430
  }
2422
2431
  this.#extractor.write(
@@ -2433,7 +2442,7 @@ scope: ${scopeExpr}
2433
2442
  this.#extractor.write("\n) => {\n");
2434
2443
  const body = this.#processBody(child);
2435
2444
  if (body == null ? void 0 : body.content) {
2436
- this.#writeChildren(child, body.content);
2445
+ this.#writeChildren(body.content);
2437
2446
  }
2438
2447
  this.#writeReturn(void 0, (body == null ? void 0 : body.content) && child.body);
2439
2448
  if (body == null ? void 0 : body.content) {
@@ -2449,7 +2458,7 @@ scope: ${scopeExpr}
2449
2458
  ).write("\n) {\n");
2450
2459
  const body = this.#processBody(child);
2451
2460
  if (body == null ? void 0 : body.content) {
2452
- this.#writeChildren(child, body.content);
2461
+ this.#writeChildren(body.content);
2453
2462
  this.#endChildren();
2454
2463
  }
2455
2464
  this.#extractor.write("\n}\n");
@@ -2468,59 +2477,29 @@ scope: ${scopeExpr}
2468
2477
  break;
2469
2478
  }
2470
2479
  }
2471
- const mutatedVars = getMutatedVars(parent);
2472
- if (returnTag || mutatedVars) {
2473
- this.#extractor.write(`var ${varLocal("return")} = {
2474
- `);
2475
- if (returnTag) {
2476
- this.#extractor.write(`return: ${varShared("returnTag")}(`);
2477
- this.#writeTagInputObject(returnTag);
2478
- this.#extractor.write(")");
2479
- if (mutatedVars) {
2480
- this.#extractor.write(",\n");
2481
- }
2482
- }
2483
- if (mutatedVars) {
2484
- this.#extractor.write(`mutate: ${varShared("mutable")}([
2485
- `);
2486
- for (const binding of mutatedVars) {
2487
- this.#extractor.write(
2488
- `${// TODO use a different format to avoid const annotation.
2489
- this.#scriptLang === "js" /* js */ ? "/** @type {const} */" : ""}[${JSON.stringify(binding.name) + (binding.sourceName && binding.sourceName !== binding.name ? `, ${JSON.stringify(binding.sourceName)}` : "")}, ${varLocal(
2490
- "rendered_" + this.#getRenderId(binding.node)
2491
- )}.return${binding.objectPath || ""}]${SEP_COMMA_NEW_LINE}`
2492
- );
2493
- }
2494
- this.#extractor.write(
2495
- `]${this.#scriptLang === "ts" /* ts */ ? " as const" : ""})`
2496
- );
2497
- }
2498
- this.#extractor.write("\n};\n");
2499
- if (mutatedVars) {
2500
- this.#extractor.write(`${varShared("noop")}({
2501
- `);
2502
- for (const binding of mutatedVars) {
2503
- this.#extractor.write(binding.name + SEP_COMMA_NEW_LINE);
2504
- }
2505
- this.#extractor.write("});\n");
2506
- }
2480
+ if (returnTag) {
2481
+ this.#extractor.write(
2482
+ `var ${varLocal("return")} = ${varShared("returnTag")}(`
2483
+ );
2484
+ this.#writeTagInputObject(returnTag);
2485
+ this.#extractor.write(");\n");
2507
2486
  }
2508
2487
  return returnTag !== void 0;
2509
2488
  }
2510
2489
  #writeTag(tag) {
2511
2490
  const tagName = tag.nameText;
2512
- const renderId = this.#getRenderId(tag);
2513
2491
  const def = tagName ? this.#lookup.getTag(tagName) : void 0;
2514
2492
  const importPath = resolveTagImport(this.#filename, def);
2515
2493
  const isHTML = !importPath && (def == null ? void 0 : def.html);
2516
- let tagIdentifier;
2517
2494
  let isTemplate = false;
2495
+ let renderVar;
2496
+ let templateVar;
2518
2497
  if (!def || importPath) {
2519
2498
  const isIdentifier = tagName && REG_TAG_NAME_IDENTIFIER.test(tagName);
2520
2499
  const isMarkoFile = importPath == null ? void 0 : importPath.endsWith(".marko");
2521
2500
  if (isIdentifier || isMarkoFile || !importPath) {
2522
- tagIdentifier = varLocal("tag_" + this.#ensureTagId(tag));
2523
- this.#extractor.write(`const ${tagIdentifier} = (
2501
+ templateVar = this.#getTemplateVar(tag, true);
2502
+ this.#extractor.write(`const ${templateVar} = (
2524
2503
  `);
2525
2504
  if (isIdentifier) {
2526
2505
  if (importPath) {
@@ -2540,22 +2519,23 @@ scope: ${scopeExpr}
2540
2519
  }
2541
2520
  this.#extractor.write("\n);\n");
2542
2521
  } else {
2543
- tagIdentifier = varShared("missingTag");
2522
+ templateVar = varShared("missingTag");
2544
2523
  }
2545
2524
  const attrTagTree = this.#getAttrTagTree(tag);
2546
2525
  if (attrTagTree) {
2547
- this.#writeAttrTagTree(attrTagTree, tagIdentifier);
2526
+ this.#writeAttrTagTree(attrTagTree, templateVar);
2548
2527
  this.#extractor.write(";\n");
2549
2528
  }
2550
2529
  }
2551
- if (renderId) {
2552
- this.#extractor.write(`const ${varLocal("rendered_" + renderId)} = `);
2530
+ if (!isHTML && tag.var || hasHoists(tag)) {
2531
+ renderVar = this.#getRenderVar(tag, true);
2532
+ this.#extractor.write(`const ${renderVar} = `);
2553
2533
  }
2554
2534
  if (isHTML) {
2555
2535
  this.#extractor.write(`${varShared("renderNativeTag")}("`).copy(isEmptyRange2(tag.name) ? tagName : tag.name).write('")');
2556
- } else if (tagIdentifier) {
2536
+ } else if (templateVar) {
2557
2537
  this.#extractor.write(
2558
- `${varShared(isTemplate ? "renderTemplate" : "renderDynamicTag")}(${tagIdentifier})`
2538
+ `${varShared(isTemplate ? "renderTemplate" : "renderDynamicTag")}(${templateVar})`
2559
2539
  );
2560
2540
  } else {
2561
2541
  this.#extractor.write(varShared("missingTag"));
@@ -2567,14 +2547,26 @@ scope: ${scopeExpr}
2567
2547
  }
2568
2548
  this.#writeTagInputObject(tag);
2569
2549
  this.#extractor.write(");\n");
2570
- if (renderId && tag.var) {
2550
+ if (tag.var) {
2571
2551
  this.#extractor.write(`{const `);
2572
2552
  this.#closeBrackets[this.#closeBrackets.length - 1]++;
2573
- this.#copyWithMutationsReplaced(tag.var.value);
2574
- this.#extractor.write(
2575
- ` = ${varLocal("rendered_" + renderId)}.return.${ATTR_UNAMED2};
2553
+ if (renderVar) {
2554
+ const mutatedVars = getMutatedVars(tag);
2555
+ this.#copyWithMutationsReplaced(tag.var.value);
2556
+ this.#extractor.write(` = ${renderVar}.return.${ATTR_UNAMED2};
2557
+ `);
2558
+ if (mutatedVars) {
2559
+ for (const binding of mutatedVars) {
2560
+ this.#extractor.write(
2561
+ `const ${varLocal(`change__${binding.name}`)} = ${varShared("change")}(${JSON.stringify(binding.name) + (binding.sourceName && binding.sourceName !== binding.name ? `, ${JSON.stringify(binding.sourceName)}` : "")}, ${renderVar}.return${binding.objectPath || ""});
2576
2562
  `
2577
- );
2563
+ );
2564
+ }
2565
+ }
2566
+ } else if (isHTML) {
2567
+ this.#extractor.copy(tag.var.value).write(` = ${varShared("el")}(${JSON.stringify(def.name)});
2568
+ `);
2569
+ }
2578
2570
  }
2579
2571
  }
2580
2572
  #endChildren() {
@@ -2673,7 +2665,7 @@ scope: ${scopeExpr}
2673
2665
  `Change"(
2674
2666
  // @ts-ignore
2675
2667
  _${valueLiteral}) {
2676
- ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}`
2668
+ ${isMutatedVar(tag.parent, valueLiteral) ? varLocal(`change__${valueLiteral}.`) : ""}`
2677
2669
  ).copy(boundRange.value).write(`= _${valueLiteral};
2678
2670
  }`);
2679
2671
  } else if (boundRange.member.computed) {
@@ -2789,8 +2781,8 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
2789
2781
  this.#writeTagNameComment(firstAttrTag);
2790
2782
  this.#extractor.write("]: ");
2791
2783
  if (isRepeated) {
2792
- const tagId = this.#tagIds.get(firstAttrTag.owner);
2793
- if (tagId) {
2784
+ const templateVar = this.#getTemplateVar(firstAttrTag.owner);
2785
+ if (templateVar) {
2794
2786
  let accessor = `"${name}"`;
2795
2787
  let curTag = firstAttrTag.parent;
2796
2788
  while (curTag) {
@@ -2802,7 +2794,7 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
2802
2794
  curTag = curTag.parent;
2803
2795
  }
2804
2796
  this.#extractor.write(
2805
- `${varShared("attrTagFor")}(${varLocal("tag_" + tagId)},${accessor})([`
2797
+ `${varShared("attrTagFor")}(${templateVar},${accessor})([`
2806
2798
  );
2807
2799
  } else {
2808
2800
  this.#extractor.write(`${varShared("attrTag")}([`);
@@ -2926,13 +2918,11 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
2926
2918
  if (tag.params || hasBodyContent) {
2927
2919
  this.#extractor.write("[");
2928
2920
  if (this.#interop) {
2929
- const tagId = this.#tagIds.get(
2921
+ const templateVar = this.#getTemplateVar(
2930
2922
  tag.type === 16 /* AttrTag */ ? tag.owner : tag
2931
2923
  );
2932
- if (tagId) {
2933
- this.#extractor.write(
2934
- `${varShared("contentFor")}(${varLocal("tag_" + tagId)})`
2935
- );
2924
+ if (templateVar) {
2925
+ this.#extractor.write(`${varShared("contentFor")}(${templateVar})`);
2936
2926
  } else {
2937
2927
  this.#extractor.write(varShared("content"));
2938
2928
  }
@@ -2953,16 +2943,13 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
2953
2943
  }
2954
2944
  let didReturn = false;
2955
2945
  if (body == null ? void 0 : body.content) {
2956
- didReturn = this.#writeChildren(tag, body.content);
2946
+ didReturn = this.#writeChildren(body.content);
2957
2947
  }
2958
2948
  if (!tag.params) {
2959
2949
  this.#extractor.write(`return () => {
2960
2950
  `);
2961
2951
  }
2962
- this.#writeReturn(
2963
- didReturn ? `${varLocal("return")}.return` : void 0,
2964
- tag.body
2965
- );
2952
+ this.#writeReturn(didReturn ? varLocal("return") : void 0, tag.body);
2966
2953
  if (body == null ? void 0 : body.content) {
2967
2954
  this.#endChildren();
2968
2955
  }
@@ -2987,7 +2974,7 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
2987
2974
  return this.#scriptLang === "ts" /* ts */ ? `${varShared("any")} as ${type}` : `/** @type {${type}} */(${varShared("any")})`;
2988
2975
  }
2989
2976
  #copyWithMutationsReplaced(range) {
2990
- const mutations = this.#mutationOffsets;
2977
+ const mutations = this.#mutations;
2991
2978
  if (!mutations) return this.#extractor.copy(range);
2992
2979
  const len = mutations.length;
2993
2980
  let curOffset = range.start;
@@ -2996,23 +2983,23 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
2996
2983
  let maxIndex = len;
2997
2984
  while (minIndex < maxIndex) {
2998
2985
  const midIndex = minIndex + maxIndex >>> 1;
2999
- if (mutations[midIndex] >= curOffset) {
2986
+ if (mutations[midIndex].start >= curOffset) {
3000
2987
  maxIndex = midIndex;
3001
2988
  } else {
3002
2989
  minIndex = midIndex + 1;
3003
2990
  }
3004
2991
  }
3005
- const nextOffset = maxIndex === len ? range.end : mutations[maxIndex];
3006
- if (nextOffset >= range.end) {
2992
+ const mutation = maxIndex !== len && mutations[maxIndex];
2993
+ if (!mutation || mutation.start >= range.end) {
3007
2994
  this.#extractor.copy({
3008
2995
  start: curOffset,
3009
2996
  end: range.end
3010
2997
  });
3011
2998
  return;
3012
2999
  }
3013
- this.#extractor.copy({ start: curOffset, end: nextOffset });
3014
- this.#extractor.write(`${varLocal("return")}.mutate.`);
3015
- curOffset = nextOffset;
3000
+ this.#extractor.copy({ start: curOffset, end: mutation.start });
3001
+ this.#extractor.write(`${varLocal(`change__${mutation.binding.name}`)}.`);
3002
+ curOffset = mutation.start;
3016
3003
  minIndex = maxIndex + 1;
3017
3004
  } while (true);
3018
3005
  }
@@ -3164,7 +3151,7 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
3164
3151
  if (body) {
3165
3152
  if (body.content) {
3166
3153
  this.#extractor.write("(() => {\n");
3167
- this.#writeChildren(tag, body.content);
3154
+ this.#writeChildren(body.content);
3168
3155
  this.#extractor.write("return ");
3169
3156
  }
3170
3157
  this.#extractor.write("{\n");
@@ -3266,13 +3253,27 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
3266
3253
  return tag.name.expressions[0].value;
3267
3254
  }
3268
3255
  }
3269
- #getRenderId(tag) {
3270
- let renderId = this.#renderIds.get(tag);
3271
- if (!renderId && (tag.var || hasHoists(tag))) {
3272
- renderId = this.#renderId++;
3273
- this.#renderIds.set(tag, renderId);
3256
+ #getRenderVar(tag, declared = false) {
3257
+ let id = RENDER_VAR.get(tag);
3258
+ if (!id && declared) {
3259
+ RENDER_VAR.set(tag, id = varLocal("rendered_" + this.#getTagId(tag)));
3260
+ }
3261
+ return id;
3262
+ }
3263
+ #getTemplateVar(tag, declared = false) {
3264
+ let id = TEMPLATE_VAR.get(tag);
3265
+ if (!id && declared) {
3266
+ TEMPLATE_VAR.set(tag, id = varLocal("tag_" + this.#getTagId(tag)));
3267
+ }
3268
+ return id;
3269
+ }
3270
+ #getTagId(tag) {
3271
+ let id = TAG_ID.get(tag);
3272
+ if (id === void 0) {
3273
+ id = this.#tagId++;
3274
+ TAG_ID.set(tag, id);
3274
3275
  }
3275
- return renderId;
3276
+ return id;
3276
3277
  }
3277
3278
  #getScopeExpression(body) {
3278
3279
  const sources = getHoistSources(body);
@@ -3280,39 +3281,27 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
3280
3281
  return sources ? `{ ${hoists ? `...${hoists}, ` : ""}${sources.join(SEP_COMMA_SPACE)} }` : hoists;
3281
3282
  }
3282
3283
  #getBodyHoistScopeExpression(body) {
3283
- let hoistIds;
3284
+ let hoistVars;
3284
3285
  if (body) {
3285
3286
  for (const child of body) {
3286
- const renderId = child.type === 1 /* Tag */ && this.#renderIds.get(child);
3287
- if (renderId && (!child.var || hasHoists(child))) {
3288
- if (hoistIds) {
3289
- hoistIds.push(renderId);
3290
- } else {
3291
- hoistIds = [renderId];
3287
+ if (child.type === 1 /* Tag */) {
3288
+ const renderVar = this.#getRenderVar(child);
3289
+ if (renderVar && (!child.var || hasHoists(child))) {
3290
+ if (hoistVars) {
3291
+ hoistVars.push(renderVar);
3292
+ } else {
3293
+ hoistVars = [renderVar];
3294
+ }
3292
3295
  }
3293
3296
  }
3294
3297
  }
3295
3298
  }
3296
- if (hoistIds) {
3297
- if (hoistIds.length === 1) {
3298
- return `${varShared("readScope")}(${varLocal("rendered_" + hoistIds[0])})`;
3299
+ if (hoistVars) {
3300
+ if (hoistVars.length === 1) {
3301
+ return `${varShared("readScope")}(${hoistVars[0]})`;
3299
3302
  }
3300
- let result = `${varShared("readScopes")}({ `;
3301
- let sep = "";
3302
- for (const renderId of hoistIds) {
3303
- result += sep + `${varLocal("rendered_" + renderId)}`;
3304
- sep = SEP_COMMA_SPACE;
3305
- }
3306
- return result + " })";
3307
- }
3308
- }
3309
- #ensureTagId(tag) {
3310
- let tagId = this.#tagIds.get(tag);
3311
- if (!tagId) {
3312
- tagId = this.#tagId++;
3313
- this.#tagIds.set(tag, tagId);
3303
+ return `${varShared("readScopes")}({ ${hoistVars.join(SEP_COMMA_SPACE)} })`;
3314
3304
  }
3315
- return tagId;
3316
3305
  }
3317
3306
  #getNamedAttrModifierIndex(attr) {
3318
3307
  const start = attr.name.start + 1;
@@ -41,6 +41,10 @@ declare global {
41
41
 
42
42
  export const content: DefaultBodyContentKey;
43
43
 
44
+ export function el<Name extends string>(
45
+ name: Name,
46
+ ): Marko.NativeTags[Name]["return"]["value"];
47
+
44
48
  export function contentFor<Name>(
45
49
  tag: Name,
46
50
  ): [0] extends [1 & Name]
@@ -122,19 +126,21 @@ declare global {
122
126
  : never
123
127
  >;
124
128
 
125
- export function mutable<Lookup>(lookup: Lookup): UnionToIntersection<
126
- Lookup extends readonly (infer Item)[]
127
- ? Item extends
128
- | readonly [infer LocalName extends string, infer Data]
129
- | readonly [infer LocalName, infer SourceName, infer Data]
130
- ? Data extends {
131
- [K in `${SourceName extends string
132
- ? SourceName
133
- : LocalName}Change`]: (value: infer V, ...args: any[]) => any;
134
- }
135
- ? { [K in LocalName]: V }
136
- : { readonly [K in LocalName]: unknown }
137
- : never
129
+ export function change<const Item>(...item: Item): UnionToIntersection<
130
+ Item extends
131
+ | readonly [infer LocalName extends string, infer Data]
132
+ | readonly [
133
+ infer LocalName extends string,
134
+ infer SourceName,
135
+ infer Data,
136
+ ]
137
+ ? Data extends {
138
+ [K in `${SourceName extends string
139
+ ? SourceName
140
+ : LocalName}Change`]: (value: infer V, ...args: any[]) => any;
141
+ }
142
+ ? { [K in LocalName]: V }
143
+ : { readonly [K in LocalName]: unknown }
138
144
  : never
139
145
  >;
140
146
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@marko/language-tools",
3
3
  "description": "Marko Language Tools",
4
- "version": "2.5.44",
4
+ "version": "2.5.46",
5
5
  "bugs": "https://github.com/marko-js/language-server/issues/new?template=Bug_report.md",
6
6
  "peerDependencies": {
7
7
  "@marko/compiler": "^5.28.4"