Skip to content

Commit 4636cb9

Browse files
authored
Feat/add ignore patterns (#223)
* fix * feat: implement ignore patterns for injectSource
1 parent 82afc87 commit 4636cb9

File tree

9 files changed

+170
-12
lines changed

9 files changed

+170
-12
lines changed

.changeset/major-lizards-notice.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@tanstack/devtools-vite': patch
3+
---
4+
5+
add ignore to inject source for granular manipulation

docs/vite-plugin.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,13 @@ export default {
165165
plugins: [
166166
devtools({
167167
injectSource: {
168-
enabled: true
168+
enabled: true,
169+
ignore: {
170+
// files to ignore source injection for
171+
files: ['node_modules', /.*\.test\.(js|ts|jsx|tsx)$/],
172+
// components to ignore source injection for
173+
components: ['YourComponent', /.*Lazy$/],
174+
},
169175
}
170176
}),
171177
// ... rest of your plugins here

packages/devtools-vite/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,14 @@
5959
"@tanstack/devtools-client": "workspace:*",
6060
"@tanstack/devtools-event-bus": "workspace:*",
6161
"chalk": "^5.6.2",
62-
"launch-editor": "^2.11.1"
62+
"launch-editor": "^2.11.1",
63+
"picomatch": "^4.0.3"
6364
},
6465
"devDependencies": {
6566
"@types/babel__core": "^7.20.5",
6667
"@types/babel__generator": "^7.27.0",
6768
"@types/babel__traverse": "^7.28.0",
69+
"@types/picomatch": "^4.0.2",
6870
"happy-dom": "^18.0.1"
6971
}
7072
}

packages/devtools-vite/src/inject-source.test.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -854,4 +854,36 @@ function test({...rest}) {
854854
)
855855
})
856856
})
857+
858+
describe('ignore patterns', () => {
859+
it('should skip injection for ignored component names (string)', () => {
860+
const output = addSourceToJsx(
861+
`
862+
function test() {
863+
return <Button />
864+
}
865+
`,
866+
'test.jsx',
867+
{
868+
components: ['Button'],
869+
},
870+
)
871+
expect(output).toBe(undefined)
872+
})
873+
874+
it('should skip injection for ignored file paths (glob)', () => {
875+
const output = addSourceToJsx(
876+
`
877+
function test() {
878+
return <div />
879+
}
880+
`,
881+
'src/components/ignored-file.jsx',
882+
{
883+
files: ['**/ignored-file.jsx'],
884+
},
885+
)
886+
expect(output).toBe(undefined)
887+
})
888+
})
857889
})

packages/devtools-vite/src/inject-source.ts

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { normalizePath } from 'vite'
22
import { gen, parse, t, trav } from './babel'
3+
import { matcher } from './matcher'
34
import type { types as Babel, NodePath } from '@babel/core'
45
import type { ParseResult } from '@babel/parser'
56

