ember-estree 0.6.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ember-estree",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "ESTree generator for gjs and gts file used by ember",
5
5
  "keywords": [
6
6
  "AST",
@@ -38,6 +38,7 @@
38
38
  },
39
39
  "devDependencies": {
40
40
  "@tsconfig/node-lts": "^22.0.2",
41
+ "@typescript-eslint/parser": "^8.59.0",
41
42
  "mitata": "^1.0.34",
42
43
  "oxfmt": "^0.40.0",
43
44
  "oxlint": "^1.55.0",
package/src/parse.js CHANGED
@@ -13,7 +13,29 @@ import { parseSync } from "oxc-parser";
13
13
  import { Preprocessor } from "content-tag";
14
14
  import { walk } from "zimmerframe";
15
15
 
16
- import { processTemplate, DocumentLines, glimmerVisitorKeys } from "./transforms.js";
16
+ import { processTemplate, DocumentLines, glimmerVisitorKeys, setParent } from "./transforms.js";
17
+
18
+ // Swap `oldNode` for `newNode` in whichever slot of `parent` currently holds it.
19
+ // Used to splice a GlimmerTemplate directly into the outer AST without
20
+ // allocating new ancestor objects — keeps WeakMap-keyed data (scope manager,
21
+ // esTreeNodeToTSNodeMap) attached to the existing nodes.
22
+ function replaceInParent(parent, oldNode, newNode) {
23
+ for (const key of Object.keys(parent)) {
24
+ const v = parent[key];
25
+ if (v === oldNode) {
26
+ parent[key] = newNode;
27
+ return true;
28
+ }
29
+ if (Array.isArray(v)) {
30
+ const idx = v.indexOf(oldNode);
31
+ if (idx !== -1) {
32
+ v[idx] = newNode;
33
+ return true;
34
+ }
35
+ }
36
+ }
37
+ return false;
38
+ }
17
39
 
18
40
  const preprocessor = new Preprocessor();
19
41
 
@@ -112,8 +134,11 @@ export function toTree(source, options = {}) {
112
134
  ? new Map(parseResults.map((r) => [r.range.startUtf16Codepoint, r]))
113
135
  : null;
114
136
 
115
- // Process a matched placeholder node: create Glimmer AST and tokens
116
- function processPlaceholder(parseResult) {
137
+ // Process a matched placeholder node: create Glimmer AST and tokens.
138
+ // `placeholderNode` is the original JS/TS node being swapped out; we stash
139
+ // it on templateInfos so consumers can forward its parser-services mapping
140
+ // (e.g. esTreeNodeToTSNodeMap) onto the GlimmerTemplate that replaces it.
141
+ function processPlaceholder(parseResult, placeholderNode) {
117
142
  let templateContent = parseResult.contents;
118
143
  let contentRange = [
119
144
  parseResult.contentRange.startUtf16Codepoint,
@@ -159,7 +184,7 @@ export function toTree(source, options = {}) {
159
184
  ];
160
185
  }
161
186
 
162
- templateInfos.push({ utf16Range: fullRange, ast });
187
+ templateInfos.push({ utf16Range: fullRange, ast, placeholder: placeholderNode });
163
188
  return ast;
164
189
  }
165
190
 
@@ -186,21 +211,23 @@ export function toTree(source, options = {}) {
186
211
  if (hasTemplates && PLACEHOLDER_TYPES.has(node.type)) {
187
212
  const parseResult = matchPlaceholder(node);
188
213
  if (parseResult) {
189
- const ast = processPlaceholder(parseResult);
190
- // Zimmerframe treats a visitor that returns a node as having
191
- // taken responsibility for the subtree it splices the result
192
- // in but does NOT descend into it. So when any handlers are
193
- // configured we re-enter the walk manually via `visit()` to
194
- // dispatch them across the Glimmer nodes. With no handlers the
195
- // walk would be pure overhead, so just return the subtree.
196
- //
197
- // Pass `state` (the placeholder's inherited parent context) so the
198
- // Glimmer root's parentPath reflects its true JS parent. The
214
+ const ast = processPlaceholder(parseResult, node);
215
+ // Splice in place: write the GlimmerTemplate directly into the
216
+ // parent's slot instead of returning it from the visitor. Returning
217
+ // would trigger zimmerframe's apply_mutations, which shallow-clones
218
+ // every ancestor up to the root orphaning any WeakMap-keyed data
219
+ // held by custom parsers (scope manager, esTreeNodeToTSNodeMap).
220
+ // In-place mutation preserves node identity for all ancestors.
221
+ const parent = state?.parentPath?.node ?? null;
222
+ if (parent) replaceInParent(parent, node, ast);
223
+ setParent(ast, parent);
224
+ // Dispatch visitors on the Glimmer subtree. We pass `state` so the
225
+ // Glimmer root's parentPath reflects its true JS parent — the
199
226
  // placeholder (TemplateLiteral / StaticBlock) is an internal
200
- // artifact the GlimmerTemplate logically lives where the
201
- // placeholder was, so its parent is e.g. VariableDeclarator or
202
- // ClassBody, not the placeholder itself.
203
- return hasVisitors ? visit(ast, state) : ast;
227
+ // artifact. Not returning anything keeps apply_mutations from
228
+ // firing up the ancestor chain.
229
+ if (hasVisitors) visit(ast, state);
230
+ return;
204
231
  }
205
232
  }
206
233
 
@@ -240,8 +267,11 @@ export function toTree(source, options = {}) {
240
267
  // original source byte-for-byte across JS and Glimmer regions.
241
268
  //
242
269
  // Tokens are sorted by range, so use binary search for O(log n) lookup.
270
+ // Only splice if the caller asked for tokens — otherwise `ti.ast.tokens`
271
+ // wasn't populated by processPlaceholder, and a custom parser may still
272
+ // have returned its own token stream we shouldn't touch.
243
273
  const astRoot = result.ast.program || result.ast;
244
- if (astRoot.tokens) {
274
+ if (generateTokens && astRoot.tokens) {
245
275
  for (const ti of templateInfos) {
246
276
  const [tStart, tEnd] = ti.utf16Range;
247
277
  const tokens = astRoot.tokens;
package/src/transforms.js CHANGED
@@ -64,7 +64,7 @@ export const glimmerVisitorKeys = (() => {
64
64
  // Block: blockParams
65
65
  const _desc = { value: undefined, configurable: true, enumerable: true, writable: true };
66
66
  const _parentDesc = { value: null, configurable: true, enumerable: false, writable: true };
67
- function setParent(node, parent) {
67
+ export function setParent(node, parent) {
68
68
  _parentDesc.value = parent;
69
69
  Object.defineProperty(node, "parent", _parentDesc);
70
70
  }