Skip to content

Commit 5bb9924

Browse files
committed
fix(devtools-bundler-core): skip primitive values when walking AST children
getChildKeys caches the object-valued keys of the first node it sees for a type. A regex Literal has an object `value`, so `value` was cached for every Literal. A later string Literal then reached `'type' in value` with a string, which throws. addSourceToJsx catches the error and returns nothing, so after one regex literal no file got data-tsd-source attributes. forEachChild now skips every non-object value. Fixes #523
1 parent afa01fe commit 5bb9924

3 files changed

Lines changed: 35 additions & 1 deletion

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@tanstack/devtools-bundler-core': patch
3+
'@tanstack/devtools-vite': patch
4+
---
5+
6+
Source injection no longer stops after the dev server parses a regex literal. Before this fix, one regex literal anywhere in the module graph made every later file get zero `data-tsd-source` attributes.

‎packages/devtools-bundler-core/src/ast-utils.test.ts‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,31 @@ describe('forEachChild', () => {
4444

4545
expect(visited).toEqual([real])
4646
})
47+
48+
it('does not throw when a cached child key holds a primitive in a later node', () => {
49+
// A regex literal has an object `value`, a string literal has a string
50+
// `value`, and both share one type, so the cached keys include `value`.
51+
const regexLiteral = {
52+
type: 'ForEachSharedLiteral',
53+
start: 0,
54+
end: 6,
55+
value: {},
56+
} as unknown as Node
57+
const stringLiteral = {
58+
type: 'ForEachSharedLiteral',
59+
start: 0,
60+
end: 12,
61+
value: 'use client',
62+
} as unknown as Node
63+
64+
forEachChild(regexLiteral, () => {})
65+
66+
const visited: Array<Node> = []
67+
expect(() =>
68+
forEachChild(stringLiteral, (child) => visited.push(child)),
69+
).not.toThrow()
70+
expect(visited).toEqual([])
71+
})
4772
})
4873

4974
describe('walk', () => {

‎packages/devtools-bundler-core/src/ast-utils.ts‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@ export function forEachChild(node: Node, callback: (child: Node) => void) {
3232
const keys = getChildKeys(node)
3333
for (const key of keys) {
3434
const value = (node as any)[key]
35-
if (value === null) continue
35+
// Keys are cached from the first node of a type, so another node of the
36+
// same type can hold a primitive there (a regex Literal has an object
37+
// `value`, a string Literal has a string).
38+
if (value === null || typeof value !== 'object') continue
3639
if (Array.isArray(value)) {
3740
for (const item of value) {
3841
if (typeof item === 'object' && item !== null && 'type' in item) {

0 commit comments

Comments
 (0)