(code: string, id: string)
| 74 | } |
| 75 | |
| 76 | export function removeDevtools(code: string, id: string) { |
| 77 | const filePath = id.split('?')[0]! |
| 78 | |
| 79 | try { |
| 80 | const result = parseSync(filePath, code, { |
| 81 | sourceType: 'module', |
| 82 | lang: 'tsx', |
| 83 | }) |
| 84 | if (result.errors.length > 0) return |
| 85 | |
| 86 | const s = new MagicString(code) |
| 87 | const devtoolsNames = new Set<string>() |
| 88 | const pluginRefs: Array<string> = [] |
| 89 | |
| 90 | // Pass 1: Collect devtools import names and mark for removal |
| 91 | walk(result.program, (node) => { |
| 92 | if ( |
| 93 | node.type === 'ImportDeclaration' && |
| 94 | isTanStackDevtoolsImport(node.source.value) |
| 95 | ) { |
| 96 | for (const spec of node.specifiers) { |
| 97 | devtoolsNames.add(spec.local.name) |
| 98 | } |
| 99 | let end = node.end |
| 100 | if (code[end] === '\n') end++ |
| 101 | s.remove(node.start, end) |
| 102 | } |
| 103 | }) |
| 104 | |
| 105 | if (devtoolsNames.size === 0) return |
| 106 | |
| 107 | // Pass 2: Find and remove devtools JSX elements, collect plugin references |
| 108 | walk(result.program, (node, parentNode) => { |
| 109 | if (node.type !== 'JSXElement') return |
| 110 | |
| 111 | const opening = node.openingElement |
| 112 | let matches = false |
| 113 | |
| 114 | if ( |
| 115 | opening.name.type === 'JSXIdentifier' && |
| 116 | devtoolsNames.has(opening.name.name) |
| 117 | ) { |
| 118 | matches = true |
| 119 | } else if ( |
| 120 | opening.name.type === 'JSXMemberExpression' && |
| 121 | opening.name.object.type === 'JSXIdentifier' && |
| 122 | devtoolsNames.has(opening.name.object.name) |
| 123 | ) { |
| 124 | matches = true |
| 125 | } |
| 126 | |
| 127 | if (!matches) return |
| 128 | |
| 129 | pluginRefs.push(...getPluginReferences(opening)) |
| 130 | |
| 131 | let end = node.end |
| 132 | if (code[end] === '\n') end++ |
| 133 | if (parentNode?.type === 'ParenthesizedExpression') { |
no test coverage detected