@tanstack/devtools-vite 0.2.3 → 0.2.5

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,18 @@
1
+ import { parse } from '@babel/parser';
2
+ import { default as generate } from '@babel/generator';
3
+ import * as t from '@babel/types';
4
+ export { parse, t };
5
+ export declare const trav: {
6
+ <S>(parent: t.Node, opts: import('@babel/traverse').TraverseOptions<S>, scope: import('@babel/traverse').Scope | undefined, state: S, parentPath?: import('@babel/traverse').NodePath): void;
7
+ (parent: t.Node, opts?: import('@babel/traverse').TraverseOptions, scope?: import('@babel/traverse').Scope, state?: any, parentPath?: import('@babel/traverse').NodePath): void;
8
+ visitors: typeof import('@babel/traverse').visitors;
9
+ verify: typeof import("@babel/traverse").visitors.verify;
10
+ explode: typeof import("@babel/traverse").visitors.explode;
11
+ cheap: (node: t.Node, enter: (node: t.Node) => void) => void;
12
+ node: (node: t.Node, opts: import('@babel/traverse').TraverseOptions, scope?: import('@babel/traverse').Scope, state?: any, path?: import('@babel/traverse').NodePath, skipKeys?: Record<string, boolean>) => void;
13
+ clearNode: (node: t.Node, opts?: t.RemovePropertiesOptions) => void;
14
+ removeProperties: (tree: t.Node, opts?: t.RemovePropertiesOptions) => t.Node;
15
+ hasType: (tree: t.Node, type: t.Node["type"], denylistTypes?: string[]) => boolean;
16
+ cache: typeof import('@babel/traverse').cache;
17
+ };
18
+ export declare const gen: typeof generate;
@@ -0,0 +1,19 @@
1
+ import { parse } from "@babel/parser";
2
+ import * as t from "@babel/types";
3
+ import generate from "@babel/generator";
4
+ import traverse from "@babel/traverse";
5
+ const trav = typeof traverse.default !== "undefined" ? (
6
+ // eslint-disable-next-line @typescript-eslint/consistent-type-imports
7
+ traverse.default
8
+ ) : traverse;
9
+ const gen = typeof generate.default !== "undefined" ? (
10
+ // eslint-disable-next-line @typescript-eslint/consistent-type-imports
11
+ generate.default
12
+ ) : generate;
13
+ export {
14
+ gen,
15
+ parse,
16
+ t,
17
+ trav
18
+ };
19
+ //# sourceMappingURL=babel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"babel.js","sources":["../../src/babel.ts"],"sourcesContent":["import { parse } from '@babel/parser'\nimport * as t from '@babel/types'\nimport generate from '@babel/generator'\nimport traverse from '@babel/traverse'\n\nexport { parse, t }\n\nexport const trav =\n typeof (traverse as any).default !== 'undefined'\n ? // eslint-disable-next-line @typescript-eslint/consistent-type-imports\n ((traverse as any).default as typeof import('@babel/traverse').default)\n : traverse\n\nexport const gen =\n typeof (generate as any).default !== 'undefined'\n ? // eslint-disable-next-line @typescript-eslint/consistent-type-imports\n ((generate as any).default as typeof import('@babel/generator').default)\n : generate\n"],"names":[],"mappings":";;;;AAOO,MAAM,OACX,OAAQ,SAAiB,YAAY;AAAA;AAAA,EAE/B,SAAiB;AAAA,IACnB;AAEC,MAAM,MACX,OAAQ,SAAiB,YAAY;AAAA;AAAA,EAE/B,SAAiB;AAAA,IACnB;"}
@@ -0,0 +1,3 @@
1
+ export declare function addSourceToJsx(code: string, id: string): import('@babel/generator').GeneratorResult | {
2
+ code: string;
3
+ };
@@ -0,0 +1,168 @@
1
+ import { normalizePath } from "vite";
2
+ import { gen, trav } from "./babel.js";
3
+ import { parse } from "@babel/parser";
4
+ import * as t from "@babel/types";
5
+ const getPropsNameFromFunctionDeclaration = (functionDeclaration) => {
6
+ let propsName = null;
7
+ if (functionDeclaration.type === "FunctionExpression") {
8
+ const firstArgument = functionDeclaration.params[0];
9
+ if (firstArgument && firstArgument.type === "Identifier") {
10
+ propsName = firstArgument.name;
11
+ }
12
+ if (firstArgument && firstArgument.type === "ObjectPattern") {
13
+ firstArgument.properties.forEach((prop) => {
14
+ if (prop.type === "RestElement" && prop.argument.type === "Identifier") {
15
+ propsName = prop.argument.name;
16
+ }
17
+ });
18
+ }
19
+ return propsName;
20
+ }
21
+ if (functionDeclaration.type === "ArrowFunctionExpression") {
22
+ const firstArgument = functionDeclaration.params[0];
23
+ if (firstArgument && firstArgument.type === "Identifier") {
24
+ propsName = firstArgument.name;
25
+ }
26
+ if (firstArgument && firstArgument.type === "ObjectPattern") {
27
+ firstArgument.properties.forEach((prop) => {
28
+ if (prop.type === "RestElement" && prop.argument.type === "Identifier") {
29
+ propsName = prop.argument.name;
30
+ }
31
+ });
32
+ }
33
+ return propsName;
34
+ }
35
+ if (functionDeclaration.type === "FunctionDeclaration") {
36
+ const firstArgument = functionDeclaration.params[0];
37
+ if (firstArgument && firstArgument.type === "Identifier") {
38
+ propsName = firstArgument.name;
39
+ }
40
+ if (firstArgument && firstArgument.type === "ObjectPattern") {
41
+ firstArgument.properties.forEach((prop) => {
42
+ if (prop.type === "RestElement" && prop.argument.type === "Identifier") {
43
+ propsName = prop.argument.name;
44
+ }
45
+ });
46
+ }
47
+ return propsName;
48
+ }
49
+ if (functionDeclaration.init?.type === "ArrowFunctionExpression" || functionDeclaration.init?.type === "FunctionExpression") {
50
+ const firstArgument = functionDeclaration.init.params[0];
51
+ if (firstArgument && firstArgument.type === "Identifier") {
52
+ propsName = firstArgument.name;
53
+ }
54
+ if (firstArgument && firstArgument.type === "ObjectPattern") {
55
+ firstArgument.properties.forEach((prop) => {
56
+ if (prop.type === "RestElement" && prop.argument.type === "Identifier") {
57
+ propsName = prop.argument.name;
58
+ }
59
+ });
60
+ }
61
+ }
62
+ return propsName;
63
+ };
64
+ const transformJSX = (element, propsName, file) => {
65
+ const loc = element.node.loc;
66
+ if (!loc) return;
67
+ const line = loc.start.line;
68
+ const column = loc.start.column;
69
+ const hasDataSource = element.node.attributes.some(
70
+ (attr) => attr.type === "JSXAttribute" && attr.name.type === "JSXIdentifier" && attr.name.name === "data-tsd-source"
71
+ );
72
+ const hasSpread = element.node.attributes.some(
73
+ (attr) => attr.type === "JSXSpreadAttribute" && attr.argument.type === "Identifier" && attr.argument.name === propsName
74
+ );
75
+ if (hasSpread || hasDataSource) {
76
+ return;
77
+ }
78
+ element.node.attributes.push(
79
+ t.jsxAttribute(
80
+ t.jsxIdentifier("data-tsd-source"),
81
+ t.stringLiteral(`${file}:${line}:${column}`)
82
+ )
83
+ );
84
+ return true;
85
+ };
86
+ const transform = (ast, file) => {
87
+ let didTransform = false;
88
+ trav(ast, {
89
+ FunctionDeclaration(functionDeclaration) {
90
+ const propsName = getPropsNameFromFunctionDeclaration(
91
+ functionDeclaration.node
92
+ );
93
+ functionDeclaration.traverse({
94
+ JSXOpeningElement(element) {
95
+ const transformed = transformJSX(element, propsName, file);
96
+ if (transformed) {
97
+ didTransform = true;
98
+ }
99
+ }
100
+ });
101
+ },
102
+ ArrowFunctionExpression(path) {
103
+ const propsName = getPropsNameFromFunctionDeclaration(path.node);
104
+ path.traverse({
105
+ JSXOpeningElement(element) {
106
+ const transformed = transformJSX(element, propsName, file);
107
+ if (transformed) {
108
+ didTransform = true;
109
+ }
110
+ }
111
+ });
112
+ },
113
+ FunctionExpression(path) {
114
+ const propsName = getPropsNameFromFunctionDeclaration(path.node);
115
+ path.traverse({
116
+ JSXOpeningElement(element) {
117
+ const transformed = transformJSX(element, propsName, file);
118
+ if (transformed) {
119
+ didTransform = true;
120
+ }
121
+ }
122
+ });
123
+ },
124
+ VariableDeclaration(path) {
125
+ const functionDeclaration = path.node.declarations.find((decl) => {
126
+ return decl.init?.type === "ArrowFunctionExpression" || decl.init?.type === "FunctionExpression";
127
+ });
128
+ if (!functionDeclaration) {
129
+ return;
130
+ }
131
+ const propsName = getPropsNameFromFunctionDeclaration(functionDeclaration);
132
+ path.traverse({
133
+ JSXOpeningElement(element) {
134
+ const transformed = transformJSX(element, propsName, file);
135
+ if (transformed) {
136
+ didTransform = true;
137
+ }
138
+ }
139
+ });
140
+ }
141
+ });
142
+ return didTransform;
143
+ };
144
+ function addSourceToJsx(code, id) {
145
+ const [filePath] = id.split("?");
146
+ const location = filePath?.replace(normalizePath(process.cwd()), "");
147
+ try {
148
+ const ast = parse(code, {
149
+ sourceType: "module",
150
+ plugins: ["jsx", "typescript"]
151
+ });
152
+ const didTransform = transform(ast, location);
153
+ if (!didTransform) {
154
+ return { code };
155
+ }
156
+ return gen(ast, {
157
+ sourceMaps: true,
158
+ filename: id,
159
+ sourceFileName: filePath
160
+ });
161
+ } catch (e) {
162
+ return { code };
163
+ }
164
+ }
165
+ export {
166
+ addSourceToJsx
167
+ };
168
+ //# sourceMappingURL=inject-source.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inject-source.js","sources":["../../src/inject-source.ts"],"sourcesContent":["import { normalizePath } from 'vite'\nimport { gen, parse, t, trav } from './babel'\nimport type { types as Babel, NodePath } from '@babel/core'\nimport type { ParseResult } from '@babel/parser'\n\nconst getPropsNameFromFunctionDeclaration = (\n functionDeclaration:\n | t.VariableDeclarator\n | t.FunctionExpression\n | t.FunctionDeclaration\n | t.ArrowFunctionExpression,\n) => {\n let propsName: string | null = null\n\n if (functionDeclaration.type === 'FunctionExpression') {\n const firstArgument = functionDeclaration.params[0]\n // handles (props) => {}\n if (firstArgument && firstArgument.type === 'Identifier') {\n propsName = firstArgument.name\n }\n // handles ({ ...props }) => {}\n if (firstArgument && firstArgument.type === 'ObjectPattern') {\n firstArgument.properties.forEach((prop) => {\n if (\n prop.type === 'RestElement' &&\n prop.argument.type === 'Identifier'\n ) {\n propsName = prop.argument.name\n }\n })\n }\n return propsName\n }\n if (functionDeclaration.type === 'ArrowFunctionExpression') {\n const firstArgument = functionDeclaration.params[0]\n // handles (props) => {}\n if (firstArgument && firstArgument.type === 'Identifier') {\n propsName = firstArgument.name\n }\n // handles ({ ...props }) => {}\n if (firstArgument && firstArgument.type === 'ObjectPattern') {\n firstArgument.properties.forEach((prop) => {\n if (\n prop.type === 'RestElement' &&\n prop.argument.type === 'Identifier'\n ) {\n propsName = prop.argument.name\n }\n })\n }\n return propsName\n }\n if (functionDeclaration.type === 'FunctionDeclaration') {\n const firstArgument = functionDeclaration.params[0]\n // handles (props) => {}\n if (firstArgument && firstArgument.type === 'Identifier') {\n propsName = firstArgument.name\n }\n // handles ({ ...props }) => {}\n if (firstArgument && firstArgument.type === 'ObjectPattern') {\n firstArgument.properties.forEach((prop) => {\n if (\n prop.type === 'RestElement' &&\n prop.argument.type === 'Identifier'\n ) {\n propsName = prop.argument.name\n }\n })\n }\n return propsName\n }\n // Arrow function case\n if (\n functionDeclaration.init?.type === 'ArrowFunctionExpression' ||\n functionDeclaration.init?.type === 'FunctionExpression'\n ) {\n const firstArgument = functionDeclaration.init.params[0]\n // handles (props) => {}\n if (firstArgument && firstArgument.type === 'Identifier') {\n propsName = firstArgument.name\n }\n // handles ({ ...props }) => {}\n if (firstArgument && firstArgument.type === 'ObjectPattern') {\n firstArgument.properties.forEach((prop) => {\n if (\n prop.type === 'RestElement' &&\n prop.argument.type === 'Identifier'\n ) {\n propsName = prop.argument.name\n }\n })\n }\n }\n return propsName\n}\n\nconst transformJSX = (\n element: NodePath<t.JSXOpeningElement>,\n propsName: string | null,\n file: string,\n) => {\n const loc = element.node.loc\n if (!loc) return\n const line = loc.start.line\n const column = loc.start.column\n\n const hasDataSource = element.node.attributes.some(\n (attr) =>\n attr.type === 'JSXAttribute' &&\n attr.name.type === 'JSXIdentifier' &&\n attr.name.name === 'data-tsd-source',\n )\n // Check if props are spread\n const hasSpread = element.node.attributes.some(\n (attr) =>\n attr.type === 'JSXSpreadAttribute' &&\n attr.argument.type === 'Identifier' &&\n attr.argument.name === propsName,\n )\n\n if (hasSpread || hasDataSource) {\n // Do not inject if props are spread\n return\n }\n\n // Inject data-source as a string: \"<file>:<line>:<column>\"\n element.node.attributes.push(\n t.jsxAttribute(\n t.jsxIdentifier('data-tsd-source'),\n t.stringLiteral(`${file}:${line}:${column}`),\n ),\n )\n\n return true\n}\n\nconst transform = (ast: ParseResult<Babel.File>, file: string) => {\n let didTransform = false\n\n trav(ast, {\n FunctionDeclaration(functionDeclaration) {\n const propsName = getPropsNameFromFunctionDeclaration(\n functionDeclaration.node,\n )\n functionDeclaration.traverse({\n JSXOpeningElement(element) {\n const transformed = transformJSX(element, propsName, file)\n if (transformed) {\n didTransform = true\n }\n },\n })\n },\n ArrowFunctionExpression(path) {\n const propsName = getPropsNameFromFunctionDeclaration(path.node)\n path.traverse({\n JSXOpeningElement(element) {\n const transformed = transformJSX(element, propsName, file)\n if (transformed) {\n didTransform = true\n }\n },\n })\n },\n FunctionExpression(path) {\n const propsName = getPropsNameFromFunctionDeclaration(path.node)\n path.traverse({\n JSXOpeningElement(element) {\n const transformed = transformJSX(element, propsName, file)\n if (transformed) {\n didTransform = true\n }\n },\n })\n },\n VariableDeclaration(path) {\n const functionDeclaration = path.node.declarations.find((decl) => {\n return (\n decl.init?.type === 'ArrowFunctionExpression' ||\n decl.init?.type === 'FunctionExpression'\n )\n })\n if (!functionDeclaration) {\n return\n }\n const propsName = getPropsNameFromFunctionDeclaration(functionDeclaration)\n\n path.traverse({\n JSXOpeningElement(element) {\n const transformed = transformJSX(element, propsName, file)\n if (transformed) {\n didTransform = true\n }\n },\n })\n },\n })\n\n return didTransform\n}\n\nexport function addSourceToJsx(code: string, id: string) {\n const [filePath] = id.split('?')\n // eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain\n const location = filePath?.replace(normalizePath(process.cwd()), '')!\n\n try {\n const ast = parse(code, {\n sourceType: 'module',\n plugins: ['jsx', 'typescript'],\n })\n const didTransform = transform(ast, location)\n if (!didTransform) {\n return { code }\n }\n return gen(ast, {\n sourceMaps: true,\n filename: id,\n sourceFileName: filePath,\n })\n } catch (e) {\n return { code }\n }\n}\n"],"names":[],"mappings":"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"}
@@ -22,6 +22,16 @@ export type TanStackDevtoolsViteConfig = {
22
22
  */
23
23
  enabled: boolean;
24
24
  };
