@tanstack/devtools-vite 0.3.6 → 0.3.8

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":"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"}
@@ -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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"}
@@ -1,30 +1,15 @@
1
- import { exec } from "node:child_process";
2
1
  import { devtoolsEventClient } from "@tanstack/devtools-client";
3
2
  import { ServerEventBus } from "@tanstack/devtools-event-bus/server";
4
3
  import { normalizePath } from "vite";
5
4
  import chalk from "chalk";
6
- import { handleDevToolsViteRequest, readPackageJson, tryParseJson } from "./utils.js";
5
+ import { handleDevToolsViteRequest, readPackageJson } from "./utils.js";
7
6
  import { DEFAULT_EDITOR_CONFIG, handleOpenSource } from "./editor.js";
8
7
  import { removeDevtools } from "./remove-devtools.js";
9
8
  import { addSourceToJsx } from "./inject-source.js";
10
9
  import { enhanceConsoleLog } from "./enhance-logs.js";
10
+ import { detectDevtoolsFile, injectPluginIntoFile } from "./inject-plugin.js";
11
+ import { emitOutdatedDeps, installPackage, addPluginToDevtools } from "./package-manager.js";
11
12
  const defineDevtoolsConfig = (config) => config;
12
- const emitOutdatedDeps = async () => {
13
- return await new Promise((resolve) => {
14
- exec("npm outdated --json", (_, stdout) => {
15
- if (stdout) {
16
- const newOutdatedDeps = tryParseJson(stdout);
17
- if (!newOutdatedDeps) {
18
- return;
19
- }
20
- devtoolsEventClient.emit("outdated-deps-read", {
21
- outdatedDeps: newOutdatedDeps
22
- });
23
- resolve(newOutdatedDeps);
24
- }
25
- });
26
- });
27
- };
28
13
  const devtools = (args) => {
29
14
  let port = 5173;
30
15
  const logging = args?.logging ?? true;
@@ -33,6 +18,7 @@ const devtools = (args) => {
33
18
  const removeDevtoolsOnBuild = args?.removeDevtoolsOnBuild ?? true;
34
19
  const serverBusEnabled = args?.eventBusConfig?.enabled ?? true;
35
20
  const bus = new ServerEventBus(args?.eventBusConfig);
21
+ let devtoolsFileId = null;
36
22
  return [
37
23
  {
38
24
  enforce: "pre",
@@ -100,13 +86,12 @@ const devtools = (args) => {
100
86
  },
101
87
  {
102
88
  name: "@tanstack/devtools:remove-devtools-on-build",
103
- apply(_, { command }) {
104
- return command === "build" && removeDevtoolsOnBuild;
89
+ apply(config, { command }) {
90
+ return (command !== "serve" || config.mode === "production") && removeDevtoolsOnBuild;
105
91
  },
106
92
  enforce: "pre",
107
93
  transform(code, id) {
108
- if (id.includes("node_modules") || id.includes("?raw") || id.includes("dist") || id.includes("build"))
109
- return;
94
+ if (id.includes("node_modules") || id.includes("?raw")) return;
110
95
  const transform = removeDevtools(code, id);
111
96
  if (!transform) return;
112
97
  if (logging) {
@@ -129,6 +114,139 @@ ${chalk.greenBright(`[@tanstack/devtools-vite]`)} Removed devtools code from: ${
129
114
  async configureServer() {
130
115
  const packageJson = await readPackageJson();
131
116
  const outdatedDeps = emitOutdatedDeps().then((deps) => deps);
117
+ devtoolsEventClient.on("install-devtools", async (event) => {
118
+ const result = await installPackage(event.payload.packageName);
119
+ devtoolsEventClient.emit("devtools-installed", {
120
+ packageName: event.payload.packageName,
121
+ success: result.success,
122
+ error: result.error
123
+ });
124
+ if (result.success) {
125
+ const { packageName, pluginName, pluginImport } = event.payload;
126
+ console.log(
127
+ chalk.blueBright(
128
+ `[@tanstack/devtools-vite] Auto-adding ${packageName} to devtools...`
129
+ )
130
+ );
131
+ const injectResult = addPluginToDevtools(
132
+ devtoolsFileId,
133
+ packageName,
134
+ pluginName,
135
+ pluginImport
136
+ );
137
+ if (injectResult.success) {
138
+ devtoolsEventClient.emit("plugin-added", {
139
+ packageName,
140
+ success: true
141
+ });
142
+ const updatedPackageJson = await readPackageJson();
143
+ devtoolsEventClient.emit("package-json-read", {
144
+ packageJson: updatedPackageJson
145
+ });
146
+ }
147
+ }
148
+ });
149
+ devtoolsEventClient.on("add-plugin-to-devtools", (event) => {
150
+ const { packageName, pluginName, pluginImport } = event.payload;
151
+ console.log(
152
+ chalk.blueBright(
153
+ `[@tanstack/devtools-vite] Adding ${packageName} to devtools...`
154
+ )
155
+ );
156
+ const result = addPluginToDevtools(
157
+ devtoolsFileId,
158
+ packageName,
159
+ pluginName,
160
+ pluginImport
161
+ );
162
+ devtoolsEventClient.emit("plugin-added", {
163
+ packageName,
164
+ success: result.success,
165
+ error: result.error
166
+ });
167
+ });
168
+ devtoolsEventClient.on("bump-package-version", async (event) => {
169
+ const {
170
+ packageName,
171
+ devtoolsPackage,
172
+ pluginName,
173
+ minVersion,
174
+ pluginImport
175
+ } = event.payload;
176
+ console.log(
177
+ chalk.blueBright(
178
+ `[@tanstack/devtools-vite] Bumping ${packageName} to version ${minVersion}...`
179
+ )
180
+ );
181
+ const packageWithVersion = minVersion ? `${packageName}@^${minVersion}` : packageName;
182
+ const result = await installPackage(packageWithVersion);
183
+ if (!result.success) {
184
+ console.log(
185
+ chalk.redBright(
186
+ `[@tanstack/devtools-vite] Failed to bump ${packageName}: ${result.error}`
187
+ )
188
+ );
189
+ devtoolsEventClient.emit("devtools-installed", {
190
+ packageName: devtoolsPackage,
191
+ success: false,
192
+ error: result.error
193
+ });
194
+ return;
195
+ }
196
+ console.log(
197
+ chalk.greenBright(
198
+ `[@tanstack/devtools-vite] Successfully bumped ${packageName} to ${minVersion}!`
199
+ )
200
+ );
201
+ if (!devtoolsFileId) {
202
+ console.log(
203
+ chalk.yellowBright(
204
+ `[@tanstack/devtools-vite] Devtools file not found. Skipping auto-injection.`
205
+ )
206
+ );
207
+ devtoolsEventClient.emit("devtools-installed", {
208
+ packageName: devtoolsPackage,
209
+ success: true
210
+ });
211
+ return;
212
+ }
213
+ console.log(
214
+ chalk.blueBright(
215
+ `[@tanstack/devtools-vite] Adding ${devtoolsPackage} to devtools...`
216
+ )
217
+ );
218
+ const injectResult = injectPluginIntoFile(devtoolsFileId, {
219
+ packageName: devtoolsPackage,
220
+ pluginName,
221
+ pluginImport
222
+ });
223
+ if (injectResult.success) {
224
+ console.log(
225
+ chalk.greenBright(
226
+ `[@tanstack/devtools-vite] Successfully added ${devtoolsPackage} to devtools!`
227
+ )
228
+ );
229
+ devtoolsEventClient.emit("plugin-added", {
230
+ packageName: devtoolsPackage,
231
+ success: true
232
+ });
233
+ const updatedPackageJson = await readPackageJson();
234
+ devtoolsEventClient.emit("package-json-read", {
235
+ packageJson: updatedPackageJson
236
+ });
237
+ } else {
238
+ console.log(
239
+ chalk.redBright(
240
+ `[@tanstack/devtools-vite] Failed to add ${devtoolsPackage} to devtools: ${injectResult.error}`
241
+ )
242
+ );
243
+ devtoolsEventClient.emit("plugin-added", {
244
+ packageName: devtoolsPackage,
245
+ success: false,
246
+ error: injectResult.error
247
+ });
248
+ }
249
+ });
132
250
  devtoolsEventClient.on("mounted", async () => {
133
251
  devtoolsEventClient.emit("outdated-deps-read", {
134
252
  outdatedDeps: await outdatedDeps
@@ -159,6 +277,21 @@ ${chalk.greenBright(`[@tanstack/devtools-vite]`)} Removed devtools code from: ${
159
277
  return;
160
278
  return enhanceConsoleLog(code, id, port);
161
279
  }
280
+ },
281
+ {
282
+ name: "@tanstack/devtools:inject-plugin",
283
+ apply(config, { command }) {
284
+ return config.mode === "development" && command === "serve";
285
+ },
286
+ transform(code, id) {
287
+ if (!devtoolsFileId && detectDevtoolsFile(code)) {
288
+ const [filePath] = id.split("?");
289
+ if (filePath) {
290
+ devtoolsFileId = filePath;
291
+ }
292
+ }
293
+ return void 0;
294
+ }
162
295
  }
163
296
  ];
164
297
  };