@tanstack/devtools-vite 0.3.1 → 0.3.2
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/dist/esm/remove-devtools.js +81 -3
- package/dist/esm/remove-devtools.js.map +1 -1
- package/package.json +1 -1
- package/src/remove-devtools.test.ts +347 -16
- package/src/remove-devtools.ts +125 -5
|
@@ -4,9 +4,56 @@ const isTanStackDevtoolsImport = (source) => source === "@tanstack/react-devtool
|
|
|
4
4
|
const getImportedNames = (importDecl) => {
|
|
5
5
|
return importDecl.specifiers.map((spec) => spec.local.name);
|
|
6
6
|
};
|
|
7
|
+
const getLeftoverImports = (node) => {
|
|
8
|
+
const finalReferences = [];
|
|
9
|
+
node.traverse({
|
|
10
|
+
JSXAttribute(path) {
|
|
11
|
+
const node2 = path.node;
|
|
12
|
+
const propName = typeof node2.name.name === "string" ? node2.name.name : node2.name.name.name;
|
|
13
|
+
if (propName === "plugins" && node2.value?.type === "JSXExpressionContainer" && node2.value.expression.type === "ArrayExpression") {
|
|
14
|
+
const elements = node2.value.expression.elements;
|
|
15
|
+
elements.forEach((el) => {
|
|
16
|
+
if (el?.type === "ObjectExpression") {
|
|
17
|
+
const props = el.properties;
|
|
18
|
+
const referencesToRemove = props.map((prop) => {
|
|
19
|
+
if (prop.type === "ObjectProperty" && prop.key.type === "Identifier" && prop.key.name === "render") {
|
|
20
|
+
const value = prop.value;
|
|
21
|
+
if (value.type === "JSXElement" && value.openingElement.name.type === "JSXIdentifier") {
|
|
22
|
+
const elementName = value.openingElement.name.name;
|
|
23
|
+
return elementName;
|
|
24
|
+
}
|
|
25
|
+
if (value.type === "ArrowFunctionExpression" || value.type === "FunctionExpression") {
|
|
26
|
+
const body = value.body;
|
|
27
|
+
if (body.type === "JSXElement" && body.openingElement.name.type === "JSXIdentifier") {
|
|
28
|
+
const elementName = body.openingElement.name.name;
|
|
29
|
+
return elementName;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (value.type === "Identifier") {
|
|
33
|
+
const elementName = value.name;
|
|
34
|
+
return elementName;
|
|
35
|
+
}
|
|
36
|
+
if (value.type === "CallExpression" && value.callee.type === "Identifier") {
|
|
37
|
+
const elementName = value.callee.name;
|
|
38
|
+
return elementName;
|
|
39
|
+
}
|
|
40
|
+
return "";
|
|
41
|
+
}
|
|
42
|
+
return "";
|
|
43
|
+
}).filter(Boolean);
|
|
44
|
+
finalReferences.push(...referencesToRemove);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
return finalReferences;
|
|
51
|
+
};
|
|
7
52
|
const transform = (ast) => {
|
|
8
53
|
let didTransform = false;
|
|
9
54
|
const devtoolsComponentNames = /* @__PURE__ */ new Set();
|
|
55
|
+
const finalReferences = [];
|
|
56
|
+
const transformations = [];
|
|
10
57
|
trav(ast, {
|
|
11
58
|
ImportDeclaration(path) {
|
|
12
59
|
const importSource = path.node.source.value;
|
|
@@ -14,22 +61,52 @@ const transform = (ast) => {
|
|
|
14
61
|
getImportedNames(path.node).forEach(
|
|
15
62
|
(name) => devtoolsComponentNames.add(name)
|
|
16
63
|
);
|
|
17
|
-
|
|
64
|
+
transformations.push(() => {
|
|
65
|
+
path.remove();
|
|
66
|
+
});
|
|
18
67
|
didTransform = true;
|
|
19
68
|
}
|
|
20
69
|
},
|
|
21
70
|
JSXElement(path) {
|
|
22
71
|
const opening = path.node.openingElement;
|
|
23
72
|
if (opening.name.type === "JSXIdentifier" && devtoolsComponentNames.has(opening.name.name)) {
|
|
24
|
-
path
|
|
73
|
+
const refs = getLeftoverImports(path);
|
|
74
|
+
finalReferences.push(...refs);
|
|
75
|
+
transformations.push(() => {
|
|
76
|
+
path.remove();
|
|
77
|
+
});
|
|
25
78
|
didTransform = true;
|
|
26
79
|
}
|
|
27
80
|
if (opening.name.type === "JSXMemberExpression" && opening.name.object.type === "JSXIdentifier" && devtoolsComponentNames.has(opening.name.object.name)) {
|
|
28
|
-
path
|
|
81
|
+
const refs = getLeftoverImports(path);
|
|
82
|
+
finalReferences.push(...refs);
|
|
83
|
+
transformations.push(() => {
|
|
84
|
+
path.remove();
|
|
85
|
+
});
|
|
29
86
|
didTransform = true;
|
|
30
87
|
}
|
|
31
88
|
}
|
|
32
89
|
});
|
|
90
|
+
trav(ast, {
|
|
91
|
+
ImportDeclaration(path) {
|
|
92
|
+
const imports = path.node.specifiers;
|
|
93
|
+
for (const imported of imports) {
|
|
94
|
+
if (imported.type === "ImportSpecifier") {
|
|
95
|
+
if (finalReferences.includes(imported.local.name)) {
|
|
96
|
+
transformations.push(() => {
|
|
97
|
+
path.node.specifiers = path.node.specifiers.filter(
|
|
98
|
+
(spec) => spec !== imported
|
|
99
|
+
);
|
|
100
|
+
if (path.node.specifiers.length === 0) {
|
|
101
|
+
path.remove();
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
transformations.forEach((fn) => fn());
|
|
33
110
|
return didTransform;
|
|
34
111
|
};
|
|
35
112
|
function removeDevtools(code, id) {
|
|
@@ -45,6 +122,7 @@ function removeDevtools(code, id) {
|
|
|
45
122
|
}
|
|
46
123
|
return gen(ast, {
|
|
47
124
|
sourceMaps: true,
|
|
125
|
+
retainLines: true,
|
|
48
126
|
filename: id,
|
|
49
127
|
sourceFileName: filePath
|
|
50
128
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remove-devtools.js","sources":["../../src/remove-devtools.ts"],"sourcesContent":["import { gen, parse, trav } from './babel'\nimport type { t } from './babel'\nimport type { types as Babel } from '@babel/core'\nimport type { ParseResult } from '@babel/parser'\n\nconst isTanStackDevtoolsImport = (source: string) =>\n source === '@tanstack/react-devtools' ||\n source === '@tanstack/devtools' ||\n source === '@tanstack/solid-devtools'\n\nconst getImportedNames = (importDecl: t.ImportDeclaration) => {\n return importDecl.specifiers.map((spec) => spec.local.name)\n}\n\nconst transform = (ast: ParseResult<Babel.File>) => {\n let didTransform = false\n const devtoolsComponentNames = new Set()\n\n trav(ast, {\n ImportDeclaration(path) {\n const importSource = path.node.source.value\n if (isTanStackDevtoolsImport(importSource)) {\n getImportedNames(path.node).forEach((name) =>\n devtoolsComponentNames.add(name),\n )\n path.remove()\n didTransform = true\n }\n },\n JSXElement(path) {\n const opening = path.node.openingElement\n if (\n opening.name.type === 'JSXIdentifier' &&\n devtoolsComponentNames.has(opening.name.name)\n ) {\n path.remove()\n didTransform = true\n }\n
|
|
1
|
+
{"version":3,"file":"remove-devtools.js","sources":["../../src/remove-devtools.ts"],"sourcesContent":["import { gen, parse, trav } from './babel'\nimport type { t } from './babel'\nimport type { types as Babel, NodePath } from '@babel/core'\nimport type { ParseResult } from '@babel/parser'\n\nconst isTanStackDevtoolsImport = (source: string) =>\n source === '@tanstack/react-devtools' ||\n source === '@tanstack/devtools' ||\n source === '@tanstack/solid-devtools'\n\nconst getImportedNames = (importDecl: t.ImportDeclaration) => {\n return importDecl.specifiers.map((spec) => spec.local.name)\n}\n\nconst getLeftoverImports = (node: NodePath<t.JSXElement>) => {\n const finalReferences: Array<string> = []\n node.traverse({\n JSXAttribute(path) {\n const node = path.node\n const propName =\n typeof node.name.name === 'string'\n ? node.name.name\n : node.name.name.name\n\n if (\n propName === 'plugins' &&\n node.value?.type === 'JSXExpressionContainer' &&\n node.value.expression.type === 'ArrayExpression'\n ) {\n const elements = node.value.expression.elements\n\n elements.forEach((el) => {\n if (el?.type === 'ObjectExpression') {\n // { name: \"something\", render: ()=> <Component /> }\n const props = el.properties\n const referencesToRemove = props\n .map((prop) => {\n if (\n prop.type === 'ObjectProperty' &&\n prop.key.type === 'Identifier' &&\n prop.key.name === 'render'\n ) {\n const value = prop.value\n // handle <ReactRouterPanel />\n if (\n value.type === 'JSXElement' &&\n value.openingElement.name.type === 'JSXIdentifier'\n ) {\n const elementName = value.openingElement.name.name\n return elementName\n }\n // handle () => <ReactRouterPanel /> or function() { return <ReactRouterPanel /> }\n if (\n value.type === 'ArrowFunctionExpression' ||\n value.type === 'FunctionExpression'\n ) {\n const body = value.body\n if (\n body.type === 'JSXElement' &&\n body.openingElement.name.type === 'JSXIdentifier'\n ) {\n const elementName = body.openingElement.name.name\n return elementName\n }\n }\n // handle render: SomeComponent\n if (value.type === 'Identifier') {\n const elementName = value.name\n return elementName\n }\n\n // handle render: someFunction()\n if (\n value.type === 'CallExpression' &&\n value.callee.type === 'Identifier'\n ) {\n const elementName = value.callee.name\n return elementName\n }\n\n return ''\n }\n return ''\n })\n .filter(Boolean)\n finalReferences.push(...referencesToRemove)\n }\n })\n }\n },\n })\n return finalReferences\n}\n\nconst transform = (ast: ParseResult<Babel.File>) => {\n let didTransform = false\n const devtoolsComponentNames = new Set()\n const finalReferences: Array<string> = []\n\n const transformations: Array<() => void> = []\n\n trav(ast, {\n ImportDeclaration(path) {\n const importSource = path.node.source.value\n if (isTanStackDevtoolsImport(importSource)) {\n getImportedNames(path.node).forEach((name) =>\n devtoolsComponentNames.add(name),\n )\n\n transformations.push(() => {\n path.remove()\n })\n\n didTransform = true\n }\n },\n JSXElement(path) {\n const opening = path.node.openingElement\n if (\n opening.name.type === 'JSXIdentifier' &&\n devtoolsComponentNames.has(opening.name.name)\n ) {\n const refs = getLeftoverImports(path)\n\n finalReferences.push(...refs)\n transformations.push(() => {\n path.remove()\n })\n didTransform = true\n }\n if (\n opening.name.type === 'JSXMemberExpression' &&\n opening.name.object.type === 'JSXIdentifier' &&\n devtoolsComponentNames.has(opening.name.object.name)\n ) {\n const refs = getLeftoverImports(path)\n finalReferences.push(...refs)\n transformations.push(() => {\n path.remove()\n })\n didTransform = true\n }\n },\n })\n\n trav(ast, {\n ImportDeclaration(path) {\n const imports = path.node.specifiers\n for (const imported of imports) {\n if (imported.type === 'ImportSpecifier') {\n if (finalReferences.includes(imported.local.name)) {\n transformations.push(() => {\n // remove the specifier\n path.node.specifiers = path.node.specifiers.filter(\n (spec) => spec !== imported,\n )\n // remove whole import if nothing is left\n if (path.node.specifiers.length === 0) {\n path.remove()\n }\n })\n }\n }\n }\n },\n })\n\n transformations.forEach((fn) => fn())\n\n return didTransform\n}\n\nexport function removeDevtools(code: string, id: string) {\n const [filePath] = id.split('?')\n\n try {\n const ast = parse(code, {\n sourceType: 'module',\n plugins: ['jsx', 'typescript'],\n })\n const didTransform = transform(ast)\n if (!didTransform) {\n return { code }\n }\n return gen(ast, {\n sourceMaps: true,\n retainLines: true,\n filename: id,\n sourceFileName: filePath,\n })\n } catch (e) {\n return { code }\n }\n}\n"],"names":["node"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -57,22 +57,20 @@ export default function DevtoolsExample() {
|
|
|
57
57
|
)
|
|
58
58
|
expect(output).toBe(
|
|
59
59
|
removeEmptySpace(`
|
|
60
|
-
|
|
61
|
-
import { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools';
|
|
62
|
-
import {
|
|
60
|
+
import {
|
|
63
61
|
Link,
|
|
64
62
|
Outlet,
|
|
65
63
|
RouterProvider,
|
|
66
64
|
createRootRoute,
|
|
67
65
|
createRoute,
|
|
68
66
|
createRouter
|
|
69
|
-
} from '@tanstack/react-router';
|
|
67
|
+
} from '@tanstack/react-router';
|
|
70
68
|
|
|
71
69
|
|
|
72
70
|
export default function DevtoolsExample() {
|
|
73
|
-
return <>
|
|
71
|
+
return (<>
|
|
74
72
|
<RouterProvider router={router} />
|
|
75
|
-
|
|
73
|
+
</>);
|
|
76
74
|
|
|
77
75
|
}
|
|
78
76
|
|
|
@@ -131,9 +129,7 @@ export default function DevtoolsExample() {
|
|
|
131
129
|
)
|
|
132
130
|
expect(output).toBe(
|
|
133
131
|
removeEmptySpace(`
|
|
134
|
-
|
|
135
|
-
import { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools';
|
|
136
|
-
import {
|
|
132
|
+
import {
|
|
137
133
|
Link,
|
|
138
134
|
Outlet,
|
|
139
135
|
RouterProvider,
|
|
@@ -144,9 +140,9 @@ export default function DevtoolsExample() {
|
|
|
144
140
|
|
|
145
141
|
|
|
146
142
|
export default function DevtoolsExample() {
|
|
147
|
-
return
|
|
143
|
+
return ( <>
|
|
148
144
|
<RouterProvider router={router} />
|
|
149
|
-
|
|
145
|
+
</>);
|
|
150
146
|
|
|
151
147
|
}
|
|
152
148
|
|
|
@@ -205,9 +201,7 @@ export default function DevtoolsExample() {
|
|
|
205
201
|
)
|
|
206
202
|
expect(output).toBe(
|
|
207
203
|
removeEmptySpace(`
|
|
208
|
-
|
|
209
|
-
import { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools';
|
|
210
|
-
import {
|
|
204
|
+
import {
|
|
211
205
|
Link,
|
|
212
206
|
Outlet,
|
|
213
207
|
RouterProvider,
|
|
@@ -218,13 +212,350 @@ export default function DevtoolsExample() {
|
|
|
218
212
|
|
|
219
213
|
|
|
220
214
|
export default function DevtoolsExample() {
|
|
221
|
-
return <>
|
|
215
|
+
return (<>
|
|
222
216
|
<RouterProvider router={router} />
|
|
223
|
-
|
|
217
|
+
</>);
|
|
224
218
|
|
|
225
219
|
}
|
|
226
220
|
|
|
227
221
|
`),
|
|
228
222
|
)
|
|
229
223
|
})
|
|
224
|
+
|
|
225
|
+
test('it removes devtools and all possible variations of the plugins', () => {
|
|
226
|
+
const output = removeEmptySpace(
|
|
227
|
+
removeDevtools(
|
|
228
|
+
`
|
|
229
|
+
import { ReactQueryDevtoolsPanel } from '@tanstack/react-query-devtools'
|
|
230
|
+
import { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools'
|
|
231
|
+
import {
|
|
232
|
+
Link,
|
|
233
|
+
Outlet,
|
|
234
|
+
RouterProvider,
|
|
235
|
+
createRootRoute,
|
|
236
|
+
createRoute,
|
|
237
|
+
createRouter,
|
|
238
|
+
} from '@tanstack/react-router'
|
|
239
|
+
import * as Tools from '@tanstack/react-devtools'
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
export default function DevtoolsExample() {
|
|
244
|
+
return (
|
|
245
|
+
<>
|
|
246
|
+
<Tools.TanStackDevtools
|
|
247
|
+
eventBusConfig={{
|
|
248
|
+
connectToServerBus: true,
|
|
249
|
+
}}
|
|
250
|
+
plugins={[
|
|
251
|
+
{
|
|
252
|
+
name: 'TanStack Query',
|
|
253
|
+
render: <ReactQueryDevtoolsPanel />,
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
name: 'TanStack Query',
|
|
257
|
+
render: () => <ReactQueryDevtoolsPanel />,
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
name: 'TanStack Router',
|
|
261
|
+
render: TanStackRouterDevtoolsPanel,
|
|
262
|
+
},
|
|
263
|
+
some()
|
|
264
|
+
]}
|
|
265
|
+
/>
|
|
266
|
+
<RouterProvider router={router} />
|
|
267
|
+
</>
|
|
268
|
+
)
|
|
269
|
+
}`,
|
|
270
|
+
'test.jsx',
|
|
271
|
+
).code,
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
expect(output).toBe(
|
|
275
|
+
removeEmptySpace(`
|
|
276
|
+
import {
|
|
277
|
+
Link,
|
|
278
|
+
Outlet,
|
|
279
|
+
RouterProvider,
|
|
280
|
+
createRootRoute,
|
|
281
|
+
createRoute,
|
|
282
|
+
createRouter
|
|
283
|
+
} from '@tanstack/react-router' ;
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
export default function DevtoolsExample() {
|
|
288
|
+
return (
|
|
289
|
+
<>
|
|
290
|
+
<RouterProvider router={router} />
|
|
291
|
+
</>
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
`),
|
|
295
|
+
)
|
|
296
|
+
})
|
|
297
|
+
|
|
298
|
+
describe('removing plugin imports', () => {
|
|
299
|
+
test('it removes the plugin import from the import array if multiple import identifiers exist', () => {
|
|
300
|
+
const output = removeEmptySpace(
|
|
301
|
+
removeDevtools(
|
|
302
|
+
`
|
|
303
|
+
import { ReactQueryDevtoolsPanel, test } from '@tanstack/react-query-devtools'
|
|
304
|
+
|
|
305
|
+
import * as Tools from '@tanstack/react-devtools'
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
export default function DevtoolsExample() {
|
|
310
|
+
return (
|
|
311
|
+
<>
|
|
312
|
+
<Tools.TanStackDevtools
|
|
313
|
+
eventBusConfig={{
|
|
314
|
+
connectToServerBus: true,
|
|
315
|
+
}}
|
|
316
|
+
plugins={[
|
|
317
|
+
{
|
|
318
|
+
name: 'TanStack Query',
|
|
319
|
+
render: <ReactQueryDevtoolsPanel />,
|
|
320
|
+
}
|
|
321
|
+
]}
|
|
322
|
+
/>
|
|
323
|
+
<RouterProvider router={router} />
|
|
324
|
+
</>
|
|
325
|
+
)
|
|
326
|
+
}`,
|
|
327
|
+
'test.jsx',
|
|
328
|
+
).code,
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
expect(output).toBe(
|
|
332
|
+
removeEmptySpace(`
|
|
333
|
+
import { test } from '@tanstack/react-query-devtools';
|
|
334
|
+
|
|
335
|
+
export default function DevtoolsExample() {
|
|
336
|
+
return (
|
|
337
|
+
<>
|
|
338
|
+
<RouterProvider router={router} />
|
|
339
|
+
</>
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
`),
|
|
343
|
+
)
|
|
344
|
+
})
|
|
345
|
+
|
|
346
|
+
test("it doesn't remove the whole import if imported with * as", () => {
|
|
347
|
+
const output = removeEmptySpace(
|
|
348
|
+
removeDevtools(
|
|
349
|
+
`
|
|
350
|
+
import * as Stuff from '@tanstack/react-query-devtools'
|
|
351
|
+
|
|
352
|
+
import * as Tools from '@tanstack/react-devtools'
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
export default function DevtoolsExample() {
|
|
357
|
+
return (
|
|
358
|
+
<>
|
|
359
|
+
<Tools.TanStackDevtools
|
|
360
|
+
eventBusConfig={{
|
|
361
|
+
connectToServerBus: true,
|
|
362
|
+
}}
|
|
363
|
+
plugins={[
|
|
364
|
+
{
|
|
365
|
+
name: 'TanStack Query',
|
|
366
|
+
render: <Stuff.ReactQueryDevtoolsPanel />,
|
|
367
|
+
}
|
|
368
|
+
]}
|
|
369
|
+
/>
|
|
370
|
+
<RouterProvider router={router} />
|
|
371
|
+
</>
|
|
372
|
+
)
|
|
373
|
+
}`,
|
|
374
|
+
'test.jsx',
|
|
375
|
+
).code,
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
expect(output).toBe(
|
|
379
|
+
removeEmptySpace(`
|
|
380
|
+
import * as Stuff from '@tanstack/react-query-devtools';
|
|
381
|
+
|
|
382
|
+
export default function DevtoolsExample() {
|
|
383
|
+
return (
|
|
384
|
+
<>
|
|
385
|
+
<RouterProvider router={router} />
|
|
386
|
+
</>
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
`),
|
|
390
|
+
)
|
|
391
|
+
})
|
|
392
|
+
|
|
393
|
+
test('it removes the import completely if nothing is left', () => {
|
|
394
|
+
const output = removeEmptySpace(
|
|
395
|
+
removeDevtools(
|
|
396
|
+
`
|
|
397
|
+
import { ReactQueryDevtoolsPanel } from '@tanstack/react-query-devtools'
|
|
398
|
+
import * as Tools from '@tanstack/react-devtools'
|
|
399
|
+
|
|
400
|
+
export default function DevtoolsExample() {
|
|
401
|
+
return (
|
|
402
|
+
<>
|
|
403
|
+
<Tools.TanStackDevtools
|
|
404
|
+
eventBusConfig={{
|
|
405
|
+
connectToServerBus: true,
|
|
406
|
+
}}
|
|
407
|
+
plugins={[
|
|
408
|
+
{
|
|
409
|
+
name: 'TanStack Query',
|
|
410
|
+
render: <ReactQueryDevtoolsPanel />,
|
|
411
|
+
}
|
|
412
|
+
]}
|
|
413
|
+
/>
|
|
414
|
+
<RouterProvider router={router} />
|
|
415
|
+
</>
|
|
416
|
+
)
|
|
417
|
+
}`,
|
|
418
|
+
'test.jsx',
|
|
419
|
+
).code,
|
|
420
|
+
)
|
|
421
|
+
|
|
422
|
+
expect(output).toBe(
|
|
423
|
+
removeEmptySpace(`
|
|
424
|
+
export default function DevtoolsExample() {
|
|
425
|
+
return (
|
|
426
|
+
<>
|
|
427
|
+
<RouterProvider router={router} />
|
|
428
|
+
</>
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
`),
|
|
432
|
+
)
|
|
433
|
+
})
|
|
434
|
+
|
|
435
|
+
test('it removes the import completely even if used as a function instead of jsx', () => {
|
|
436
|
+
const output = removeEmptySpace(
|
|
437
|
+
removeDevtools(
|
|
438
|
+
`
|
|
439
|
+
import { plugin } from '@tanstack/react-query-devtools'
|
|
440
|
+
import * as Tools from '@tanstack/react-devtools'
|
|
441
|
+
|
|
442
|
+
export default function DevtoolsExample() {
|
|
443
|
+
return (
|
|
444
|
+
<>
|
|
445
|
+
<Tools.TanStackDevtools
|
|
446
|
+
eventBusConfig={{
|
|
447
|
+
connectToServerBus: true,
|
|
448
|
+
}}
|
|
449
|
+
plugins={[
|
|
450
|
+
{
|
|
451
|
+
name: 'TanStack Query',
|
|
452
|
+
render: plugin()
|
|
453
|
+
}
|
|
454
|
+
]}
|
|
455
|
+
/>
|
|
456
|
+
<RouterProvider router={router} />
|
|
457
|
+
</>
|
|
458
|
+
)
|
|
459
|
+
}`,
|
|
460
|
+
'test.jsx',
|
|
461
|
+
).code,
|
|
462
|
+
)
|
|
463
|
+
|
|
464
|
+
expect(output).toBe(
|
|
465
|
+
removeEmptySpace(`
|
|
466
|
+
export default function DevtoolsExample() {
|
|
467
|
+
return (
|
|
468
|
+
<>
|
|
469
|
+
<RouterProvider router={router} />
|
|
470
|
+
</>
|
|
471
|
+
);
|
|
472
|
+
}
|
|
473
|
+
`),
|
|
474
|
+
)
|
|
475
|
+
})
|
|
476
|
+
|
|
477
|
+
test('it removes the import completely even if used as a function inside of render', () => {
|
|
478
|
+
const output = removeEmptySpace(
|
|
479
|
+
removeDevtools(
|
|
480
|
+
`
|
|
481
|
+
import { ReactQueryDevtoolsPanel } from '@tanstack/react-query-devtools'
|
|
482
|
+
import * as Tools from '@tanstack/react-devtools'
|
|
483
|
+
|
|
484
|
+
export default function DevtoolsExample() {
|
|
485
|
+
return (
|
|
486
|
+
<>
|
|
487
|
+
<Tools.TanStackDevtools
|
|
488
|
+
eventBusConfig={{
|
|
489
|
+
connectToServerBus: true,
|
|
490
|
+
}}
|
|
491
|
+
plugins={[
|
|
492
|
+
{
|
|
493
|
+
name: 'TanStack Query',
|
|
494
|
+
render: () => <ReactQueryDevtoolsPanel />
|
|
495
|
+
}
|
|
496
|
+
]}
|
|
497
|
+
/>
|
|
498
|
+
<RouterProvider router={router} />
|
|
499
|
+
</>
|
|
500
|
+
)
|
|
501
|
+
}`,
|
|
502
|
+
'test.jsx',
|
|
503
|
+
).code,
|
|
504
|
+
)
|
|
505
|
+
|
|
506
|
+
expect(output).toBe(
|
|
507
|
+
removeEmptySpace(`
|
|
508
|
+
export default function DevtoolsExample() {
|
|
509
|
+
return (
|
|
510
|
+
<>
|
|
511
|
+
<RouterProvider router={router} />
|
|
512
|
+
</>
|
|
513
|
+
);
|
|
514
|
+
}
|
|
515
|
+
`),
|
|
516
|
+
)
|
|
517
|
+
})
|
|
518
|
+
|
|
519
|
+
test('it removes the import completely even if used as a reference inside of render', () => {
|
|
520
|
+
const output = removeEmptySpace(
|
|
521
|
+
removeDevtools(
|
|
522
|
+
`
|
|
523
|
+
import { ReactQueryDevtoolsPanel } from '@tanstack/react-query-devtools'
|
|
524
|
+
import * as Tools from '@tanstack/react-devtools'
|
|
525
|
+
|
|
526
|
+
export default function DevtoolsExample() {
|
|
527
|
+
return (
|
|
528
|
+
<>
|
|
529
|
+
<Tools.TanStackDevtools
|
|
530
|
+
eventBusConfig={{
|
|
531
|
+
connectToServerBus: true,
|
|
532
|
+
}}
|
|
533
|
+
plugins={[
|
|
534
|
+
{
|
|
535
|
+
name: 'TanStack Query',
|
|
536
|
+
render: ReactQueryDevtoolsPanel
|
|
537
|
+
}
|
|
538
|
+
]}
|
|
539
|
+
/>
|
|
540
|
+
<RouterProvider router={router} />
|
|
541
|
+
</>
|
|
542
|
+
)
|
|
543
|
+
}`,
|
|
544
|
+
'test.jsx',
|
|
545
|
+
).code,
|
|
546
|
+
)
|
|
547
|
+
|
|
548
|
+
expect(output).toBe(
|
|
549
|
+
removeEmptySpace(`
|
|
550
|
+
export default function DevtoolsExample() {
|
|
551
|
+
return (
|
|
552
|
+
<>
|
|
553
|
+
<RouterProvider router={router} />
|
|
554
|
+
</>
|
|
555
|
+
);
|
|
556
|
+
}
|
|
557
|
+
`),
|
|
558
|
+
)
|
|
559
|
+
})
|
|
560
|
+
})
|
|
230
561
|
})
|
package/src/remove-devtools.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { gen, parse, trav } from './babel'
|
|
2
2
|
import type { t } from './babel'
|
|
3
|
-
import type { types as Babel } from '@babel/core'
|
|
3
|
+
import type { types as Babel, NodePath } from '@babel/core'
|
|
4
4
|
import type { ParseResult } from '@babel/parser'
|
|
5
5
|
|
|
6
6
|
const isTanStackDevtoolsImport = (source: string) =>
|
|
@@ -12,9 +12,92 @@ const getImportedNames = (importDecl: t.ImportDeclaration) => {
|
|
|
12
12
|
return importDecl.specifiers.map((spec) => spec.local.name)
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
const getLeftoverImports = (node: NodePath<t.JSXElement>) => {
|
|
16
|
+
const finalReferences: Array<string> = []
|
|
17
|
+
node.traverse({
|
|
18
|
+
JSXAttribute(path) {
|
|
19
|
+
const node = path.node
|
|
20
|
+
const propName =
|
|
21
|
+
typeof node.name.name === 'string'
|
|
22
|
+
? node.name.name
|
|
23
|
+
: node.name.name.name
|
|
24
|
+
|
|
25
|
+
if (
|
|
26
|
+
propName === 'plugins' &&
|
|
27
|
+
node.value?.type === 'JSXExpressionContainer' &&
|
|
28
|
+
node.value.expression.type === 'ArrayExpression'
|
|
29
|
+
) {
|
|
30
|
+
const elements = node.value.expression.elements
|
|
31
|
+
|
|
32
|
+
elements.forEach((el) => {
|
|
33
|
+
if (el?.type === 'ObjectExpression') {
|
|
34
|
+
// { name: "something", render: ()=> <Component /> }
|
|
35
|
+
const props = el.properties
|
|
36
|
+
const referencesToRemove = props
|
|
37
|
+
.map((prop) => {
|
|
38
|
+
if (
|
|
39
|
+
prop.type === 'ObjectProperty' &&
|
|
40
|
+
prop.key.type === 'Identifier' &&
|
|
41
|
+
prop.key.name === 'render'
|
|
42
|
+
) {
|
|
43
|
+
const value = prop.value
|
|
44
|
+
// handle <ReactRouterPanel />
|
|
45
|
+
if (
|
|
46
|
+
value.type === 'JSXElement' &&
|
|
47
|
+
value.openingElement.name.type === 'JSXIdentifier'
|
|
48
|
+
) {
|
|
49
|
+
const elementName = value.openingElement.name.name
|
|
50
|
+
return elementName
|
|
51
|
+
}
|
|
52
|
+
// handle () => <ReactRouterPanel /> or function() { return <ReactRouterPanel /> }
|
|
53
|
+
if (
|
|
54
|
+
value.type === 'ArrowFunctionExpression' ||
|
|
55
|
+
value.type === 'FunctionExpression'
|
|
56
|
+
) {
|
|
57
|
+
const body = value.body
|
|
58
|
+
if (
|
|
59
|
+
body.type === 'JSXElement' &&
|
|
60
|
+
body.openingElement.name.type === 'JSXIdentifier'
|
|
61
|
+
) {
|
|
62
|
+
const elementName = body.openingElement.name.name
|
|
63
|
+
return elementName
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// handle render: SomeComponent
|
|
67
|
+
if (value.type === 'Identifier') {
|
|
68
|
+
const elementName = value.name
|
|
69
|
+
return elementName
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// handle render: someFunction()
|
|
73
|
+
if (
|
|
74
|
+
value.type === 'CallExpression' &&
|
|
75
|
+
value.callee.type === 'Identifier'
|
|
76
|
+
) {
|
|
77
|
+
const elementName = value.callee.name
|
|
78
|
+
return elementName
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return ''
|
|
82
|
+
}
|
|
83
|
+
return ''
|
|
84
|
+
})
|
|
85
|
+
.filter(Boolean)
|
|
86
|
+
finalReferences.push(...referencesToRemove)
|
|
87
|
+
}
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
})
|
|
92
|
+
return finalReferences
|
|
93
|
+
}
|
|
94
|
+
|
|
15
95
|
const transform = (ast: ParseResult<Babel.File>) => {
|
|
16
96
|
let didTransform = false
|
|
17
97
|
const devtoolsComponentNames = new Set()
|
|
98
|
+
const finalReferences: Array<string> = []
|
|
99
|
+
|
|
100
|
+
const transformations: Array<() => void> = []
|
|
18
101
|
|
|
19
102
|
trav(ast, {
|
|
20
103
|
ImportDeclaration(path) {
|
|
@@ -23,7 +106,11 @@ const transform = (ast: ParseResult<Babel.File>) => {
|
|
|
23
106
|
getImportedNames(path.node).forEach((name) =>
|
|
24
107
|
devtoolsComponentNames.add(name),
|
|
25
108
|
)
|
|
26
|
-
|
|
109
|
+
|
|
110
|
+
transformations.push(() => {
|
|
111
|
+
path.remove()
|
|
112
|
+
})
|
|
113
|
+
|
|
27
114
|
didTransform = true
|
|
28
115
|
}
|
|
29
116
|
},
|
|
@@ -33,21 +120,53 @@ const transform = (ast: ParseResult<Babel.File>) => {
|
|
|
33
120
|
opening.name.type === 'JSXIdentifier' &&
|
|
34
121
|
devtoolsComponentNames.has(opening.name.name)
|
|
35
122
|
) {
|
|
36
|
-
path
|
|
123
|
+
const refs = getLeftoverImports(path)
|
|
124
|
+
|
|
125
|
+
finalReferences.push(...refs)
|
|
126
|
+
transformations.push(() => {
|
|
127
|
+
path.remove()
|
|
128
|
+
})
|
|
37
129
|
didTransform = true
|
|
38
130
|
}
|
|
39
|
-
|
|
40
131
|
if (
|
|
41
132
|
opening.name.type === 'JSXMemberExpression' &&
|
|
42
133
|
opening.name.object.type === 'JSXIdentifier' &&
|
|
43
134
|
devtoolsComponentNames.has(opening.name.object.name)
|
|
44
135
|
) {
|
|
45
|
-
path
|
|
136
|
+
const refs = getLeftoverImports(path)
|
|
137
|
+
finalReferences.push(...refs)
|
|
138
|
+
transformations.push(() => {
|
|
139
|
+
path.remove()
|
|
140
|
+
})
|
|
46
141
|
didTransform = true
|
|
47
142
|
}
|
|
48
143
|
},
|
|
49
144
|
})
|
|
50
145
|
|
|
146
|
+
trav(ast, {
|
|
147
|
+
ImportDeclaration(path) {
|
|
148
|
+
const imports = path.node.specifiers
|
|
149
|
+
for (const imported of imports) {
|
|
150
|
+
if (imported.type === 'ImportSpecifier') {
|
|
151
|
+
if (finalReferences.includes(imported.local.name)) {
|
|
152
|
+
transformations.push(() => {
|
|
153
|
+
// remove the specifier
|
|
154
|
+
path.node.specifiers = path.node.specifiers.filter(
|
|
155
|
+
(spec) => spec !== imported,
|
|
156
|
+
)
|
|
157
|
+
// remove whole import if nothing is left
|
|
158
|
+
if (path.node.specifiers.length === 0) {
|
|
159
|
+
path.remove()
|
|
160
|
+
}
|
|
161
|
+
})
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
transformations.forEach((fn) => fn())
|
|
169
|
+
|
|
51
170
|
return didTransform
|
|
52
171
|
}
|
|
53
172
|
|
|
@@ -65,6 +184,7 @@ export function removeDevtools(code: string, id: string) {
|
|
|
65
184
|
}
|
|
66
185
|
return gen(ast, {
|
|
67
186
|
sourceMaps: true,
|
|
187
|
+
retainLines: true,
|
|
68
188
|
filename: id,
|
|
69
189
|
sourceFileName: filePath,
|
|
70
190
|
})
|