@tanstack/devtools-vite 0.3.5 → 0.3.7

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.
@@ -0,0 +1,21 @@
1
+ import { PluginInjection } from '@tanstack/devtools-client';
2
+ import { types as Babel } from '@babel/core';
3
+ import { ParseResult } from '@babel/parser';
4
+ /**
5
+ * Finds the TanStackDevtools component name in the file
6
+ * Handles renamed imports and namespace imports
7
+ */
8
+ export declare const findDevtoolsComponentName: (ast: ParseResult<Babel.File>) => string | null;
9
+ export declare const transformAndInject: (ast: ParseResult<Babel.File>, injection: PluginInjection, devtoolsComponentName: string) => boolean;
10
+ /**
11
+ * Detects if a file contains TanStack devtools import
12
+ */
13
+ export declare function detectDevtoolsFile(code: string): boolean;
14
+ /**
15
+ * Injects a plugin into the TanStackDevtools component in a file
16
+ * Reads the file, transforms it, and writes it back
17
+ */
18
+ export declare function injectPluginIntoFile(filePath: string, injection: PluginInjection): {
19
+ success: boolean;
20
+ error?: string;
21
+ };
@@ -0,0 +1,228 @@
1
+ import { readFileSync, writeFileSync } from "node:fs";
2
+ import { gen, trav } from "./babel.js";
3
+ import { parse } from "@babel/parser";
4
+ import * as t from "@babel/types";
5
+ const detectDevtoolsImport = (code) => {
6
+ const devtoolsPackages = [
7
+ "@tanstack/react-devtools",
8
+ "@tanstack/solid-devtools",
9
+ "@tanstack/vue-devtools",
10
+ "@tanstack/svelte-devtools",
11
+ "@tanstack/angular-devtools"
12
+ ];
13
+ try {
14
+ const ast = parse(code, {
15
+ sourceType: "module",
16
+ plugins: ["jsx", "typescript"]
17
+ });
18
+ let hasDevtoolsImport = false;
19
+ trav(ast, {
20
+ ImportDeclaration(path) {
21
+ const importSource = path.node.source.value;
22
+ if (devtoolsPackages.includes(importSource)) {
23
+ hasDevtoolsImport = true;
24
+ path.stop();
25
+ }
26
+ }
27
+ });
28
+ return hasDevtoolsImport;
29
+ } catch (e) {
30
+ return false;
31
+ }
32
+ };
33
+ const findDevtoolsComponentName = (ast) => {
34
+ let componentName = null;
35
+ const devtoolsPackages = [
36
+ "@tanstack/react-devtools",
37
+ "@tanstack/solid-devtools",
38
+ "@tanstack/vue-devtools",
39
+ "@tanstack/svelte-devtools",
40
+ "@tanstack/angular-devtools"
41
+ ];
42
+ trav(ast, {
43
+ ImportDeclaration(path) {
44
+ const importSource = path.node.source.value;
45
+ if (devtoolsPackages.includes(importSource)) {
46
+ const namedImport = path.node.specifiers.find(
47
+ (spec) => t.isImportSpecifier(spec) && t.isIdentifier(spec.imported) && spec.imported.name === "TanStackDevtools"
48
+ );
49
+ if (namedImport && t.isImportSpecifier(namedImport)) {
50
+ componentName = namedImport.local.name;
51
+ path.stop();
52
+ return;
53
+ }
54
+ const namespaceImport = path.node.specifiers.find(
55
+ (spec) => t.isImportNamespaceSpecifier(spec)
56
+ );
57
+ if (namespaceImport && t.isImportNamespaceSpecifier(namespaceImport)) {
58
+ componentName = `${namespaceImport.local.name}.TanStackDevtools`;
59
+ path.stop();
60
+ return;
61
+ }
62
+ }
63
+ }
64
+ });
65
+ return componentName;
66
+ };
67
+ const transformAndInject = (ast, injection, devtoolsComponentName) => {
68
+ let didTransform = false;
69
+ const importName = injection.pluginImport?.importName;
70
+ const pluginType = injection.pluginImport?.type || "jsx";
71
+ const displayName = injection.pluginName;
72
+ if (!importName) {
73
+ return false;
74
+ }
75
+ const isNamespaceImport = devtoolsComponentName.includes(".");
76
+ trav(ast, {
77
+ JSXOpeningElement(path) {
78
+ const elementName = path.node.name;
79
+ let matches = false;
80
+ if (isNamespaceImport) {
81
+ if (t.isJSXMemberExpression(elementName)) {
82
+ const fullName = `${t.isJSXIdentifier(elementName.object) ? elementName.object.name : ""}.${t.isJSXIdentifier(elementName.property) ? elementName.property.name : ""}`;
83
+ matches = fullName === devtoolsComponentName;
84
+ }
85
+ } else {
86
+ matches = t.isJSXIdentifier(elementName) && elementName.name === devtoolsComponentName;
87
+ }
88
+ if (matches) {
89
+ const pluginsProp = path.node.attributes.find(
90
+ (attr) => t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name) && attr.name.name === "plugins"
91
+ );
92
+ if (pluginsProp && t.isJSXAttribute(pluginsProp)) {
93
+ if (pluginsProp.value && t.isJSXExpressionContainer(pluginsProp.value)) {
94
+ const expression = pluginsProp.value.expression;
95
+ if (t.isArrayExpression(expression)) {
96
+ const pluginExists = expression.elements.some((element) => {
97
+ if (!element) return false;
98
+ if (pluginType === "function") {
99
+ return t.isCallExpression(element) && t.isIdentifier(element.callee) && element.callee.name === importName;
100
+ }
101
+ if (!t.isObjectExpression(element)) return false;
102
+ return element.properties.some((prop) => {
103
+ if (!t.isObjectProperty(prop) || !t.isIdentifier(prop.key) || prop.key.name !== "name") {
104
+ return false;
105
+ }
106
+ return t.isStringLiteral(prop.value) && prop.value.value === displayName;
107
+ });
108
+ });
109
+ if (!pluginExists) {
110
+ if (pluginType === "function") {
111
+ expression.elements.push(
112
+ t.callExpression(t.identifier(importName), [])
113
+ );
114
+ } else {
115
+ const renderValue = t.jsxElement(
116
+ t.jsxOpeningElement(t.jsxIdentifier(importName), [], true),
117
+ null,
118
+ [],
119
+ true
120
+ );
121
+ expression.elements.push(
122
+ t.objectExpression([
123
+ t.objectProperty(
124
+ t.identifier("name"),
125
+ t.stringLiteral(displayName)
126
+ ),
127
+ t.objectProperty(t.identifier("render"), renderValue)
128
+ ])
129
+ );
130
+ }
131
+ didTransform = true;
132
+ }
133
+ }
134
+ }
135
+ } else {
136
+ let pluginElement;
137
+ if (pluginType === "function") {
138
+ pluginElement = t.callExpression(t.identifier(importName), []);
139
+ } else {
140
+ const renderValue = t.jsxElement(
141
+ t.jsxOpeningElement(t.jsxIdentifier(importName), [], true),
142
+ null,
143
+ [],
144
+ true
145
+ );
146
+ pluginElement = t.objectExpression([
147
+ t.objectProperty(
148
+ t.identifier("name"),
149
+ t.stringLiteral(displayName)
150
+ ),
151
+ t.objectProperty(t.identifier("render"), renderValue)
152
+ ]);
153
+ }
154
+ path.node.attributes.push(
155
+ t.jsxAttribute(
156
+ t.jsxIdentifier("plugins"),
157
+ t.jsxExpressionContainer(t.arrayExpression([pluginElement]))
158
+ )
159
+ );
160
+ didTransform = true;
161
+ }
162
+ }
163
+ }
164
+ });
165
+ if (didTransform) {
166
+ const importDeclaration = t.importDeclaration(
167
+ [t.importSpecifier(t.identifier(importName), t.identifier(importName))],
168
+ t.stringLiteral(injection.packageName)
169
+ );
170
+ let lastImportIndex = -1;
171
+ ast.program.body.forEach((node, index) => {
172
+ if (t.isImportDeclaration(node)) {
173
+ lastImportIndex = index;
174
+ }
175
+ });
176
+ ast.program.body.splice(lastImportIndex + 1, 0, importDeclaration);
177
+ }
178
+ return didTransform;
179
+ };
180
+ function detectDevtoolsFile(code) {
181
+ return detectDevtoolsImport(code);
182
+ }
183
+ function injectPluginIntoFile(filePath, injection) {
184
+ try {
185
+ const code = readFileSync(filePath, "utf-8");
186
+ const ast = parse(code, {
187
+ sourceType: "module",
188
+ plugins: ["jsx", "typescript"]
189
+ });
190
+ const devtoolsComponentName = findDevtoolsComponentName(ast);
191
+ if (!devtoolsComponentName) {
192
+ return {
193
+ success: false,
194
+ error: "Could not find TanStackDevtools import"
195
+ };
196
+ }
197
+ const didTransform = transformAndInject(
198
+ ast,
199
+ injection,
200
+ devtoolsComponentName
201
+ );
202
+ if (!didTransform) {
203
+ return {
204
+ success: false,
205
+ error: "Plugin already exists or no TanStackDevtools component found"
206
+ };
207
+ }
208
+ const result = gen(ast, {
209
+ sourceMaps: false,
210
+ retainLines: false
211
+ });
212
+ writeFileSync(filePath, result.code, "utf-8");
213
+ return { success: true };
214
+ } catch (e) {
215
+ console.error("Error injecting plugin:", e);
216
+ return {
217
+ success: false,
218
+ error: e instanceof Error ? e.message : "Unknown error"
219
+ };
220
+ }
221
+ }
222
+ export {
223
+ detectDevtoolsFile,
224
+ findDevtoolsComponentName,
225
+ injectPluginIntoFile,
226
+ transformAndInject
227
+ };
228
+ //# sourceMappingURL=inject-plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inject-plugin.js","sources":["../../src/inject-plugin.ts"],"sourcesContent":["import { readFileSync, writeFileSync } from 'node:fs'\nimport { gen, parse, t, trav } from './babel'\nimport type { PluginInjection } from '@tanstack/devtools-client'\nimport type { types as Babel } from '@babel/core'\nimport type { ParseResult } from '@babel/parser'\n\n/**\n * Detects if a file imports TanStack devtools packages\n * Handles: import X from '@tanstack/react-devtools'\n * import * as X from '@tanstack/react-devtools'\n * import { TanStackDevtools } from '@tanstack/react-devtools'\n */\nconst detectDevtoolsImport = (code: string): boolean => {\n const devtoolsPackages = [\n '@tanstack/react-devtools',\n '@tanstack/solid-devtools',\n '@tanstack/vue-devtools',\n '@tanstack/svelte-devtools',\n '@tanstack/angular-devtools',\n ]\n\n try {\n const ast = parse(code, {\n sourceType: 'module',\n plugins: ['jsx', 'typescript'],\n })\n\n let hasDevtoolsImport = false\n\n trav(ast, {\n ImportDeclaration(path) {\n const importSource = path.node.source.value\n if (devtoolsPackages.includes(importSource)) {\n hasDevtoolsImport = true\n path.stop()\n }\n },\n })\n\n return hasDevtoolsImport\n } catch (e) {\n return false\n }\n}\n\n/**\n * Finds the TanStackDevtools component name in the file\n * Handles renamed imports and namespace imports\n */\nexport const findDevtoolsComponentName = (\n ast: ParseResult<Babel.File>,\n): string | null => {\n let componentName: string | null = null\n const devtoolsPackages = [\n '@tanstack/react-devtools',\n '@tanstack/solid-devtools',\n '@tanstack/vue-devtools',\n '@tanstack/svelte-devtools',\n '@tanstack/angular-devtools',\n ]\n\n trav(ast, {\n ImportDeclaration(path) {\n const importSource = path.node.source.value\n if (devtoolsPackages.includes(importSource)) {\n // Check for: import { TanStackDevtools } from '@tanstack/...'\n const namedImport = path.node.specifiers.find(\n (spec) =>\n t.isImportSpecifier(spec) &&\n t.isIdentifier(spec.imported) &&\n spec.imported.name === 'TanStackDevtools',\n )\n if (namedImport && t.isImportSpecifier(namedImport)) {\n componentName = namedImport.local.name\n path.stop()\n return\n }\n\n // Check for: import * as DevtoolsName from '@tanstack/...'\n const namespaceImport = path.node.specifiers.find((spec) =>\n t.isImportNamespaceSpecifier(spec),\n )\n if (namespaceImport && t.isImportNamespaceSpecifier(namespaceImport)) {\n // For namespace imports, we need to look for DevtoolsName.TanStackDevtools\n componentName = `${namespaceImport.local.name}.TanStackDevtools`\n path.stop()\n return\n }\n }\n },\n })\n\n return componentName\n}\n\nexport const transformAndInject = (\n ast: ParseResult<Babel.File>,\n injection: PluginInjection,\n devtoolsComponentName: string,\n) => {\n let didTransform = false\n\n // Use pluginImport if provided, otherwise generate from package name\n const importName = injection.pluginImport?.importName\n const pluginType = injection.pluginImport?.type || 'jsx'\n const displayName = injection.pluginName\n\n if (!importName) {\n return false\n }\n // Handle namespace imports like DevtoolsModule.TanStackDevtools\n const isNamespaceImport = devtoolsComponentName.includes('.')\n\n // Find and modify the TanStackDevtools JSX element\n trav(ast, {\n JSXOpeningElement(path) {\n const elementName = path.node.name\n let matches = false\n\n if (isNamespaceImport) {\n // Handle <DevtoolsModule.TanStackDevtools />\n if (t.isJSXMemberExpression(elementName)) {\n const fullName = `${t.isJSXIdentifier(elementName.object) ? elementName.object.name : ''}.${t.isJSXIdentifier(elementName.property) ? elementName.property.name : ''}`\n matches = fullName === devtoolsComponentName\n }\n } else {\n // Handle <TanStackDevtools /> or <RenamedDevtools />\n matches =\n t.isJSXIdentifier(elementName) &&\n elementName.name === devtoolsComponentName\n }\n\n if (matches) {\n // Find the plugins prop\n const pluginsProp = path.node.attributes.find(\n (attr) =>\n t.isJSXAttribute(attr) &&\n t.isJSXIdentifier(attr.name) &&\n attr.name.name === 'plugins',\n )\n // plugins found\n if (pluginsProp && t.isJSXAttribute(pluginsProp)) {\n // Check if plugins prop has a value\n if (\n pluginsProp.value &&\n t.isJSXExpressionContainer(pluginsProp.value)\n ) {\n const expression = pluginsProp.value.expression\n\n // If it's an array expression, add our plugin to it\n if (t.isArrayExpression(expression)) {\n // Check if plugin already exists\n const pluginExists = expression.elements.some((element) => {\n if (!element) return false\n\n // For function-based plugins, check if the function call exists\n if (pluginType === 'function') {\n return (\n t.isCallExpression(element) &&\n t.isIdentifier(element.callee) &&\n element.callee.name === importName\n )\n }\n\n // For JSX plugins, check object with name property\n if (!t.isObjectExpression(element)) return false\n\n return element.properties.some((prop) => {\n if (\n !t.isObjectProperty(prop) ||\n !t.isIdentifier(prop.key) ||\n prop.key.name !== 'name'\n ) {\n return false\n }\n\n return (\n t.isStringLiteral(prop.value) &&\n prop.value.value === displayName\n )\n })\n })\n\n if (!pluginExists) {\n // For function-based plugins, add them directly as function calls\n // For JSX plugins, wrap them in objects with name and render\n if (pluginType === 'function') {\n // Add directly: FormDevtoolsPlugin()\n expression.elements.push(\n t.callExpression(t.identifier(importName), []),\n )\n } else {\n // Add as object: { name: \"...\", render: <Component /> }\n const renderValue = t.jsxElement(\n t.jsxOpeningElement(t.jsxIdentifier(importName), [], true),\n null,\n [],\n true,\n )\n\n expression.elements.push(\n t.objectExpression([\n t.objectProperty(\n t.identifier('name'),\n t.stringLiteral(displayName),\n ),\n t.objectProperty(t.identifier('render'), renderValue),\n ]),\n )\n }\n\n didTransform = true\n }\n }\n }\n } else {\n // No plugins prop exists, create one with our plugin\n // For function-based plugins, add them directly as function calls\n // For JSX plugins, wrap them in objects with name and render\n let pluginElement\n if (pluginType === 'function') {\n // Add directly: plugins={[FormDevtoolsPlugin()]}\n pluginElement = t.callExpression(t.identifier(importName), [])\n } else {\n // Add as object: plugins={[{ name: \"...\", render: <Component /> }]}\n const renderValue = t.jsxElement(\n t.jsxOpeningElement(t.jsxIdentifier(importName), [], true),\n null,\n [],\n true,\n )\n\n pluginElement = t.objectExpression([\n t.objectProperty(\n t.identifier('name'),\n t.stringLiteral(displayName),\n ),\n t.objectProperty(t.identifier('render'), renderValue),\n ])\n }\n\n path.node.attributes.push(\n t.jsxAttribute(\n t.jsxIdentifier('plugins'),\n t.jsxExpressionContainer(t.arrayExpression([pluginElement])),\n ),\n )\n\n didTransform = true\n }\n }\n },\n })\n\n // Add import at the top of the file if transform happened\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (didTransform) {\n const importDeclaration = t.importDeclaration(\n [t.importSpecifier(t.identifier(importName), t.identifier(importName))],\n t.stringLiteral(injection.packageName),\n )\n\n // Find the last import declaration\n let lastImportIndex = -1\n ast.program.body.forEach((node, index) => {\n if (t.isImportDeclaration(node)) {\n lastImportIndex = index\n }\n })\n\n // Insert after the last import or at the beginning\n ast.program.body.splice(lastImportIndex + 1, 0, importDeclaration)\n }\n\n return didTransform\n}\n\n/**\n * Detects if a file contains TanStack devtools import\n */\nexport function detectDevtoolsFile(code: string): boolean {\n return detectDevtoolsImport(code)\n}\n\n/**\n * Injects a plugin into the TanStackDevtools component in a file\n * Reads the file, transforms it, and writes it back\n */\nexport function injectPluginIntoFile(\n filePath: string,\n injection: PluginInjection,\n): { success: boolean; error?: string } {\n try {\n // Read the file\n const code = readFileSync(filePath, 'utf-8')\n\n // Parse the code\n const ast = parse(code, {\n sourceType: 'module',\n plugins: ['jsx', 'typescript'],\n })\n\n // Find the devtools component name (handles renamed imports)\n const devtoolsComponentName = findDevtoolsComponentName(ast)\n if (!devtoolsComponentName) {\n return {\n success: false,\n error: 'Could not find TanStackDevtools import',\n }\n }\n\n // Transform and inject\n const didTransform = transformAndInject(\n ast,\n injection,\n devtoolsComponentName,\n )\n\n if (!didTransform) {\n return {\n success: false,\n error: 'Plugin already exists or no TanStackDevtools component found',\n }\n }\n\n // Generate the new code\n const result = gen(ast, {\n sourceMaps: false,\n retainLines: false,\n })\n\n // Write back to file\n writeFileSync(filePath, result.code, 'utf-8')\n\n return { success: true }\n } catch (e) {\n console.error('Error injecting plugin:', e)\n return {\n success: false,\n error: e instanceof Error ? e.message : 'Unknown error',\n }\n }\n}\n"],"names":[],"mappings":";;;;AAYA,MAAM,uBAAuB,CAAC,SAA0B;AACtD,QAAM,mBAAmB;AAAA,IACvB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAGF,MAAI;AACF,UAAM,MAAM,MAAM,MAAM;AAAA,MACtB,YAAY;AAAA,MACZ,SAAS,CAAC,OAAO,YAAY;AAAA,IAAA,CAC9B;AAED,QAAI,oBAAoB;AAExB,SAAK,KAAK;AAAA,MACR,kBAAkB,MAAM;AACtB,cAAM,eAAe,KAAK,KAAK,OAAO;AACtC,YAAI,iBAAiB,SAAS,YAAY,GAAG;AAC3C,8BAAoB;AACpB,eAAK,KAAA;AAAA,QACP;AAAA,MACF;AAAA,IAAA,CACD;AAED,WAAO;AAAA,EACT,SAAS,GAAG;AACV,WAAO;AAAA,EACT;AACF;AAMO,MAAM,4BAA4B,CACvC,QACkB;AAClB,MAAI,gBAA+B;AACnC,QAAM,mBAAmB;AAAA,IACvB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAGF,OAAK,KAAK;AAAA,IACR,kBAAkB,MAAM;AACtB,YAAM,eAAe,KAAK,KAAK,OAAO;AACtC,UAAI,iBAAiB,SAAS,YAAY,GAAG;AAE3C,cAAM,cAAc,KAAK,KAAK,WAAW;AAAA,UACvC,CAAC,SACC,EAAE,kBAAkB,IAAI,KACxB,EAAE,aAAa,KAAK,QAAQ,KAC5B,KAAK,SAAS,SAAS;AAAA,QAAA;AAE3B,YAAI,eAAe,EAAE,kBAAkB,WAAW,GAAG;AACnD,0BAAgB,YAAY,MAAM;AAClC,eAAK,KAAA;AACL;AAAA,QACF;AAGA,cAAM,kBAAkB,KAAK,KAAK,WAAW;AAAA,UAAK,CAAC,SACjD,EAAE,2BAA2B,IAAI;AAAA,QAAA;AAEnC,YAAI,mBAAmB,EAAE,2BAA2B,eAAe,GAAG;AAEpE,0BAAgB,GAAG,gBAAgB,MAAM,IAAI;AAC7C,eAAK,KAAA;AACL;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EAAA,CACD;AAED,SAAO;AACT;AAEO,MAAM,qBAAqB,CAChC,KACA,WACA,0BACG;AACH,MAAI,eAAe;AAGnB,QAAM,aAAa,UAAU,cAAc;AAC3C,QAAM,aAAa,UAAU,cAAc,QAAQ;AACnD,QAAM,cAAc,UAAU;AAE9B,MAAI,CAAC,YAAY;AACf,WAAO;AAAA,EACT;AAEA,QAAM,oBAAoB,sBAAsB,SAAS,GAAG;AAG5D,OAAK,KAAK;AAAA,IACR,kBAAkB,MAAM;AACtB,YAAM,cAAc,KAAK,KAAK;AAC9B,UAAI,UAAU;AAEd,UAAI,mBAAmB;AAErB,YAAI,EAAE,sBAAsB,WAAW,GAAG;AACxC,gBAAM,WAAW,GAAG,EAAE,gBAAgB,YAAY,MAAM,IAAI,YAAY,OAAO,OAAO,EAAE,IAAI,EAAE,gBAAgB,YAAY,QAAQ,IAAI,YAAY,SAAS,OAAO,EAAE;AACpK,oBAAU,aAAa;AAAA,QACzB;AAAA,MACF,OAAO;AAEL,kBACE,EAAE,gBAAgB,WAAW,KAC7B,YAAY,SAAS;AAAA,MACzB;AAEA,UAAI,SAAS;AAEX,cAAM,cAAc,KAAK,KAAK,WAAW;AAAA,UACvC,CAAC,SACC,EAAE,eAAe,IAAI,KACrB,EAAE,gBAAgB,KAAK,IAAI,KAC3B,KAAK,KAAK,SAAS;AAAA,QAAA;AAGvB,YAAI,eAAe,EAAE,eAAe,WAAW,GAAG;AAEhD,cACE,YAAY,SACZ,EAAE,yBAAyB,YAAY,KAAK,GAC5C;AACA,kBAAM,aAAa,YAAY,MAAM;AAGrC,gBAAI,EAAE,kBAAkB,UAAU,GAAG;AAEnC,oBAAM,eAAe,WAAW,SAAS,KAAK,CAAC,YAAY;AACzD,oBAAI,CAAC,QAAS,QAAO;AAGrB,oBAAI,eAAe,YAAY;AAC7B,yBACE,EAAE,iBAAiB,OAAO,KAC1B,EAAE,aAAa,QAAQ,MAAM,KAC7B,QAAQ,OAAO,SAAS;AAAA,gBAE5B;AAGA,oBAAI,CAAC,EAAE,mBAAmB,OAAO,EAAG,QAAO;AAE3C,uBAAO,QAAQ,WAAW,KAAK,CAAC,SAAS;AACvC,sBACE,CAAC,EAAE,iBAAiB,IAAI,KACxB,CAAC,EAAE,aAAa,KAAK,GAAG,KACxB,KAAK,IAAI,SAAS,QAClB;AACA,2BAAO;AAAA,kBACT;AAEA,yBACE,EAAE,gBAAgB,KAAK,KAAK,KAC5B,KAAK,MAAM,UAAU;AAAA,gBAEzB,CAAC;AAAA,cACH,CAAC;AAED,kBAAI,CAAC,cAAc;AAGjB,oBAAI,eAAe,YAAY;AAE7B,6BAAW,SAAS;AAAA,oBAClB,EAAE,eAAe,EAAE,WAAW,UAAU,GAAG,CAAA,CAAE;AAAA,kBAAA;AAAA,gBAEjD,OAAO;AAEL,wBAAM,cAAc,EAAE;AAAA,oBACpB,EAAE,kBAAkB,EAAE,cAAc,UAAU,GAAG,CAAA,GAAI,IAAI;AAAA,oBACzD;AAAA,oBACA,CAAA;AAAA,oBACA;AAAA,kBAAA;AAGF,6BAAW,SAAS;AAAA,oBAClB,EAAE,iBAAiB;AAAA,sBACjB,EAAE;AAAA,wBACA,EAAE,WAAW,MAAM;AAAA,wBACnB,EAAE,cAAc,WAAW;AAAA,sBAAA;AAAA,sBAE7B,EAAE,eAAe,EAAE,WAAW,QAAQ,GAAG,WAAW;AAAA,oBAAA,CACrD;AAAA,kBAAA;AAAA,gBAEL;AAEA,+BAAe;AAAA,cACjB;AAAA,YACF;AAAA,UACF;AAAA,QACF,OAAO;AAIL,cAAI;AACJ,cAAI,eAAe,YAAY;AAE7B,4BAAgB,EAAE,eAAe,EAAE,WAAW,UAAU,GAAG,EAAE;AAAA,UAC/D,OAAO;AAEL,kBAAM,cAAc,EAAE;AAAA,cACpB,EAAE,kBAAkB,EAAE,cAAc,UAAU,GAAG,CAAA,GAAI,IAAI;AAAA,cACzD;AAAA,cACA,CAAA;AAAA,cACA;AAAA,YAAA;AAGF,4BAAgB,EAAE,iBAAiB;AAAA,cACjC,EAAE;AAAA,gBACA,EAAE,WAAW,MAAM;AAAA,gBACnB,EAAE,cAAc,WAAW;AAAA,cAAA;AAAA,cAE7B,EAAE,eAAe,EAAE,WAAW,QAAQ,GAAG,WAAW;AAAA,YAAA,CACrD;AAAA,UACH;AAEA,eAAK,KAAK,WAAW;AAAA,YACnB,EAAE;AAAA,cACA,EAAE,cAAc,SAAS;AAAA,cACzB,EAAE,uBAAuB,EAAE,gBAAgB,CAAC,aAAa,CAAC,CAAC;AAAA,YAAA;AAAA,UAC7D;AAGF,yBAAe;AAAA,QACjB;AAAA,MACF;AAAA,IACF;AAAA,EAAA,CACD;AAID,MAAI,cAAc;AAChB,UAAM,oBAAoB,EAAE;AAAA,MAC1B,CAAC,EAAE,gBAAgB,EAAE,WAAW,UAAU,GAAG,EAAE,WAAW,UAAU,CAAC,CAAC;AAAA,MACtE,EAAE,cAAc,UAAU,WAAW;AAAA,IAAA;AAIvC,QAAI,kBAAkB;AACtB,QAAI,QAAQ,KAAK,QAAQ,CAAC,MAAM,UAAU;AACxC,UAAI,EAAE,oBAAoB,IAAI,GAAG;AAC/B,0BAAkB;AAAA,MACpB;AAAA,IACF,CAAC;AAGD,QAAI,QAAQ,KAAK,OAAO,kBAAkB,GAAG,GAAG,iBAAiB;AAAA,EACnE;AAEA,SAAO;AACT;AAKO,SAAS,mBAAmB,MAAuB;AACxD,SAAO,qBAAqB,IAAI;AAClC;AAMO,SAAS,qBACd,UACA,WACsC;AACtC,MAAI;AAEF,UAAM,OAAO,aAAa,UAAU,OAAO;AAG3C,UAAM,MAAM,MAAM,MAAM;AAAA,MACtB,YAAY;AAAA,MACZ,SAAS,CAAC,OAAO,YAAY;AAAA,IAAA,CAC9B;AAGD,UAAM,wBAAwB,0BAA0B,GAAG;AAC3D,QAAI,CAAC,uBAAuB;AAC1B,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO;AAAA,MAAA;AAAA,IAEX;AAGA,UAAM,eAAe;AAAA,MACnB;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAGF,QAAI,CAAC,cAAc;AACjB,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO;AAAA,MAAA;AAAA,IAEX;AAGA,UAAM,SAAS,IAAI,KAAK;AAAA,MACtB,YAAY;AAAA,MACZ,aAAa;AAAA,IAAA,CACd;AAGD,kBAAc,UAAU,OAAO,MAAM,OAAO;AAE5C,WAAO,EAAE,SAAS,KAAA;AAAA,EACpB,SAAS,GAAG;AACV,YAAQ,MAAM,2BAA2B,CAAC;AAC1C,WAAO;AAAA,MACL,SAAS;AAAA,MACT,OAAO,aAAa,QAAQ,EAAE,UAAU;AAAA,IAAA;AAAA,EAE5C;AACF;"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,16 @@
1
+ import { OutdatedDeps } from '@tanstack/devtools-client';
2
+ /**
3
+ * Adds a plugin to the devtools configuration file
4
+ */
5
+ export declare const addPluginToDevtools: (devtoolsFileId: string | null, packageName: string, pluginName: string, pluginImport?: {
6
+ importName: string;
7
+ type: "jsx" | "function";
8
+ }) => {
9
+ success: boolean;
10
+ error?: string;
11
+ };
12
+ export declare const installPackage: (packageName: string) => Promise<{
13
+ success: boolean;
14
+ error?: string;
15
+ }>;
16
+ export declare const emitOutdatedDeps: () => Promise<OutdatedDeps | null>;
@@ -0,0 +1,139 @@
1
+ import { exec } from "node:child_process";
2
+ import { existsSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { devtoolsEventClient } from "@tanstack/devtools-client";
5
+ import chalk from "chalk";
6
+ import { tryParseJson, readPackageJson } from "./utils.js";
7
+ import { injectPluginIntoFile } from "./inject-plugin.js";
8
+ const getOutdatedCommand = (packageManager) => {
9
+ switch (packageManager) {
10
+ case "yarn":
11
+ return "yarn outdated --json";
12
+ case "pnpm":
13
+ return "pnpm outdated --format json";
14
+ case "bun":
15
+ return "bun outdated --json";
16
+ case "npm":
17
+ default:
18
+ return "npm outdated --json";
19
+ }
20
+ };
21
+ const addPluginToDevtools = (devtoolsFileId, packageName, pluginName, pluginImport) => {
22
+ if (!devtoolsFileId) {
23
+ const error = "Devtools file not found";
24
+ console.log(
25
+ chalk.yellowBright(
26
+ `[@tanstack/devtools-vite] Could not add plugin. ${error}.`
27
+ )
28
+ );
29
+ return { success: false, error };
30
+ }
31
+ const result = injectPluginIntoFile(devtoolsFileId, {
32
+ packageName,
33
+ pluginName,
34
+ pluginImport
35
+ });
36
+ if (result.success) {
37
+ console.log(
38
+ chalk.greenBright(
39
+ `[@tanstack/devtools-vite] Successfully added ${packageName} to devtools!`
40
+ )
41
+ );
42
+ } else {
43
+ console.log(
44
+ chalk.yellowBright(
45
+ `[@tanstack/devtools-vite] Could not add plugin: ${result.error}`
46
+ )
47
+ );
48
+ }
49
+ return result;
50
+ };
51
+ const getInstallCommand = (packageManager, packageName) => {
52
+ switch (packageManager) {
53
+ case "yarn":
54
+ return `yarn add -D ${packageName}`;
55
+ case "pnpm":
56
+ return `pnpm add -D ${packageName}`;
57
+ case "bun":
58
+ return `bun add -D ${packageName}`;
59
+ case "npm":
60
+ default:
61
+ return `npm install -D ${packageName}`;
62
+ }
63
+ };
64
+ const installPackage = async (packageName) => {
65
+ return new Promise((resolve) => {
66
+ const packageManager = detectPackageManager();
67
+ const installCommand = getInstallCommand(packageManager, packageName);
68
+ console.log(
69
+ chalk.blueBright(
70
+ `[@tanstack/devtools-vite] Installing ${packageName}...`
71
+ )
72
+ );
73
+ exec(installCommand, async (installError) => {
74
+ if (installError) {
75
+ console.error(
76
+ chalk.redBright(
77
+ `[@tanstack/devtools-vite] Failed to install ${packageName}:`
78
+ ),
79
+ installError.message
80
+ );
81
+ resolve({
82
+ success: false,
83
+ error: installError.message
84
+ });
85
+ return;
86
+ }
87
+ console.log(
88
+ chalk.greenBright(
89
+ `[@tanstack/devtools-vite] Successfully installed ${packageName}`
90
+ )
91
+ );
92
+ const updatedPackageJson = await readPackageJson();
93
+ devtoolsEventClient.emit("package-json-updated", {
94
+ packageJson: updatedPackageJson
95
+ });
96
+ resolve({ success: true });
97
+ });
98
+ });
99
+ };
100
+ const detectPackageManager = () => {
101
+ const cwd = process.cwd();
102
+ if (existsSync(join(cwd, "bun.lockb")) || existsSync(join(cwd, "bun.lock"))) {
103
+ return "bun";
104
+ }
105
+ if (existsSync(join(cwd, "pnpm-lock.yaml"))) {
106
+ return "pnpm";
107
+ }
108
+ if (existsSync(join(cwd, "yarn.lock"))) {
109
+ return "yarn";
110
+ }
111
+ if (existsSync(join(cwd, "package-lock.json"))) {
112
+ return "npm";
113
+ }
114
+ return "pnpm";
115
+ };
116
+ const emitOutdatedDeps = async () => {
117
+ return await new Promise((resolve) => {
118
+ const packageManager = detectPackageManager();
119
+ const outdatedCommand = getOutdatedCommand(packageManager);
120
+ exec(outdatedCommand, (_, stdout) => {
121
+ if (stdout) {
122
+ const newOutdatedDeps = tryParseJson(stdout);
123
+ if (!newOutdatedDeps) {
124
+ return;
125
+ }
126
+ devtoolsEventClient.emit("outdated-deps-read", {
127
+ outdatedDeps: newOutdatedDeps
128
+ });
129
+ resolve(newOutdatedDeps);
130
+ }
131
+ });
132
+ });
133
+ };
134
+ export {
135
+ addPluginToDevtools,
136
+ emitOutdatedDeps,
137
+ installPackage
138
+ };
139
+ //# sourceMappingURL=package-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"package-manager.js","sources":["../../src/package-manager.ts"],"sourcesContent":["import { exec } from 'node:child_process'\nimport { existsSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { devtoolsEventClient } from '@tanstack/devtools-client'\nimport chalk from 'chalk'\nimport { readPackageJson, tryParseJson } from './utils'\nimport { injectPluginIntoFile } from './inject-plugin'\nimport type { OutdatedDeps } from '@tanstack/devtools-client'\n\n/**\n * Gets the outdated command for the detected package manager\n */\nconst getOutdatedCommand = (packageManager: string): string => {\n switch (packageManager) {\n case 'yarn':\n return 'yarn outdated --json'\n case 'pnpm':\n return 'pnpm outdated --format json'\n case 'bun':\n return 'bun outdated --json'\n case 'npm':\n default:\n return 'npm outdated --json'\n }\n}\n\n/**\n * Adds a plugin to the devtools configuration file\n */\nexport const addPluginToDevtools = (\n devtoolsFileId: string | null,\n packageName: string,\n pluginName: string,\n pluginImport?: { importName: string; type: 'jsx' | 'function' },\n): { success: boolean; error?: string } => {\n // Check if we found the devtools file\n if (!devtoolsFileId) {\n const error = 'Devtools file not found'\n console.log(\n chalk.yellowBright(\n `[@tanstack/devtools-vite] Could not add plugin. ${error}.`,\n ),\n )\n return { success: false, error }\n }\n\n // Inject the plugin into the file\n const result = injectPluginIntoFile(devtoolsFileId, {\n packageName,\n pluginName,\n pluginImport,\n })\n\n if (result.success) {\n console.log(\n chalk.greenBright(\n `[@tanstack/devtools-vite] Successfully added ${packageName} to devtools!`,\n ),\n )\n } else {\n console.log(\n chalk.yellowBright(\n `[@tanstack/devtools-vite] Could not add plugin: ${result.error}`,\n ),\n )\n }\n\n return result\n}\n/**\n * Gets the install command for the detected package manager\n */\nconst getInstallCommand = (\n packageManager: string,\n packageName: string,\n): string => {\n switch (packageManager) {\n case 'yarn':\n return `yarn add -D ${packageName}`\n case 'pnpm':\n return `pnpm add -D ${packageName}`\n case 'bun':\n return `bun add -D ${packageName}`\n case 'npm':\n default:\n return `npm install -D ${packageName}`\n }\n}\n\nexport const installPackage = async (\n packageName: string,\n): Promise<{\n success: boolean\n error?: string\n}> => {\n return new Promise((resolve) => {\n const packageManager = detectPackageManager()\n const installCommand = getInstallCommand(packageManager, packageName)\n\n console.log(\n chalk.blueBright(\n `[@tanstack/devtools-vite] Installing ${packageName}...`,\n ),\n )\n\n exec(installCommand, async (installError) => {\n if (installError) {\n console.error(\n chalk.redBright(\n `[@tanstack/devtools-vite] Failed to install ${packageName}:`,\n ),\n installError.message,\n )\n resolve({\n success: false,\n error: installError.message,\n })\n return\n }\n\n console.log(\n chalk.greenBright(\n `[@tanstack/devtools-vite] Successfully installed ${packageName}`,\n ),\n )\n\n // Read the updated package.json and emit the event\n const updatedPackageJson = await readPackageJson()\n devtoolsEventClient.emit('package-json-updated', {\n packageJson: updatedPackageJson,\n })\n\n resolve({ success: true })\n })\n })\n}\n\n/**\n * Detects the package manager used in the project by checking for lock files\n */\nconst detectPackageManager = (): 'npm' | 'yarn' | 'pnpm' | 'bun' => {\n const cwd = process.cwd()\n\n // Check for lock files in order of specificity\n if (existsSync(join(cwd, 'bun.lockb')) || existsSync(join(cwd, 'bun.lock'))) {\n return 'bun'\n }\n if (existsSync(join(cwd, 'pnpm-lock.yaml'))) {\n return 'pnpm'\n }\n if (existsSync(join(cwd, 'yarn.lock'))) {\n return 'yarn'\n }\n if (existsSync(join(cwd, 'package-lock.json'))) {\n return 'npm'\n }\n\n // Default to pnpm if no lock file is found\n return 'pnpm'\n}\n\nexport const emitOutdatedDeps = async () => {\n return await new Promise<OutdatedDeps | null>((resolve) => {\n const packageManager = detectPackageManager()\n const outdatedCommand = getOutdatedCommand(packageManager)\n\n exec(outdatedCommand, (_, stdout) => {\n // outdated commands exit with code 1 if there are outdated packages, but still output valid JSON\n if (stdout) {\n const newOutdatedDeps = tryParseJson<OutdatedDeps>(stdout)\n if (!newOutdatedDeps) {\n return\n }\n devtoolsEventClient.emit('outdated-deps-read', {\n outdatedDeps: newOutdatedDeps,\n })\n resolve(newOutdatedDeps)\n }\n })\n })\n}\n"],"names":[],"mappings":"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"}
@@ -9,7 +9,13 @@ export type TanStackDevtoolsViteConfig = {
9
9
  /**
10
10
  * The configuration options for the server event bus
11
11
  */
12
- eventBusConfig?: ServerEventBusConfig;
12
+ eventBusConfig?: ServerEventBusConfig & {
13
+ /**
14
+ * Should the server event bus be enabled or not
15
+ * @default true
16
+ */
17
+ enabled?: boolean;
18
+ };
13
19
  /**
14
20
  * Configuration for enhanced logging.
15
21
  */