@bamboocss/vite 1.49.0 → 1.50.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.
@@ -4,7 +4,7 @@ import { dirname, relative, resolve } from "node:path";
4
4
  import MagicString from "magic-string";
5
5
  import { resolveTsPathPattern } from "@bamboocss/config/ts-path";
6
6
  import { box, maybeBoxNode } from "@bamboocss/extractor";
7
- import { Node, SyntaxKind, VariableDeclarationKind, ts } from "ts-morph";
7
+ import { Node, SyntaxKind, childOf, forEachDescendant, getAliasNode, getDefaultImport, getDescendantsOfKind, getExportDeclarations, getFirstAncestor, getFirstAncestorByKind, getImportDeclarations, getModuleSpecifierValue, getName, getNamedExports, getNamedImports, getNamespaceExport, getNamespaceImport, isStarExport, isTypeOnly, literalValueOf, nameNodeOf, pathOf, stringLiteralValue, ts } from "@bamboocss/ts-ast";
8
8
  //#region src/fold-analysis.ts
9
9
  /**
10
10
  * Nodes that *compose* a value out of their children rather than computing one.
@@ -43,9 +43,9 @@ const isBindingElementDefault = (node) => {
43
43
  if (node && Node.isCallExpression(node)) return false;
44
44
  let current = node;
45
45
  while (current) {
46
- const parent = current.getParent();
46
+ const parent = current.parent;
47
47
  if (!parent) return false;
48
- if (Node.isBindingElement(parent)) return parent.getInitializer() === current;
48
+ if (Node.isBindingElement(parent)) return parent.initializer === current;
49
49
  if (!composesValue(parent)) return false;
50
50
  current = parent;
51
51
  }
@@ -72,7 +72,7 @@ const isFromBindingDefault = (node) => {
72
72
  const own = node.getNode?.();
73
73
  if (isBindingElementDefault(own)) return true;
74
74
  for (const entry of node.getStack?.() ?? []) {
75
- if (!Node.isBindingElement(entry) || !entry.getInitializer()) continue;
75
+ if (!Node.isBindingElement(entry) || !entry.initializer) continue;
76
76
  if (own && contains(entry, own)) continue;
77
77
  return true;
78
78
  }
@@ -115,7 +115,7 @@ const isStaticBox = (node, seen = /* @__PURE__ */ new Set()) => {
115
115
  * surface. Recognising fewer wrappers than it does is not a cosmetic difference: an
116
116
  * unrecognised one leaves an object literal wrapped, and the object checks below skip it.
117
117
  */
118
- const unwrapExpression = (node) => Node.isAsExpression(node) || Node.isParenthesizedExpression(node) || Node.isNonNullExpression(node) || Node.isTypeAssertion(node) || Node.isSatisfiesExpression(node) ? unwrapExpression(node.getExpression()) : node;
118
+ const unwrapExpression = (node) => Node.isAsExpression(node) || Node.isParenthesizedExpression(node) || Node.isNonNullExpression(node) || Node.isTypeAssertion(node) || Node.isSatisfiesExpression(node) ? unwrapExpression(node.expression) : node;
119
119
  /**
120
120
  * Mirrors the parser's evaluator environment, so re-boxing an operand here gets the same
121
121
  * answer the extraction did. Left to its default, ts-evaluator presets to `NODE` and
@@ -148,7 +148,7 @@ const resolvesExactly = (node) => {
148
148
  */
149
149
  const isWrittenHere = (node) => {
150
150
  const inner = unwrapExpression(node);
151
- return Node.isStringLiteral(inner) || Node.isNumericLiteral(inner) || Node.isNoSubstitutionTemplateLiteral(inner) || Node.isObjectLiteralExpression(inner) || Node.isArrayLiteralExpression(inner) || Node.isTrueLiteral(inner) || Node.isFalseLiteral(inner) || inner.getKind() === SyntaxKind.NullKeyword || Node.isPrefixUnaryExpression(inner) && Node.isNumericLiteral(inner.getOperand());
151
+ return Node.isStringLiteral(inner) || Node.isNumericLiteral(inner) || Node.isNoSubstitutionTemplateLiteral(inner) || Node.isObjectLiteralExpression(inner) || Node.isArrayLiteralExpression(inner) || Node.isTrueLiteral(inner) || Node.isFalseLiteral(inner) || inner.kind === SyntaxKind.NullKeyword || Node.isPrefixUnaryExpression(inner) && Node.isNumericLiteral(inner.operand);
152
152
  };
153
153
  /** An inline value's truthiness, which its box carries directly. */
154
154
  const isTruthy = (boxNode) => box.isLiteral(boxNode) ? Boolean(boxNode.value) : box.isMap(boxNode) || box.isArray(boxNode) || box.isObject(boxNode);
@@ -166,15 +166,15 @@ const isTruthy = (boxNode) => box.isLiteral(boxNode) ? Boolean(boxNode.value) :
166
166
  * be established is that the left is the side that wins.
167
167
  */
168
168
  const decidedAtBuildTime = (node) => {
169
- if (Node.isConditionalExpression(node)) return resolvesExactly(node.getWhenTrue()) && resolvesExactly(node.getWhenFalse());
170
- const operator = node.getOperatorToken().getKind();
169
+ if (Node.isConditionalExpression(node)) return resolvesExactly(node.whenTrue) && resolvesExactly(node.whenFalse);
170
+ const operator = node.operatorToken.kind;
171
171
  if (!SHORT_CIRCUIT.includes(operator)) return false;
172
- if (!isWrittenHere(node.getLeft())) return false;
173
- const left = rebox(node.getLeft());
172
+ if (!isWrittenHere(node.left)) return false;
173
+ const left = rebox(node.left);
174
174
  if (!left) return false;
175
175
  if (operator === SyntaxKind.BarBarToken) return isTruthy(left);
176
176
  if (operator === SyntaxKind.QuestionQuestionToken) return !box.isLiteral(left) || left.value != null;
177
- return isTruthy(left) ? resolvesExactly(node.getRight()) : true;
177
+ return isTruthy(left) ? resolvesExactly(node.right) : true;
178
178
  };
179
179
  const SHORT_CIRCUIT = [
180
180
  SyntaxKind.AmpersandAmpersandToken,
@@ -202,7 +202,7 @@ const COLLAPSED_BINARY = new Set([
202
202
  SyntaxKind.InKeyword,
203
203
  SyntaxKind.InstanceOfKeyword
204
204
  ]);
205
- const isCollapsedBinary = (node) => Node.isBinaryExpression(node) && COLLAPSED_BINARY.has(node.getOperatorToken().getKind());
205
+ const isCollapsedBinary = (node) => Node.isBinaryExpression(node) && COLLAPSED_BINARY.has(node.operatorToken.kind);
206
206
  /**
207
207
  * Does the extracted box account for every property the source declares?
208
208
  *
@@ -229,14 +229,14 @@ const isCollapsedBinary = (node) => Node.isBinaryExpression(node) && COLLAPSED_B
229
229
  * expression — reading an initializer that is not there reports the property as having no
230
230
  * source, and everything hidden behind the name goes unchecked.
231
231
  */
232
- const valueOf = (property) => Node.isPropertyAssignment(property) ? property.getInitializer() : Node.isShorthandPropertyAssignment(property) ? property.getNameNode() : void 0;
232
+ const valueOf = (property) => Node.isPropertyAssignment(property) ? property.initializer : Node.isShorthandPropertyAssignment(property) ? property.name : void 0;
233
233
  const accountsForSource = (node, boxNode) => {
234
234
  if (!node) return true;
235
235
  const unwrapped = unwrapExpression(node);
236
236
  if ((Node.isConditionalExpression(unwrapped) || isCollapsedBinary(unwrapped)) && !box.isConditional(boxNode) && !decidedAtBuildTime(unwrapped)) return false;
237
237
  if (Node.isArrayLiteralExpression(unwrapped)) {
238
238
  if (!box.isArray(boxNode)) return false;
239
- const elements = unwrapped.getElements();
239
+ const elements = unwrapped.elements;
240
240
  if (elements.length !== boxNode.value.length) return false;
241
241
  return elements.every((element, index) => accountsForSource(element, boxNode.value[index]));
242
242
  }
@@ -245,9 +245,9 @@ const accountsForSource = (node, boxNode) => {
245
245
  return !origin || origin === unwrapped || !Node.isObjectLiteralExpression(origin) ? true : accountsForSource(origin, boxNode);
246
246
  }
247
247
  if (!box.isMap(boxNode)) return false;
248
- for (const property of unwrapped.getProperties()) {
248
+ for (const property of unwrapped.properties) {
249
249
  if (Node.isSpreadAssignment(property)) {
250
- const expression = unwrapExpression(property.getExpression());
250
+ const expression = unwrapExpression(property.expression);
251
251
  if (Node.isObjectLiteralExpression(expression)) continue;
252
252
  const walked = boxNode.resolvedSpreads?.find((entry) => entry.node === expression);
253
253
  if (!walked) return false;
@@ -256,9 +256,9 @@ const accountsForSource = (node, boxNode) => {
256
256
  }
257
257
  if (Node.isMethodDeclaration(property) || Node.isGetAccessorDeclaration(property) || Node.isSetAccessorDeclaration(property)) return false;
258
258
  if (!Node.isPropertyAssignment(property) && !Node.isShorthandPropertyAssignment(property)) return false;
259
- const nameNode = property.getNameNode();
259
+ const nameNode = property.name;
260
260
  if (Node.isComputedPropertyName(nameNode)) return false;
261
- const key = Node.isStringLiteral(nameNode) || Node.isNumericLiteral(nameNode) ? String(nameNode.getLiteralValue()) : nameNode.getText();
261
+ const key = Node.isStringLiteral(nameNode) || Node.isNumericLiteral(nameNode) ? String(literalValueOf(nameNode)) : nameNode.getText();
262
262
  const value = valueOf(property);
263
263
  if (value && Node.isIdentifier(value) && value.getText() === "undefined") continue;
264
264
  if (!boxNode.value.has(key)) return false;
@@ -309,19 +309,19 @@ const collectModuleScopeNames = (sourceFile) => {
309
309
  const addBinding = (node) => {
310
310
  if (!node) return;
311
311
  if (Node.isObjectBindingPattern(node) || Node.isArrayBindingPattern(node)) {
312
- for (const element of node.getElements()) if (Node.isBindingElement(element)) addBinding(element.getNameNode());
312
+ for (const element of node.elements) if (Node.isBindingElement(element)) addBinding(element.name);
313
313
  return;
314
314
  }
315
315
  if (Node.isIdentifier(node)) names.add(node.getText());
316
316
  };
317
317
  const addDeclarations = (list) => {
318
318
  if (Node.isVariableStatement(list)) {
319
- for (const declaration of list.getDeclarations()) addBinding(declaration.getNameNode());
319
+ for (const declaration of list.declarationList.declarations) addBinding(declaration.name);
320
320
  return;
321
321
  }
322
- if (Node.isVariableDeclarationList(list)) for (const declaration of list.getDeclarations()) addBinding(declaration.getNameNode());
322
+ if (Node.isVariableDeclarationList(list)) for (const declaration of list.declarations) addBinding(declaration.name);
323
323
  };
324
- const isVar = (node) => (Node.isVariableStatement(node) || Node.isVariableDeclarationList(node)) && node.getDeclarationKind() === VariableDeclarationKind.Var;
324
+ const isVar = (node) => (Node.isVariableStatement(node) || Node.isVariableDeclarationList(node)) && (childOf(node, "declarationList")?.flags ?? 0) === 0;
325
325
  /**
326
326
  * `var` is scoped to the enclosing *function*, not the enclosing block, so one written
327
327
  * inside any statement at the top level still binds at module scope. Walking only the
@@ -336,61 +336,61 @@ const collectModuleScopeNames = (sourceFile) => {
336
336
  return;
337
337
  }
338
338
  if (Node.isBlock(node)) {
339
- for (const statement of node.getStatements()) addHoistedVars(statement);
339
+ for (const statement of node.statements) addHoistedVars(statement);
340
340
  return;
341
341
  }
342
342
  if (Node.isIfStatement(node)) {
343
- addHoistedVars(node.getThenStatement());
344
- const otherwise = node.getElseStatement();
343
+ addHoistedVars(node.thenStatement);
344
+ const otherwise = node.elseStatement;
345
345
  if (otherwise) addHoistedVars(otherwise);
346
346
  return;
347
347
  }
348
348
  if (Node.isForStatement(node)) {
349
- const initializer = node.getInitializer();
349
+ const initializer = node.initializer;
350
350
  if (initializer) addHoistedVars(initializer);
351
- addHoistedVars(node.getStatement());
351
+ addHoistedVars(node.statement);
352
352
  return;
353
353
  }
354
354
  if (Node.isForInStatement(node) || Node.isForOfStatement(node)) {
355
- addHoistedVars(node.getInitializer());
356
- addHoistedVars(node.getStatement());
355
+ addHoistedVars(node.initializer);
356
+ addHoistedVars(node.statement);
357
357
  return;
358
358
  }
359
359
  if (Node.isWhileStatement(node) || Node.isDoStatement(node) || Node.isWithStatement(node)) {
360
- addHoistedVars(node.getStatement());
360
+ addHoistedVars(node.statement);
361
361
  return;
362
362
  }
363
363
  if (Node.isLabeledStatement(node)) {
364
- addHoistedVars(node.getStatement());
364
+ addHoistedVars(node.statement);
365
365
  return;
366
366
  }
367
367
  if (Node.isTryStatement(node)) {
368
- addHoistedVars(node.getTryBlock());
369
- const caught = node.getCatchClause();
370
- if (caught) addHoistedVars(caught.getBlock());
371
- const finally_ = node.getFinallyBlock();
368
+ addHoistedVars(node.tryBlock);
369
+ const caught = childOf(node, "catchClause");
370
+ if (caught) addHoistedVars(childOf(caught, "block"));
371
+ const finally_ = childOf(node, "finallyBlock");
372
372
  if (finally_) addHoistedVars(finally_);
373
373
  return;
374
374
  }
375
- if (Node.isSwitchStatement(node)) for (const clause of node.getCaseBlock().getClauses()) for (const statement of clause.getStatements()) addHoistedVars(statement);
375
+ if (Node.isSwitchStatement(node)) for (const clause of childOf(node, "caseBlock")?.clauses ?? []) for (const statement of childOf(clause, "statements") ?? []) addHoistedVars(statement);
376
376
  };
377
- for (const statement of sourceFile.getStatements()) {
377
+ for (const statement of sourceFile.statements) {
378
378
  if (Node.isVariableStatement(statement)) {
379
379
  addDeclarations(statement);
380
380
  continue;
381
381
  }
382
382
  if (Node.isImportDeclaration(statement)) {
383
- addBinding(statement.getDefaultImport());
384
- addBinding(statement.getNamespaceImport());
385
- for (const named of statement.getNamedImports()) addBinding(named.getAliasNode() ?? named.getNameNode());
383
+ addBinding(getDefaultImport(statement));
384
+ addBinding(getNamespaceImport(statement));
385
+ for (const named of getNamedImports(statement)) addBinding(getAliasNode(named) ?? nameNodeOf(named));
386
386
  continue;
387
387
  }
388
388
  if (Node.isImportEqualsDeclaration(statement)) {
389
- addBinding(statement.getNameNode());
389
+ addBinding(statement.name);
390
390
  continue;
391
391
  }
392
392
  if (Node.isFunctionDeclaration(statement) || Node.isClassDeclaration(statement) || Node.isEnumDeclaration(statement) || Node.isModuleDeclaration(statement) || Node.isTypeAliasDeclaration(statement) || Node.isInterfaceDeclaration(statement)) {
393
- addBinding(statement.getNameNode());
393
+ addBinding(statement.name);
394
394
  continue;
395
395
  }
396
396
  addHoistedVars(statement);
@@ -408,35 +408,34 @@ const collectModuleScopeNames = (sourceFile) => {
408
408
  *
409
409
  * `SyntaxKind.Identifier` sorts *below* `SyntaxKind.FirstNode`, which is what ts-morph tests to
410
410
  * decide whether it may search the parse tree. For a kind below that line it falls back to
411
- * materialising the whole **token** tree — every brace, comma and keyword becomes a ts-morph node
411
+ * materialising the whole **token** tree — every brace, comma and keyword became a ts-morph node
412
412
  * on the way to collecting the identifiers. On 55 KB of real tsx that measured 22ms against
413
- * 0.22ms for the same collection over compiler nodes, and the node cache does not help: a second
413
+ * 0.22ms for the same collection over compiler nodes, and the node cache did not help: a second
414
414
  * call cost the same 22ms.
415
415
  *
416
- * Wrapping is what costs, so only the buckets a caller actually reads are wrapped, on the first
417
- * read and cached after. Nothing enumerates this index both callers ask for one name — so the
418
- * rest is never built. `_getNodeFromCompilerNode` is ts-morph's own memoized wrapper factory, so
419
- * a node handed back here is the very object `getDescendantsOfKind` would have returned, which
420
- * `localReferencesTo` depends on: it compares against the declaration by identity.
416
+ * That cost was ts-morph's wrapper objects, and TypeScript 7 has none the walk below yields
417
+ * the compiler's own nodes, which is exactly what `getDescendantsOfKind` would return. So the
418
+ * lazy per-bucket wrapping this used to do has nothing left to defer, and the buckets are handed
419
+ * back as collected. `localReferencesTo` still depends on the identity of what comes out, since
420
+ * it compares each reference against the declaration.
421
421
  *
422
422
  * JSDoc is walked explicitly. `ts.forEachChild` does not descend into it, while the token path
423
423
  * this replaces does, so a name mentioned only in a `@type` annotation was previously found and
424
424
  * would otherwise stop being — a silent narrowing of what counts as a surviving reference.
425
- * Keyed on `escapedText` because that is what ts-morph's `Identifier.getText()` returns: the name
426
- * as the compiler resolves it, so `\u0062adge` and `badge` share a bucket exactly as before.
425
+ * Keyed on the identifier's `text` because that is the name as the compiler resolves it, so
426
+ * `\u0062adge` and `badge` share a bucket exactly as before.
427
427
  *
428
428
  * Deliberately *not* memoized across passes, unlike the module-scope names beside it. That
429
- * cache holds strings, which outlive anything; this one holds nodes, and a node does not
430
- * survive its source file being replaced `addSourceFile` overwrites, which forgets every
431
- * node previously taken from it. Keying on the source text does not help, because identical
432
- * text re-parsed is a fresh tree: the cache hits and returns nodes that throw
433
- * `Attempted to get information from a node that was removed or forgotten` on the next read.
429
+ * cache holds strings, which outlive anything; this one holds nodes, and a node belongs to the
430
+ * tree it was taken from. Keying on the source text does not help, because identical text
431
+ * re-parsed is a fresh tree: the cache would hit and hand back nodes from a previous snapshot,
432
+ * which no longer match anything the current pass compares them against.
434
433
  */
435
434
  const identifierIndex = (sourceFile) => {
436
435
  const compilerNodes = /* @__PURE__ */ new Map();
437
436
  const collect = (node) => {
438
437
  if (node.kind === ts.SyntaxKind.Identifier) {
439
- const name = String(node.escapedText);
438
+ const name = String(node.text);
440
439
  const known = compilerNodes.get(name);
441
440
  if (known) known.push(node);
442
441
  else compilerNodes.set(name, [node]);
@@ -445,32 +444,24 @@ const identifierIndex = (sourceFile) => {
445
444
  if (jsDoc) for (const doc of jsDoc) collect(doc);
446
445
  ts.forEachChild(node, collect);
447
446
  };
448
- ts.forEachChild(sourceFile.compilerNode, collect);
449
- const wrapped = /* @__PURE__ */ new Map();
450
- const wrap = sourceFile;
451
- return { get: (name) => {
452
- const known = wrapped.get(name);
453
- if (known) return known;
454
- const nodes = (compilerNodes.get(name) ?? []).map((node) => wrap._getNodeFromCompilerNode(node));
455
- wrapped.set(name, nodes);
456
- return nodes;
457
- } };
447
+ ts.forEachChild(sourceFile, collect);
448
+ return { get: (name) => compilerNodes.get(name) ?? [] };
458
449
  };
459
450
  const localReferencesTo = (index, name, declaration) => {
460
451
  const references = [];
461
452
  for (const identifier of index.get(name)) {
462
453
  if (identifier === declaration) continue;
463
- const parent = identifier.getParent();
454
+ const parent = identifier.parent;
464
455
  if (!parent) continue;
465
- if (Node.isPropertyAccessExpression(parent) && parent.getNameNode() === identifier) continue;
466
- if (Node.isPropertyAssignment(parent) && parent.getNameNode() === identifier) continue;
467
- if (Node.isBindingElement(parent) && parent.getPropertyNameNode() === identifier) continue;
468
- if ((Node.isJsxOpeningElement(parent) || Node.isJsxSelfClosingElement(parent) || Node.isJsxClosingElement(parent)) && parent.getTagNameNode() === identifier) continue;
469
- if (Node.isJsxAttribute(parent) && parent.getNameNode() === identifier) continue;
470
- if ((Node.isMethodDeclaration(parent) || Node.isPropertyDeclaration(parent) || Node.isGetAccessorDeclaration(parent) || Node.isSetAccessorDeclaration(parent) || Node.isMethodSignature(parent) || Node.isPropertySignature(parent) || Node.isEnumMember(parent)) && parent.getNameNode() === identifier) continue;
471
- if ((Node.isVariableDeclaration(parent) || Node.isParameterDeclaration(parent) || Node.isFunctionDeclaration(parent) || Node.isClassDeclaration(parent) || Node.isBindingElement(parent)) && parent.getNameNode() === identifier) continue;
456
+ if (Node.isPropertyAccessExpression(parent) && parent.name === identifier) continue;
457
+ if (Node.isPropertyAssignment(parent) && parent.name === identifier) continue;
458
+ if (Node.isBindingElement(parent) && parent.propertyName === identifier) continue;
459
+ if ((Node.isJsxOpeningElement(parent) || Node.isJsxSelfClosingElement(parent) || Node.isJsxClosingElement(parent)) && parent.tagName === identifier) continue;
460
+ if (Node.isJsxAttribute(parent) && parent.name === identifier) continue;
461
+ if ((Node.isMethodDeclaration(parent) || Node.isPropertyDeclaration(parent) || Node.isGetAccessorDeclaration(parent) || Node.isSetAccessorDeclaration(parent) || Node.isMethodSignatureDeclaration(parent) || Node.isPropertySignatureDeclaration(parent) || Node.isEnumMember(parent)) && nameNodeOf(parent) === identifier) continue;
462
+ if ((Node.isVariableDeclaration(parent) || Node.isParameterDeclaration(parent) || Node.isFunctionDeclaration(parent) || Node.isClassDeclaration(parent) || Node.isBindingElement(parent)) && parent.name === identifier) continue;
472
463
  if (Node.isImportSpecifier(parent) || Node.isExportSpecifier(parent)) {
473
- if (parent.getNameNode() !== identifier) continue;
464
+ if (parent.name !== identifier) continue;
474
465
  if (Node.isExportSpecifier(parent)) {
475
466
  references.push(identifier);
476
467
  continue;
@@ -498,7 +489,9 @@ const collectRecipeConfigs = (parserResult) => {
498
489
  for (const definition of definitions) {
499
490
  const node = definition.box?.getNode?.();
500
491
  if (!node) continue;
501
- const nameNode = ((Node.isCallExpression(node) ? node : node.getFirstAncestorByKind(SyntaxKind.CallExpression))?.getFirstAncestorByKind(SyntaxKind.VariableDeclaration))?.getNameNode();
492
+ const call = Node.isCallExpression(node) ? node : getFirstAncestorByKind(node, SyntaxKind.CallExpression);
493
+ const declaration = call && getFirstAncestorByKind(call, SyntaxKind.VariableDeclaration);
494
+ const nameNode = declaration && nameNodeOf(declaration);
502
495
  if (!nameNode || !Node.isIdentifier(nameNode)) continue;
503
496
  if (definition.data?.length !== 1) {
504
497
  configs.set(nameNode.getText(), AMBIGUOUS);
@@ -547,11 +540,11 @@ const LITERAL_KINDS = new Set([
547
540
  * string missing the variant — the element renders, wrongly, with no report.
548
541
  */
549
542
  const literalValue = (node) => {
550
- if (!node || !LITERAL_KINDS.has(node.getKind())) return void 0;
551
- if (Node.isStringLiteral(node) || Node.isNoSubstitutionTemplateLiteral(node)) return node.getLiteralValue();
552
- if (Node.isNumericLiteral(node)) return node.getLiteralValue();
553
- if (node.getKind() === SyntaxKind.TrueKeyword) return true;
554
- if (node.getKind() === SyntaxKind.FalseKeyword) return false;
543
+ if (!node || !LITERAL_KINDS.has(node.kind)) return void 0;
544
+ if (Node.isStringLiteral(node) || Node.isNoSubstitutionTemplateLiteral(node)) return literalValueOf(node);
545
+ if (Node.isNumericLiteral(node)) return literalValueOf(node);
546
+ if (node.kind === SyntaxKind.TrueKeyword) return true;
547
+ if (node.kind === SyntaxKind.FalseKeyword) return false;
555
548
  };
556
549
  /**
557
550
  * The property name a key node denotes.
@@ -563,8 +556,8 @@ const literalValue = (node) => {
563
556
  */
564
557
  const propertyKey = (nameNode) => {
565
558
  if (Node.isIdentifier(nameNode)) return nameNode.getText();
566
- if (Node.isStringLiteral(nameNode) || Node.isNoSubstitutionTemplateLiteral(nameNode)) return nameNode.getLiteralValue();
567
- if (Node.isNumericLiteral(nameNode)) return String(nameNode.getLiteralValue());
559
+ if (Node.isStringLiteral(nameNode) || Node.isNoSubstitutionTemplateLiteral(nameNode)) return stringLiteralValue(nameNode);
560
+ if (Node.isNumericLiteral(nameNode)) return String(literalValueOf(nameNode));
568
561
  };
569
562
  /**
570
563
  * Make a generated compile helper callable at this call site, by whatever name the file gives it.
@@ -577,26 +570,26 @@ const ensureRecipeHelperImport = (imported, call, isBambooCssModule, isGenerated
577
570
  const sourceFile = call.getSourceFile();
578
571
  let host;
579
572
  let subpathModule;
580
- for (const declaration of sourceFile.getImportDeclarations()) {
581
- const mod = declaration.getModuleSpecifierValue();
582
- if (declaration.isTypeOnly()) continue;
583
- for (const named of declaration.getNamedImports()) {
584
- if (named.isTypeOnly()) continue;
585
- if (named.getNameNode().getText() === imported) {
586
- if (!isBambooCssModule(mod)) return void 0;
587
- const local = (named.getAliasNode() ?? named.getNameNode()).getText();
573
+ for (const declaration of getImportDeclarations(sourceFile)) {
574
+ const mod = getModuleSpecifierValue(declaration);
575
+ if (isTypeOnly(declaration)) continue;
576
+ for (const named of getNamedImports(declaration)) {
577
+ if (isTypeOnly(named)) continue;
578
+ if (nameNodeOf(named)?.getText() === imported) {
579
+ if (!isBambooCssModule(mod ?? "")) return void 0;
580
+ const local = (getAliasNode(named) ?? nameNodeOf(named))?.getText() ?? "";
588
581
  return isShadowed(call, local) ? void 0 : { name: local };
589
582
  }
590
583
  }
591
- if (!host && isGeneratedCssModule(mod) && declaration.getNamedImports().length > 0) host = declaration;
592
- if (!subpathModule) subpathModule = helperModuleFromSubpath?.(mod);
584
+ if (!host && isGeneratedCssModule(mod ?? "") && getNamedImports(declaration).length > 0) host = declaration;
585
+ if (!subpathModule) subpathModule = helperModuleFromSubpath?.(mod ?? "");
593
586
  }
594
587
  const fallbackModule = newImportModule ?? subpathModule;
595
588
  if (!host && !fallbackModule) return void 0;
596
589
  if (declaredAtModuleScope(sourceFile).has(imported)) return void 0;
597
590
  if (isShadowed(call, imported)) return void 0;
598
591
  if (!host) {
599
- const anchor = sourceFile.getImportDeclarations().at(-1);
592
+ const anchor = getImportDeclarations(sourceFile).at(-1);
600
593
  if (!anchor) return void 0;
601
594
  return {
602
595
  name: imported,
@@ -607,7 +600,7 @@ const ensureRecipeHelperImport = (imported, call, isBambooCssModule, isGenerated
607
600
  }
608
601
  };
609
602
  }
610
- const last = host.getNamedImports().at(-1);
603
+ const last = getNamedImports(host).at(-1);
611
604
  if (!last) return void 0;
612
605
  return {
613
606
  name: imported,
@@ -647,7 +640,7 @@ const lowerRecipeCall = (call, entry, styleCompiler, isInert, resolvedSelection,
647
640
  kind: "decline",
648
641
  reason: "unsupported-shape"
649
642
  };
650
- const args = call.getArguments();
643
+ const args = call.arguments;
651
644
  if (args.length > 1) return {
652
645
  kind: "decline",
653
646
  reason: "unsupported-shape"
@@ -689,21 +682,21 @@ const lowerRecipeCall = (call, entry, styleCompiler, isInert, resolvedSelection,
689
682
  kind: "decline",
690
683
  reason: "dynamic"
691
684
  };
692
- else for (const property of arg.getProperties()) {
685
+ else for (const property of arg.properties) {
693
686
  if (Node.isSpreadAssignment(property)) return {
694
687
  kind: "decline",
695
688
  reason: "dynamic"
696
689
  };
697
690
  if (Node.isShorthandPropertyAssignment(property)) {
698
- dynamicAxes.set(property.getName(), property.getName());
699
- delete selection[property.getName()];
691
+ dynamicAxes.set(getName(property) ?? "", getName(property) ?? "");
692
+ delete selection[getName(property) ?? ""];
700
693
  continue;
701
694
  }
702
695
  if (!Node.isPropertyAssignment(property)) return {
703
696
  kind: "decline",
704
697
  reason: "dynamic"
705
698
  };
706
- const nameNode = property.getNameNode();
699
+ const nameNode = property.name;
707
700
  if (Node.isComputedPropertyName(nameNode)) return {
708
701
  kind: "decline",
709
702
  reason: "dynamic"
@@ -713,7 +706,7 @@ const lowerRecipeCall = (call, entry, styleCompiler, isInert, resolvedSelection,
713
706
  kind: "decline",
714
707
  reason: "dynamic"
715
708
  };
716
- const initializer = property.getInitializer();
709
+ const initializer = property.initializer;
717
710
  if (initializer && !isInert(initializer)) {
718
711
  if (!Object.hasOwn(config.variants ?? {}, key)) return {
719
712
  kind: "decline",
@@ -1151,24 +1144,24 @@ const RECIPE_CALL_TYPE = "cva-call";
1151
1144
  * A type position is excluded for a different reason: it is erased, and with it the import.
1152
1145
  */
1153
1146
  const isValueReference = (identifier) => {
1154
- const parent = identifier.getParent();
1147
+ const parent = identifier.parent;
1155
1148
  if (!parent) return false;
1156
1149
  if (Node.isImportSpecifier(parent) || Node.isExportSpecifier(parent)) return false;
1157
1150
  if (Node.isImportClause(parent) || Node.isNamespaceImport(parent)) return false;
1158
- if (Node.isPropertyAccessExpression(parent) && parent.getNameNode() === identifier) return false;
1159
- if (Node.isQualifiedName(parent) && parent.getRight() === identifier) return false;
1160
- if (Node.isPropertyAssignment(parent) && parent.getNameNode() === identifier) return false;
1161
- if (Node.isMethodDeclaration(parent) || Node.isPropertyDeclaration(parent) || Node.isGetAccessorDeclaration(parent) || Node.isSetAccessorDeclaration(parent) || Node.isMethodSignature(parent) || Node.isPropertySignature(parent) || Node.isEnumMember(parent)) {
1162
- if (parent.getNameNode() === identifier) return false;
1151
+ if (Node.isPropertyAccessExpression(parent) && parent.name === identifier) return false;
1152
+ if (Node.isQualifiedName(parent) && parent.right === identifier) return false;
1153
+ if (Node.isPropertyAssignment(parent) && parent.name === identifier) return false;
1154
+ if (Node.isMethodDeclaration(parent) || Node.isPropertyDeclaration(parent) || Node.isGetAccessorDeclaration(parent) || Node.isSetAccessorDeclaration(parent) || Node.isMethodSignatureDeclaration(parent) || Node.isPropertySignatureDeclaration(parent) || Node.isEnumMember(parent)) {
1155
+ if (nameNodeOf(parent) === identifier) return false;
1163
1156
  }
1164
1157
  if (Node.isLabeledStatement(parent) || Node.isBreakStatement(parent) || Node.isContinueStatement(parent)) return false;
1165
1158
  if (Node.isJsxOpeningElement(parent) || Node.isJsxSelfClosingElement(parent) || Node.isJsxClosingElement(parent)) {
1166
- if (parent.getTagNameNode() === identifier) return identifier.getText()[0] === identifier.getText()[0]?.toUpperCase();
1159
+ if (parent.tagName === identifier) return identifier.getText()[0] === identifier.getText()[0]?.toUpperCase();
1167
1160
  }
1168
1161
  if (Node.isJsxAttribute(parent)) return false;
1169
- if (Node.isBindingElement(parent) && parent.getPropertyNameNode() === identifier) return false;
1170
- if ((Node.isVariableDeclaration(parent) || Node.isParameterDeclaration(parent) || Node.isBindingElement(parent) || Node.isFunctionDeclaration(parent) || Node.isClassDeclaration(parent)) && parent.getNameNode() === identifier) return false;
1171
- return !identifier.getFirstAncestor((ancestor) => Node.isTypeNode(ancestor) || Node.isTypeAliasDeclaration(ancestor) || Node.isInterfaceDeclaration(ancestor));
1162
+ if (Node.isBindingElement(parent) && parent.propertyName === identifier) return false;
1163
+ if ((Node.isVariableDeclaration(parent) || Node.isParameterDeclaration(parent) || Node.isBindingElement(parent) || Node.isFunctionDeclaration(parent) || Node.isClassDeclaration(parent)) && parent.name === identifier) return false;
1164
+ return !getFirstAncestor(identifier, (ancestor) => Node.isTypeNode(ancestor) || Node.isTypeAliasDeclaration(ancestor) || Node.isInterfaceDeclaration(ancestor));
1172
1165
  };
1173
1166
  /**
1174
1167
  * Imports a surviving reference to is not a failure.
@@ -1209,7 +1202,7 @@ const TRAILING_INDEX = /\/index$/;
1209
1202
  * contract is behaviour preservation regardless: a literal is the cheap way to prove it, since
1210
1203
  * it means no call, no property read, no getter.
1211
1204
  */
1212
- const isInertArgument = (node) => Node.isStringLiteral(node) || Node.isNumericLiteral(node) || Node.isNoSubstitutionTemplateLiteral(node) || node.getKind() === SyntaxKind.TrueKeyword || node.getKind() === SyntaxKind.FalseKeyword || node.getKind() === SyntaxKind.NullKeyword || Node.isIdentifier(node) && node.getText() === "undefined";
1205
+ const isInertArgument = (node) => Node.isStringLiteral(node) || Node.isNumericLiteral(node) || Node.isNoSubstitutionTemplateLiteral(node) || node.kind === SyntaxKind.TrueKeyword || node.kind === SyntaxKind.FalseKeyword || node.kind === SyntaxKind.NullKeyword || Node.isIdentifier(node) && node.getText() === "undefined";
1213
1206
  /**
1214
1207
  * An expression whose evaluation cannot do anything observable, so deleting it preserves
1215
1208
  * behaviour.
@@ -1222,25 +1215,25 @@ const isInertArgument = (node) => Node.isStringLiteral(node) || Node.isNumericLi
1222
1215
  const isInertExpression = (node) => {
1223
1216
  if (isInertArgument(node)) return true;
1224
1217
  if (Node.isIdentifier(node)) return true;
1225
- if (Node.isAsExpression(node) || Node.isSatisfiesExpression(node) || Node.isNonNullExpression(node) || Node.isTypeAssertion(node) || Node.isParenthesizedExpression(node)) return isInertExpression(node.getExpression());
1218
+ if (Node.isAsExpression(node) || Node.isSatisfiesExpression(node) || Node.isNonNullExpression(node) || Node.isTypeAssertion(node) || Node.isParenthesizedExpression(node)) return isInertExpression(node.expression);
1226
1219
  if (Node.isArrowFunction(node) || Node.isFunctionExpression(node)) return true;
1227
1220
  if (Node.isRegularExpressionLiteral(node) || Node.isBigIntLiteral(node)) return true;
1228
1221
  if (Node.isPrefixUnaryExpression(node)) {
1229
- const operator = node.getOperatorToken();
1230
- return (operator === SyntaxKind.MinusToken || operator === SyntaxKind.PlusToken || operator === SyntaxKind.ExclamationToken || operator === SyntaxKind.TildeToken) && isInertExpression(node.getOperand());
1222
+ const operator = node.operator;
1223
+ return (operator === SyntaxKind.MinusToken || operator === SyntaxKind.PlusToken || operator === SyntaxKind.ExclamationToken || operator === SyntaxKind.TildeToken) && isInertExpression(node.operand);
1231
1224
  }
1232
1225
  if (Node.isBinaryExpression(node)) {
1233
- const operator = node.getOperatorToken().getKind();
1234
- return (operator === SyntaxKind.QuestionQuestionToken || operator === SyntaxKind.BarBarToken || operator === SyntaxKind.AmpersandAmpersandToken) && isInertExpression(node.getLeft()) && isInertExpression(node.getRight());
1226
+ const operator = node.operatorToken.kind;
1227
+ return (operator === SyntaxKind.QuestionQuestionToken || operator === SyntaxKind.BarBarToken || operator === SyntaxKind.AmpersandAmpersandToken) && isInertExpression(node.left) && isInertExpression(node.right);
1235
1228
  }
1236
- if (Node.isObjectLiteralExpression(node)) return node.getProperties().every((property) => {
1229
+ if (Node.isObjectLiteralExpression(node)) return node.properties.every((property) => {
1237
1230
  if (Node.isShorthandPropertyAssignment(property)) return true;
1238
1231
  if (!Node.isPropertyAssignment(property)) return false;
1239
- if (Node.isComputedPropertyName(property.getNameNode())) return false;
1240
- const initializer = property.getInitializer();
1232
+ if (Node.isComputedPropertyName(property.name)) return false;
1233
+ const initializer = property.initializer;
1241
1234
  return initializer !== void 0 && isInertExpression(initializer);
1242
1235
  });
1243
- if (Node.isArrayLiteralExpression(node)) return node.getElements().every(isInertExpression);
1236
+ if (Node.isArrayLiteralExpression(node)) return node.elements.every(isInertExpression);
1244
1237
  return false;
1245
1238
  };
1246
1239
  /**
@@ -1272,7 +1265,7 @@ const createDependencyScan = (ownFile) => {
1272
1265
  if (sourceFile === ownFile) return;
1273
1266
  let path = paths.get(sourceFile);
1274
1267
  if (path === void 0) {
1275
- path = sourceFile.getFilePath();
1268
+ path = pathOf(sourceFile);
1276
1269
  paths.set(sourceFile, path);
1277
1270
  }
1278
1271
  if (path) results.add(path);
@@ -1295,7 +1288,7 @@ const findCallExpression = (node) => {
1295
1288
  let current = own;
1296
1289
  for (let depth = 0; current && depth < 3; depth++) {
1297
1290
  if (Node.isCallExpression(current)) return current;
1298
- current = current.getParent();
1291
+ current = current.parent;
1299
1292
  }
1300
1293
  };
1301
1294
  /**
@@ -1305,14 +1298,14 @@ const findCallExpression = (node) => {
1305
1298
  */
1306
1299
  const isRawCall = (call) => {
1307
1300
  if (!Node.isCallExpression(call)) return false;
1308
- const callee = call.getExpression().getText();
1301
+ const callee = call.expression.getText();
1309
1302
  return callee === "raw" || callee.endsWith(".raw");
1310
1303
  };
1311
1304
  /** The identifier a callee is rooted at: `css` for `css(…)`, `panda` for `panda.css(…)`. */
1312
1305
  const calleeRootName = (call) => {
1313
1306
  if (!Node.isCallExpression(call)) return void 0;
1314
- let current = call.getExpression();
1315
- while (Node.isPropertyAccessExpression(current)) current = current.getExpression();
1307
+ let current = call.expression;
1308
+ while (Node.isPropertyAccessExpression(current)) current = current.expression;
1316
1309
  return Node.isIdentifier(current) ? current.getText() : void 0;
1317
1310
  };
1318
1311
  /**
@@ -1332,26 +1325,26 @@ const calleeRootName = (call) => {
1332
1325
  */
1333
1326
  const bambooImportedNames = (sourceFile, ctx) => {
1334
1327
  const names = /* @__PURE__ */ new Set();
1335
- for (const declaration of sourceFile.getImportDeclarations()) {
1336
- const mod = declaration.getModuleSpecifierValue();
1337
- for (const named of declaration.getNamedImports()) {
1338
- const name = named.getNameNode().getText();
1339
- const alias = named.getAliasNode()?.getText() ?? name;
1328
+ for (const declaration of getImportDeclarations(sourceFile)) {
1329
+ const mod = getModuleSpecifierValue(declaration);
1330
+ for (const named of getNamedImports(declaration)) {
1331
+ const name = nameNodeOf(named)?.getText();
1332
+ const alias = getAliasNode(named)?.getText() ?? name;
1340
1333
  if (ctx.imports.match({
1341
- mod,
1342
- name,
1343
- alias
1344
- })) names.add(alias);
1334
+ mod: mod ?? "",
1335
+ name: name ?? "",
1336
+ alias: alias ?? ""
1337
+ })) names.add(alias ?? "");
1345
1338
  }
1346
- const namespace = declaration.getNamespaceImport();
1339
+ const namespace = getNamespaceImport(declaration);
1347
1340
  if (namespace) {
1348
1341
  const alias = namespace.getText();
1349
1342
  if (ctx.imports.match({
1350
- mod,
1351
- name: alias,
1352
- alias,
1343
+ mod: mod ?? "",
1344
+ name: alias ?? "",
1345
+ alias: alias ?? "",
1353
1346
  kind: "namespace"
1354
- })) names.add(alias);
1347
+ })) names.add(alias ?? "");
1355
1348
  }
1356
1349
  }
1357
1350
  return names;
@@ -1366,7 +1359,7 @@ const bambooImportedNames = (sourceFile, ctx) => {
1366
1359
  * attributes, which match neither and cost nothing.
1367
1360
  */
1368
1361
  const isShadowed = (call, name) => {
1369
- for (let node = call.getParent(); node; node = node.getParent()) {
1362
+ for (let node = call.parent; node; node = node.parent) {
1370
1363
  if (Node.isSourceFile(node)) return false;
1371
1364
  if (bindsName(node, name)) return true;
1372
1365
  }
@@ -1383,20 +1376,20 @@ const isShadowed = (call, name) => {
1383
1376
  const bindingIntroduces = (nameNode, name) => {
1384
1377
  if (!nameNode) return false;
1385
1378
  if (Node.isIdentifier(nameNode)) return nameNode.getText() === name;
1386
- if (Node.isObjectBindingPattern(nameNode) || Node.isArrayBindingPattern(nameNode)) return nameNode.getElements().some((element) => Node.isBindingElement(element) && bindingIntroduces(element.getNameNode(), name));
1379
+ if (Node.isObjectBindingPattern(nameNode) || Node.isArrayBindingPattern(nameNode)) return nameNode.elements.some((element) => Node.isBindingElement(element) && bindingIntroduces(element.name, name));
1387
1380
  return false;
1388
1381
  };
1389
- const declarationsBind = (list, name) => Node.isVariableDeclarationList(list) && list.getDeclarations().some((declaration) => bindingIntroduces(declaration.getNameNode(), name));
1382
+ const declarationsBind = (list, name) => list !== void 0 && Node.isVariableDeclarationList(list) && list.declarations.some((declaration) => bindingIntroduces(declaration.name, name));
1390
1383
  const bindsName = (scope, name) => {
1391
- if (Node.isBlock(scope)) return scope.getStatements().some((statement) => statementBinds(statement, name));
1392
- if (Node.isFunctionDeclaration(scope) || Node.isArrowFunction(scope) || Node.isFunctionExpression(scope) || Node.isMethodDeclaration(scope)) return scope.getParameters().some((parameter) => bindingIntroduces(parameter.getNameNode(), name));
1393
- if (Node.isCatchClause(scope)) return bindingIntroduces(scope.getVariableDeclaration()?.getNameNode(), name);
1394
- if (Node.isForStatement(scope) || Node.isForOfStatement(scope) || Node.isForInStatement(scope)) return declarationsBind(scope.getInitializer(), name);
1384
+ if (Node.isBlock(scope)) return scope.statements.some((statement) => statementBinds(statement, name));
1385
+ if (Node.isFunctionDeclaration(scope) || Node.isArrowFunction(scope) || Node.isFunctionExpression(scope) || Node.isMethodDeclaration(scope)) return scope.parameters.some((parameter) => bindingIntroduces(parameter.name, name));
1386
+ if (Node.isCatchClause(scope)) return bindingIntroduces(scope.variableDeclaration?.name, name);
1387
+ if (Node.isForStatement(scope) || Node.isForOfStatement(scope) || Node.isForInStatement(scope)) return declarationsBind(scope.initializer, name);
1395
1388
  return false;
1396
1389
  };
1397
1390
  const statementBinds = (statement, name) => {
1398
- if (Node.isVariableStatement(statement)) return statement.getDeclarations().some((declaration) => bindingIntroduces(declaration.getNameNode(), name));
1399
- if (Node.isFunctionDeclaration(statement) || Node.isClassDeclaration(statement)) return statement.getNameNode()?.getText() === name;
1391
+ if (Node.isVariableStatement(statement)) return statement.declarationList.declarations.some((declaration) => bindingIntroduces(declaration.name, name));
1392
+ if (Node.isFunctionDeclaration(statement) || Node.isClassDeclaration(statement)) return statement.name?.getText() === name;
1400
1393
  return false;
1401
1394
  };
1402
1395
  const hasStyles = (data) => data.length > 0 && data.every((entry) => entry != null && typeof entry === "object");
@@ -1407,7 +1400,7 @@ const hasStyles = (data) => data.length > 0 && data.every((entry) => entry != nu
1407
1400
  */
1408
1401
  const argumentsAccountedFor = (call, boxNode) => {
1409
1402
  if (!Node.isCallExpression(call)) return false;
1410
- const args = call.getArguments();
1403
+ const args = call.arguments;
1411
1404
  if (args.length === 0) return true;
1412
1405
  if (box.isArray(boxNode) && boxNode.getNode() === call) {
1413
1406
  if (boxNode.value.length !== args.length) return false;
@@ -1513,13 +1506,13 @@ const foldSource = (options) => {
1513
1506
  * its `/recipes` suffix; falling back to the configured generated entry covers bare imports.
1514
1507
  */
1515
1508
  const configRecipeCssSpecifier = (call, binding) => {
1516
- for (const declaration of call.getSourceFile().getImportDeclarations()) {
1517
- if (declaration.isTypeOnly()) continue;
1518
- if (!declaration.getNamedImports().some((named) => {
1519
- if (named.isTypeOnly()) return false;
1520
- return (named.getAliasNode() ?? named.getNameNode()).getText() === binding;
1509
+ for (const declaration of getImportDeclarations(call.getSourceFile())) {
1510
+ if (isTypeOnly(declaration)) continue;
1511
+ if (!getNamedImports(declaration).some((named) => {
1512
+ if (isTypeOnly(named)) return false;
1513
+ return (getAliasNode(named) ?? nameNodeOf(named))?.getText() === binding;
1521
1514
  })) continue;
1522
- const mod = declaration.getModuleSpecifierValue().replaceAll("\\", "/");
1515
+ const mod = (getModuleSpecifierValue(declaration) ?? "").replaceAll("\\", "/");
1523
1516
  const at = mod.lastIndexOf("/recipes");
1524
1517
  if (at >= 0) return `${mod.slice(0, at)}/css`;
1525
1518
  }
@@ -1537,10 +1530,10 @@ const foldSource = (options) => {
1537
1530
  * expressed from the module being folded.
1538
1531
  */
1539
1532
  const cssModuleSpecifierFrom = (declaring) => {
1540
- for (const declaration of declaring.getImportDeclarations()) {
1541
- if (declaration.isTypeOnly()) continue;
1542
- const mod = declaration.getModuleSpecifierValue();
1543
- if (isGeneratedCssModule(mod)) return mod;
1533
+ for (const declaration of getImportDeclarations(declaring)) {
1534
+ if (isTypeOnly(declaration)) continue;
1535
+ const mod = getModuleSpecifierValue(declaration);
1536
+ if (isGeneratedCssModule(mod ?? "")) return mod;
1544
1537
  }
1545
1538
  };
1546
1539
  /**
@@ -1591,7 +1584,7 @@ const foldSource = (options) => {
1591
1584
  const resolve = () => {
1592
1585
  if (!parseModule) return void 0;
1593
1586
  const consuming = call.getSourceFile();
1594
- if (origin.filePath === consuming.getFilePath()) return void 0;
1587
+ if (origin.filePath === pathOf(consuming)) return void 0;
1595
1588
  let foreign = configsByModule.get(origin.filePath);
1596
1589
  if (!foreign) {
1597
1590
  const result = parseModule(origin.filePath);
@@ -1618,7 +1611,7 @@ const foldSource = (options) => {
1618
1611
  name: origin.name,
1619
1612
  digest: digestRecipeConfig(entry)
1620
1613
  });
1621
- helperModules.set(name, foreign.cssSpecifier ? rebaseSpecifier(foreign.cssSpecifier, origin.filePath, consuming.getFilePath()) : void 0);
1614
+ helperModules.set(name, foreign.cssSpecifier ? rebaseSpecifier(foreign.cssSpecifier, origin.filePath, pathOf(consuming)) : void 0);
1622
1615
  return entry;
1623
1616
  };
1624
1617
  const resolved = resolve();
@@ -1635,7 +1628,7 @@ const foldSource = (options) => {
1635
1628
  if (entry === AMBIGUOUS) continue;
1636
1629
  const definition = entry.box?.getNode?.();
1637
1630
  if (!definition) continue;
1638
- const call = Node.isCallExpression(definition) ? definition : definition.getFirstAncestorByKind(SyntaxKind.CallExpression);
1631
+ const call = Node.isCallExpression(definition) ? definition : getFirstAncestorByKind(definition, SyntaxKind.CallExpression);
1639
1632
  if (!call || code.slice(call.getStart(), call.getEnd()) !== call.getText()) continue;
1640
1633
  recipeDefinitions.push({
1641
1634
  name,
@@ -1692,8 +1685,8 @@ const foldSource = (options) => {
1692
1685
  });
1693
1686
  continue;
1694
1687
  }
1695
- const callee = Node.isCallExpression(call) ? call.getExpression() : void 0;
1696
- const propertyName = Node.isPropertyAccessExpression(callee) ? callee.getNameNode().getText() : void 0;
1688
+ const callee = Node.isCallExpression(call) ? call.expression : void 0;
1689
+ const propertyName = callee !== void 0 && Node.isPropertyAccessExpression(callee) ? nameNodeOf(callee)?.getText() ?? "" : void 0;
1697
1690
  const wantsValue = type === "tokenValue";
1698
1691
  if (wantsValue !== (propertyName === "value")) {
1699
1692
  skipped.push({
@@ -1732,7 +1725,7 @@ const foldSource = (options) => {
1732
1725
  });
1733
1726
  continue;
1734
1727
  }
1735
- if (!(Node.isCallExpression(call) ? call.getArguments().slice(1) : []).every(isInertArgument)) {
1728
+ if (!(Node.isCallExpression(call) ? call.arguments.slice(1) : []).every(isInertArgument)) {
1736
1729
  skipped.push({
1737
1730
  name,
1738
1731
  reason: "dynamic",
@@ -1806,16 +1799,16 @@ const foldSource = (options) => {
1806
1799
  let inlineSlot;
1807
1800
  let inlineEnd = end;
1808
1801
  if (Array.isArray(entry?.config.slots)) {
1809
- const parent = call.getParent();
1810
- if (Node.isPropertyAccessExpression(parent) && parent.getExpression() === call) {
1811
- const accessed = parent.getName();
1802
+ const parent = call.parent;
1803
+ if (Node.isPropertyAccessExpression(parent) && parent.expression === call) {
1804
+ const accessed = getName(parent);
1812
1805
  if (entry.config.slots.includes(accessed)) {
1813
1806
  inlineSlot = accessed;
1814
1807
  inlineEnd = parent.getEnd();
1815
1808
  }
1816
- } else if (Node.isElementAccessExpression(parent) && parent.getExpression() === call) {
1817
- const argument = parent.getArgumentExpression();
1818
- const accessed = argument && (Node.isStringLiteral(argument) || Node.isNoSubstitutionTemplateLiteral(argument)) ? argument.getLiteralValue() : void 0;
1809
+ } else if (Node.isElementAccessExpression(parent) && parent.expression === call) {
1810
+ const argument = parent.argumentExpression;
1811
+ const accessed = argument && (Node.isStringLiteral(argument) || Node.isNoSubstitutionTemplateLiteral(argument)) ? literalValueOf(argument) : void 0;
1819
1812
  if (typeof accessed === "string" && entry.config.slots.includes(accessed)) {
1820
1813
  inlineSlot = accessed;
1821
1814
  inlineEnd = parent.getEnd();
@@ -1913,9 +1906,9 @@ const foldSource = (options) => {
1913
1906
  }
1914
1907
  const start = call.getStart();
1915
1908
  const end = call.getEnd();
1916
- const memberParent = call.getParent();
1917
- const accessed = type === "recipe" && Node.isPropertyAccessExpression(memberParent) && memberParent.getExpression() === call ? memberParent : void 0;
1918
- const accessedName = accessed?.getNameNode().getText();
1909
+ const memberParent = call.parent;
1910
+ const accessed = type === "recipe" && Node.isPropertyAccessExpression(memberParent) && memberParent.expression === call ? memberParent : void 0;
1911
+ const accessedName = accessed?.name.getText();
1919
1912
  const declaredSlots = accessed ? ctx.recipes.getConfig(name)?.slots ?? [] : [];
1920
1913
  const memberAccess = accessedName && declaredSlots.includes(accessedName) ? accessed : void 0;
1921
1914
  const slot = memberAccess ? accessedName : void 0;
@@ -1951,7 +1944,7 @@ const foldSource = (options) => {
1951
1944
  });
1952
1945
  continue;
1953
1946
  }
1954
- if (!(Node.isCallExpression(call) && call.getArguments().length === 0 && item.data.length === 1) && !isStaticBox(item.box) || !hasStyles(item.data) || !argumentsAccountedFor(call, item.box)) {
1947
+ if (!(Node.isCallExpression(call) && call.arguments.length === 0 && item.data.length === 1) && !isStaticBox(item.box) || !hasStyles(item.data) || !argumentsAccountedFor(call, item.box)) {
1955
1948
  skipped.push({
1956
1949
  name,
1957
1950
  reason: "dynamic",
@@ -1999,17 +1992,17 @@ const foldSource = (options) => {
1999
1992
  const sourceFile = ownSourceFile ?? candidates[0]?.node.getSourceFile();
2000
1993
  if (sourceFile) {
2001
1994
  const cxBindings = /* @__PURE__ */ new Set();
2002
- for (const declaration of sourceFile.getImportDeclarations()) {
2003
- if (declaration.isTypeOnly() || !isBambooCssModule(declaration.getModuleSpecifierValue())) continue;
2004
- for (const named of declaration.getNamedImports()) {
2005
- if (named.isTypeOnly() || named.getNameNode().getText() !== "cx") continue;
2006
- cxBindings.add((named.getAliasNode() ?? named.getNameNode()).getText());
1995
+ for (const declaration of getImportDeclarations(sourceFile)) {
1996
+ if (isTypeOnly(declaration) || !isBambooCssModule(getModuleSpecifierValue(declaration) ?? "")) continue;
1997
+ for (const named of getNamedImports(declaration)) {
1998
+ if (isTypeOnly(named) || nameNodeOf(named)?.getText() !== "cx") continue;
1999
+ cxBindings.add((getAliasNode(named) ?? nameNodeOf(named))?.getText() ?? "");
2007
2000
  }
2008
2001
  }
2009
2002
  const byRange = new Map(candidates.map((candidate) => [`${candidate.start}:${candidate.end}`, candidate]));
2010
- for (const call of cxBindings.size ? sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression) : []) {
2011
- const callee = call.getExpression();
2012
- if (!Node.isIdentifier(callee) || !cxBindings.has(callee.getText()) || isShadowed(call, callee.getText())) continue;
2003
+ for (const call of cxBindings.size ? getDescendantsOfKind(sourceFile, SyntaxKind.CallExpression) : []) {
2004
+ const callee = childOf(call, "expression");
2005
+ if (callee === void 0 || !Node.isIdentifier(callee) || !cxBindings.has(callee.getText()) || isShadowed(call, callee.getText())) continue;
2013
2006
  const matched = [];
2014
2007
  const parts = [];
2015
2008
  const dynamic = [];
@@ -2045,18 +2038,18 @@ const foldSource = (options) => {
2045
2038
  if (Node.isStringLiteral(arg) || Node.isNoSubstitutionTemplateLiteral(arg)) {
2046
2039
  parts.push({
2047
2040
  kind: "class",
2048
- value: arg.getLiteralValue()
2041
+ value: stringLiteralValue(arg)
2049
2042
  });
2050
2043
  return true;
2051
2044
  }
2052
2045
  if (Node.isArrayLiteralExpression(arg)) {
2053
- for (const element of arg.getElements()) if (Node.isSpreadElement(element) || !take(element)) return false;
2046
+ for (const element of arg.elements) if (Node.isSpreadElement(element) || !take(element)) return false;
2054
2047
  return true;
2055
2048
  }
2056
- if (arg.getKind() === SyntaxKind.FalseKeyword || arg.getKind() === SyntaxKind.TrueKeyword || Node.isNumericLiteral(arg) || arg.getKind() === SyntaxKind.NullKeyword || Node.isIdentifier(arg) && arg.getText() === "undefined") return true;
2049
+ if (arg.kind === SyntaxKind.FalseKeyword || arg.kind === SyntaxKind.TrueKeyword || Node.isNumericLiteral(arg) || arg.kind === SyntaxKind.NullKeyword || Node.isIdentifier(arg) && arg.getText() === "undefined") return true;
2057
2050
  return false;
2058
2051
  };
2059
- for (const arg of call.getArguments()) {
2052
+ for (const arg of childOf(call, "arguments") ?? []) {
2060
2053
  if (take(arg)) continue;
2061
2054
  supported = false;
2062
2055
  break;
@@ -2286,10 +2279,10 @@ const foldSource = (options) => {
2286
2279
  collectSourceFiles(item.box, dependencyScan);
2287
2280
  }
2288
2281
  const recipeSourceFile = candidates[0]?.node.getSourceFile();
2289
- if (recipeSourceFile && mayNameSplitVariantProps(recipeSourceFile.getFullText())) for (const access of recipeSourceFile.getDescendantsOfKind(SyntaxKind.PropertyAccessExpression)) {
2290
- if (access.getName() !== "splitVariantProps") continue;
2291
- const target = access.getExpression();
2292
- if (!Node.isIdentifier(target)) continue;
2282
+ if (recipeSourceFile && mayNameSplitVariantProps(recipeSourceFile.getFullText())) for (const access of getDescendantsOfKind(recipeSourceFile, SyntaxKind.PropertyAccessExpression)) {
2283
+ if (getName(access) !== "splitVariantProps") continue;
2284
+ const target = childOf(access, "expression");
2285
+ if (target === void 0 || !Node.isIdentifier(target)) continue;
2293
2286
  if (isShadowed(access, target.getText())) continue;
2294
2287
  const local = recipeConfigs?.get(target.getText());
2295
2288
  const importedConfig = !local && importsFor(recipeSourceFile).has(target.getText()) ? ctx.recipes.getConfig(target.getText()) : void 0;
@@ -2298,9 +2291,9 @@ const foldSource = (options) => {
2298
2291
  box: void 0
2299
2292
  } : void 0;
2300
2293
  if (!entry) continue;
2301
- const call = access.getParent();
2302
- if (!Node.isCallExpression(call) || call.getExpression() !== access) continue;
2303
- const args = call.getArguments();
2294
+ const call = access.parent;
2295
+ if (!Node.isCallExpression(call) || call.expression !== access) continue;
2296
+ const args = call.arguments;
2304
2297
  if (args.length !== 1) continue;
2305
2298
  const start = call.getStart();
2306
2299
  const end = call.getEnd();
@@ -2342,11 +2335,11 @@ const foldSource = (options) => {
2342
2335
  */
2343
2336
  /** The specifier this module imported `binding` through, if it did. */
2344
2337
  function importSpecifierFor(sourceFile, binding) {
2345
- for (const declaration of sourceFile.getImportDeclarations()) {
2346
- if (declaration.isTypeOnly()) continue;
2347
- for (const named of declaration.getNamedImports()) {
2348
- if (named.isTypeOnly()) continue;
2349
- if ((named.getAliasNode() ?? named.getNameNode()).getText() === binding) return named.getAliasNode() ?? named.getNameNode();
2338
+ for (const declaration of getImportDeclarations(sourceFile)) {
2339
+ if (isTypeOnly(declaration)) continue;
2340
+ for (const named of getNamedImports(declaration)) {
2341
+ if (isTypeOnly(named)) continue;
2342
+ if ((getAliasNode(named) ?? nameNodeOf(named))?.getText() === binding) return getAliasNode(named) ?? nameNodeOf(named);
2350
2343
  }
2351
2344
  }
2352
2345
  }
@@ -2374,9 +2367,9 @@ const foldSource = (options) => {
2374
2367
  if (entry === AMBIGUOUS) continue;
2375
2368
  if (!entry && !importedRecipeBindings.has(binding)) continue;
2376
2369
  const definition = entry?.box?.getNode?.();
2377
- const nameNode = definition?.getSourceFile() === sourceFile ? definition?.getFirstAncestorByKind(SyntaxKind.VariableDeclaration)?.getNameNode() : importSpecifierFor(sourceFile, binding);
2370
+ const nameNode = definition?.getSourceFile() === sourceFile ? nameNodeOf(getFirstAncestorByKind(definition, SyntaxKind.VariableDeclaration)) : importSpecifierFor(sourceFile, binding);
2378
2371
  if (!nameNode || !Node.isIdentifier(nameNode)) continue;
2379
- const references = localReferencesTo(identifiersByName(), binding, nameNode).filter((reference) => !reference.getFirstAncestorByKind(SyntaxKind.TypeQuery));
2372
+ const references = localReferencesTo(identifiersByName(), binding, nameNode).filter((reference) => !getFirstAncestorByKind(reference, SyntaxKind.TypeQuery));
2380
2373
  if (skipped.filter((item) => SURVIVES_TO_RUNTIME.has(item.reason) && item.end > item.start).some((item) => references.some((ref) => ref.getStart() >= item.start && ref.getStart() < item.end))) continue;
2381
2374
  const survivor = references.find((ref) => !applied.some(([from, to]) => ref.getStart() >= from && ref.getStart() < to));
2382
2375
  if (!survivor) continue;
@@ -2395,16 +2388,16 @@ const foldSource = (options) => {
2395
2388
  ];
2396
2389
  const runtimeCalls = [];
2397
2390
  const importEquals = [];
2398
- sourceFile.forEachDescendant((node) => {
2391
+ forEachDescendant(sourceFile, (node) => {
2399
2392
  if (Node.isCallExpression(node)) runtimeCalls.push(node);
2400
2393
  else if (Node.isImportEqualsDeclaration(node)) importEquals.push(node);
2401
2394
  });
2402
2395
  for (const call of runtimeCalls) {
2403
- const callee = call.getExpression();
2404
- const argument = call.getArguments()[0];
2396
+ const callee = call.expression;
2397
+ const argument = call.arguments[0];
2405
2398
  if (!argument || !Node.isStringLiteral(argument) && !Node.isNoSubstitutionTemplateLiteral(argument)) continue;
2406
- if (!matchesModule(argument.getLiteralValue(), bambooModules)) continue;
2407
- const isDynamicImport = callee.getKind() === SyntaxKind.ImportKeyword;
2399
+ if (!matchesModule(stringLiteralValue(argument), bambooModules)) continue;
2400
+ const isDynamicImport = callee.kind === SyntaxKind.ImportKeyword;
2408
2401
  const isRequire = Node.isIdentifier(callee) && callee.getText() === "require" && !isShadowed(call, "require");
2409
2402
  if (!isDynamicImport && !isRequire) continue;
2410
2403
  skipped.push({
@@ -2415,13 +2408,13 @@ const foldSource = (options) => {
2415
2408
  });
2416
2409
  }
2417
2410
  for (const declaration of importEquals) {
2418
- if (declaration.isTypeOnly()) continue;
2419
- const reference = declaration.getModuleReference();
2411
+ if (isTypeOnly(declaration)) continue;
2412
+ const reference = declaration.moduleReference;
2420
2413
  if (!Node.isExternalModuleReference(reference)) continue;
2421
- const expression = reference.getExpression();
2422
- if (!expression || !Node.isStringLiteral(expression) || !matchesModule(expression.getLiteralValue(), bambooModules)) continue;
2414
+ const expression = reference.expression;
2415
+ if (!expression || !Node.isStringLiteral(expression) || !matchesModule(stringLiteralValue(expression), bambooModules)) continue;
2423
2416
  skipped.push({
2424
- name: declaration.getName(),
2417
+ name: getName(declaration) ?? "",
2425
2418
  reason: "runtime-binding",
2426
2419
  start: declaration.getStart(),
2427
2420
  end: declaration.getEnd()
@@ -2429,52 +2422,53 @@ const foldSource = (options) => {
2429
2422
  }
2430
2423
  /** Local name -> what to call it in the report. */
2431
2424
  const watched = /* @__PURE__ */ new Map();
2432
- for (const declaration of sourceFile.getImportDeclarations()) {
2433
- if (declaration.isTypeOnly()) continue;
2434
- if (!matchesModule(declaration.getModuleSpecifierValue(), bambooModules)) continue;
2435
- for (const named of declaration.getNamedImports()) {
2436
- if (named.isTypeOnly()) continue;
2437
- const imported = named.getNameNode().getText();
2438
- if (PERMITTED_BINDINGS.has(imported)) continue;
2439
- watched.set((named.getAliasNode() ?? named.getNameNode()).getText(), imported);
2425
+ for (const declaration of getImportDeclarations(sourceFile)) {
2426
+ if (isTypeOnly(declaration)) continue;
2427
+ if (!matchesModule(getModuleSpecifierValue(declaration) ?? "", bambooModules)) continue;
2428
+ for (const named of getNamedImports(declaration)) {
2429
+ if (isTypeOnly(named)) continue;
2430
+ const imported = nameNodeOf(named)?.getText();
2431
+ if (PERMITTED_BINDINGS.has(imported ?? "")) continue;
2432
+ watched.set((getAliasNode(named) ?? nameNodeOf(named))?.getText() ?? "", imported ?? "");
2440
2433
  }
2441
- const namespace = declaration.getNamespaceImport();
2434
+ const namespace = getNamespaceImport(declaration);
2442
2435
  if (namespace) watched.set(namespace.getText(), `${namespace.getText()}.*`);
2443
- const defaultImport = declaration.getDefaultImport();
2436
+ const defaultImport = getDefaultImport(declaration);
2444
2437
  if (defaultImport) watched.set(defaultImport.getText(), defaultImport.getText());
2445
2438
  }
2446
- for (const declaration of sourceFile.getExportDeclarations()) {
2447
- if (declaration.isTypeOnly()) continue;
2448
- if (!matchesModule(declaration.getModuleSpecifierValue() ?? "", bambooModules)) continue;
2449
- if (declaration.isNamespaceExport()) {
2439
+ for (const declaration of getExportDeclarations(sourceFile)) {
2440
+ if (isTypeOnly(declaration)) continue;
2441
+ if (!matchesModule(getModuleSpecifierValue(declaration) ?? "", bambooModules)) continue;
2442
+ if (isStarExport(declaration)) {
2443
+ const namespace = getNamespaceExport(declaration);
2450
2444
  skipped.push({
2451
- name: declaration.getNamespaceExport()?.getName() ?? "*",
2445
+ name: (namespace && getName(namespace)) ?? "*",
2452
2446
  reason: "runtime-binding",
2453
2447
  start: declaration.getStart(),
2454
2448
  end: declaration.getEnd()
2455
2449
  });
2456
2450
  continue;
2457
2451
  }
2458
- for (const named of declaration.getNamedExports()) {
2459
- if (named.isTypeOnly()) continue;
2460
- const imported = named.getNameNode().getText();
2461
- if (PERMITTED_BINDINGS.has(imported)) continue;
2452
+ for (const named of getNamedExports(declaration)) {
2453
+ if (isTypeOnly(named)) continue;
2454
+ const imported = nameNodeOf(named)?.getText();
2455
+ if (PERMITTED_BINDINGS.has(imported ?? "")) continue;
2462
2456
  skipped.push({
2463
- name: imported,
2457
+ name: imported ?? "",
2464
2458
  reason: "runtime-binding",
2465
2459
  start: named.getStart(),
2466
2460
  end: named.getEnd()
2467
2461
  });
2468
2462
  }
2469
2463
  }
2470
- for (const declaration of sourceFile.getExportDeclarations()) {
2471
- if (declaration.isTypeOnly() || declaration.getModuleSpecifier()) continue;
2472
- for (const named of declaration.getNamedExports()) {
2473
- if (named.isTypeOnly()) continue;
2474
- const imported = watched.get(named.getNameNode().getText());
2464
+ for (const declaration of getExportDeclarations(sourceFile)) {
2465
+ if (isTypeOnly(declaration) || childOf(declaration, "moduleSpecifier")) continue;
2466
+ for (const named of getNamedExports(declaration)) {
2467
+ if (isTypeOnly(named)) continue;
2468
+ const imported = watched.get(nameNodeOf(named)?.getText() ?? "");
2475
2469
  if (imported === void 0) continue;
2476
2470
  skipped.push({
2477
- name: imported,
2471
+ name: imported ?? "",
2478
2472
  reason: "runtime-binding",
2479
2473
  start: named.getStart(),
2480
2474
  end: named.getEnd()
@@ -2486,7 +2480,7 @@ const foldSource = (options) => {
2486
2480
  const survivors = [];
2487
2481
  for (const [local, imported] of watched) for (const identifier of identifiersByName().get(local)) {
2488
2482
  const start = identifier.getStart();
2489
- if (identifier.getFirstAncestorByKind(SyntaxKind.ImportDeclaration)) continue;
2483
+ if (getFirstAncestorByKind(identifier, SyntaxKind.ImportDeclaration)) continue;
2490
2484
  if (applied.some(([from, to]) => start >= from && start < to)) continue;
2491
2485
  if (declined.some(([from, to]) => start >= from && start < to)) continue;
2492
2486
  if (!isValueReference(identifier)) continue;