25
+ /**
26
+ * Configuration for source injection.
27
+ */
28
+ injectSource: {
29
+ /**
30
+ * Whether to enable source injection via data-tsd-source.
31
+ * @default true
32
+ */
33
+ enabled: boolean;
34
+ };
25
35
  };
26
36
  export declare const defineDevtoolsConfig: (config: TanStackDevtoolsViteConfig) => TanStackDevtoolsViteConfig;
27
37
  export declare const devtools: (args?: TanStackDevtoolsViteConfig) => Array<Plugin>;
@@ -3,13 +3,27 @@ import chalk from "chalk";
3
3
  import { ServerEventBus } from "@tanstack/devtools-event-bus/server";
4
4
  import { handleDevToolsViteRequest } from "./utils.js";
5
5
  import { DEFAULT_EDITOR_CONFIG, handleOpenSource } from "./editor.js";
6
+ import { addSourceToJsx } from "./inject-source.js";
6
7
  const defineDevtoolsConfig = (config) => config;
7
8
  const devtools = (args) => {
8
9
  let port = 5173;
9
10
  const appDir = args?.appDir || "./src";
10
11
  const enhancedLogsConfig = args?.enhancedLogs ?? { enabled: true };
12
+ const injectSourceConfig = args?.injectSource ?? { enabled: true };
11
13
  const bus = new ServerEventBus(args?.eventBusConfig);
12
14
  return [
15
+ {
16
+ enforce: "pre",
17
+ name: "@tanstack/devtools:inject-source",
18
+ apply(config) {
19
+ return config.mode === "development" && injectSourceConfig.enabled;
20
+ },
21
+ transform(code, id) {
22
+ if (id.includes("node_modules") || id.includes("?raw") || id.includes("dist") || id.includes("build"))
23
+ return code;
24
+ return addSourceToJsx(code, id);
25
+ }
26
+ },
13
27
  {
14
28
  enforce: "pre",
15
29
  name: "@tanstack/devtools:custom-server",
@@ -50,6 +64,12 @@ const devtools = (args) => {
50
64
  return;
51
65
  })
52
66
  );
67
+ },
68
+ transform(code) {
69
+ if (code.includes("__TSD_PORT__")) {
70
+ code = code.replace("__TSD_PORT__", String(port));
71
+ }
72
+ return code;
53
73
  }