@@ -111,14 +112,19 @@ const transformJSX = (
111112
element: NodePath<t.JSXOpeningElement>,
112113
propsName: string | null,
113114
file: string,
115+
ignorePatterns: Array<string | RegExp>,
114116
) => {
115117
const loc = element.node.loc
116118
if (!loc) return
117119
const line = loc.start.line
118120
const column = loc.start.column
119121
const nameOfElement = getNameOfElement(element.node.name)
120-
121-
if (nameOfElement === 'Fragment' || nameOfElement === 'React.Fragment') {
122+
const isIgnored = matcher(ignorePatterns, nameOfElement)
123+
if (
124+
nameOfElement === 'Fragment' ||
125+
nameOfElement === 'React.Fragment' ||
126+
isIgnored
127+
) {
122128
return
123129
}
124130
const hasDataSource = element.node.attributes.some(
@@ -151,7 +157,11 @@ const transformJSX = (
151157
return true
152158
}
153159

154-
const transform = (ast: ParseResult<Babel.File>, file: string) => {
160+
const transform = (
161+
ast: ParseResult<Babel.File>,
162+
file: string,
163+
ignorePatterns: Array<string | RegExp>,
164+
) => {
155165
let didTransform = false
156166

157167
trav(ast, {
@@ -161,7 +171,12 @@ const transform = (ast: ParseResult<Babel.File>, file: string) => {
161171
)
162172
functionDeclaration.traverse({
163173
JSXOpeningElement(element) {
164-
const transformed = transformJSX(element, propsName, file)
174+
const transformed = transformJSX(
175+
element,
176+
propsName,
177+
file,
178+
ignorePatterns,
179+
)
165180
if (transformed) {
166181
didTransform = true
167182
}
@@ -172,7 +187,12 @@ const transform = (ast: ParseResult<Babel.File>, file: string) => {
172187
const propsName = getPropsNameFromFunctionDeclaration(path.node)
173188
path.traverse({
174189
JSXOpeningElement(element) {
175-
const transformed = transformJSX(element, propsName, file)
190+
const transformed = transformJSX(
191+
element,
192+
propsName,
193+
file,
194+
ignorePatterns,
195+
)
176196
if (transformed) {
177197
didTransform = true
178198
}
@@ -183,7 +203,12 @@ const transform = (ast: ParseResult<Babel.File>, file: string) => {
183203
const propsName = getPropsNameFromFunctionDeclaration(path.node)
184204
path.traverse({
185205
JSXOpeningElement(element) {
186-
const transformed = transformJSX(element, propsName, file)
206+
const transformed = transformJSX(
207+
element,
208+
propsName,
209+
file,
210+
ignorePatterns,
211+
)
187212
if (transformed) {
188213
didTransform = true
189214
}
@@ -204,7 +229,12 @@ const transform = (ast: ParseResult<Babel.File>, file: string) => {
204229

205230
path.traverse({
206231
JSXOpeningElement(element) {
207-
const transformed = transformJSX(element, propsName, file)
232+
const transformed = transformJSX(
233+
element,
234+
propsName,
235+
file,
236+
ignorePatterns,
237+
)
208238
if (transformed) {
209239
didTransform = true
210240
}
@@ -216,17 +246,28 @@ const transform = (ast: ParseResult<Babel.File>, file: string) => {
216246
return didTransform
217247
}
218248

219-
export function addSourceToJsx(code: string, id: string) {
249+
export function addSourceToJsx(
250+
code: string,
251+
id: string,
252+
ignore: {
253+
files?: Array<string | RegExp>
254+
components?: Array<string | RegExp>
255+
} = {},
256+
) {
220257
const [filePath] = id.split('?')
221258
// eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain
222259
const location = filePath?.replace(normalizePath(process.cwd()), '')!
223260

261+
const fileIgnored = matcher(ignore.files || [], location)
262+
if (fileIgnored) {
263+
return
264+
}
224265
try {
225266
const ast = parse(code, {
226267
sourceType: 'module',
227268
plugins: ['jsx', 'typescript'],
228269
})
229-
const didTransform = transform(ast, location)
270+
const didTransform = transform(ast, location, ignore.components || [])
230271
if (!didTransform) {
231272
return
232273
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { describe, expect, it } from 'vitest'
2+
import { matcher } from './matcher'
3+
4+
describe('matcher', () => {
5+
it('returns false when patterns is empty', () => {
6+
expect(matcher([], 'foo')).toBe(false)
7+
})
8+
9+
it('matches string patterns against component names', () => {
10+
const patterns = ['Button', 'MyComponent']
11+
expect(matcher(patterns, 'Button')).toBe(true)
12+
expect(matcher(patterns, 'Other')).toBe(false)
13+
})
14+
15+
it('matches regex patterns against component names', () => {
16+
const patterns = [/^Button$/, /Comp/]
17+
expect(matcher(patterns, 'Button')).toBe(true)
18+
expect(matcher(patterns, 'MyComp')).toBe(true)
19+
expect(matcher(patterns, 'NoMatch')).toBe(false)
20+
})
21+
22+
it('matches file paths with glob patterns', () => {
23+
const patterns = ['**/ignored-file.jsx']
24+
expect(matcher(patterns, 'src/components/ignored-file.jsx')).toBe(true)
25+
expect(matcher(patterns, 'src/components/other.jsx')).toBe(false)
26+
})
27+
28+
it('matches file paths with regex', () => {
29+
const patterns = [/ignored-file\.jsx$/]
30+
expect(matcher(patterns, 'src/components/ignored-file.jsx')).toBe(true)
31+
expect(matcher(patterns, 'src/components/other.jsx')).toBe(false)
32+
})
33+
})
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import picomatch from 'picomatch'
2+
3+
export const matcher = (
4+
patterns: Array<string | RegExp>,
5+
str: string,
6+
): boolean => {
7+
if (patterns.length === 0) {
8+
return false
9+
}
10+
const matchers = patterns.map((pattern) => {
11+
if (typeof pattern === 'string') {
12+
return picomatch(pattern)
13+
} else {
14+
return (s: string) => pattern.test(s)
15+
}
16+
})
17+
18+
return matchers.some((isMatch) => isMatch(str))
19+
}

packages/devtools-vite/src/plugin.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,13 @@ export type TanStackDevtoolsViteConfig = {
6262
* @default true
6363
*/
6464
enabled: boolean
65+
/**
66+
* List of files or patterns to ignore for source injection.
67+
*/
68+
ignore?: {
69+
files?: Array<string | RegExp>
70+
components?: Array<string | RegExp>
71+
}
6572
}
6673
}
6774

@@ -95,7 +102,7 @@ export const devtools = (args?: TanStackDevtoolsViteConfig): Array<Plugin> => {
95102
)
96103
return
97104

98-
return addSourceToJsx(code, id)
105+
return addSourceToJsx(code, id, args?.injectSource?.ignore)
99106
},
100107
},
101108
{

pnpm-lock.yaml

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)