@marko/language-tools 2.5.43 → 2.5.45

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,17 +1384,10 @@ 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;
1385
- }
1386
- }
1387
- return false;
1388
+ var _a;
1389
+ const binding = (_a = Scopes.get(node.body).bindings) == null ? void 0 : _a[name];
1390
+ return (binding == null ? void 0 : binding.type) === 0 /* var */ && binding.mutated;
1388
1391
  }
1389
1392
  function hasHoists(node) {
1390
1393
  return node.body ? Scopes.get(node.body).hoists : false;
@@ -1629,12 +1632,11 @@ function trackMutations(node, scope, mutations, parentBlock, parentBlockShadows,
1629
1632
  }
1630
1633
  if (block !== parentBlock && blockMutations.length) {
1631
1634
  for (const { name, start } of blockMutations) {
1632
- if (blockShadows.has(name)) continue;
1635
+ if (start == null || blockShadows.has(name)) continue;
1633
1636
  const binding = resolveWritableVar(scope, name);
1634
1637
  if (binding) {
1635
1638
  binding.mutated = true;
1636
- mutations.push(start);
1637
- (scope.mutatedBindings ||= /* @__PURE__ */ new Set()).add(binding);
1639
+ mutations.push({ start, binding });
1638
1640
  }
1639
1641
  }
1640
1642
  }
@@ -1660,6 +1662,9 @@ function traverse(node, enter) {
1660
1662
  }
1661
1663
  }
1662
1664
  }
1665
+ function byStart(a, b) {
1666
+ return a.start - b.start;
1667
+ }
1663
1668
 
1664
1669
  // src/extractors/script/util/get-component-filename.ts
1665
1670
  var import_fs = __toESM(require("fs"));
@@ -2002,6 +2007,9 @@ var REG_OBJECT_PROPERTY = /^[_$a-z][_$a-z0-9]*$/i;
2002
2007
  var REG_COMMENT_PRAGMA = /\/\/(?:\s*@ts-|\/\s*<)/y;
2003
2008
  var REG_TAG_NAME_IDENTIFIER = /^[A-Z][a-zA-Z0-9_$]+$/;
2004
2009
  var IF_TAG_ALTERNATES = /* @__PURE__ */ new WeakMap();
2010
+ var TAG_ID = /* @__PURE__ */ new WeakMap();
2011
+ var RENDER_VAR = /* @__PURE__ */ new WeakMap();
2012
+ var TEMPLATE_VAR = /* @__PURE__ */ new WeakMap();
2005
2013
  var WROTE_COMMENT = /* @__PURE__ */ new WeakSet();
2006
2014
  var START_OF_FILE = { start: 0, end: 0 };