54
74
  },
55
75
  {
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","sources":["../../src/plugin.ts"],"sourcesContent":["import { normalizePath } from 'vite'\nimport chalk from 'chalk'\nimport { ServerEventBus } from '@tanstack/devtools-event-bus/server'\nimport { handleDevToolsViteRequest } from './utils'\nimport { DEFAULT_EDITOR_CONFIG, handleOpenSource } from './editor'\nimport type { EditorConfig } from './editor'\nimport type { ServerEventBusConfig } from '@tanstack/devtools-event-bus/server'\nimport type { Plugin } from 'vite'\n\nexport type TanStackDevtoolsViteConfig = {\n /** The directory where the react router app is located. Defaults to the \"./src\" relative to where vite.config is being defined. */\n appDir?: string\n /**\n * Configuration for the editor integration. Defaults to opening in VS code\n */\n editor?: EditorConfig\n /**\n * The configuration options for the server event bus\n */\n eventBusConfig?: ServerEventBusConfig\n /**\n * Configuration for enhanced logging.\n */\n enhancedLogs?: {\n /**\n * Whether to enable enhanced logging.\n * @default true\n */\n enabled: boolean\n }\n}\n\nexport const defineDevtoolsConfig = (config: TanStackDevtoolsViteConfig) =>\n config\n\nexport const devtools = (args?: TanStackDevtoolsViteConfig): Array<Plugin> => {\n let port = 5173\n const appDir = args?.appDir || './src'\n const enhancedLogsConfig = args?.enhancedLogs ?? { enabled: true }\n const bus = new ServerEventBus(args?.eventBusConfig)\n\n return [\n {\n enforce: 'pre',\n name: '@tanstack/devtools:custom-server',\n apply(config) {\n // Custom server is only needed in development for piping events to the client\n return config.mode === 'development'\n },\n configureServer(server) {\n bus.start()\n server.middlewares.use((req, _res, next) => {\n if (req.socket.localPort && req.socket.localPort !== port) {\n port = req.socket.localPort\n }\n next()\n })\n if (server.config.server.port) {\n port = server.config.server.port\n }\n\n server.httpServer?.on('listening', () => {\n port = server.config.server.port\n })\n\n const editor = args?.editor ?? DEFAULT_EDITOR_CONFIG\n const openInEditor = async (\n path: string | undefined,\n lineNum: string | undefined,\n ) => {\n if (!path) {\n return\n }\n await editor.open(path, lineNum)\n }\n server.middlewares.use((req, res, next) =>\n handleDevToolsViteRequest(req, res, next, (parsedData) => {\n const { data, routine } = parsedData\n if (routine === 'open-source') {\n return handleOpenSource({\n data: { type: data.type, data },\n openInEditor,\n appDir,\n })\n }\n return\n }),\n )\n },\n },\n {\n name: '@tanstack/devtools:better-console-logs',\n enforce: 'pre',\n apply(config) {\n return config.mode === 'development' && enhancedLogsConfig.enabled\n },\n transform(code, id) {\n // Ignore anything external\n if (\n id.includes('node_modules') ||\n id.includes('?raw') ||\n id.includes('dist') ||\n id.includes('build')\n )\n return code\n\n if (!code.includes('console.')) {\n return code\n }\n const lines = code.split('\\n')\n return lines\n .map((line, lineNumber) => {\n if (\n line.trim().startsWith('//') ||\n line.trim().startsWith('/**') ||\n line.trim().startsWith('*')\n ) {\n return line\n }\n // Do not add for arrow functions or return statements\n if (\n line.replaceAll(' ', '').includes('=>console.') ||\n line.includes('return console.')\n ) {\n return line\n }\n\n const column = line.indexOf('console.')\n const location = `${id.replace(normalizePath(process.cwd()), '')}:${lineNumber + 1}:${column + 1}`\n const logMessage = `'${chalk.magenta('LOG')} ${chalk.blueBright(`${location} - http://localhost:${port}/__tsd/open-source?source=${encodeURIComponent(id.replace(normalizePath(process.cwd()), ''))}&line=${lineNumber + 1}&column=${column + 1}`)}\\\\n → '`\n if (line.includes('console.log(')) {\n const newLine = `console.log(${logMessage},`\n return line.replace('console.log(', newLine)\n }\n if (line.includes('console.error(')) {\n const newLine = `console.error(${logMessage},`\n return line.replace('console.error(', newLine)\n }\n return line\n })\n .join('\\n')\n },\n },\n ]\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"plugin.js","sources":["../../src/plugin.ts"],"sourcesContent":["import { normalizePath } from 'vite'\nimport chalk from 'chalk'\nimport { ServerEventBus } from '@tanstack/devtools-event-bus/server'\nimport { handleDevToolsViteRequest } from './utils'\nimport { DEFAULT_EDITOR_CONFIG, handleOpenSource } from './editor'\nimport { addSourceToJsx } from './inject-source'\nimport type { EditorConfig } from './editor'\nimport type { ServerEventBusConfig } from '@tanstack/devtools-event-bus/server'\nimport type { Plugin } from 'vite'\n\nexport type TanStackDevtoolsViteConfig = {\n /** The directory where the react router app is located. Defaults to the \"./src\" relative to where vite.config is being defined. */\n appDir?: string\n /**\n * Configuration for the editor integration. Defaults to opening in VS code\n */\n editor?: EditorConfig\n /**\n * The configuration options for the server event bus\n */\n eventBusConfig?: ServerEventBusConfig\n /**\n * Configuration for enhanced logging.\n */\n enhancedLogs?: {\n /**\n * Whether to enable enhanced logging.\n * @default true\n */\n enabled: boolean\n }\n /**\n * Configuration for source injection.\n */\n injectSource: {\n /**\n * Whether to enable source injection via data-tsd-source.\n * @default true\n */\n enabled: boolean\n }\n}\n\nexport const defineDevtoolsConfig = (config: TanStackDevtoolsViteConfig) =>\n config\n\nexport const devtools = (args?: TanStackDevtoolsViteConfig): Array<Plugin> => {\n let port = 5173\n const appDir = args?.appDir || './src'\n const enhancedLogsConfig = args?.enhancedLogs ?? { enabled: true }\n const injectSourceConfig = args?.injectSource ?? { enabled: true }\n const bus = new ServerEventBus(args?.eventBusConfig)\n\n return [\n {\n enforce: 'pre',\n name: '@tanstack/devtools:inject-source',\n apply(config) {\n return config.mode === 'development' && injectSourceConfig.enabled\n },\n transform(code, id) {\n if (\n id.includes('node_modules') ||\n id.includes('?raw') ||\n id.includes('dist') ||\n id.includes('build')\n )\n return code\n\n return addSourceToJsx(code, id)\n },\n },\n {\n enforce: 'pre',\n name: '@tanstack/devtools:custom-server',\n apply(config) {\n // Custom server is only needed in development for piping events to the client\n return config.mode === 'development'\n },\n configureServer(server) {\n bus.start()\n server.middlewares.use((req, _res, next) => {\n if (req.socket.localPort && req.socket.localPort !== port) {\n port = req.socket.localPort\n }\n next()\n })\n if (server.config.server.port) {\n port = server.config.server.port\n }\n\n server.httpServer?.on('listening', () => {\n port = server.config.server.port\n })\n\n const editor = args?.editor ?? DEFAULT_EDITOR_CONFIG\n const openInEditor = async (\n path: string | undefined,\n lineNum: string | undefined,\n ) => {\n if (!path) {\n return\n }\n await editor.open(path, lineNum)\n }\n server.middlewares.use((req, res, next) =>\n handleDevToolsViteRequest(req, res, next, (parsedData) => {\n const { data, routine } = parsedData\n if (routine === 'open-source') {\n return handleOpenSource({\n data: { type: data.type, data },\n openInEditor,\n appDir,\n })\n }\n return\n }),\n )\n },\n transform(code) {\n if (code.includes('__TSD_PORT__')) {\n code = code.replace('__TSD_PORT__', String(port))\n }\n return code\n },\n },\n {\n name: '@tanstack/devtools:better-console-logs',\n enforce: 'pre',\n apply(config) {\n return config.mode === 'development' && enhancedLogsConfig.enabled\n },\n transform(code, id) {\n // Ignore anything external\n if (\n id.includes('node_modules') ||\n id.includes('?raw') ||\n id.includes('dist') ||\n id.includes('build')\n )\n return code\n\n if (!code.includes('console.')) {\n return code\n }\n const lines = code.split('\\n')\n return lines\n .map((line, lineNumber) => {\n if (\n line.trim().startsWith('//') ||\n line.trim().startsWith('/**') ||\n line.trim().startsWith('*')\n ) {\n return line\n }\n // Do not add for arrow functions or return statements\n if (\n line.replaceAll(' ', '').includes('=>console.') ||\n line.includes('return console.')\n ) {\n return line\n }\n\n const column = line.indexOf('console.')\n const location = `${id.replace(normalizePath(process.cwd()), '')}:${lineNumber + 1}:${column + 1}`\n const logMessage = `'${chalk.magenta('LOG')} ${chalk.blueBright(`${location} - http://localhost:${port}/__tsd/open-source?source=${encodeURIComponent(id.replace(normalizePath(process.cwd()), ''))}&line=${lineNumber + 1}&column=${column + 1}`)}\\\\n → '`\n if (line.includes('console.log(')) {\n const newLine = `console.log(${logMessage},`\n return line.replace('console.log(', newLine)\n }\n if (line.includes('console.error(')) {\n const newLine = `console.error(${logMessage},`\n return line.replace('console.error(', newLine)\n }\n return line\n })\n .join('\\n')\n },\n },\n ]\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/devtools-vite",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "TanStack Vite plugin used to enhance the core devtools with additional functionalities",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -41,9 +41,20 @@
41
41
  "vite": "^7.0.0"
42
42
  },
43
43
  "dependencies": {
44
+ "@babel/core": "^7.28.3",
45
+ "@babel/generator": "^7.28.3",
46
+ "@babel/parser": "^7.28.3",
47
+ "@babel/traverse": "^7.28.3",
48
+ "@babel/types": "^7.28.2",
44
49
  "chalk": "^5.6.0",
45
50
  "@tanstack/devtools-event-bus": "0.2.2"
46
51
  },
52
+ "devDependencies": {
53
+ "@types/babel__core": "^7.20.5",
54
+ "@types/babel__generator": "^7.27.0",
55
+ "@types/babel__traverse": "^7.28.0",
56
+ "happy-dom": "^18.0.1"
57
+ },
47
58
  "scripts": {
48
59
  "clean": "premove ./build ./dist",
49
60
  "lint:fix": "eslint ./src --fix",
package/src/babel.ts ADDED
@@ -0,0 +1,18 @@
1
+ import { parse } from '@babel/parser'
2
+ import * as t from '@babel/types'
3
+ import generate from '@babel/generator'
4
+ import traverse from '@babel/traverse'
5
+
6
+ export { parse, t }
7
+
8
+ export const trav =
9
+ typeof (traverse as any).default !== 'undefined'
10
+ ? // eslint-disable-next-line @typescript-eslint/consistent-type-imports
11
+ ((traverse as any).default as typeof import('@babel/traverse').default)
12
+ : traverse
13
+
14
+ export const gen =
15
+ typeof (generate as any).default !== 'undefined'
16
+ ? // eslint-disable-next-line @typescript-eslint/consistent-type-imports
17
+ ((generate as any).default as typeof import('@babel/generator').default)
18
+ : generate
@@ -0,0 +1,224 @@
1
+ import { normalizePath } from 'vite'
2
+ import { gen, parse, t, trav } from './babel'
3
+ import type { types as Babel, NodePath } from '@babel/core'
4
+ import type { ParseResult } from '@babel/parser'
5
+
6
+ const getPropsNameFromFunctionDeclaration = (
7
+ functionDeclaration:
8
+ | t.VariableDeclarator
9
+ | t.FunctionExpression
10
+ | t.FunctionDeclaration
11
+ | t.ArrowFunctionExpression,
12
+ ) => {
13
+ let propsName: string | null = null
14
+
15
+ if (functionDeclaration.type === 'FunctionExpression') {
16
+ const firstArgument = functionDeclaration.params[0]
17
+ // handles (props) => {}
18
+ if (firstArgument && firstArgument.type === 'Identifier') {
19
+ propsName = firstArgument.name
20
+ }
21
+ // handles ({ ...props }) => {}
22
+ if (firstArgument && firstArgument.type === 'ObjectPattern') {
23
+ firstArgument.properties.forEach((prop) => {
24
+ if (
25
+ prop.type === 'RestElement' &&
26
+ prop.argument.type === 'Identifier'
27
+ ) {
28
+ propsName = prop.argument.name
29
+ }
30
+ })
31
+ }
32
+ return propsName
33
+ }
34
+ if (functionDeclaration.type === 'ArrowFunctionExpression') {
35
+ const firstArgument = functionDeclaration.params[0]
36
+ // handles (props) => {}
37
+ if (firstArgument && firstArgument.type === 'Identifier') {
38
+ propsName = firstArgument.name
39
+ }
40
+ // handles ({ ...props }) => {}
41
+ if (firstArgument && firstArgument.type === 'ObjectPattern') {
42
+ firstArgument.properties.forEach((prop) => {
43
+ if (
44
+ prop.type === 'RestElement' &&
45
+ prop.argument.type === 'Identifier'
46
+ ) {
47
+ propsName = prop.argument.name
48
+ }
49
+ })
50
+ }
51
+ return propsName
52
+ }
53
+ if (functionDeclaration.type === 'FunctionDeclaration') {
54
+ const firstArgument = functionDeclaration.params[0]
55
+ // handles (props) => {}
56
+ if (firstArgument && firstArgument.type === 'Identifier') {
57
+ propsName = firstArgument.name
58
+ }
59
+ // handles ({ ...props }) => {}
60
+ if (firstArgument && firstArgument.type === 'ObjectPattern') {
61
+ firstArgument.properties.forEach((prop) => {
62
+ if (
63
+ prop.type === 'RestElement' &&
64
+ prop.argument.type === 'Identifier'
65
+ ) {
66
+ propsName = prop.argument.name
67
+ }
68
+ })
69
+ }
70
+ return propsName
71
+ }
72
+ // Arrow function case
73
+ if (
74
+ functionDeclaration.init?.type === 'ArrowFunctionExpression' ||
75
+ functionDeclaration.init?.type === 'FunctionExpression'
76
+ ) {
77
+ const firstArgument = functionDeclaration.init.params[0]
78
+ // handles (props) => {}
79
+ if (firstArgument && firstArgument.type === 'Identifier') {
80
+ propsName = firstArgument.name
81
+ }
82
+ // handles ({ ...props }) => {}
83
+ if (firstArgument && firstArgument.type === 'ObjectPattern') {
84
+ firstArgument.properties.forEach((prop) => {
85
+ if (
86
+ prop.type === 'RestElement' &&
87
+ prop.argument.type === 'Identifier'
88
+ ) {
89
+ propsName = prop.argument.name
90
+ }
91
+ })
92
+ }
93
+ }
94
+ return propsName
95
+ }
96
+
97
+ const transformJSX = (
98
+ element: NodePath<t.JSXOpeningElement>,
99
+ propsName: string | null,
100
+ file: string,
101
+ ) => {
102
+ const loc = element.node.loc
103
+ if (!loc) return
104
+ const line = loc.start.line
105
+ const column = loc.start.column
106
+
107
+ const hasDataSource = element.node.attributes.some(
108
+ (attr) =>
109
+ attr.type === 'JSXAttribute' &&
110
+ attr.name.type === 'JSXIdentifier' &&
111
+ attr.name.name === 'data-tsd-source',
112
+ )
113
+ // Check if props are spread
114
+ const hasSpread = element.node.attributes.some(
115
+ (attr) =>
116
+ attr.type === 'JSXSpreadAttribute' &&
117
+ attr.argument.type === 'Identifier' &&
118
+ attr.argument.name === propsName,
119
+ )
120
+
121
+ if (hasSpread || hasDataSource) {
122
+ // Do not inject if props are spread
123
+ return
124
+ }
125
+
126
+ // Inject data-source as a string: "<file>:<line>:<column>"
127
+ element.node.attributes.push(
128
+ t.jsxAttribute(
129
+ t.jsxIdentifier('data-tsd-source'),
130
+ t.stringLiteral(`${file}:${line}:${column}`),
131
+ ),
132
+ )
133
+
134
+ return true
135
+ }
136
+
137
+ const transform = (ast: ParseResult<Babel.File>, file: string) => {
138
+ let didTransform = false
139
+
140
+ trav(ast, {
141
+ FunctionDeclaration(functionDeclaration) {
142
+ const propsName = getPropsNameFromFunctionDeclaration(
143
+ functionDeclaration.node,
144
+ )
145
+ functionDeclaration.traverse({
146
+ JSXOpeningElement(element) {
147
+ const transformed = transformJSX(element, propsName, file)
148
+ if (transformed) {
149
+ didTransform = true
150
+ }
151
+ },
152
+ })
153
+ },
154
+ ArrowFunctionExpression(path) {
155
+ const propsName = getPropsNameFromFunctionDeclaration(path.node)
156
+ path.traverse({
157
+ JSXOpeningElement(element) {
158
+ const transformed = transformJSX(element, propsName, file)
159
+ if (transformed) {
160
+ didTransform = true
161
+ }
162
+ },
163
+ })
164
+ },
165
+ FunctionExpression(path) {
166
+ const propsName = getPropsNameFromFunctionDeclaration(path.node)
167
+ path.traverse({
168
+ JSXOpeningElement(element) {
169
+ const transformed = transformJSX(element, propsName, file)
170
+ if (transformed) {
171
+ didTransform = true
172
+ }
173
+ },
174
+ })
175
+ },
176
+ VariableDeclaration(path) {
177
+ const functionDeclaration = path.node.declarations.find((decl) => {
178
+ return (
179
+ decl.init?.type === 'ArrowFunctionExpression' ||
180
+ decl.init?.type === 'FunctionExpression'
181
+ )
182
+ })
183
+ if (!functionDeclaration) {
184
+ return
185
+ }
186
+ const propsName = getPropsNameFromFunctionDeclaration(functionDeclaration)
187
+
188
+ path.traverse({
189
+ JSXOpeningElement(element) {
190
+ const transformed = transformJSX(element, propsName, file)
191
+ if (transformed) {
192
+ didTransform = true
193
+ }
194
+ },
195
+ })
196
+ },
197
+ })
198
+
199
+ return didTransform
200
+ }
201
+
202
+ export function addSourceToJsx(code: string, id: string) {
203
+ const [filePath] = id.split('?')
204
+ // eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain
205
+ const location = filePath?.replace(normalizePath(process.cwd()), '')!
206
+
207
+ try {
208
+ const ast = parse(code, {
209
+ sourceType: 'module',
210
+ plugins: ['jsx', 'typescript'],
211
+ })
212
+ const didTransform = transform(ast, location)
213
+ if (!didTransform) {
214
+ return { code }
215
+ }
216
+ return gen(ast, {
217
+ sourceMaps: true,
218
+ filename: id,
219
+ sourceFileName: filePath,
220
+ })
221
+ } catch (e) {
222
+ return { code }
223
+ }
224
+ }
package/src/plugin.ts CHANGED
@@ -3,6 +3,7 @@ import chalk from 'chalk'
3
3
  import { ServerEventBus } from '@tanstack/devtools-event-bus/server'
4
4
  import { handleDevToolsViteRequest } from './utils'
5
5
  import { DEFAULT_EDITOR_CONFIG, handleOpenSource } from './editor'
6
+ import { addSourceToJsx } from './inject-source'
6
7
  import type { EditorConfig } from './editor'
7
8
  import type { ServerEventBusConfig } from '@tanstack/devtools-event-bus/server'
8
9
  import type { Plugin } from 'vite'
@@ -28,6 +29,16 @@ export type TanStackDevtoolsViteConfig = {
28
29
  */
29
30
  enabled: boolean
30
31
  }
32
+ /**
33
+ * Configuration for source injection.
34
+ */
35
+ injectSource: {
36
+ /**
37
+ * Whether to enable source injection via data-tsd-source.
38
+ * @default true
39
+ */
40
+ enabled: boolean
41
+ }
31
42
  }
32
43
 
33
44
  export const defineDevtoolsConfig = (config: TanStackDevtoolsViteConfig) =>
@@ -37,9 +48,28 @@ export const devtools = (args?: TanStackDevtoolsViteConfig): Array<Plugin> => {
37
48
  let port = 5173
38
49
  const appDir = args?.appDir || './src'
39
50
  const enhancedLogsConfig = args?.enhancedLogs ?? { enabled: true }
51
+ const injectSourceConfig = args?.injectSource ?? { enabled: true }
40
52
  const bus = new ServerEventBus(args?.eventBusConfig)
41
53
 
42
54
  return [
55
+ {
56
+ enforce: 'pre',
57
+ name: '@tanstack/devtools:inject-source',
58
+ apply(config) {
59
+ return config.mode === 'development' && injectSourceConfig.enabled
60
+ },
61
+ transform(code, id) {
62
+ if (
63
+ id.includes('node_modules') ||
64
+ id.includes('?raw') ||
65
+ id.includes('dist') ||
66
+ id.includes('build')
67
+ )
68
+ return code
69
+
70
+ return addSourceToJsx(code, id)
71
+ },
72
+ },
43
73
  {
44
74
  enforce: 'pre',
45
75
  name: '@tanstack/devtools:custom-server',
@@ -87,6 +117,12 @@ export const devtools = (args?: TanStackDevtoolsViteConfig): Array<Plugin> => {
87
117
  }),
88
118
  )
89
119
  },
120
+ transform(code) {
121
+ if (code.includes('__TSD_PORT__')) {
122
+ code = code.replace('__TSD_PORT__', String(port))
123
+ }
124
+ return code
125
+ },
90
126
  },
91
127
  {
92
128
  name: '@tanstack/devtools:better-console-logs',