@tanstack/devtools-vite 0.3.0 → 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.
@@ -57,22 +57,20 @@ export default function DevtoolsExample() {
57
57
  )
58
58
  expect(output).toBe(
59
59
  removeEmptySpace(`
60
- import { ReactQueryDevtoolsPanel } from '@tanstack/react-query-devtools';
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
- import { ReactQueryDevtoolsPanel } from '@tanstack/react-query-devtools';
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
- import { ReactQueryDevtoolsPanel } from '@tanstack/react-query-devtools';
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
  })
@@ -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
- path.remove()
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.remove()
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.remove()
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
  })
package/src/utils.ts CHANGED
@@ -11,6 +11,7 @@ export const handleDevToolsViteRequest = (
11
11
  ) => {
12
12
  if (req.url?.includes('__tsd/open-source')) {
13
13
  const searchParams = new URLSearchParams(req.url.split('?')[1])
14
+
14
15
  const source = searchParams.get('source')
15
16
  if (!source) {
16
17
  return