2007
2015
  var ScriptLang = /* @__PURE__ */ ((ScriptLang2) => {
@@ -2022,14 +2030,11 @@ var ScriptExtractor = class {
2022
2030
  #scriptParser;
2023
2031
  #read;
2024
2032
  #lookup;
2025
- #tagIds = /* @__PURE__ */ new Map();
2026
- #renderIds = /* @__PURE__ */ new Map();
2027
2033
  #scriptLang;
2028
2034
  #ts;
2029
2035
  #runtimeTypes;
2030
- #mutationOffsets;
2036
+ #mutations;
2031
2037
  #tagId = 1;
2032
- #renderId = 1;
2033
2038
  #closeBrackets = [0];
2034
2039
  constructor(opts) {
2035
2040
  const { parsed, lookup, scriptLang } = opts;
@@ -2050,7 +2055,7 @@ var ScriptExtractor = class {
2050
2055
  this.#extractor = new Extractor(parsed);
2051
2056
  this.#scriptParser = new ScriptParser(parsed);
2052
2057
  this.#read = parsed.read.bind(parsed);
2053
- this.#mutationOffsets = crawlProgramScope(this.#parsed, this.#scriptParser);
2058
+ this.#mutations = crawlProgramScope(this.#parsed, this.#scriptParser);
2054
2059
  this.#writeProgram(parsed.program);
2055
2060
  }
2056
2061
  end() {
@@ -2213,7 +2218,7 @@ function ${templateName}() {
2213
2218
  this.#extractor.write(` const input = ${this.#getCastedType(`Input${typeArgsStr}`)};${this.#api === RuntimeAPI.class ? `
2214
2219
  const component = ${this.#getCastedType(`Component${typeArgsStr}`)};
2215
2220
  const state = ${varShared("state")}(component);
2216
- const out = ${varShared("out")};` : ""}
2221
+ const out = ${this.#getCastedType("Marko.Out")};` : ""}
2217
2222
  const $signal = ${this.#getCastedType("AbortSignal")};
2218
2223
  const $global = ${varShared("getGlobal")}(
2219
2224
  // @ts-expect-error We expect the compiler to error because we are checking if the MarkoRun.Context is defined.
@@ -2231,7 +2236,7 @@ function ${templateName}() {
2231
2236
  }
2232
2237
  }
2233
2238
  if (body == null ? void 0 : body.content) {
2234
- this.#writeChildren(program, body.content);
2239
+ this.#writeChildren(body.content);
2235
2240
  if (bindings) {
2236
2241
  if (bindings.vars) {
2237
2242
  for (const name of bindings.vars) {
@@ -2255,7 +2260,7 @@ function ${templateName}() {
2255
2260
  `
2256
2261
  );
2257
2262
  if (didReturn) {
2258
- this.#extractor.write(`return ${varLocal("return")}.return;
2263
+ this.#extractor.write(`return ${varLocal("return")};
2259
2264
  }
2260
2265
  `);
2261
2266
  } else {
@@ -2360,7 +2365,7 @@ constructor(_?: Return) {}
2360
2365
  this.#extractor.copy(returned);
2361
2366
  this.#extractor.write(");\n");
2362
2367
  }
2363
- #writeChildren(parent, children, skipRenderId = false) {
2368
+ #writeChildren(children, skipRenderId = false) {
2364
2369
  var _a, _b;
2365
2370
  const last = children.length - 1;
2366
2371
  let returnTag;
@@ -2376,21 +2381,19 @@ constructor(_?: Return) {}
2376
2381
  break;
2377
2382
  case "if": {
2378
2383
  const alternates = IF_TAG_ALTERNATES.get(child);
2379
- let renderId;
2384
+ let didHoist = false;
2380
2385
  if (!skipRenderId) {
2381
- renderId = this.#getRenderId(child);
2382
- if (!renderId && alternates) {
2386
+ didHoist = hasHoists(child);
2387
+ if (!didHoist && alternates) {
2383
2388
  for (const { node } of alternates) {
2384
- if (renderId = this.#getRenderId(node)) {
2385
- this.#renderIds.set(child, renderId);
2386
- this.#renderIds.delete(node);
2389
+ if (didHoist = hasHoists(node)) {
2387
2390
  break;
2388
2391
  }
2389
2392
  }
2390
2393
  }
2391
- if (renderId) {
2394
+ if (didHoist) {
2392
2395
  this.#extractor.write(
2393
- `const ${varLocal("rendered_" + renderId)} = (() => {
2396
+ `const ${this.#getRenderVar(child, true)} = (() => {
2394
2397
  `
2395
2398
  );
2396
2399
  }
@@ -2401,7 +2404,7 @@ constructor(_?: Return) {}
2401
2404
  ).write(") {\n");
2402
2405
  const ifBody = this.#processBody(child);
2403
2406
  if (ifBody == null ? void 0 : ifBody.content) {
2404
- this.#writeChildren(child, ifBody.content, true);
2407
+ this.#writeChildren(ifBody.content, true);
2405
2408
  const scopeExpr = this.#getScopeExpression(child.body);
2406
2409
  if (scopeExpr) {
2407
2410
  this.#extractor.write(`return {
@@ -2425,7 +2428,7 @@ scope: ${scopeExpr}
2425
2428
  }
2426
2429
  const alternateBody = this.#processBody(node);
2427
2430
  if (alternateBody == null ? void 0 : alternateBody.content) {
2428
- this.#writeChildren(node, alternateBody.content, true);
2431
+ this.#writeChildren(alternateBody.content, true);
2429
2432
  const scopeExpr = this.#getScopeExpression(node.body);
2430
2433
  if (scopeExpr) {
2431
2434
  this.#extractor.write(
@@ -2439,21 +2442,20 @@ scope: ${scopeExpr}
2439
2442
  }
2440
2443
  }
2441
2444
  }
2442
- if (needsAlternate && renderId) {
2445
+ if (needsAlternate && didHoist) {
2443
2446
  this.#extractor.write("\n} else {\nreturn undefined;\n}\n");
2444
2447
  } else {
2445
2448
  this.#extractor.write("\n}\n");
2446
2449
  }
2447
- if (renderId) {
2450
+ if (didHoist) {
2448
2451
  this.#extractor.write("\n})();\n");
2449
2452
  }
2450
2453
  break;
2451
2454
  }
2452
2455
  case "for": {
2453
- const renderId = this.#getRenderId(child);
2454
- if (renderId) {
2456
+ if (hasHoists(child)) {
2455
2457
  this.#extractor.write(
2456
- `const ${varLocal("rendered_" + renderId)} = `
2458
+ `const ${this.#getRenderVar(child, true)} = `
2457
2459
  );
2458
2460
  }
2459
2461
  this.#extractor.write(
@@ -2470,7 +2472,7 @@ scope: ${scopeExpr}
2470
2472
  this.#extractor.write("\n) => {\n");
2471
2473
  const body = this.#processBody(child);
2472
2474
  if (body == null ? void 0 : body.content) {
2473
- this.#writeChildren(child, body.content);
2475
+ this.#writeChildren(body.content);
2474
2476
  }
2475
2477
  this.#writeReturn(void 0, (body == null ? void 0 : body.content) && child.body);
2476
2478
  if (body == null ? void 0 : body.content) {
@@ -2486,7 +2488,7 @@ scope: ${scopeExpr}
2486
2488
  ).write("\n) {\n");
2487
2489
  const body = this.#processBody(child);
2488
2490
  if (body == null ? void 0 : body.content) {
2489
- this.#writeChildren(child, body.content);
2491
+ this.#writeChildren(body.content);
2490
2492
  this.#endChildren();
2491
2493
  }
2492
2494
  this.#extractor.write("\n}\n");
@@ -2505,59 +2507,29 @@ scope: ${scopeExpr}
2505
2507
  break;
2506
2508
  }
2507
2509
  }
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
- }
2510
+ if (returnTag) {
2511
+ this.#extractor.write(
2512
+ `var ${varLocal("return")} = ${varShared("returnTag")}(`
2513
+ );
2514
+ this.#writeTagInputObject(returnTag);
2515
+ this.#extractor.write(");\n");
2544
2516
  }
2545
2517
  return returnTag !== void 0;
2546
2518
  }
2547
2519
  #writeTag(tag) {
2548
2520
  const tagName = tag.nameText;
2549
- const renderId = this.#getRenderId(tag);
2550
2521
  const def = tagName ? this.#lookup.getTag(tagName) : void 0;
2551
2522
  const importPath = resolveTagImport(this.#filename, def);
2552
2523
  const isHTML = !importPath && (def == null ? void 0 : def.html);
2553
- let tagIdentifier;
2554
2524
  let isTemplate = false;
2525
+ let renderVar;
2526
+ let templateVar;
2555
2527
  if (!def || importPath) {
2556
2528
  const isIdentifier = tagName && REG_TAG_NAME_IDENTIFIER.test(tagName);
2557
2529
  const isMarkoFile = importPath == null ? void 0 : importPath.endsWith(".marko");
2558
2530
  if (isIdentifier || isMarkoFile || !importPath) {
2559
- tagIdentifier = varLocal("tag_" + this.#ensureTagId(tag));
2560
- this.#extractor.write(`const ${tagIdentifier} = (
2531
+ templateVar = this.#getTemplateVar(tag, true);
2532
+ this.#extractor.write(`const ${templateVar} = (
2561
2533
  `);
2562
2534
  if (isIdentifier) {
2563
2535
  if (importPath) {
@@ -2577,22 +2549,23 @@ scope: ${scopeExpr}
2577
2549
  }
2578
2550
  this.#extractor.write("\n);\n");
2579
2551
  } else {
2580
- tagIdentifier = varShared("missingTag");
2552
+ templateVar = varShared("missingTag");
2581
2553
  }
2582
2554
  const attrTagTree = this.#getAttrTagTree(tag);
2583
2555
  if (attrTagTree) {
2584
- this.#writeAttrTagTree(attrTagTree, tagIdentifier);
2556
+ this.#writeAttrTagTree(attrTagTree, templateVar);
2585
2557
  this.#extractor.write(";\n");
2586
2558
  }
2587
2559
  }
2588
- if (renderId) {
2589
- this.#extractor.write(`const ${varLocal("rendered_" + renderId)} = `);
2560
+ if (!isHTML && tag.var || hasHoists(tag)) {
2561
+ renderVar = this.#getRenderVar(tag, true);
2562
+ this.#extractor.write(`const ${renderVar} = `);
2590
2563
  }
2591
2564
  if (isHTML) {
2592
2565
  this.#extractor.write(`${varShared("renderNativeTag")}("`).copy(isEmptyRange2(tag.name) ? tagName : tag.name).write('")');
2593
- } else if (tagIdentifier) {
2566
+ } else if (templateVar) {
2594
2567
  this.#extractor.write(
2595
- `${varShared(isTemplate ? "renderTemplate" : "renderDynamicTag")}(${tagIdentifier})`
2568
+ `${varShared(isTemplate ? "renderTemplate" : "renderDynamicTag")}(${templateVar})`
2596
2569
  );
2597
2570
  } else {
2598
2571
  this.#extractor.write(varShared("missingTag"));
@@ -2604,14 +2577,26 @@ scope: ${scopeExpr}
2604
2577
  }
2605
2578
  this.#writeTagInputObject(tag);
2606
2579
  this.#extractor.write(");\n");
2607
- if (renderId && tag.var) {
2580
+ if (tag.var) {
2608
2581
  this.#extractor.write(`{const `);
2609
2582
  this.#closeBrackets[this.#closeBrackets.length - 1]++;
2610
- this.#copyWithMutationsReplaced(tag.var.value);
2611
- this.#extractor.write(
2612
- ` = ${varLocal("rendered_" + renderId)}.return.${ATTR_UNAMED2};
2583
+ if (renderVar) {
2584
+ const mutatedVars = getMutatedVars(tag);
2585
+ this.#copyWithMutationsReplaced(tag.var.value);
2586
+ this.#extractor.write(` = ${renderVar}.return.${ATTR_UNAMED2};
2587
+ `);
2588
+ if (mutatedVars) {
2589
+ for (const binding of mutatedVars) {
2590
+ this.#extractor.write(
2591
+ `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
2592
  `
2614
- );
2593
+ );
2594
+ }
2595
+ }
2596
+ } else if (isHTML) {
2597
+ this.#extractor.copy(tag.var.value).write(` = ${varShared("el")}(${JSON.stringify(def.name)});
2598
+ `);
2599
+ }
2615
2600
  }
2616
2601
  }
2617
2602
  #endChildren() {
@@ -2707,8 +2692,10 @@ scope: ${scopeExpr}
2707
2692
  const valueLiteral = this.#read(boundRange.value);
2708
2693
  this.#extractor.copy(boundRange.value).copy(boundRange.types).write(`
2709
2694
  )${SEP_COMMA_NEW_LINE}"`).copy(defaultMapPosition).copy(name).write(
2710
- `Change"(_${valueLiteral}) {
2711
- ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}`
2695
+ `Change"(
2696
+ // @ts-ignore
2697
+ _${valueLiteral}) {
2698
+ ${isMutatedVar(tag.parent, valueLiteral) ? varLocal(`change__${valueLiteral}.`) : ""}`
2712
2699
  ).copy(boundRange.value).write(`= _${valueLiteral};
2713
2700
  }`);
2714
2701
  } else if (boundRange.member.computed) {
@@ -2824,8 +2811,8 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
2824
2811
  this.#writeTagNameComment(firstAttrTag);
2825
2812
  this.#extractor.write("]: ");
2826
2813
  if (isRepeated) {
2827
- const tagId = this.#tagIds.get(firstAttrTag.owner);
2828
- if (tagId) {
2814
+ const templateVar = this.#getTemplateVar(firstAttrTag.owner);
2815
+ if (templateVar) {
2829
2816
  let accessor = `"${name}"`;
2830
2817
  let curTag = firstAttrTag.parent;
2831
2818
  while (curTag) {
@@ -2837,7 +2824,7 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
2837
2824
  curTag = curTag.parent;
2838
2825
  }
2839
2826
  this.#extractor.write(
2840
- `${varShared("attrTagFor")}(${varLocal("tag_" + tagId)},${accessor})([`
2827
+ `${varShared("attrTagFor")}(${templateVar},${accessor})([`
2841
2828
  );
2842
2829
  } else {
2843
2830
  this.#extractor.write(`${varShared("attrTag")}([`);
@@ -2961,13 +2948,11 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
2961
2948
  if (tag.params || hasBodyContent) {
2962
2949
  this.#extractor.write("[");
2963
2950
  if (this.#interop) {
2964
- const tagId = this.#tagIds.get(
2951
+ const templateVar = this.#getTemplateVar(
2965
2952
  tag.type === 16 /* AttrTag */ ? tag.owner : tag
2966
2953
  );
2967
- if (tagId) {
2968
- this.#extractor.write(
2969
- `${varShared("contentFor")}(${varLocal("tag_" + tagId)})`
2970
- );
2954
+ if (templateVar) {
2955
+ this.#extractor.write(`${varShared("contentFor")}(${templateVar})`);
2971
2956
  } else {
2972
2957
  this.#extractor.write(varShared("content"));
2973
2958
  }
@@ -2988,16 +2973,13 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
2988
2973
  }
2989
2974
  let didReturn = false;
2990
2975
  if (body == null ? void 0 : body.content) {
2991
- didReturn = this.#writeChildren(tag, body.content);
2976
+ didReturn = this.#writeChildren(body.content);
2992
2977
  }
2993
2978
  if (!tag.params) {
2994
2979
  this.#extractor.write(`return () => {
2995
2980
  `);
2996
2981
  }
2997
- this.#writeReturn(
2998
- didReturn ? `${varLocal("return")}.return` : void 0,
2999
- tag.body
3000
- );
2982
+ this.#writeReturn(didReturn ? varLocal("return") : void 0, tag.body);
3001
2983
  if (body == null ? void 0 : body.content) {
3002
2984
  this.#endChildren();
3003
2985
  }
@@ -3022,7 +3004,7 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
3022
3004
  return this.#scriptLang === "ts" /* ts */ ? `${varShared("any")} as ${type}` : `/** @type {${type}} */(${varShared("any")})`;
3023
3005
  }
3024
3006
  #copyWithMutationsReplaced(range) {
3025
- const mutations = this.#mutationOffsets;
3007
+ const mutations = this.#mutations;
3026
3008
  if (!mutations) return this.#extractor.copy(range);
3027
3009
  const len = mutations.length;
3028
3010
  let curOffset = range.start;
@@ -3031,23 +3013,23 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
3031
3013
  let maxIndex = len;
3032
3014
  while (minIndex < maxIndex) {
3033
3015
  const midIndex = minIndex + maxIndex >>> 1;
3034
- if (mutations[midIndex] >= curOffset) {
3016
+ if (mutations[midIndex].start >= curOffset) {
3035
3017
  maxIndex = midIndex;
3036
3018
  } else {
3037
3019
  minIndex = midIndex + 1;
3038
3020
  }
3039
3021
  }
3040
- const nextOffset = maxIndex === len ? range.end : mutations[maxIndex];
3041
- if (nextOffset >= range.end) {
3022
+ const mutation = maxIndex !== len && mutations[maxIndex];
3023
+ if (!mutation || mutation.start >= range.end) {
3042
3024
  this.#extractor.copy({
3043
3025
  start: curOffset,
3044
3026
  end: range.end
3045
3027
  });
3046
3028
  return;
3047
3029
  }
3048
- this.#extractor.copy({ start: curOffset, end: nextOffset });
3049
- this.#extractor.write(`${varLocal("return")}.mutate.`);
3050
- curOffset = nextOffset;
3030
+ this.#extractor.copy({ start: curOffset, end: mutation.start });
3031
+ this.#extractor.write(`${varLocal(`change__${mutation.binding.name}`)}.`);
3032
+ curOffset = mutation.start;
3051
3033
  minIndex = maxIndex + 1;
3052
3034
  } while (true);
3053
3035
  }
@@ -3199,7 +3181,7 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
3199
3181
  if (body) {
3200
3182
  if (body.content) {
3201
3183
  this.#extractor.write("(() => {\n");
3202
- this.#writeChildren(tag, body.content);
3184
+ this.#writeChildren(body.content);
3203
3185
  this.#extractor.write("return ");
3204
3186
  }
3205
3187
  this.#extractor.write("{\n");
@@ -3301,13 +3283,27 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
3301
3283
  return tag.name.expressions[0].value;
3302
3284
  }
3303
3285
  }
3304
- #getRenderId(tag) {
3305
- let renderId = this.#renderIds.get(tag);
3306
- if (!renderId && (tag.var || hasHoists(tag))) {
3307
- renderId = this.#renderId++;
3308
- this.#renderIds.set(tag, renderId);
3286
+ #getRenderVar(tag, declared = false) {
3287
+ let id = RENDER_VAR.get(tag);
3288
+ if (!id && declared) {
3289
+ RENDER_VAR.set(tag, id = varLocal("rendered_" + this.#getTagId(tag)));
3290
+ }
3291
+ return id;
3292
+ }
3293
+ #getTemplateVar(tag, declared = false) {
3294
+ let id = TEMPLATE_VAR.get(tag);
3295
+ if (!id && declared) {
3296
+ TEMPLATE_VAR.set(tag, id = varLocal("tag_" + this.#getTagId(tag)));
3297
+ }
3298
+ return id;
3299
+ }
3300
+ #getTagId(tag) {
3301
+ let id = TAG_ID.get(tag);
3302
+ if (id === void 0) {
3303
+ id = this.#tagId++;
3304
+ TAG_ID.set(tag, id);
3309
3305
  }
3310
- return renderId;
3306
+ return id;
3311
3307
  }
3312
3308
  #getScopeExpression(body) {
3313
3309
  const sources = getHoistSources(body);
@@ -3315,39 +3311,27 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
3315
3311
  return sources ? `{ ${hoists ? `...${hoists}, ` : ""}${sources.join(SEP_COMMA_SPACE)} }` : hoists;
3316
3312
  }
3317
3313
  #getBodyHoistScopeExpression(body) {
3318
- let hoistIds;
3314
+ let hoistVars;
3319
3315
  if (body) {
3320
3316
  for (const child of body) {
3321
- const renderId = child.type === 1 /* Tag */ && this.#renderIds.get(child);
3322
- if (renderId && (!child.var || hasHoists(child))) {
3323
- if (hoistIds) {
3324
- hoistIds.push(renderId);
3325
- } else {
3326
- hoistIds = [renderId];
3317
+ if (child.type === 1 /* Tag */) {
3318
+ const renderVar = this.#getRenderVar(child);
3319
+ if (renderVar && (!child.var || hasHoists(child))) {
3320
+ if (hoistVars) {
3321
+ hoistVars.push(renderVar);
3322
+ } else {
3323
+ hoistVars = [renderVar];
3324
+ }
3327
3325
  }
3328
3326
  }
3329
3327
  }
3330
3328
  }
3331
- if (hoistIds) {
3332
- if (hoistIds.length === 1) {
3333
- return `${varShared("readScope")}(${varLocal("rendered_" + hoistIds[0])})`;
3329
+ if (hoistVars) {
3330
+ if (hoistVars.length === 1) {
3331
+ return `${varShared("readScope")}(${hoistVars[0]})`;
3334
3332
  }
3335
- let result = `${varShared("readScopes")}({ `;
3336
- let sep = "";
3337
- for (const renderId of hoistIds) {
3338
- result += sep + `${varLocal("rendered_" + renderId)}`;
3339
- sep = SEP_COMMA_SPACE;
3340
- }
3341
- return result + " })";
3342
- }
3343
- }
3344
- #ensureTagId(tag) {
3345
- let tagId = this.#tagIds.get(tag);
3346
- if (!tagId) {
3347
- tagId = this.#tagId++;
3348
- this.#tagIds.set(tag, tagId);
3333
+ return `${varShared("readScopes")}({ ${hoistVars.join(SEP_COMMA_SPACE)} })`;
3349
3334
  }
3350
- return tagId;
3351
3335
  }
3352
3336
  #getNamedAttrModifierIndex(attr) {
3353
3337
  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,17 +1344,10 @@ 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;
1345
- }
1346
- }
1347
- return false;
1348
+ var _a;
1349
+ const binding = (_a = Scopes.get(node.body).bindings) == null ? void 0 : _a[name];
1350
+ return (binding == null ? void 0 : binding.type) === 0 /* var */ && binding.mutated;
1348
1351
  }
1349
1352
  function hasHoists(node) {
1350
1353
  return node.body ? Scopes.get(node.body).hoists : false;
@@ -1589,12 +1592,11 @@ function trackMutations(node, scope, mutations, parentBlock, parentBlockShadows,
1589
1592
  }
1590
1593
  if (block !== parentBlock && blockMutations.length) {
1591
1594
  for (const { name, start } of blockMutations) {
1592
- if (blockShadows.has(name)) continue;
1595
+ if (start == null || blockShadows.has(name)) continue;
1593
1596
  const binding = resolveWritableVar(scope, name);
1594
1597
  if (binding) {
1595
1598
  binding.mutated = true;
1596
- mutations.push(start);
1597
- (scope.mutatedBindings ||= /* @__PURE__ */ new Set()).add(binding);
1599
+ mutations.push({ start, binding });
1598
1600
  }
1599
1601
  }
1600
1602
  }
@@ -1620,6 +1622,9 @@ function traverse(node, enter) {
1620
1622
  }
1621
1623
  }
1622
1624
  }
1625
+ function byStart(a, b) {
1626
+ return a.start - b.start;
1627
+ }
1623
1628
 
1624
1629
  // src/extractors/script/util/get-component-filename.ts
1625
1630
  import fs from "fs";
@@ -1965,6 +1970,9 @@ var REG_OBJECT_PROPERTY = /^[_$a-z][_$a-z0-9]*$/i;
1965
1970
  var REG_COMMENT_PRAGMA = /\/\/(?:\s*@ts-|\/\s*<)/y;
1966
1971
  var REG_TAG_NAME_IDENTIFIER = /^[A-Z][a-zA-Z0-9_$]+$/;
1967
1972
  var IF_TAG_ALTERNATES = /* @__PURE__ */ new WeakMap();
1973
+ var TAG_ID = /* @__PURE__ */ new WeakMap();
1974
+ var RENDER_VAR = /* @__PURE__ */ new WeakMap();
1975
+ var TEMPLATE_VAR = /* @__PURE__ */ new WeakMap();
1968
1976
  var WROTE_COMMENT = /* @__PURE__ */ new WeakSet();
1969
1977
  var START_OF_FILE = { start: 0, end: 0 };
1970
1978
  var ScriptLang = /* @__PURE__ */ ((ScriptLang2) => {
@@ -1985,14 +1993,11 @@ var ScriptExtractor = class {
1985
1993
  #scriptParser;
1986
1994
  #read;
1987
1995
  #lookup;
1988
- #tagIds = /* @__PURE__ */ new Map();
1989
- #renderIds = /* @__PURE__ */ new Map();
1990
1996
  #scriptLang;
1991
1997
  #ts;
1992
1998
  #runtimeTypes;
1993
- #mutationOffsets;
1999
+ #mutations;
1994
2000
  #tagId = 1;
1995
- #renderId = 1;
1996
2001
  #closeBrackets = [0];
1997
2002
  constructor(opts) {
1998
2003
  const { parsed, lookup, scriptLang } = opts;
@@ -2013,7 +2018,7 @@ var ScriptExtractor = class {
2013
2018
  this.#extractor = new Extractor(parsed);
2014
2019
  this.#scriptParser = new ScriptParser(parsed);
2015
2020
  this.#read = parsed.read.bind(parsed);
2016
- this.#mutationOffsets = crawlProgramScope(this.#parsed, this.#scriptParser);
2021
+ this.#mutations = crawlProgramScope(this.#parsed, this.#scriptParser);
2017
2022
  this.#writeProgram(parsed.program);
2018
2023
  }
2019
2024
  end() {
@@ -2176,7 +2181,7 @@ function ${templateName}() {
2176
2181
  this.#extractor.write(` const input = ${this.#getCastedType(`Input${typeArgsStr}`)};${this.#api === RuntimeAPI.class ? `
2177
2182
  const component = ${this.#getCastedType(`Component${typeArgsStr}`)};
2178
2183
  const state = ${varShared("state")}(component);
2179
- const out = ${varShared("out")};` : ""}
2184
+ const out = ${this.#getCastedType("Marko.Out")};` : ""}
2180
2185
  const $signal = ${this.#getCastedType("AbortSignal")};
2181
2186
  const $global = ${varShared("getGlobal")}(
2182
2187
  // @ts-expect-error We expect the compiler to error because we are checking if the MarkoRun.Context is defined.
@@ -2194,7 +2199,7 @@ function ${templateName}() {
2194
2199
  }
2195
2200
  }
2196
2201
  if (body == null ? void 0 : body.content) {
2197
- this.#writeChildren(program, body.content);
2202
+ this.#writeChildren(body.content);
2198
2203
  if (bindings) {
2199
2204
  if (bindings.vars) {
2200
2205
  for (const name of bindings.vars) {
@@ -2218,7 +2223,7 @@ function ${templateName}() {
2218
2223
  `
2219
2224
  );
2220
2225
  if (didReturn) {
2221
- this.#extractor.write(`return ${varLocal("return")}.return;
2226
+ this.#extractor.write(`return ${varLocal("return")};
2222
2227
  }
2223
2228
  `);
2224
2229
  } else {
@@ -2323,7 +2328,7 @@ constructor(_?: Return) {}
2323
2328
  this.#extractor.copy(returned);
2324
2329
  this.#extractor.write(");\n");
2325
2330
  }
2326
- #writeChildren(parent, children, skipRenderId = false) {
2331
+ #writeChildren(children, skipRenderId = false) {
2327
2332
  var _a, _b;
2328
2333
  const last = children.length - 1;
2329
2334
  let returnTag;
@@ -2339,21 +2344,19 @@ constructor(_?: Return) {}
2339
2344
  break;
2340
2345
  case "if": {
2341
2346
  const alternates = IF_TAG_ALTERNATES.get(child);
2342
- let renderId;
2347
+ let didHoist = false;
2343
2348
  if (!skipRenderId) {
2344
- renderId = this.#getRenderId(child);
2345
- if (!renderId && alternates) {
2349
+ didHoist = hasHoists(child);
2350
+ if (!didHoist && alternates) {
2346
2351
  for (const { node } of alternates) {
2347
- if (renderId = this.#getRenderId(node)) {
2348
- this.#renderIds.set(child, renderId);
2349
- this.#renderIds.delete(node);
2352
+ if (didHoist = hasHoists(node)) {
2350
2353
  break;
2351
2354
  }
2352
2355
  }
2353
2356
  }
2354
- if (renderId) {
2357
+ if (didHoist) {
2355
2358
  this.#extractor.write(
2356
- `const ${varLocal("rendered_" + renderId)} = (() => {
2359
+ `const ${this.#getRenderVar(child, true)} = (() => {
2357
2360
  `
2358
2361
  );
2359
2362
  }
@@ -2364,7 +2367,7 @@ constructor(_?: Return) {}
2364
2367
  ).write(") {\n");
2365
2368
  const ifBody = this.#processBody(child);
2366
2369
  if (ifBody == null ? void 0 : ifBody.content) {
2367
- this.#writeChildren(child, ifBody.content, true);
2370
+ this.#writeChildren(ifBody.content, true);
2368
2371
  const scopeExpr = this.#getScopeExpression(child.body);
2369
2372
  if (scopeExpr) {
2370
2373
  this.#extractor.write(`return {
@@ -2388,7 +2391,7 @@ scope: ${scopeExpr}
2388
2391
  }
2389
2392
  const alternateBody = this.#processBody(node);
2390
2393
  if (alternateBody == null ? void 0 : alternateBody.content) {
2391
- this.#writeChildren(node, alternateBody.content, true);
2394
+ this.#writeChildren(alternateBody.content, true);
2392
2395
  const scopeExpr = this.#getScopeExpression(node.body);
2393
2396
  if (scopeExpr) {
2394
2397
  this.#extractor.write(
@@ -2402,21 +2405,20 @@ scope: ${scopeExpr}
2402
2405
  }
2403
2406
  }
2404
2407
  }
2405
- if (needsAlternate && renderId) {
2408
+ if (needsAlternate && didHoist) {
2406
2409
  this.#extractor.write("\n} else {\nreturn undefined;\n}\n");
2407
2410
  } else {
2408
2411
  this.#extractor.write("\n}\n");
2409
2412
  }
2410
- if (renderId) {
2413
+ if (didHoist) {
2411
2414
  this.#extractor.write("\n})();\n");
2412
2415
  }
2413
2416
  break;
2414
2417
  }
2415
2418
  case "for": {
2416
- const renderId = this.#getRenderId(child);
2417
- if (renderId) {
2419
+ if (hasHoists(child)) {
2418
2420
  this.#extractor.write(
2419
- `const ${varLocal("rendered_" + renderId)} = `
2421
+ `const ${this.#getRenderVar(child, true)} = `
2420
2422
  );
2421
2423
  }
2422
2424
  this.#extractor.write(
@@ -2433,7 +2435,7 @@ scope: ${scopeExpr}
2433
2435
  this.#extractor.write("\n) => {\n");
2434
2436
  const body = this.#processBody(child);
2435
2437
  if (body == null ? void 0 : body.content) {
2436
- this.#writeChildren(child, body.content);
2438
+ this.#writeChildren(body.content);
2437
2439
  }
2438
2440
  this.#writeReturn(void 0, (body == null ? void 0 : body.content) && child.body);
2439
2441
  if (body == null ? void 0 : body.content) {
@@ -2449,7 +2451,7 @@ scope: ${scopeExpr}
2449
2451
  ).write("\n) {\n");
2450
2452
  const body = this.#processBody(child);
2451
2453
  if (body == null ? void 0 : body.content) {
2452
- this.#writeChildren(child, body.content);
2454
+ this.#writeChildren(body.content);
2453
2455
  this.#endChildren();
2454
2456
  }
2455
2457
  this.#extractor.write("\n}\n");
@@ -2468,59 +2470,29 @@ scope: ${scopeExpr}
2468
2470
  break;
2469
2471
  }
2470
2472
  }
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
- }
2473
+ if (returnTag) {
2474
+ this.#extractor.write(
2475
+ `var ${varLocal("return")} = ${varShared("returnTag")}(`
2476
+ );
2477
+ this.#writeTagInputObject(returnTag);
2478
+ this.#extractor.write(");\n");
2507
2479
  }
2508
2480
  return returnTag !== void 0;
2509
2481
  }
2510
2482
  #writeTag(tag) {
2511
2483
  const tagName = tag.nameText;
2512
- const renderId = this.#getRenderId(tag);
2513
2484
  const def = tagName ? this.#lookup.getTag(tagName) : void 0;
2514
2485
  const importPath = resolveTagImport(this.#filename, def);
2515
2486
  const isHTML = !importPath && (def == null ? void 0 : def.html);
2516
- let tagIdentifier;
2517
2487
  let isTemplate = false;
2488
+ let renderVar;
2489
+ let templateVar;
2518
2490
  if (!def || importPath) {
2519
2491
  const isIdentifier = tagName && REG_TAG_NAME_IDENTIFIER.test(tagName);
2520
2492
  const isMarkoFile = importPath == null ? void 0 : importPath.endsWith(".marko");
2521
2493
  if (isIdentifier || isMarkoFile || !importPath) {
2522
- tagIdentifier = varLocal("tag_" + this.#ensureTagId(tag));
2523
- this.#extractor.write(`const ${tagIdentifier} = (
2494
+ templateVar = this.#getTemplateVar(tag, true);
2495
+ this.#extractor.write(`const ${templateVar} = (
2524
2496
  `);
2525
2497
  if (isIdentifier) {
2526
2498
  if (importPath) {
@@ -2540,22 +2512,23 @@ scope: ${scopeExpr}
2540
2512
  }
2541
2513
  this.#extractor.write("\n);\n");
2542
2514
  } else {
2543
- tagIdentifier = varShared("missingTag");
2515
+ templateVar = varShared("missingTag");
2544
2516
  }
2545
2517
  const attrTagTree = this.#getAttrTagTree(tag);
2546
2518
  if (attrTagTree) {
2547
- this.#writeAttrTagTree(attrTagTree, tagIdentifier);
2519
+ this.#writeAttrTagTree(attrTagTree, templateVar);
2548
2520
  this.#extractor.write(";\n");
2549
2521
  }
2550
2522
  }
2551
- if (renderId) {
2552
- this.#extractor.write(`const ${varLocal("rendered_" + renderId)} = `);
2523
+ if (!isHTML && tag.var || hasHoists(tag)) {
2524
+ renderVar = this.#getRenderVar(tag, true);
2525
+ this.#extractor.write(`const ${renderVar} = `);
2553
2526
  }
2554
2527
  if (isHTML) {
2555
2528
  this.#extractor.write(`${varShared("renderNativeTag")}("`).copy(isEmptyRange2(tag.name) ? tagName : tag.name).write('")');
2556
- } else if (tagIdentifier) {
2529
+ } else if (templateVar) {
2557
2530
  this.#extractor.write(
2558
- `${varShared(isTemplate ? "renderTemplate" : "renderDynamicTag")}(${tagIdentifier})`
2531
+ `${varShared(isTemplate ? "renderTemplate" : "renderDynamicTag")}(${templateVar})`
2559
2532
  );
2560
2533
  } else {
2561
2534
  this.#extractor.write(varShared("missingTag"));
@@ -2567,14 +2540,26 @@ scope: ${scopeExpr}
2567
2540
  }
2568
2541
  this.#writeTagInputObject(tag);
2569
2542
  this.#extractor.write(");\n");
2570
- if (renderId && tag.var) {
2543
+ if (tag.var) {
2571
2544
  this.#extractor.write(`{const `);
2572
2545
  this.#closeBrackets[this.#closeBrackets.length - 1]++;
2573
- this.#copyWithMutationsReplaced(tag.var.value);
2574
- this.#extractor.write(
2575
- ` = ${varLocal("rendered_" + renderId)}.return.${ATTR_UNAMED2};
2546
+ if (renderVar) {
2547
+ const mutatedVars = getMutatedVars(tag);
2548
+ this.#copyWithMutationsReplaced(tag.var.value);
2549
+ this.#extractor.write(` = ${renderVar}.return.${ATTR_UNAMED2};
2550
+ `);
2551
+ if (mutatedVars) {
2552
+ for (const binding of mutatedVars) {
2553
+ this.#extractor.write(
2554
+ `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
2555
  `
2577
- );
2556
+ );
2557
+ }
2558
+ }
2559
+ } else if (isHTML) {
2560
+ this.#extractor.copy(tag.var.value).write(` = ${varShared("el")}(${JSON.stringify(def.name)});
2561
+ `);
2562
+ }
2578
2563
  }
2579
2564
  }
2580
2565
  #endChildren() {
@@ -2670,8 +2655,10 @@ scope: ${scopeExpr}
2670
2655
  const valueLiteral = this.#read(boundRange.value);
2671
2656
  this.#extractor.copy(boundRange.value).copy(boundRange.types).write(`
2672
2657
  )${SEP_COMMA_NEW_LINE}"`).copy(defaultMapPosition).copy(name).write(
2673
- `Change"(_${valueLiteral}) {
2674
- ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}`
2658
+ `Change"(
2659
+ // @ts-ignore
2660
+ _${valueLiteral}) {
2661
+ ${isMutatedVar(tag.parent, valueLiteral) ? varLocal(`change__${valueLiteral}.`) : ""}`
2675
2662
  ).copy(boundRange.value).write(`= _${valueLiteral};
2676
2663
  }`);
2677
2664
  } else if (boundRange.member.computed) {
@@ -2787,8 +2774,8 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
2787
2774
  this.#writeTagNameComment(firstAttrTag);
2788
2775
  this.#extractor.write("]: ");
2789
2776
  if (isRepeated) {
2790
- const tagId = this.#tagIds.get(firstAttrTag.owner);
2791
- if (tagId) {
2777
+ const templateVar = this.#getTemplateVar(firstAttrTag.owner);
2778
+ if (templateVar) {
2792
2779
  let accessor = `"${name}"`;
2793
2780
  let curTag = firstAttrTag.parent;
2794
2781
  while (curTag) {
@@ -2800,7 +2787,7 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
2800
2787
  curTag = curTag.parent;
2801
2788
  }
2802
2789
  this.#extractor.write(
2803
- `${varShared("attrTagFor")}(${varLocal("tag_" + tagId)},${accessor})([`
2790
+ `${varShared("attrTagFor")}(${templateVar},${accessor})([`
2804
2791
  );
2805
2792
  } else {
2806
2793
  this.#extractor.write(`${varShared("attrTag")}([`);
@@ -2924,13 +2911,11 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
2924
2911
  if (tag.params || hasBodyContent) {
2925
2912
  this.#extractor.write("[");
2926
2913
  if (this.#interop) {
2927
- const tagId = this.#tagIds.get(
2914
+ const templateVar = this.#getTemplateVar(
2928
2915
  tag.type === 16 /* AttrTag */ ? tag.owner : tag
2929
2916
  );
2930
- if (tagId) {
2931
- this.#extractor.write(
2932
- `${varShared("contentFor")}(${varLocal("tag_" + tagId)})`
2933
- );
2917
+ if (templateVar) {
2918
+ this.#extractor.write(`${varShared("contentFor")}(${templateVar})`);
2934
2919
  } else {
2935
2920
  this.#extractor.write(varShared("content"));
2936
2921
  }
@@ -2951,16 +2936,13 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
2951
2936
  }
2952
2937
  let didReturn = false;
2953
2938
  if (body == null ? void 0 : body.content) {
2954
- didReturn = this.#writeChildren(tag, body.content);
2939
+ didReturn = this.#writeChildren(body.content);
2955
2940
  }
2956
2941
  if (!tag.params) {
2957
2942
  this.#extractor.write(`return () => {
2958
2943
  `);
2959
2944
  }
2960
- this.#writeReturn(
2961
- didReturn ? `${varLocal("return")}.return` : void 0,
2962
- tag.body
2963
- );
2945
+ this.#writeReturn(didReturn ? varLocal("return") : void 0, tag.body);
2964
2946
  if (body == null ? void 0 : body.content) {
2965
2947
  this.#endChildren();
2966
2948
  }
@@ -2985,7 +2967,7 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
2985
2967
  return this.#scriptLang === "ts" /* ts */ ? `${varShared("any")} as ${type}` : `/** @type {${type}} */(${varShared("any")})`;
2986
2968
  }
2987
2969
  #copyWithMutationsReplaced(range) {
2988
- const mutations = this.#mutationOffsets;
2970
+ const mutations = this.#mutations;
2989
2971
  if (!mutations) return this.#extractor.copy(range);
2990
2972
  const len = mutations.length;
2991
2973
  let curOffset = range.start;
@@ -2994,23 +2976,23 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
2994
2976
  let maxIndex = len;
2995
2977
  while (minIndex < maxIndex) {
2996
2978
  const midIndex = minIndex + maxIndex >>> 1;
2997
- if (mutations[midIndex] >= curOffset) {
2979
+ if (mutations[midIndex].start >= curOffset) {
2998
2980
  maxIndex = midIndex;
2999
2981
  } else {
3000
2982
  minIndex = midIndex + 1;
3001
2983
  }
3002
2984
  }
3003
- const nextOffset = maxIndex === len ? range.end : mutations[maxIndex];
3004
- if (nextOffset >= range.end) {
2985
+ const mutation = maxIndex !== len && mutations[maxIndex];
2986
+ if (!mutation || mutation.start >= range.end) {
3005
2987
  this.#extractor.copy({
3006
2988
  start: curOffset,
3007
2989
  end: range.end
3008
2990
  });
3009
2991
  return;
3010
2992
  }
3011
- this.#extractor.copy({ start: curOffset, end: nextOffset });
3012
- this.#extractor.write(`${varLocal("return")}.mutate.`);
3013
- curOffset = nextOffset;
2993
+ this.#extractor.copy({ start: curOffset, end: mutation.start });
2994
+ this.#extractor.write(`${varLocal(`change__${mutation.binding.name}`)}.`);
2995
+ curOffset = mutation.start;
3014
2996
  minIndex = maxIndex + 1;
3015
2997
  } while (true);
3016
2998
  }
@@ -3162,7 +3144,7 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
3162
3144
  if (body) {
3163
3145
  if (body.content) {
3164
3146
  this.#extractor.write("(() => {\n");
3165
- this.#writeChildren(tag, body.content);
3147
+ this.#writeChildren(body.content);
3166
3148
  this.#extractor.write("return ");
3167
3149
  }
3168
3150
  this.#extractor.write("{\n");
@@ -3264,13 +3246,27 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
3264
3246
  return tag.name.expressions[0].value;
3265
3247
  }
3266
3248
  }
3267
- #getRenderId(tag) {
3268
- let renderId = this.#renderIds.get(tag);
3269
- if (!renderId && (tag.var || hasHoists(tag))) {
3270
- renderId = this.#renderId++;
3271
- this.#renderIds.set(tag, renderId);
3249
+ #getRenderVar(tag, declared = false) {
3250
+ let id = RENDER_VAR.get(tag);
3251
+ if (!id && declared) {
3252
+ RENDER_VAR.set(tag, id = varLocal("rendered_" + this.#getTagId(tag)));
3253
+ }
3254
+ return id;
3255
+ }
3256
+ #getTemplateVar(tag, declared = false) {
3257
+ let id = TEMPLATE_VAR.get(tag);
3258
+ if (!id && declared) {
3259
+ TEMPLATE_VAR.set(tag, id = varLocal("tag_" + this.#getTagId(tag)));
3260
+ }
3261
+ return id;
3262
+ }
3263
+ #getTagId(tag) {
3264
+ let id = TAG_ID.get(tag);
3265
+ if (id === void 0) {
3266
+ id = this.#tagId++;
3267
+ TAG_ID.set(tag, id);
3272
3268
  }
3273
- return renderId;
3269
+ return id;
3274
3270
  }
3275
3271
  #getScopeExpression(body) {
3276
3272
  const sources = getHoistSources(body);
@@ -3278,39 +3274,27 @@ ${isMutatedVar(tag.parent, valueLiteral) ? `${varLocal("return")}.mutate.` : ""}
3278
3274
  return sources ? `{ ${hoists ? `...${hoists}, ` : ""}${sources.join(SEP_COMMA_SPACE)} }` : hoists;
3279
3275
  }
3280
3276
  #getBodyHoistScopeExpression(body) {
3281
- let hoistIds;
3277
+ let hoistVars;
3282
3278
  if (body) {
3283
3279
  for (const child of body) {
3284
- const renderId = child.type === 1 /* Tag */ && this.#renderIds.get(child);
3285
- if (renderId && (!child.var || hasHoists(child))) {
3286
- if (hoistIds) {
3287
- hoistIds.push(renderId);
3288
- } else {
3289
- hoistIds = [renderId];
3280
+ if (child.type === 1 /* Tag */) {
3281
+ const renderVar = this.#getRenderVar(child);
3282
+ if (renderVar && (!child.var || hasHoists(child))) {
3283
+ if (hoistVars) {
3284
+ hoistVars.push(renderVar);
3285
+ } else {
3286
+ hoistVars = [renderVar];
3287
+ }
3290
3288
  }
3291
3289
  }
3292
3290
  }
3293
3291
  }
3294
- if (hoistIds) {
3295
- if (hoistIds.length === 1) {
3296
- return `${varShared("readScope")}(${varLocal("rendered_" + hoistIds[0])})`;
3292
+ if (hoistVars) {
3293
+ if (hoistVars.length === 1) {
3294
+ return `${varShared("readScope")}(${hoistVars[0]})`;
3297
3295
  }
3298
- let result = `${varShared("readScopes")}({ `;
3299
- let sep = "";
3300
- for (const renderId of hoistIds) {
3301
- result += sep + `${varLocal("rendered_" + renderId)}`;
3302
- sep = SEP_COMMA_SPACE;
3303
- }
3304
- return result + " })";
3305
- }
3306
- }
3307
- #ensureTagId(tag) {
3308
- let tagId = this.#tagIds.get(tag);
3309
- if (!tagId) {
3310
- tagId = this.#tagId++;
3311
- this.#tagIds.set(tag, tagId);
3296
+ return `${varShared("readScopes")}({ ${hoistVars.join(SEP_COMMA_SPACE)} })`;
3312
3297
  }
3313
- return tagId;
3314
3298
  }
3315
3299
  #getNamedAttrModifierIndex(attr) {
3316
3300
  const start = attr.name.start + 1;
@@ -17,7 +17,6 @@ declare global {
17
17
  namespace _ {
18
18
  export const voidReturn: MarkoReturn<void>;
19
19
  export const scope: unique symbol;
20
- export const out: Marko.Out;
21
20
  export const never: never;
22
21
  export const any: any;
23
22
 
@@ -42,6 +41,10 @@ declare global {
42
41
 
43
42
  export const content: DefaultBodyContentKey;
44
43
 
44
+ export function el<Name extends string>(
45
+ name: Name,
46
+ ): Marko.NativeTags[Name]["return"]["value"];
47
+
45
48
  export function contentFor<Name>(
46
49
  tag: Name,
47
50
  ): [0] extends [1 & Name]
@@ -105,8 +108,7 @@ declare global {
105
108
  : never
106
109
  : never;
107
110
  }[keyof Rendered]
108
- > &
109
- Record<any, never>;
111
+ >;
110
112
 
111
113
  export function readScope<Value>(
112
114
  value: Value,
@@ -122,22 +124,23 @@ declare global {
122
124
  ? never
123
125
  : Scope
124
126
  : never
125
- > &
126
- Record<any, never>;
127
-
128
- export function mutable<Lookup>(lookup: Lookup): UnionToIntersection<
129
- Lookup extends readonly (infer Item)[]
130
- ? Item extends
131
- | readonly [infer LocalName extends string, infer Data]
132
- | readonly [infer LocalName, infer SourceName, infer Data]
133
- ? Data extends {
134
- [K in `${SourceName extends string
135
- ? SourceName
136
- : LocalName}Change`]: (value: infer V, ...args: any[]) => any;
137
- }
138
- ? { [K in LocalName]: V }
139
- : { readonly [K in LocalName]: unknown }
140
- : never
127
+ >;
128
+
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 }
141
144
  : never
142
145
  >;
143
146
 
@@ -513,10 +516,6 @@ type BodyParamsWithDefault<Body extends AnyMarkoBody> =
513
516
  : Params
514
517
  : never;
515
518
 
516
- type Scopes<Input> = [0] extends [1 & Input]
517
- ? never
518
- : MergeScopes<FlatScopes<Input>>;
519
-
520
519
  type ComponentEventHandlers<Component extends Marko.Component> = {
521
520
  [K in Exclude<
522
521
  keyof Component,
@@ -527,7 +526,7 @@ type ComponentEventHandlers<Component extends Marko.Component> = {
527
526
  >]: Component[K] extends (...args: any) => any ? Component[K] : never;
528
527
  };
529
528
 
530
- type FlatScopes<Input> = [0] extends [1 & Input]
529
+ type Scopes<Input> = [0] extends [1 & Input]
531
530
  ? never
532
531
  :
533
532
  | (Input[("content" | "renderBody") & keyof Input] extends infer Prop
@@ -539,7 +538,7 @@ type FlatScopes<Input> = [0] extends [1 & Input]
539
538
  ? Prop extends { [Symbol.iterator]: any }
540
539
  ? Prop extends readonly any[]
541
540
  ? never
542
- : FlatScopes<Prop>
541
+ : Scopes<Prop>
543
542
  : never
544
543
  : never);
545
544
 
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.43",
4
+ "version": "2.5.45",
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"