@tanstack/devtools-vite 0.2.14 → 0.3.1
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.
- package/dist/esm/enhance-logs.d.ts +3 -0
- package/dist/esm/enhance-logs.js +58 -0
- package/dist/esm/enhance-logs.js.map +1 -0
- package/dist/esm/enhance-logs.test.d.ts +1 -0
- package/dist/esm/inject-source.js +2 -1
- package/dist/esm/inject-source.js.map +1 -1
- package/dist/esm/inject-source.test.d.ts +1 -0
- package/dist/esm/plugin.d.ts +5 -0
- package/dist/esm/plugin.js +16 -23
- package/dist/esm/plugin.js.map +1 -1
- package/dist/esm/remove-devtools.d.ts +3 -0
- package/dist/esm/remove-devtools.js +58 -0
- package/dist/esm/remove-devtools.js.map +1 -0
- package/dist/esm/remove-devtools.test.d.ts +1 -0
- package/dist/esm/utils.js.map +1 -1
- package/package.json +2 -2
- package/src/enhance-logs.test.ts +157 -0
- package/src/enhance-logs.ts +70 -0
- package/src/inject-source.test.ts +1017 -0
- package/src/inject-source.ts +2 -1
- package/src/plugin.ts +27 -34
- package/src/remove-devtools.test.ts +230 -0
- package/src/remove-devtools.ts +74 -0
- package/src/utils.ts +1 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { normalizePath } from "vite";
|
|
3
|
+
import { gen, trav } from "./babel.js";
|
|
4
|
+
import { parse } from "@babel/parser";
|
|
5
|
+
import * as t from "@babel/types";
|
|
6
|
+
const transform = (ast, filePath, port) => {
|
|
7
|
+
let didTransform = false;
|
|
8
|
+
trav(ast, {
|
|
9
|
+
CallExpression(path) {
|
|
10
|
+
const callee = path.node.callee;
|
|
11
|
+
if (callee.type === "MemberExpression" && callee.object.type === "Identifier" && callee.object.name === "console" && callee.property.type === "Identifier" && (callee.property.name === "log" || callee.property.name === "error")) {
|
|
12
|
+
const location = path.node.loc;
|
|
13
|
+
if (!location) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const [lineNumber, column] = [
|
|
17
|
+
location.start.line,
|
|
18
|
+
location.start.column
|
|
19
|
+
];
|
|
20
|
+
const finalPath = `${filePath}:${lineNumber}:${column + 1}`;
|
|
21
|
+
path.node.arguments.unshift(
|
|
22
|
+
t.stringLiteral(
|
|
23
|
+
`${chalk.magenta("LOG")} ${chalk.blueBright(`${finalPath} - http://localhost:${port}/__tsd/open-source?source=${encodeURIComponent(finalPath)}`)}
|
|
24
|
+
→ `
|
|
25
|
+
)
|
|
26
|
+
);
|
|
27
|
+
didTransform = true;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
return didTransform;
|
|
32
|
+
};
|
|
33
|
+
function enhanceConsoleLog(code, id, port) {
|
|
34
|
+
const [filePath] = id.split("?");
|
|
35
|
+
const location = filePath?.replace(normalizePath(process.cwd()), "");
|
|
36
|
+
try {
|
|
37
|
+
const ast = parse(code, {
|
|
38
|
+
sourceType: "module",
|
|
39
|
+
plugins: ["jsx", "typescript"]
|
|
40
|
+
});
|
|
41
|
+
const didTransform = transform(ast, location, port);
|
|
42
|
+
if (!didTransform) {
|
|
43
|
+
return { code };
|
|
44
|
+
}
|
|
45
|
+
return gen(ast, {
|
|
46
|
+
sourceMaps: true,
|
|
47
|
+
retainLines: true,
|
|
48
|
+
filename: id,
|
|
49
|
+
sourceFileName: filePath
|
|
50
|
+
});
|
|
51
|
+
} catch (e) {
|
|
52
|
+
return { code };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export {
|
|
56
|
+
enhanceConsoleLog
|
|
57
|
+
};
|
|
58
|
+
//# sourceMappingURL=enhance-logs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enhance-logs.js","sources":["../../src/enhance-logs.ts"],"sourcesContent":["import chalk from 'chalk'\nimport { normalizePath } from 'vite'\nimport { gen, parse, t, trav } from './babel'\nimport type { types as Babel } from '@babel/core'\nimport type { ParseResult } from '@babel/parser'\n\nconst transform = (\n ast: ParseResult<Babel.File>,\n filePath: string,\n port: number,\n) => {\n let didTransform = false\n\n trav(ast, {\n CallExpression(path) {\n const callee = path.node.callee\n // Match console.log(...) or console.error(...)\n if (\n callee.type === 'MemberExpression' &&\n callee.object.type === 'Identifier' &&\n callee.object.name === 'console' &&\n callee.property.type === 'Identifier' &&\n (callee.property.name === 'log' || callee.property.name === 'error')\n ) {\n const location = path.node.loc\n if (!location) {\n return\n }\n const [lineNumber, column] = [\n location.start.line,\n location.start.column,\n ]\n const finalPath = `${filePath}:${lineNumber}:${column + 1}`\n path.node.arguments.unshift(\n t.stringLiteral(\n `${chalk.magenta('LOG')} ${chalk.blueBright(`${finalPath} - http://localhost:${port}/__tsd/open-source?source=${encodeURIComponent(finalPath)}`)}\\n → `,\n ),\n )\n didTransform = true\n }\n },\n })\n\n return didTransform\n}\n\nexport function enhanceConsoleLog(code: string, id: string, port: number) {\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, port)\n if (!didTransform) {\n return { code }\n }\n return gen(ast, {\n sourceMaps: true,\n retainLines: true,\n filename: id,\n sourceFileName: filePath,\n })\n } catch (e) {\n return { code }\n }\n}\n"],"names":[],"mappings":";;;;;AAMA,MAAM,YAAY,CAChB,KACA,UACA,SACG;AACH,MAAI,eAAe;AAEnB,OAAK,KAAK;AAAA,IACR,eAAe,MAAM;AACnB,YAAM,SAAS,KAAK,KAAK;AAEzB,UACE,OAAO,SAAS,sBAChB,OAAO,OAAO,SAAS,gBACvB,OAAO,OAAO,SAAS,aACvB,OAAO,SAAS,SAAS,iBACxB,OAAO,SAAS,SAAS,SAAS,OAAO,SAAS,SAAS,UAC5D;AACA,cAAM,WAAW,KAAK,KAAK;AAC3B,YAAI,CAAC,UAAU;AACb;AAAA,QACF;AACA,cAAM,CAAC,YAAY,MAAM,IAAI;AAAA,UAC3B,SAAS,MAAM;AAAA,UACf,SAAS,MAAM;AAAA,QAAA;AAEjB,cAAM,YAAY,GAAG,QAAQ,IAAI,UAAU,IAAI,SAAS,CAAC;AACzD,aAAK,KAAK,UAAU;AAAA,UAClB,EAAE;AAAA,YACA,GAAG,MAAM,QAAQ,KAAK,CAAC,IAAI,MAAM,WAAW,GAAG,SAAS,uBAAuB,IAAI,6BAA6B,mBAAmB,SAAS,CAAC,EAAE,CAAC;AAAA;AAAA,UAAA;AAAA,QAClJ;AAEF,uBAAe;AAAA,MACjB;AAAA,IACF;AAAA,EAAA,CACD;AAED,SAAO;AACT;AAEO,SAAS,kBAAkB,MAAc,IAAY,MAAc;AACxE,QAAM,CAAC,QAAQ,IAAI,GAAG,MAAM,GAAG;AAE/B,QAAM,WAAW,UAAU,QAAQ,cAAc,QAAQ,IAAA,CAAK,GAAG,EAAE;AAEnE,MAAI;AACF,UAAM,MAAM,MAAM,MAAM;AAAA,MACtB,YAAY;AAAA,MACZ,SAAS,CAAC,OAAO,YAAY;AAAA,IAAA,CAC9B;AACD,UAAM,eAAe,UAAU,KAAK,UAAU,IAAI;AAClD,QAAI,CAAC,cAAc;AACjB,aAAO,EAAE,KAAA;AAAA,IACX;AACA,WAAO,IAAI,KAAK;AAAA,MACd,YAAY;AAAA,MACZ,aAAa;AAAA,MACb,UAAU;AAAA,MACV,gBAAgB;AAAA,IAAA,CACjB;AAAA,EACH,SAAS,GAAG;AACV,WAAO,EAAE,KAAA;AAAA,EACX;AACF;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -91,7 +91,7 @@ const transformJSX = (element, propsName, file) => {
|
|
|
91
91
|
element.node.attributes.push(
|
|
92
92
|
t.jsxAttribute(
|
|
93
93
|
t.jsxIdentifier("data-tsd-source"),
|
|
94
|
-
t.stringLiteral(`${file}:${line}:${column}`)
|
|
94
|
+
t.stringLiteral(`${file}:${line}:${column + 1}`)
|
|
95
95
|
)
|
|
96
96
|
);
|
|
97
97
|
return true;
|
|
@@ -168,6 +168,7 @@ function addSourceToJsx(code, id) {
|
|
|
168
168
|
}
|
|
169
169
|
return gen(ast, {
|
|
170
170
|
sourceMaps: true,
|
|
171
|
+
retainLines: true,
|
|
171
172
|
filename: id,
|
|
172
173
|
sourceFileName: filePath
|
|
173
174
|
});
|
|
@@ -1 +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 getNameOfElement = (\n element: t.JSXIdentifier | t.JSXMemberExpression | t.JSXNamespacedName,\n): string => {\n if (element.type === 'JSXIdentifier') {\n return element.name\n }\n if (element.type === 'JSXMemberExpression') {\n return `${getNameOfElement(element.object)}.${getNameOfElement(element.property)}`\n }\n\n return `${element.namespace.name}:${element.name.name}`\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 const nameOfElement = getNameOfElement(element.node.name)\n\n if (nameOfElement === 'Fragment' || nameOfElement === 'React.Fragment') {\n return\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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"}
|
|
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 getNameOfElement = (\n element: t.JSXIdentifier | t.JSXMemberExpression | t.JSXNamespacedName,\n): string => {\n if (element.type === 'JSXIdentifier') {\n return element.name\n }\n if (element.type === 'JSXMemberExpression') {\n return `${getNameOfElement(element.object)}.${getNameOfElement(element.property)}`\n }\n\n return `${element.namespace.name}:${element.name.name}`\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 const nameOfElement = getNameOfElement(element.node.name)\n\n if (nameOfElement === 'Fragment' || nameOfElement === 'React.Fragment') {\n return\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 + 1}`),\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 retainLines: true,\n filename: id,\n sourceFileName: filePath,\n })\n } catch (e) {\n return { code }\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/esm/plugin.d.ts
CHANGED
|
@@ -20,6 +20,11 @@ export type TanStackDevtoolsViteConfig = {
|
|
|
20
20
|
*/
|
|
21
21
|
enabled: boolean;
|
|
22
22
|
};
|
|
23
|
+
/**
|
|
24
|
+
* Whether to remove devtools from the production build.
|
|
25
|
+
* @default true
|
|
26
|
+
*/
|
|
27
|
+
removeDevtoolsOnBuild?: boolean;
|
|
23
28
|
/**
|
|
24
29
|
* Configuration for source injection.
|
|
25
30
|
*/
|
package/dist/esm/plugin.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import { normalizePath } from "vite";
|
|
2
|
-
import chalk from "chalk";
|
|
3
1
|
import { ServerEventBus } from "@tanstack/devtools-event-bus/server";
|
|
4
2
|
import { handleDevToolsViteRequest } from "./utils.js";
|
|
5
3
|
import { DEFAULT_EDITOR_CONFIG, handleOpenSource } from "./editor.js";
|
|
4
|
+
import { removeDevtools } from "./remove-devtools.js";
|
|
6
5
|
import { addSourceToJsx } from "./inject-source.js";
|
|
6
|
+
import { enhanceConsoleLog } from "./enhance-logs.js";
|
|
7
7
|
const defineDevtoolsConfig = (config) => config;
|
|
8
8
|
const devtools = (args) => {
|
|
9
9
|
let port = 5173;
|
|
10
10
|
const enhancedLogsConfig = args?.enhancedLogs ?? { enabled: true };
|
|
11
11
|
const injectSourceConfig = args?.injectSource ?? { enabled: true };
|
|
12
|
+
const removeDevtoolsOnBuild = args?.removeDevtoolsOnBuild ?? true;
|
|
12
13
|
const bus = new ServerEventBus(args?.eventBusConfig);
|
|
13
14
|
return [
|
|
14
15
|
{
|
|
@@ -64,6 +65,18 @@ const devtools = (args) => {
|
|
|
64
65
|
);
|
|
65
66
|
}
|
|
66
67
|
},
|
|
68
|
+
{
|
|
69
|
+
name: "@tanstack/devtools:remove-devtools-on-build",
|
|
70
|
+
apply(_, { command }) {
|
|
71
|
+
return command === "build" && removeDevtoolsOnBuild;
|
|
72
|
+
},
|
|
73
|
+
enforce: "pre",
|
|
74
|
+
transform(code, id) {
|
|
75
|
+
if (id.includes("node_modules") || id.includes("?raw") || id.includes("dist") || id.includes("build"))
|
|
76
|
+
return code;
|
|
77
|
+
return removeDevtools(code, id);
|
|
78
|
+
}
|
|
79
|
+
},
|
|
67
80
|
{
|
|
68
81
|
name: "@tanstack/devtools:better-console-logs",
|
|
69
82
|
enforce: "pre",
|
|
@@ -76,27 +89,7 @@ const devtools = (args) => {
|
|
|
76
89
|
if (!code.includes("console.")) {
|
|
77
90
|
return code;
|
|
78
91
|
}
|
|
79
|
-
|
|
80
|
-
return lines.map((line, lineNumber) => {
|
|
81
|
-
if (line.trim().startsWith("//") || line.trim().startsWith("/**") || line.trim().startsWith("*")) {
|
|
82
|
-
return line;
|
|
83
|
-
}
|
|
84
|
-
if (line.replaceAll(" ", "").includes("=>console.") || line.includes("return console.")) {
|
|
85
|
-
return line;
|
|
86
|
-
}
|
|
87
|
-
const column = line.indexOf("console.");
|
|
88
|
-
const location = `${id.replace(normalizePath(process.cwd()), "")}:${lineNumber + 1}:${column + 1}`;
|
|
89
|
-
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 → '`;
|
|
90
|
-
if (line.includes("console.log(")) {
|
|
91
|
-
const newLine = `console.log(${logMessage},`;
|
|
92
|
-
return line.replace("console.log(", newLine);
|
|
93
|
-
}
|
|
94
|
-
if (line.includes("console.error(")) {
|
|
95
|
-
const newLine = `console.error(${logMessage},`;
|
|
96
|
-
return line.replace("console.error(", newLine);
|
|
97
|
-
}
|
|
98
|
-
return line;
|
|
99
|
-
}).join("\n");
|
|
92
|
+
return enhanceConsoleLog(code, id, port);
|
|
100
93
|
}
|
|
101
94
|
}
|
|
102
95
|
];
|
package/dist/esm/plugin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","sources":["../../src/plugin.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"plugin.js","sources":["../../src/plugin.ts"],"sourcesContent":["import { ServerEventBus } from '@tanstack/devtools-event-bus/server'\nimport { handleDevToolsViteRequest } from './utils'\nimport { DEFAULT_EDITOR_CONFIG, handleOpenSource } from './editor'\nimport { removeDevtools } from './remove-devtools'\nimport { addSourceToJsx } from './inject-source'\nimport { enhanceConsoleLog } from './enhance-logs'\nimport type { EditorConfig } from './editor'\nimport type { ServerEventBusConfig } from '@tanstack/devtools-event-bus/server'\nimport type { Plugin } from 'vite'\n\nexport type TanStackDevtoolsViteConfig = {\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 * Whether to remove devtools from the production build.\n * @default true\n */\n removeDevtoolsOnBuild?: boolean\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 enhancedLogsConfig = args?.enhancedLogs ?? { enabled: true }\n const injectSourceConfig = args?.injectSource ?? { enabled: true }\n const removeDevtoolsOnBuild = args?.removeDevtoolsOnBuild ?? 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\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: EditorConfig['open'] = async (\n path,\n lineNum,\n columnNum,\n ) => {\n if (!path) {\n return\n }\n await editor.open(path, lineNum, columnNum)\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 })\n }\n return\n }),\n )\n },\n },\n {\n name: '@tanstack/devtools:remove-devtools-on-build',\n apply(_, { command }) {\n return command === 'build' && removeDevtoolsOnBuild\n },\n enforce: 'pre',\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 removeDevtools(code, id)\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 return enhanceConsoleLog(code, id, port)\n },\n },\n ]\n}\n"],"names":[],"mappings":";;;;;;AA8CO,MAAM,uBAAuB,CAAC,WACnC;AAEK,MAAM,WAAW,CAAC,SAAqD;AAC5E,MAAI,OAAO;AACX,QAAM,qBAAqB,MAAM,gBAAgB,EAAE,SAAS,KAAA;AAC5D,QAAM,qBAAqB,MAAM,gBAAgB,EAAE,SAAS,KAAA;AAC5D,QAAM,wBAAwB,MAAM,yBAAyB;AAC7D,QAAM,MAAM,IAAI,eAAe,MAAM,cAAc;AAEnD,SAAO;AAAA,IACL;AAAA,MACE,SAAS;AAAA,MACT,MAAM;AAAA,MACN,MAAM,QAAQ;AACZ,eAAO,OAAO,SAAS,iBAAiB,mBAAmB;AAAA,MAC7D;AAAA,MACA,UAAU,MAAM,IAAI;AAClB,YACE,GAAG,SAAS,cAAc,KAC1B,GAAG,SAAS,MAAM,KAClB,GAAG,SAAS,MAAM,KAClB,GAAG,SAAS,OAAO;AAEnB,iBAAO;AAET,eAAO,eAAe,MAAM,EAAE;AAAA,MAChC;AAAA,IAAA;AAAA,IAEF;AAAA,MACE,SAAS;AAAA,MACT,MAAM;AAAA,MACN,MAAM,QAAQ;AAEZ,eAAO,OAAO,SAAS;AAAA,MACzB;AAAA,MACA,gBAAgB,QAAQ;AACtB,YAAI,MAAA;AAEJ,eAAO,YAAY,IAAI,CAAC,KAAK,MAAM,SAAS;AAC1C,cAAI,IAAI,OAAO,aAAa,IAAI,OAAO,cAAc,MAAM;AACzD,mBAAO,IAAI,OAAO;AAAA,UACpB;AACA,eAAA;AAAA,QACF,CAAC;AACD,YAAI,OAAO,OAAO,OAAO,MAAM;AAC7B,iBAAO,OAAO,OAAO,OAAO;AAAA,QAC9B;AAEA,eAAO,YAAY,GAAG,aAAa,MAAM;AACvC,iBAAO,OAAO,OAAO,OAAO;AAAA,QAC9B,CAAC;AAED,cAAM,SAAS,MAAM,UAAU;AAC/B,cAAM,eAAqC,OACzC,MACA,SACA,cACG;AACH,cAAI,CAAC,MAAM;AACT;AAAA,UACF;AACA,gBAAM,OAAO,KAAK,MAAM,SAAS,SAAS;AAAA,QAC5C;AACA,eAAO,YAAY;AAAA,UAAI,CAAC,KAAK,KAAK,SAChC,0BAA0B,KAAK,KAAK,MAAM,CAAC,eAAe;AACxD,kBAAM,EAAE,MAAM,QAAA,IAAY;AAC1B,gBAAI,YAAY,eAAe;AAC7B,qBAAO,iBAAiB;AAAA,gBACtB,MAAM,EAAE,MAAM,KAAK,MAAM,KAAA;AAAA,gBACzB;AAAA,cAAA,CACD;AAAA,YACH;AACA;AAAA,UACF,CAAC;AAAA,QAAA;AAAA,MAEL;AAAA,IAAA;AAAA,IAEF;AAAA,MACE,MAAM;AAAA,MACN,MAAM,GAAG,EAAE,WAAW;AACpB,eAAO,YAAY,WAAW;AAAA,MAChC;AAAA,MACA,SAAS;AAAA,MACT,UAAU,MAAM,IAAI;AAClB,YACE,GAAG,SAAS,cAAc,KAC1B,GAAG,SAAS,MAAM,KAClB,GAAG,SAAS,MAAM,KAClB,GAAG,SAAS,OAAO;AAEnB,iBAAO;AAET,eAAO,eAAe,MAAM,EAAE;AAAA,MAChC;AAAA,IAAA;AAAA,IAEF;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,MACT,MAAM,QAAQ;AACZ,eAAO,OAAO,SAAS,iBAAiB,mBAAmB;AAAA,MAC7D;AAAA,MACA,UAAU,MAAM,IAAI;AAElB,YACE,GAAG,SAAS,cAAc,KAC1B,GAAG,SAAS,MAAM,KAClB,GAAG,SAAS,MAAM,KAClB,GAAG,SAAS,OAAO;AAEnB,iBAAO;AAET,YAAI,CAAC,KAAK,SAAS,UAAU,GAAG;AAC9B,iBAAO;AAAA,QACT;AACA,eAAO,kBAAkB,MAAM,IAAI,IAAI;AAAA,MACzC;AAAA,IAAA;AAAA,EACF;AAEJ;"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { gen, trav } from "./babel.js";
|
|
2
|
+
import { parse } from "@babel/parser";
|
|
3
|
+
const isTanStackDevtoolsImport = (source) => source === "@tanstack/react-devtools" || source === "@tanstack/devtools" || source === "@tanstack/solid-devtools";
|
|
4
|
+
const getImportedNames = (importDecl) => {
|
|
5
|
+
return importDecl.specifiers.map((spec) => spec.local.name);
|
|
6
|
+
};
|
|
7
|
+
const transform = (ast) => {
|
|
8
|
+
let didTransform = false;
|
|
9
|
+
const devtoolsComponentNames = /* @__PURE__ */ new Set();
|
|
10
|
+
trav(ast, {
|
|
11
|
+
ImportDeclaration(path) {
|
|
12
|
+
const importSource = path.node.source.value;
|
|
13
|
+
if (isTanStackDevtoolsImport(importSource)) {
|
|
14
|
+
getImportedNames(path.node).forEach(
|
|
15
|
+
(name) => devtoolsComponentNames.add(name)
|
|
16
|
+
);
|
|
17
|
+
path.remove();
|
|
18
|
+
didTransform = true;
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
JSXElement(path) {
|
|
22
|
+
const opening = path.node.openingElement;
|
|
23
|
+
if (opening.name.type === "JSXIdentifier" && devtoolsComponentNames.has(opening.name.name)) {
|
|
24
|
+
path.remove();
|
|
25
|
+
didTransform = true;
|
|
26
|
+
}
|
|
27
|
+
if (opening.name.type === "JSXMemberExpression" && opening.name.object.type === "JSXIdentifier" && devtoolsComponentNames.has(opening.name.object.name)) {
|
|
28
|
+
path.remove();
|
|
29
|
+
didTransform = true;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
return didTransform;
|
|
34
|
+
};
|
|
35
|
+
function removeDevtools(code, id) {
|
|
36
|
+
const [filePath] = id.split("?");
|
|
37
|
+
try {
|
|
38
|
+
const ast = parse(code, {
|
|
39
|
+
sourceType: "module",
|
|
40
|
+
plugins: ["jsx", "typescript"]
|
|
41
|
+
});
|
|
42
|
+
const didTransform = transform(ast);
|
|
43
|
+
if (!didTransform) {
|
|
44
|
+
return { code };
|
|
45
|
+
}
|
|
46
|
+
return gen(ast, {
|
|
47
|
+
sourceMaps: true,
|
|
48
|
+
filename: id,
|
|
49
|
+
sourceFileName: filePath
|
|
50
|
+
});
|
|
51
|
+
} catch (e) {
|
|
52
|
+
return { code };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export {
|
|
56
|
+
removeDevtools
|
|
57
|
+
};
|
|
58
|
+
//# sourceMappingURL=remove-devtools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remove-devtools.js","sources":["../../src/remove-devtools.ts"],"sourcesContent":["import { gen, parse, trav } from './babel'\nimport type { t } from './babel'\nimport type { types as Babel } from '@babel/core'\nimport type { ParseResult } from '@babel/parser'\n\nconst isTanStackDevtoolsImport = (source: string) =>\n source === '@tanstack/react-devtools' ||\n source === '@tanstack/devtools' ||\n source === '@tanstack/solid-devtools'\n\nconst getImportedNames = (importDecl: t.ImportDeclaration) => {\n return importDecl.specifiers.map((spec) => spec.local.name)\n}\n\nconst transform = (ast: ParseResult<Babel.File>) => {\n let didTransform = false\n const devtoolsComponentNames = new Set()\n\n trav(ast, {\n ImportDeclaration(path) {\n const importSource = path.node.source.value\n if (isTanStackDevtoolsImport(importSource)) {\n getImportedNames(path.node).forEach((name) =>\n devtoolsComponentNames.add(name),\n )\n path.remove()\n didTransform = true\n }\n },\n JSXElement(path) {\n const opening = path.node.openingElement\n if (\n opening.name.type === 'JSXIdentifier' &&\n devtoolsComponentNames.has(opening.name.name)\n ) {\n path.remove()\n didTransform = true\n }\n\n if (\n opening.name.type === 'JSXMemberExpression' &&\n opening.name.object.type === 'JSXIdentifier' &&\n devtoolsComponentNames.has(opening.name.object.name)\n ) {\n path.remove()\n didTransform = true\n }\n },\n })\n\n return didTransform\n}\n\nexport function removeDevtools(code: string, id: string) {\n const [filePath] = id.split('?')\n\n try {\n const ast = parse(code, {\n sourceType: 'module',\n plugins: ['jsx', 'typescript'],\n })\n const didTransform = transform(ast)\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":";;AAKA,MAAM,2BAA2B,CAAC,WAChC,WAAW,8BACX,WAAW,wBACX,WAAW;AAEb,MAAM,mBAAmB,CAAC,eAAoC;AAC5D,SAAO,WAAW,WAAW,IAAI,CAAC,SAAS,KAAK,MAAM,IAAI;AAC5D;AAEA,MAAM,YAAY,CAAC,QAAiC;AAClD,MAAI,eAAe;AACnB,QAAM,6CAA6B,IAAA;AAEnC,OAAK,KAAK;AAAA,IACR,kBAAkB,MAAM;AACtB,YAAM,eAAe,KAAK,KAAK,OAAO;AACtC,UAAI,yBAAyB,YAAY,GAAG;AAC1C,yBAAiB,KAAK,IAAI,EAAE;AAAA,UAAQ,CAAC,SACnC,uBAAuB,IAAI,IAAI;AAAA,QAAA;AAEjC,aAAK,OAAA;AACL,uBAAe;AAAA,MACjB;AAAA,IACF;AAAA,IACA,WAAW,MAAM;AACf,YAAM,UAAU,KAAK,KAAK;AAC1B,UACE,QAAQ,KAAK,SAAS,mBACtB,uBAAuB,IAAI,QAAQ,KAAK,IAAI,GAC5C;AACA,aAAK,OAAA;AACL,uBAAe;AAAA,MACjB;AAEA,UACE,QAAQ,KAAK,SAAS,yBACtB,QAAQ,KAAK,OAAO,SAAS,mBAC7B,uBAAuB,IAAI,QAAQ,KAAK,OAAO,IAAI,GACnD;AACA,aAAK,OAAA;AACL,uBAAe;AAAA,MACjB;AAAA,IACF;AAAA,EAAA,CACD;AAED,SAAO;AACT;AAEO,SAAS,eAAe,MAAc,IAAY;AACvD,QAAM,CAAC,QAAQ,IAAI,GAAG,MAAM,GAAG;AAE/B,MAAI;AACF,UAAM,MAAM,MAAM,MAAM;AAAA,MACtB,YAAY;AAAA,MACZ,SAAS,CAAC,OAAO,YAAY;AAAA,IAAA,CAC9B;AACD,UAAM,eAAe,UAAU,GAAG;AAClC,QAAI,CAAC,cAAc;AACjB,aAAO,EAAE,KAAA;AAAA,IACX;AACA,WAAO,IAAI,KAAK;AAAA,MACd,YAAY;AAAA,MACZ,UAAU;AAAA,MACV,gBAAgB;AAAA,IAAA,CACjB;AAAA,EACH,SAAS,GAAG;AACV,WAAO,EAAE,KAAA;AAAA,EACX;AACF;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/esm/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sources":["../../src/utils.ts"],"sourcesContent":["import { normalizePath } from 'vite'\n// import fs from 'node:fs/promises'\nimport type { Connect } from 'vite'\nimport type { IncomingMessage, ServerResponse } from 'node:http'\n\nexport const handleDevToolsViteRequest = (\n req: Connect.IncomingMessage,\n res: ServerResponse<IncomingMessage>,\n next: Connect.NextFunction,\n cb: (data: any) => void,\n) => {\n if (req.url?.includes('__tsd/open-source')) {\n const searchParams = new URLSearchParams(req.url.split('?')[1])\n const source = searchParams.get('source')\n if (!source) {\n return\n }\n\n const parsed = parseOpenSourceParam(source)\n if (!parsed) {\n return\n }\n const { file, line, column } = parsed\n\n cb({\n type: 'open-source',\n routine: 'open-source',\n data: {\n source: file ? normalizePath(`${process.cwd()}/${file}`) : undefined,\n line,\n column,\n },\n })\n res.setHeader('Content-Type', 'text/html')\n res.write(`<script> window.close(); </script>`)\n res.end()\n return\n }\n if (!req.url?.includes('__tsd')) {\n return next()\n }\n\n const chunks: Array<any> = []\n req.on('data', (chunk) => {\n chunks.push(chunk)\n })\n req.on('end', () => {\n const dataToParse = Buffer.concat(chunks)\n try {\n const parsedData = JSON.parse(dataToParse.toString())\n cb(parsedData)\n } catch (e) {}\n res.write('OK')\n })\n}\n\nexport const parseOpenSourceParam = (source: string) => {\n // Capture everything up to the last two colon-separated numeric parts as the file.\n // This supports filenames that may themselves contain colons.\n const parts = source.match(/^(.+):(\\d+):(\\d+)$/)\n\n if (!parts) return null\n\n const [, file, line, column] = parts\n return { file, line, column }\n}\n\n/* export const tryReadFile = async (\n filePath: string\n) => {\n try {\n const data = await fs.readFile(filePath, 'utf-8')\n return data\n } catch (error) {\n\n return null\n }\n}\n\nexport const tryParseJson = (jsonString: string) => {\n try {\n const result = JSON.parse(jsonString)\n return result\n } catch (error) {\n return null\n }\n} */\n"],"names":[],"mappings":";AAKO,MAAM,4BAA4B,CACvC,KACA,KACA,MACA,OACG;AACH,MAAI,IAAI,KAAK,SAAS,mBAAmB,GAAG;AAC1C,UAAM,eAAe,IAAI,gBAAgB,IAAI,IAAI,MAAM,GAAG,EAAE,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"utils.js","sources":["../../src/utils.ts"],"sourcesContent":["import { normalizePath } from 'vite'\n// import fs from 'node:fs/promises'\nimport type { Connect } from 'vite'\nimport type { IncomingMessage, ServerResponse } from 'node:http'\n\nexport const handleDevToolsViteRequest = (\n req: Connect.IncomingMessage,\n res: ServerResponse<IncomingMessage>,\n next: Connect.NextFunction,\n cb: (data: any) => void,\n) => {\n if (req.url?.includes('__tsd/open-source')) {\n const searchParams = new URLSearchParams(req.url.split('?')[1])\n\n const source = searchParams.get('source')\n if (!source) {\n return\n }\n\n const parsed = parseOpenSourceParam(source)\n if (!parsed) {\n return\n }\n const { file, line, column } = parsed\n\n cb({\n type: 'open-source',\n routine: 'open-source',\n data: {\n source: file ? normalizePath(`${process.cwd()}/${file}`) : undefined,\n line,\n column,\n },\n })\n res.setHeader('Content-Type', 'text/html')\n res.write(`<script> window.close(); </script>`)\n res.end()\n return\n }\n if (!req.url?.includes('__tsd')) {\n return next()\n }\n\n const chunks: Array<any> = []\n req.on('data', (chunk) => {\n chunks.push(chunk)\n })\n req.on('end', () => {\n const dataToParse = Buffer.concat(chunks)\n try {\n const parsedData = JSON.parse(dataToParse.toString())\n cb(parsedData)\n } catch (e) {}\n res.write('OK')\n })\n}\n\nexport const parseOpenSourceParam = (source: string) => {\n // Capture everything up to the last two colon-separated numeric parts as the file.\n // This supports filenames that may themselves contain colons.\n const parts = source.match(/^(.+):(\\d+):(\\d+)$/)\n\n if (!parts) return null\n\n const [, file, line, column] = parts\n return { file, line, column }\n}\n\n/* export const tryReadFile = async (\n filePath: string\n) => {\n try {\n const data = await fs.readFile(filePath, 'utf-8')\n return data\n } catch (error) {\n\n return null\n }\n}\n\nexport const tryParseJson = (jsonString: string) => {\n try {\n const result = JSON.parse(jsonString)\n return result\n } catch (error) {\n return null\n }\n} */\n"],"names":[],"mappings":";AAKO,MAAM,4BAA4B,CACvC,KACA,KACA,MACA,OACG;AACH,MAAI,IAAI,KAAK,SAAS,mBAAmB,GAAG;AAC1C,UAAM,eAAe,IAAI,gBAAgB,IAAI,IAAI,MAAM,GAAG,EAAE,CAAC,CAAC;AAE9D,UAAM,SAAS,aAAa,IAAI,QAAQ;AACxC,QAAI,CAAC,QAAQ;AACX;AAAA,IACF;AAEA,UAAM,SAAS,qBAAqB,MAAM;AAC1C,QAAI,CAAC,QAAQ;AACX;AAAA,IACF;AACA,UAAM,EAAE,MAAM,MAAM,OAAA,IAAW;AAE/B,OAAG;AAAA,MACD,MAAM;AAAA,MACN,SAAS;AAAA,MACT,MAAM;AAAA,QACJ,QAAQ,OAAO,cAAc,GAAG,QAAQ,KAAK,IAAI,IAAI,EAAE,IAAI;AAAA,QAC3D;AAAA,QACA;AAAA,MAAA;AAAA,IACF,CACD;AACD,QAAI,UAAU,gBAAgB,WAAW;AACzC,QAAI,MAAM,qCAAoC;AAC9C,QAAI,IAAA;AACJ;AAAA,EACF;AACA,MAAI,CAAC,IAAI,KAAK,SAAS,OAAO,GAAG;AAC/B,WAAO,KAAA;AAAA,EACT;AAEA,QAAM,SAAqB,CAAA;AAC3B,MAAI,GAAG,QAAQ,CAAC,UAAU;AACxB,WAAO,KAAK,KAAK;AAAA,EACnB,CAAC;AACD,MAAI,GAAG,OAAO,MAAM;AAClB,UAAM,cAAc,OAAO,OAAO,MAAM;AACxC,QAAI;AACF,YAAM,aAAa,KAAK,MAAM,YAAY,UAAU;AACpD,SAAG,UAAU;AAAA,IACf,SAAS,GAAG;AAAA,IAAC;AACb,QAAI,MAAM,IAAI;AAAA,EAChB,CAAC;AACH;AAEO,MAAM,uBAAuB,CAAC,WAAmB;AAGtD,QAAM,QAAQ,OAAO,MAAM,oBAAoB;AAE/C,MAAI,CAAC,MAAO,QAAO;AAEnB,QAAM,GAAG,MAAM,MAAM,MAAM,IAAI;AAC/B,SAAO,EAAE,MAAM,MAAM,OAAA;AACvB;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/devtools-vite",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "TanStack Vite plugin used to enhance the core devtools with additional functionalities",
|
|
5
5
|
"author": "Tanner Linsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"src"
|
|
39
39
|
],
|
|
40
40
|
"peerDependencies": {
|
|
41
|
-
"vite": "^7.0.0"
|
|
41
|
+
"vite": "^6.0.0 || ^7.0.0"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@babel/core": "^7.28.3",
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest'
|
|
2
|
+
import { enhanceConsoleLog } from './enhance-logs'
|
|
3
|
+
|
|
4
|
+
const removeEmptySpace = (str: string) => {
|
|
5
|
+
return str.replace(/\s/g, '').trim()
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
describe('remove-devtools', () => {
|
|
9
|
+
test('it adds enhanced console.logs to console.log()', () => {
|
|
10
|
+
const output = removeEmptySpace(
|
|
11
|
+
enhanceConsoleLog(
|
|
12
|
+
`
|
|
13
|
+
console.log('This is a log')
|
|
14
|
+
`,
|
|
15
|
+
'test.jsx',
|
|
16
|
+
3000,
|
|
17
|
+
).code,
|
|
18
|
+
)
|
|
19
|
+
expect(
|
|
20
|
+
output.includes(
|
|
21
|
+
'http://localhost:3000/__tsd/open-source?source=test.jsx',
|
|
22
|
+
),
|
|
23
|
+
).toEqual(true)
|
|
24
|
+
expect(output.includes('test.jsx:2:9')).toEqual(true)
|
|
25
|
+
expect(output.includes(removeEmptySpace("'This is a log'"))).toEqual(true)
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test('it does not add enhanced console.logs to console.log that is not called', () => {
|
|
29
|
+
const output = removeEmptySpace(
|
|
30
|
+
enhanceConsoleLog(
|
|
31
|
+
`
|
|
32
|
+
console.log
|
|
33
|
+
`,
|
|
34
|
+
'test.jsx',
|
|
35
|
+
3000,
|
|
36
|
+
).code,
|
|
37
|
+
)
|
|
38
|
+
expect(output).toBe(
|
|
39
|
+
removeEmptySpace(`
|
|
40
|
+
console.log
|
|
41
|
+
`),
|
|
42
|
+
)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
test('it does not add enhanced console.logs to console.log that is inside a comment', () => {
|
|
46
|
+
const output = removeEmptySpace(
|
|
47
|
+
enhanceConsoleLog(
|
|
48
|
+
`
|
|
49
|
+
// console.log('This is a log')
|
|
50
|
+
`,
|
|
51
|
+
'test.jsx',
|
|
52
|
+
3000,
|
|
53
|
+
).code,
|
|
54
|
+
)
|
|
55
|
+
expect(output).toBe(
|
|
56
|
+
removeEmptySpace(`
|
|
57
|
+
// console.log('This is a log')
|
|
58
|
+
`),
|
|
59
|
+
)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
test('it does not add enhanced console.logs to console.log that is inside a multiline comment', () => {
|
|
63
|
+
const output = removeEmptySpace(
|
|
64
|
+
enhanceConsoleLog(
|
|
65
|
+
`
|
|
66
|
+
/*
|
|
67
|
+
console.log('This is a log')
|
|
68
|
+
*/
|
|
69
|
+
`,
|
|
70
|
+
'test.jsx',
|
|
71
|
+
3000,
|
|
72
|
+
).code,
|
|
73
|
+
)
|
|
74
|
+
expect(output).toBe(
|
|
75
|
+
removeEmptySpace(`
|
|
76
|
+
/*
|
|
77
|
+
console.log('This is a log')
|
|
78
|
+
*/
|
|
79
|
+
`),
|
|
80
|
+
)
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
test('it does not add enhanced console.error to console.error that is inside a comment', () => {
|
|
84
|
+
const output = removeEmptySpace(
|
|
85
|
+
enhanceConsoleLog(
|
|
86
|
+
`
|
|
87
|
+
// console.error('This is a log')
|
|
88
|
+
`,
|
|
89
|
+
'test.jsx',
|
|
90
|
+
3000,
|
|
91
|
+
).code,
|
|
92
|
+
)
|
|
93
|
+
expect(output).toBe(
|
|
94
|
+
removeEmptySpace(`
|
|
95
|
+
// console.error('This is a log')
|
|
96
|
+
`),
|
|
97
|
+
)
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
test('it does not add enhanced console.error to console.error that is inside a multiline comment', () => {
|
|
101
|
+
const output = removeEmptySpace(
|
|
102
|
+
enhanceConsoleLog(
|
|
103
|
+
`
|
|
104
|
+
/*
|
|
105
|
+
console.error('This is a log')
|
|
106
|
+
*/
|
|
107
|
+
`,
|
|
108
|
+
'test.jsx',
|
|
109
|
+
3000,
|
|
110
|
+
).code,
|
|
111
|
+
)
|
|
112
|
+
expect(output).toBe(
|
|
113
|
+
removeEmptySpace(`
|
|
114
|
+
/*
|
|
115
|
+
console.error('This is a log')
|
|
116
|
+
*/
|
|
117
|
+
`),
|
|
118
|
+
)
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
test('it adds enhanced console.error to console.error()', () => {
|
|
122
|
+
const output = removeEmptySpace(
|
|
123
|
+
enhanceConsoleLog(
|
|
124
|
+
`
|
|
125
|
+
console.error('This is a log')
|
|
126
|
+
`,
|
|
127
|
+
'test.jsx',
|
|
128
|
+
3000,
|
|
129
|
+
).code,
|
|
130
|
+
)
|
|
131
|
+
console.log('output', output)
|
|
132
|
+
expect(
|
|
133
|
+
output.includes(
|
|
134
|
+
'http://localhost:3000/__tsd/open-source?source=test.jsx',
|
|
135
|
+
),
|
|
136
|
+
).toEqual(true)
|
|
137
|
+
expect(output.includes('test.jsx:2:9')).toEqual(true)
|
|
138
|
+
expect(output.includes(removeEmptySpace("'This is a log'"))).toEqual(true)
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
test('it does not add enhanced console.error to console.error that is not called', () => {
|
|
142
|
+
const output = removeEmptySpace(
|
|
143
|
+
enhanceConsoleLog(
|
|
144
|
+
`
|
|
145
|
+
console.log
|
|
146
|
+
`,
|
|
147
|
+
'test.jsx',
|
|
148
|
+
3000,
|
|
149
|
+
).code,
|
|
150
|
+
)
|
|
151
|
+
expect(output).toBe(
|
|
152
|
+
removeEmptySpace(`
|
|
153
|
+
console.log
|
|
154
|
+
`),
|
|
155
|
+
)
|
|
156
|
+
})
|
|
157
|
+
})
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import chalk from 'chalk'
|
|
2
|
+
import { normalizePath } from 'vite'
|
|
3
|
+
import { gen, parse, t, trav } from './babel'
|
|
4
|
+
import type { types as Babel } from '@babel/core'
|
|
5
|
+
import type { ParseResult } from '@babel/parser'
|
|
6
|
+
|
|
7
|
+
const transform = (
|
|
8
|
+
ast: ParseResult<Babel.File>,
|
|
9
|
+
filePath: string,
|
|
10
|
+
port: number,
|
|
11
|
+
) => {
|
|
12
|
+
let didTransform = false
|
|
13
|
+
|
|
14
|
+
trav(ast, {
|
|
15
|
+
CallExpression(path) {
|
|
16
|
+
const callee = path.node.callee
|
|
17
|
+
// Match console.log(...) or console.error(...)
|
|
18
|
+
if (
|
|
19
|
+
callee.type === 'MemberExpression' &&
|
|
20
|
+
callee.object.type === 'Identifier' &&
|
|
21
|
+
callee.object.name === 'console' &&
|
|
22
|
+
callee.property.type === 'Identifier' &&
|
|
23
|
+
(callee.property.name === 'log' || callee.property.name === 'error')
|
|
24
|
+
) {
|
|
25
|
+
const location = path.node.loc
|
|
26
|
+
if (!location) {
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
const [lineNumber, column] = [
|
|
30
|
+
location.start.line,
|
|
31
|
+
location.start.column,
|
|
32
|
+
]
|
|
33
|
+
const finalPath = `${filePath}:${lineNumber}:${column + 1}`
|
|
34
|
+
path.node.arguments.unshift(
|
|
35
|
+
t.stringLiteral(
|
|
36
|
+
`${chalk.magenta('LOG')} ${chalk.blueBright(`${finalPath} - http://localhost:${port}/__tsd/open-source?source=${encodeURIComponent(finalPath)}`)}\n → `,
|
|
37
|
+
),
|
|
38
|
+
)
|
|
39
|
+
didTransform = true
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
return didTransform
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function enhanceConsoleLog(code: string, id: string, port: number) {
|
|
48
|
+
const [filePath] = id.split('?')
|
|
49
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain
|
|
50
|
+
const location = filePath?.replace(normalizePath(process.cwd()), '')!
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
const ast = parse(code, {
|
|
54
|
+
sourceType: 'module',
|
|
55
|
+
plugins: ['jsx', 'typescript'],
|
|
56
|
+
})
|
|
57
|
+
const didTransform = transform(ast, location, port)
|
|
58
|
+
if (!didTransform) {
|
|
59
|
+
return { code }
|
|
60
|
+
}
|
|
61
|
+
return gen(ast, {
|
|
62
|
+
sourceMaps: true,
|
|
63
|
+
retainLines: true,
|
|
64
|
+
filename: id,
|
|
65
|
+
sourceFileName: filePath,
|
|
66
|
+
})
|
|
67
|
+
} catch (e) {
|
|
68
|
+
return { code }
|
|
69
|
+
}
|
|
70
|
+
}
|