@tanstack/devtools-vite 0.3.0 → 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/plugin.js +2 -23
- package/dist/esm/plugin.js.map +1 -1
- package/dist/esm/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/enhance-logs.test.ts +157 -0
- package/src/enhance-logs.ts +70 -0
- package/src/inject-source.test.ts +30 -30
- package/src/inject-source.ts +2 -1
- package/src/plugin.ts +2 -34
- 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":";;;;AAKA,MAAM,sCAAsC,CAC1C,wBAKG;AACH,MAAI,YAA2B;AAE/B,MAAI,oBAAoB,SAAS,sBAAsB;AACrD,UAAM,gBAAgB,oBAAoB,OAAO,CAAC;AAElD,QAAI,iBAAiB,cAAc,SAAS,cAAc;AACxD,kBAAY,cAAc;AAAA,IAC5B;AAEA,QAAI,iBAAiB,cAAc,SAAS,iBAAiB;AAC3D,oBAAc,WAAW,QAAQ,CAAC,SAAS;AACzC,YACE,KAAK,SAAS,iBACd,KAAK,SAAS,SAAS,cACvB;AACA,sBAAY,KAAK,SAAS;AAAA,QAC5B;AAAA,MACF,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT;AACA,MAAI,oBAAoB,SAAS,2BAA2B;AAC1D,UAAM,gBAAgB,oBAAoB,OAAO,CAAC;AAElD,QAAI,iBAAiB,cAAc,SAAS,cAAc;AACxD,kBAAY,cAAc;AAAA,IAC5B;AAEA,QAAI,iBAAiB,cAAc,SAAS,iBAAiB;AAC3D,oBAAc,WAAW,QAAQ,CAAC,SAAS;AACzC,YACE,KAAK,SAAS,iBACd,KAAK,SAAS,SAAS,cACvB;AACA,sBAAY,KAAK,SAAS;AAAA,QAC5B;AAAA,MACF,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT;AACA,MAAI,oBAAoB,SAAS,uBAAuB;AACtD,UAAM,gBAAgB,oBAAoB,OAAO,CAAC;AAElD,QAAI,iBAAiB,cAAc,SAAS,cAAc;AACxD,kBAAY,cAAc;AAAA,IAC5B;AAEA,QAAI,iBAAiB,cAAc,SAAS,iBAAiB;AAC3D,oBAAc,WAAW,QAAQ,CAAC,SAAS;AACzC,YACE,KAAK,SAAS,iBACd,KAAK,SAAS,SAAS,cACvB;AACA,sBAAY,KAAK,SAAS;AAAA,QAC5B;AAAA,MACF,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT;AAEA,MACE,oBAAoB,MAAM,SAAS,6BACnC,oBAAoB,MAAM,SAAS,sBACnC;AACA,UAAM,gBAAgB,oBAAoB,KAAK,OAAO,CAAC;AAEvD,QAAI,iBAAiB,cAAc,SAAS,cAAc;AACxD,kBAAY,cAAc;AAAA,IAC5B;AAEA,QAAI,iBAAiB,cAAc,SAAS,iBAAiB;AAC3D,oBAAc,WAAW,QAAQ,CAAC,SAAS;AACzC,YACE,KAAK,SAAS,iBACd,KAAK,SAAS,SAAS,cACvB;AACA,sBAAY,KAAK,SAAS;AAAA,QAC5B;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACA,SAAO;AACT;AAEA,MAAM,mBAAmB,CACvB,YACW;AACX,MAAI,QAAQ,SAAS,iBAAiB;AACpC,WAAO,QAAQ;AAAA,EACjB;AACA,MAAI,QAAQ,SAAS,uBAAuB;AAC1C,WAAO,GAAG,iBAAiB,QAAQ,MAAM,CAAC,IAAI,iBAAiB,QAAQ,QAAQ,CAAC;AAAA,EAClF;AAEA,SAAO,GAAG,QAAQ,UAAU,IAAI,IAAI,QAAQ,KAAK,IAAI;AACvD;AAEA,MAAM,eAAe,CACnB,SACA,WACA,SACG;AACH,QAAM,MAAM,QAAQ,KAAK;AACzB,MAAI,CAAC,IAAK;AACV,QAAM,OAAO,IAAI,MAAM;AACvB,QAAM,SAAS,IAAI,MAAM;AACzB,QAAM,gBAAgB,iBAAiB,QAAQ,KAAK,IAAI;AAExD,MAAI,kBAAkB,cAAc,kBAAkB,kBAAkB;AACtE;AAAA,EACF;AACA,QAAM,gBAAgB,QAAQ,KAAK,WAAW;AAAA,IAC5C,CAAC,SACC,KAAK,SAAS,kBACd,KAAK,KAAK,SAAS,mBACnB,KAAK,KAAK,SAAS;AAAA,EAAA;AAGvB,QAAM,YAAY,QAAQ,KAAK,WAAW;AAAA,IACxC,CAAC,SACC,KAAK,SAAS,wBACd,KAAK,SAAS,SAAS,gBACvB,KAAK,SAAS,SAAS;AAAA,EAAA;AAG3B,MAAI,aAAa,eAAe;AAE9B;AAAA,EACF;AAGA,UAAQ,KAAK,WAAW;AAAA,IACtB,EAAE;AAAA,MACA,EAAE,cAAc,iBAAiB;AAAA,MACjC,EAAE,cAAc,GAAG,IAAI,IAAI,IAAI,IAAI,SAAS,CAAC,EAAE;AAAA,IAAA;AAAA,EACjD;AAGF,SAAO;AACT;AAEA,MAAM,YAAY,CAAC,KAA8B,SAAiB;AAChE,MAAI,eAAe;AAEnB,OAAK,KAAK;AAAA,IACR,oBAAoB,qBAAqB;AACvC,YAAM,YAAY;AAAA,QAChB,oBAAoB;AAAA,MAAA;AAEtB,0BAAoB,SAAS;AAAA,QAC3B,kBAAkB,SAAS;AACzB,gBAAM,cAAc,aAAa,SAAS,WAAW,IAAI;AACzD,cAAI,aAAa;AACf,2BAAe;AAAA,UACjB;AAAA,QACF;AAAA,MAAA,CACD;AAAA,IACH;AAAA,IACA,wBAAwB,MAAM;AAC5B,YAAM,YAAY,oCAAoC,KAAK,IAAI;AAC/D,WAAK,SAAS;AAAA,QACZ,kBAAkB,SAAS;AACzB,gBAAM,cAAc,aAAa,SAAS,WAAW,IAAI;AACzD,cAAI,aAAa;AACf,2BAAe;AAAA,UACjB;AAAA,QACF;AAAA,MAAA,CACD;AAAA,IACH;AAAA,IACA,mBAAmB,MAAM;AACvB,YAAM,YAAY,oCAAoC,KAAK,IAAI;AAC/D,WAAK,SAAS;AAAA,QACZ,kBAAkB,SAAS;AACzB,gBAAM,cAAc,aAAa,SAAS,WAAW,IAAI;AACzD,cAAI,aAAa;AACf,2BAAe;AAAA,UACjB;AAAA,QACF;AAAA,MAAA,CACD;AAAA,IACH;AAAA,IACA,oBAAoB,MAAM;AACxB,YAAM,sBAAsB,KAAK,KAAK,aAAa,KAAK,CAAC,SAAS;AAChE,eACE,KAAK,MAAM,SAAS,6BACpB,KAAK,MAAM,SAAS;AAAA,MAExB,CAAC;AACD,UAAI,CAAC,qBAAqB;AACxB;AAAA,MACF;AACA,YAAM,YAAY,oCAAoC,mBAAmB;AAEzE,WAAK,SAAS;AAAA,QACZ,kBAAkB,SAAS;AACzB,gBAAM,cAAc,aAAa,SAAS,WAAW,IAAI;AACzD,cAAI,aAAa;AACf,2BAAe;AAAA,UACjB;AAAA,QACF;AAAA,MAAA,CACD;AAAA,IACH;AAAA,EAAA,CACD;AAED,SAAO;AACT;AAEO,SAAS,eAAe,MAAc,IAAY;AACvD,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,QAAQ;AAC5C,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;"}
|
package/dist/esm/plugin.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
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";
|
|
6
4
|
import { removeDevtools } from "./remove-devtools.js";
|
|
7
5
|
import { addSourceToJsx } from "./inject-source.js";
|
|
6
|
+
import { enhanceConsoleLog } from "./enhance-logs.js";
|
|
8
7
|
const defineDevtoolsConfig = (config) => config;
|
|
9
8
|
const devtools = (args) => {
|
|
10
9
|
let port = 5173;
|
|
@@ -90,27 +89,7 @@ const devtools = (args) => {
|
|
|
90
89
|
if (!code.includes("console.")) {
|
|
91
90
|
return code;
|
|
92
91
|
}
|
|
93
|
-
|
|
94
|
-
return lines.map((line, lineNumber) => {
|
|
95
|
-
if (line.trim().startsWith("//") || line.trim().startsWith("/**") || line.trim().startsWith("*")) {
|
|
96
|
-
return line;
|
|
97
|
-
}
|
|
98
|
-
if (line.replaceAll(" ", "").includes("=>console.") || line.includes("return console.")) {
|
|
99
|
-
return line;
|
|
100
|
-
}
|
|
101
|
-
const column = line.indexOf("console.");
|
|
102
|
-
const location = `${id.replace(normalizePath(process.cwd()), "")}:${lineNumber + 1}:${column + 1}`;
|
|
103
|
-
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 → '`;
|
|
104
|
-
if (line.includes("console.log(")) {
|
|
105
|
-
const newLine = `console.log(${logMessage},`;
|
|
106
|
-
return line.replace("console.log(", newLine);
|
|
107
|
-
}
|
|
108
|
-
if (line.includes("console.error(")) {
|
|
109
|
-
const newLine = `console.error(${logMessage},`;
|
|
110
|
-
return line.replace("console.error(", newLine);
|
|
111
|
-
}
|
|
112
|
-
return line;
|
|
113
|
-
}).join("\n");
|
|
92
|
+
return enhanceConsoleLog(code, id, port);
|
|
114
93
|
}
|
|
115
94
|
}
|
|
116
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;"}
|
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
|
@@ -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
|
+
}
|
|
@@ -79,7 +79,7 @@ describe('inject source', () => {
|
|
|
79
79
|
expect(output).toBe(
|
|
80
80
|
removeEmptySpace(`
|
|
81
81
|
export const Route = createFileRoute("/test")({
|
|
82
|
-
component: function() { return <div data-tsd-source="test.jsx:3:
|
|
82
|
+
component: function() { return <div data-tsd-source="test.jsx:3:38">Hello World</div>; }
|
|
83
83
|
});
|
|
84
84
|
`),
|
|
85
85
|
)
|
|
@@ -159,7 +159,7 @@ describe('inject source', () => {
|
|
|
159
159
|
expect(output).toBe(
|
|
160
160
|
removeEmptySpace(`
|
|
161
161
|
export const Route = createFileRoute("/test")({
|
|
162
|
-
component: function({...rest}) { return <div data-tsd-source="test.jsx:3:
|
|
162
|
+
component: function({...rest}) { return <div data-tsd-source="test.jsx:3:47"><div {...rest}>Hello World</div></div>; }
|
|
163
163
|
});
|
|
164
164
|
`),
|
|
165
165
|
)
|
|
@@ -181,7 +181,7 @@ describe('inject source', () => {
|
|
|
181
181
|
expect(output).toBe(
|
|
182
182
|
removeEmptySpace(`
|
|
183
183
|
export const Route = createFileRoute("/test")({
|
|
184
|
-
component: () => <div data-tsd-source="test.jsx:3:
|
|
184
|
+
component: () => <div data-tsd-source="test.jsx:3:24">Hello World</div>
|
|
185
185
|
});
|
|
186
186
|
`),
|
|
187
187
|
)
|
|
@@ -261,7 +261,7 @@ describe('inject source', () => {
|
|
|
261
261
|
expect(output).toBe(
|
|
262
262
|
removeEmptySpace(`
|
|
263
263
|
export const Route = createFileRoute("/test")({
|
|
264
|
-
component: ({...rest}) => <div data-tsd-source="test.jsx:3:
|
|
264
|
+
component: ({...rest}) => <div data-tsd-source="test.jsx:3:33"><div {...rest}>Hello World</div></div>
|
|
265
265
|
});
|
|
266
266
|
`),
|
|
267
267
|
)
|
|
@@ -286,7 +286,7 @@ describe('inject source', () => {
|
|
|
286
286
|
removeEmptySpace(`
|
|
287
287
|
function Parent({ ...props }) {
|
|
288
288
|
function Child({ ...props }) {
|
|
289
|
-
return <div data-tsd-source="test.jsx:4:
|
|
289
|
+
return <div data-tsd-source="test.jsx:4:18" />;
|
|
290
290
|
}
|
|
291
291
|
return <Child {...props} />;
|
|
292
292
|
}
|
|
@@ -312,7 +312,7 @@ function test({...props }) {
|
|
|
312
312
|
import Custom from "external";
|
|
313
313
|
|
|
314
314
|
function test({...props }) {
|
|
315
|
-
return <Custom children={props.children} data-tsd-source="test.tsx:6:
|
|
315
|
+
return <Custom children={props.children} data-tsd-source="test.tsx:6:10" />;
|
|
316
316
|
}`),
|
|
317
317
|
)
|
|
318
318
|
})
|
|
@@ -330,7 +330,7 @@ function test({...props }) {
|
|
|
330
330
|
expect(output).toBe(
|
|
331
331
|
removeEmptySpace(`
|
|
332
332
|
function test(props) {
|
|
333
|
-
return <button children={props.children} data-tsd-source="test.jsx:3:
|
|
333
|
+
return <button children={props.children} data-tsd-source="test.jsx:3:16" />;
|
|
334
334
|
}
|
|
335
335
|
`),
|
|
336
336
|
)
|
|
@@ -372,7 +372,7 @@ function test(props) {
|
|
|
372
372
|
expect(output).toBe(
|
|
373
373
|
removeEmptySpace(`
|
|
374
374
|
function test(props) {
|
|
375
|
-
return <div data-tsd-source="test.jsx:3:
|
|
375
|
+
return <div data-tsd-source="test.jsx:3:17">
|
|
376
376
|
<button {...props} />
|
|
377
377
|
</div>;
|
|
378
378
|
}
|
|
@@ -396,7 +396,7 @@ function test(props) {
|
|
|
396
396
|
expect(output).toBe(
|
|
397
397
|
removeEmptySpace(`
|
|
398
398
|
function test({...props}) {
|
|
399
|
-
return <div data-tsd-source="test.jsx:3:
|
|
399
|
+
return <div data-tsd-source="test.jsx:3:17">
|
|
400
400
|
<button {...props} />
|
|
401
401
|
</div>;
|
|
402
402
|
}
|
|
@@ -420,7 +420,7 @@ function test({...props}) {
|
|
|
420
420
|
expect(output).toBe(
|
|
421
421
|
removeEmptySpace(`
|
|
422
422
|
function test({...rest}) {
|
|
423
|
-
return <div data-tsd-source="test.jsx:3:
|
|
423
|
+
return <div data-tsd-source="test.jsx:3:17">
|
|
424
424
|
<button {...rest} />
|
|
425
425
|
</div>;
|
|
426
426
|
}
|
|
@@ -462,7 +462,7 @@ function test({ children, ...rest }) {
|
|
|
462
462
|
expect(output).toBe(
|
|
463
463
|
removeEmptySpace(`
|
|
464
464
|
function test({ ...props }) {
|
|
465
|
-
return <button children={props.children} data-tsd-source="test.jsx:3:
|
|
465
|
+
return <button children={props.children} data-tsd-source="test.jsx:3:16" />;
|
|
466
466
|
}
|
|
467
467
|
`),
|
|
468
468
|
)
|
|
@@ -502,7 +502,7 @@ function test({ children, ...rest }) {
|
|
|
502
502
|
expect(output).toBe(
|
|
503
503
|
removeEmptySpace(`
|
|
504
504
|
function test({ ...props }) {
|
|
505
|
-
return <CustomButton children={props.children} data-tsd-source="test.jsx:3:
|
|
505
|
+
return <CustomButton children={props.children} data-tsd-source="test.jsx:3:16" />;
|
|
506
506
|
}
|
|
507
507
|
`),
|
|
508
508
|
)
|
|
@@ -542,7 +542,7 @@ function test({ children, ...rest }) {
|
|
|
542
542
|
expect(output).toBe(
|
|
543
543
|
removeEmptySpace(`
|
|
544
544
|
function test({ ...props }) {
|
|
545
|
-
return <CustomButton children={props.children} data-tsd-source="test.jsx:3:
|
|
545
|
+
return <CustomButton children={props.children} data-tsd-source="test.jsx:3:16" />;
|
|
546
546
|
}
|
|
547
547
|
`),
|
|
548
548
|
)
|
|
@@ -563,7 +563,7 @@ function test({ children, ...rest }) {
|
|
|
563
563
|
expect(output).toBe(
|
|
564
564
|
removeEmptySpace(`
|
|
565
565
|
const ButtonWithProps = function test(props) {
|
|
566
|
-
return <button children={props.children} data-tsd-source="test.jsx:3:
|
|
566
|
+
return <button children={props.children} data-tsd-source="test.jsx:3:16" />;
|
|
567
567
|
};
|
|
568
568
|
`),
|
|
569
569
|
)
|
|
@@ -605,7 +605,7 @@ function test({ children, ...rest }) {
|
|
|
605
605
|
expect(output).toBe(
|
|
606
606
|
removeEmptySpace(`
|
|
607
607
|
const ButtonWithProps = function test(props) {
|
|
608
|
-
return <div data-tsd-source="test.jsx:3:
|
|
608
|
+
return <div data-tsd-source="test.jsx:3:17">
|
|
609
609
|
<button {...props} />
|
|
610
610
|
</div>;
|
|
611
611
|
};
|
|
@@ -629,7 +629,7 @@ function test({ children, ...rest }) {
|
|
|
629
629
|
expect(output).toBe(
|
|
630
630
|
removeEmptySpace(`
|
|
631
631
|
const ButtonWithProps = function test({...props}) {
|
|
632
|
-
return <div data-tsd-source="test.jsx:3:
|
|
632
|
+
return <div data-tsd-source="test.jsx:3:17">
|
|
633
633
|
<button {...props} />
|
|
634
634
|
</div>;
|
|
635
635
|
};
|
|
@@ -653,7 +653,7 @@ function test({ children, ...rest }) {
|
|
|
653
653
|
expect(output).toBe(
|
|
654
654
|
removeEmptySpace(`
|
|
655
655
|
const ButtonWithProps = function test({...rest}) {
|
|
656
|
-
return <div data-tsd-source="test.jsx:3:
|
|
656
|
+
return <div data-tsd-source="test.jsx:3:17">
|
|
657
657
|
<button {...rest} />
|
|
658
658
|
</div>;
|
|
659
659
|
};
|
|
@@ -695,7 +695,7 @@ function test({ children, ...rest }) {
|
|
|
695
695
|
expect(output).toBe(
|
|
696
696
|
removeEmptySpace(`
|
|
697
697
|
const ButtonWithProps = function test({ ...props }) {
|
|
698
|
-
return <button children={props.children} data-tsd-source="test.jsx:3:
|
|
698
|
+
return <button children={props.children} data-tsd-source="test.jsx:3:16" />;
|
|
699
699
|
};
|
|
700
700
|
`),
|
|
701
701
|
)
|
|
@@ -735,7 +735,7 @@ function test({ children, ...rest }) {
|
|
|
735
735
|
expect(output).toBe(
|
|
736
736
|
removeEmptySpace(`
|
|
737
737
|
const ButtonWithProps = function test({ ...props }) {
|
|
738
|
-
return <CustomButton children={props.children} data-tsd-source="test.jsx:3:
|
|
738
|
+
return <CustomButton children={props.children} data-tsd-source="test.jsx:3:16" />;
|
|
739
739
|
};
|
|
740
740
|
`),
|
|
741
741
|
)
|
|
@@ -775,7 +775,7 @@ function test({ children, ...rest }) {
|
|
|
775
775
|
expect(output).toBe(
|
|
776
776
|
removeEmptySpace(`
|
|
777
777
|
export const ButtonWithProps = function test({ ...props }) {
|
|
778
|
-
return <CustomButton children={props.children} data-tsd-source="test.jsx:3:
|
|
778
|
+
return <CustomButton children={props.children} data-tsd-source="test.jsx:3:16" />;
|
|
779
779
|
};
|
|
780
780
|
`),
|
|
781
781
|
)
|
|
@@ -795,8 +795,8 @@ function test({ children, ...rest }) {
|
|
|
795
795
|
)
|
|
796
796
|
expect(output).toBe(
|
|
797
797
|
removeEmptySpace(`
|
|
798
|
-
const ButtonWithProps = props => {
|
|
799
|
-
return <button children={props.children} data-tsd-source="test.jsx:3:
|
|
798
|
+
const ButtonWithProps = (props) => {
|
|
799
|
+
return <button children={props.children} data-tsd-source="test.jsx:3:16" />;
|
|
800
800
|
};
|
|
801
801
|
`),
|
|
802
802
|
)
|
|
@@ -837,8 +837,8 @@ function test({ children, ...rest }) {
|
|
|
837
837
|
)
|
|
838
838
|
expect(output).toBe(
|
|
839
839
|
removeEmptySpace(`
|
|
840
|
-
const ButtonWithProps = props => {
|
|
841
|
-
return <div data-tsd-source="test.jsx:3:
|
|
840
|
+
const ButtonWithProps = (props) => {
|
|
841
|
+
return <div data-tsd-source="test.jsx:3:17">
|
|
842
842
|
<button {...props} />
|
|
843
843
|
</div>;
|
|
844
844
|
};
|
|
@@ -862,7 +862,7 @@ function test({ children, ...rest }) {
|
|
|
862
862
|
expect(output).toBe(
|
|
863
863
|
removeEmptySpace(`
|
|
864
864
|
const ButtonWithProps = ({...props}) => {
|
|
865
|
-
return <div data-tsd-source="test.jsx:3:
|
|
865
|
+
return <div data-tsd-source="test.jsx:3:17">
|
|
866
866
|
<button {...props} />
|
|
867
867
|
</div>;
|
|
868
868
|
};
|
|
@@ -886,7 +886,7 @@ function test({ children, ...rest }) {
|
|
|
886
886
|
expect(output).toBe(
|
|
887
887
|
removeEmptySpace(`
|
|
888
888
|
const ButtonWithProps = ({...rest}) => {
|
|
889
|
-
return <div data-tsd-source= "test.jsx:3:
|
|
889
|
+
return <div data-tsd-source= "test.jsx:3:17">
|
|
890
890
|
<button {...rest} />
|
|
891
891
|
</div>;
|
|
892
892
|
};
|
|
@@ -908,7 +908,7 @@ function test({ children, ...rest }) {
|
|
|
908
908
|
expect(output).toBe(
|
|
909
909
|
removeEmptySpace(`
|
|
910
910
|
const ButtonWithProps = ({ children, ...rest }) => {
|
|
911
|
-
return <button children={children} data-tsd-source="test.jsx:3:
|
|
911
|
+
return <button children={children} data-tsd-source="test.jsx:3:16" />;
|
|
912
912
|
};
|
|
913
913
|
`),
|
|
914
914
|
)
|
|
@@ -928,7 +928,7 @@ function test({ children, ...rest }) {
|
|
|
928
928
|
expect(output).toBe(
|
|
929
929
|
removeEmptySpace(`
|
|
930
930
|
const ButtonWithProps = ({ ...props }) => {
|
|
931
|
-
return <button children={props.children} data-tsd-source="test.jsx:3:
|
|
931
|
+
return <button children={props.children} data-tsd-source="test.jsx:3:16" />;
|
|
932
932
|
};
|
|
933
933
|
`),
|
|
934
934
|
)
|
|
@@ -968,7 +968,7 @@ function test({ children, ...rest }) {
|
|
|
968
968
|
expect(output).toBe(
|
|
969
969
|
removeEmptySpace(`
|
|
970
970
|
const ButtonWithProps = ({ ...props }) => {
|
|
971
|
-
return <CustomButton children={props.children} data-tsd-source="test.jsx:3:
|
|
971
|
+
return <CustomButton children={props.children} data-tsd-source="test.jsx:3:16" />;
|
|
972
972
|
};
|
|
973
973
|
`),
|
|
974
974
|
)
|
|
@@ -1008,7 +1008,7 @@ function test({ children, ...rest }) {
|
|
|
1008
1008
|
expect(output).toBe(
|
|
1009
1009
|
removeEmptySpace(`
|
|
1010
1010
|
export const ButtonWithProps = ({ ...props }) => {
|
|
1011
|
-
return <CustomButton children={props.children} data-tsd-source="test.jsx:3:
|
|
1011
|
+
return <CustomButton children={props.children} data-tsd-source="test.jsx:3:16" />;
|
|
1012
1012
|
};
|
|
1013
1013
|
`),
|
|
1014
1014
|
)
|
package/src/inject-source.ts
CHANGED
|
@@ -144,7 +144,7 @@ const transformJSX = (
|
|
|
144
144
|
element.node.attributes.push(
|
|
145
145
|
t.jsxAttribute(
|
|
146
146
|
t.jsxIdentifier('data-tsd-source'),
|
|
147
|
-
t.stringLiteral(`${file}:${line}:${column}`),
|
|
147
|
+
t.stringLiteral(`${file}:${line}:${column + 1}`),
|
|
148
148
|
),
|
|
149
149
|
)
|
|
150
150
|
|
|
@@ -232,6 +232,7 @@ export function addSourceToJsx(code: string, id: string) {
|
|
|
232
232
|
}
|
|
233
233
|
return gen(ast, {
|
|
234
234
|
sourceMaps: true,
|
|
235
|
+
retainLines: true,
|
|
235
236
|
filename: id,
|
|
236
237
|
sourceFileName: filePath,
|
|
237
238
|
})
|
package/src/plugin.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
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'
|
|
5
3
|
import { DEFAULT_EDITOR_CONFIG, handleOpenSource } from './editor'
|
|
6
4
|
import { removeDevtools } from './remove-devtools'
|
|
7
5
|
import { addSourceToJsx } from './inject-source'
|
|
6
|
+
import { enhanceConsoleLog } from './enhance-logs'
|
|
8
7
|
import type { EditorConfig } from './editor'
|
|
9
8
|
import type { ServerEventBusConfig } from '@tanstack/devtools-event-bus/server'
|
|
10
9
|
import type { Plugin } from 'vite'
|
|
@@ -160,38 +159,7 @@ export const devtools = (args?: TanStackDevtoolsViteConfig): Array<Plugin> => {
|
|
|
160
159
|
if (!code.includes('console.')) {
|
|
161
160
|
return code
|
|
162
161
|
}
|
|
163
|
-
|
|
164
|
-
return lines
|
|
165
|
-
.map((line, lineNumber) => {
|
|
166
|
-
if (
|
|
167
|
-
line.trim().startsWith('//') ||
|
|
168
|
-
line.trim().startsWith('/**') ||
|
|
169
|
-
line.trim().startsWith('*')
|
|
170
|
-
) {
|
|
171
|
-
return line
|
|
172
|
-
}
|
|
173
|
-
// Do not add for arrow functions or return statements
|
|
174
|
-
if (
|
|
175
|
-
line.replaceAll(' ', '').includes('=>console.') ||
|
|
176
|
-
line.includes('return console.')
|
|
177
|
-
) {
|
|
178
|
-
return line
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const column = line.indexOf('console.')
|
|
182
|
-
const location = `${id.replace(normalizePath(process.cwd()), '')}:${lineNumber + 1}:${column + 1}`
|
|
183
|
-
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 → '`
|
|
184
|
-
if (line.includes('console.log(')) {
|
|
185
|
-
const newLine = `console.log(${logMessage},`
|
|
186
|
-
return line.replace('console.log(', newLine)
|
|
187
|
-
}
|
|
188
|
-
if (line.includes('console.error(')) {
|
|
189
|
-
const newLine = `console.error(${logMessage},`
|
|
190
|
-
return line.replace('console.error(', newLine)
|
|
191
|
-
}
|
|
192
|
-
return line
|
|
193
|
-
})
|
|
194
|
-
.join('\n')
|
|
162
|
+
return enhanceConsoleLog(code, id, port)
|
|
195
163
|
},
|
|
196
164
|
},
|
|
197
165
|
]
|
package/src/utils.ts
CHANGED