react-code-locator 0.1.6 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -26
- package/dist/babel.cjs +17 -2
- package/dist/babel.cjs.map +1 -1
- package/dist/babel.js +17 -2
- package/dist/babel.js.map +1 -1
- package/dist/babelInjectComponentSource.cjs +17 -2
- package/dist/babelInjectComponentSource.cjs.map +1 -1
- package/dist/babelInjectComponentSource.js +17 -2
- package/dist/babelInjectComponentSource.js.map +1 -1
- package/dist/vite.cjs +61 -58
- package/dist/vite.cjs.map +1 -1
- package/dist/vite.d.cts +2 -2
- package/dist/vite.d.ts +2 -2
- package/dist/vite.js +60 -58
- package/dist/vite.js.map +1 -1
- package/package.json +4 -9
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
개발 중인 React 앱에서 요소를 `Shift + Click`하면 해당 UI와 연결된 소스 위치를 찾는 패키지입니다.
|
|
4
4
|
|
|
5
|
-
- JSX
|
|
5
|
+
- JSX 디버그 정보가 있으면 우선 사용해서 정확한 JSX 위치를 찾습니다.
|
|
6
6
|
- JSX 정보가 없으면 컴포넌트 정의 위치로 fallback 합니다.
|
|
7
7
|
- Vite, Webpack, Babel, 브라우저 런타임을 각각 분리해서 사용할 수 있습니다.
|
|
8
8
|
|
|
@@ -20,19 +20,27 @@ npm i -D /absolute/path/to/react-code-locator
|
|
|
20
20
|
|
|
21
21
|
## 빠른 시작
|
|
22
22
|
|
|
23
|
-
Vite
|
|
23
|
+
Vite 환경에서는 기존 `@vitejs/plugin-react` 설정에 Babel 플러그인을 추가하고, `react-code-locator/vite`는 클라이언트 자동 주입만 맡깁니다.
|
|
24
24
|
|
|
25
25
|
```ts
|
|
26
26
|
import { defineConfig } from "vite";
|
|
27
|
-
import
|
|
27
|
+
import react from "@vitejs/plugin-react";
|
|
28
|
+
import { babelInjectComponentSource, reactComponentJump } from "react-code-locator/vite";
|
|
28
29
|
|
|
29
30
|
export default defineConfig(({ command }) => ({
|
|
30
|
-
plugins:
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
plugins: [
|
|
32
|
+
react({
|
|
33
|
+
babel: {
|
|
34
|
+
plugins: [babelInjectComponentSource],
|
|
35
|
+
},
|
|
36
|
+
}),
|
|
37
|
+
...reactComponentJump({
|
|
38
|
+
command,
|
|
39
|
+
locator: {
|
|
40
|
+
triggerKey: "shift",
|
|
41
|
+
},
|
|
42
|
+
}),
|
|
43
|
+
],
|
|
36
44
|
}));
|
|
37
45
|
```
|
|
38
46
|
|
|
@@ -48,22 +56,31 @@ export default defineConfig(({ command }) => ({
|
|
|
48
56
|
|
|
49
57
|
Vite + React 프로젝트용 기본 진입점입니다.
|
|
50
58
|
|
|
51
|
-
- 개발 서버에서만
|
|
52
|
-
-
|
|
59
|
+
- 개발 서버에서만 브라우저 클라이언트를 자동 주입합니다.
|
|
60
|
+
- HTML에는 bare import를 직접 넣지 않고 Vite 가상 모듈을 통해 클라이언트를 로드합니다.
|
|
53
61
|
- 기본값으로 브라우저 클라이언트도 자동 주입합니다.
|
|
62
|
+
- React 플러그인은 직접 만들지 않습니다. 기존 `@vitejs/plugin-react` 설정에 Babel 플러그인을 추가해서 사용해야 합니다.
|
|
54
63
|
|
|
55
64
|
```ts
|
|
56
65
|
import { defineConfig } from "vite";
|
|
57
|
-
import
|
|
66
|
+
import react from "@vitejs/plugin-react";
|
|
67
|
+
import { babelInjectComponentSource, reactComponentJump } from "react-code-locator/vite";
|
|
58
68
|
|
|
59
69
|
export default defineConfig(({ command }) => ({
|
|
60
|
-
plugins:
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
70
|
+
plugins: [
|
|
71
|
+
react({
|
|
72
|
+
babel: {
|
|
73
|
+
plugins: [babelInjectComponentSource],
|
|
74
|
+
},
|
|
75
|
+
}),
|
|
76
|
+
...reactComponentJump({
|
|
77
|
+
command,
|
|
78
|
+
locator: {
|
|
79
|
+
triggerKey: "shift",
|
|
80
|
+
},
|
|
81
|
+
injectClient: true,
|
|
82
|
+
}),
|
|
83
|
+
],
|
|
67
84
|
}));
|
|
68
85
|
```
|
|
69
86
|
|
|
@@ -74,6 +91,7 @@ export default defineConfig(({ command }) => ({
|
|
|
74
91
|
- `locator.onLocate(result)`: 위치를 찾았을 때 커스텀 처리
|
|
75
92
|
- `locator.onError(error)`: 위치를 못 찾았을 때 커스텀 처리
|
|
76
93
|
- `injectClient`: `false`로 두면 브라우저 런타임 자동 주입 비활성화
|
|
94
|
+
- `babelInjectComponentSource`: 기존 `@vitejs/plugin-react`의 `babel.plugins`에 추가
|
|
77
95
|
|
|
78
96
|
### `react-code-locator/client`
|
|
79
97
|
|
|
@@ -97,13 +115,21 @@ dispose();
|
|
|
97
115
|
|
|
98
116
|
```ts
|
|
99
117
|
import { defineConfig } from "vite";
|
|
100
|
-
import
|
|
118
|
+
import react from "@vitejs/plugin-react";
|
|
119
|
+
import { babelInjectComponentSource, reactComponentJump } from "react-code-locator/vite";
|
|
101
120
|
|
|
102
121
|
export default defineConfig(({ command }) => ({
|
|
103
|
-
plugins:
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
122
|
+
plugins: [
|
|
123
|
+
react({
|
|
124
|
+
babel: {
|
|
125
|
+
plugins: [babelInjectComponentSource],
|
|
126
|
+
},
|
|
127
|
+
}),
|
|
128
|
+
...reactComponentJump({
|
|
129
|
+
command,
|
|
130
|
+
injectClient: false,
|
|
131
|
+
}),
|
|
132
|
+
],
|
|
107
133
|
}));
|
|
108
134
|
```
|
|
109
135
|
|
|
@@ -121,11 +147,12 @@ if (import.meta.env.DEV) {
|
|
|
121
147
|
|
|
122
148
|
Babel 플러그인만 따로 사용할 때 사용합니다.
|
|
123
149
|
|
|
124
|
-
이 플러그인은
|
|
150
|
+
이 플러그인은 기본적으로 한 가지를 주입합니다.
|
|
125
151
|
|
|
126
|
-
- JSX 요소에 `__componentSourceLoc`
|
|
127
152
|
- React 컴포넌트 함수/클래스에 `__componentSourceLoc`
|
|
128
153
|
|
|
154
|
+
옵션으로 JSX 요소에도 `__componentSourceLoc`를 주입할 수 있지만 기본값은 `false`입니다.
|
|
155
|
+
|
|
129
156
|
```js
|
|
130
157
|
const { babelInjectComponentSource } = require("react-code-locator/babel");
|
|
131
158
|
|
|
@@ -144,6 +171,16 @@ export default {
|
|
|
144
171
|
};
|
|
145
172
|
```
|
|
146
173
|
|
|
174
|
+
JSX 주입이 꼭 필요하면:
|
|
175
|
+
|
|
176
|
+
```ts
|
|
177
|
+
import { babelInjectComponentSource } from "react-code-locator/babel";
|
|
178
|
+
|
|
179
|
+
export default {
|
|
180
|
+
plugins: [[babelInjectComponentSource, { injectJsxSource: true }]],
|
|
181
|
+
};
|
|
182
|
+
```
|
|
183
|
+
|
|
147
184
|
### `react-code-locator/webpack`
|
|
148
185
|
|
|
149
186
|
Webpack 설정에 Babel 플러그인과 런타임 엔트리를 함께 주입합니다.
|
package/dist/babel.cjs
CHANGED
|
@@ -45,6 +45,21 @@ var SOURCE_PROP = "__componentSourceLoc";
|
|
|
45
45
|
function isComponentName(name) {
|
|
46
46
|
return /^[A-Z]/.test(name);
|
|
47
47
|
}
|
|
48
|
+
function isSupportedComponentInit(node) {
|
|
49
|
+
if (!node) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
if (import_core.types.isArrowFunctionExpression(node) || import_core.types.isFunctionExpression(node)) {
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
if (!import_core.types.isCallExpression(node)) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
if (import_core.types.isIdentifier(node.callee) && (node.callee.name === "memo" || node.callee.name === "forwardRef")) {
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
return import_core.types.isMemberExpression(node.callee) && import_core.types.isIdentifier(node.callee.object, { name: "React" }) && import_core.types.isIdentifier(node.callee.property) && (node.callee.property.name === "memo" || node.callee.property.name === "forwardRef");
|
|
62
|
+
}
|
|
48
63
|
function getSourceValue(state, loc) {
|
|
49
64
|
const filename = state.file?.opts?.filename;
|
|
50
65
|
if (!filename || !loc) {
|
|
@@ -86,7 +101,7 @@ function visitDeclaration(declarationPath, insertAfterPath, state, seen) {
|
|
|
86
101
|
if (!declarator.init) {
|
|
87
102
|
return [];
|
|
88
103
|
}
|
|
89
|
-
if (!
|
|
104
|
+
if (!isSupportedComponentInit(declarator.init)) {
|
|
90
105
|
return [];
|
|
91
106
|
}
|
|
92
107
|
const sourceValue = getSourceValue(state, declarator.loc?.start ?? declarator.init.loc?.start);
|
|
@@ -101,7 +116,7 @@ function visitDeclaration(declarationPath, insertAfterPath, state, seen) {
|
|
|
101
116
|
}
|
|
102
117
|
}
|
|
103
118
|
function babelInjectComponentSource(options = {}) {
|
|
104
|
-
const { injectJsxSource =
|
|
119
|
+
const { injectJsxSource = false, injectComponentSource = true } = options;
|
|
105
120
|
return {
|
|
106
121
|
name: "babel-inject-component-source",
|
|
107
122
|
visitor: {
|
package/dist/babel.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/babel.ts","../src/babelInjectComponentSource.ts","../src/constants.ts"],"sourcesContent":["export { babelInjectComponentSource } from \"./babelInjectComponentSource\";\nexport type { BabelInjectComponentSourceOptions } from \"./babelInjectComponentSource\";\n","import path from \"node:path\";\nimport { types as t, type NodePath, type PluginObj } from \"@babel/core\";\nimport { SOURCE_PROP } from \"./constants\";\n\nexport type BabelInjectComponentSourceOptions = {\n injectJsxSource?: boolean;\n injectComponentSource?: boolean;\n};\n\ntype BabelState = {\n file?: {\n opts?: {\n filename?: string;\n };\n };\n};\n\nfunction isComponentName(name: string) {\n return /^[A-Z]/.test(name);\n}\n\nfunction getSourceValue(state: BabelState, loc: { line: number; column: number } | null | undefined) {\n const filename = state.file?.opts?.filename;\n if (!filename || !loc) {\n return null;\n }\n\n const relPath = path.relative(process.cwd(), filename).replace(/\\\\/g, \"/\");\n return `${relPath}:${loc.line}:${loc.column + 1}`;\n}\n\nfunction buildAssignment(name: string, sourceValue: string) {\n return t.expressionStatement(\n t.assignmentExpression(\n \"=\",\n t.memberExpression(t.identifier(name), t.identifier(SOURCE_PROP)),\n t.stringLiteral(sourceValue),\n ),\n );\n}\n\nfunction visitDeclaration(\n declarationPath: NodePath,\n insertAfterPath: NodePath,\n state: BabelState,\n seen: Set<string>,\n) {\n if (declarationPath.isFunctionDeclaration() || declarationPath.isClassDeclaration()) {\n const name = declarationPath.node.id?.name;\n if (!name || !isComponentName(name) || seen.has(name)) {\n return;\n }\n\n const sourceValue = getSourceValue(state, declarationPath.node.loc?.start);\n if (!sourceValue) {\n return;\n }\n\n seen.add(name);\n insertAfterPath.insertAfter(buildAssignment(name, sourceValue));\n return;\n }\n\n if (!declarationPath.isVariableDeclaration()) {\n return;\n }\n\n const assignments = declarationPath.node.declarations.flatMap((declarator) => {\n if (!t.isIdentifier(declarator.id) || !isComponentName(declarator.id.name) || seen.has(declarator.id.name)) {\n return [];\n }\n\n if (!declarator.init) {\n return [];\n }\n\n if (!
|
|
1
|
+
{"version":3,"sources":["../src/babel.ts","../src/babelInjectComponentSource.ts","../src/constants.ts"],"sourcesContent":["export { babelInjectComponentSource } from \"./babelInjectComponentSource\";\nexport type { BabelInjectComponentSourceOptions } from \"./babelInjectComponentSource\";\n","import path from \"node:path\";\nimport { types as t, type NodePath, type PluginObj } from \"@babel/core\";\nimport { SOURCE_PROP } from \"./constants\";\n\nexport type BabelInjectComponentSourceOptions = {\n injectJsxSource?: boolean;\n injectComponentSource?: boolean;\n};\n\ntype BabelState = {\n file?: {\n opts?: {\n filename?: string;\n };\n };\n};\n\nfunction isComponentName(name: string) {\n return /^[A-Z]/.test(name);\n}\n\nfunction isSupportedComponentInit(node: t.Expression | null | undefined): boolean {\n if (!node) {\n return false;\n }\n\n if (t.isArrowFunctionExpression(node) || t.isFunctionExpression(node)) {\n return true;\n }\n\n if (!t.isCallExpression(node)) {\n return false;\n }\n\n if (t.isIdentifier(node.callee) && (node.callee.name === \"memo\" || node.callee.name === \"forwardRef\")) {\n return true;\n }\n\n return (\n t.isMemberExpression(node.callee) &&\n t.isIdentifier(node.callee.object, { name: \"React\" }) &&\n t.isIdentifier(node.callee.property) &&\n (node.callee.property.name === \"memo\" || node.callee.property.name === \"forwardRef\")\n );\n}\n\nfunction getSourceValue(state: BabelState, loc: { line: number; column: number } | null | undefined) {\n const filename = state.file?.opts?.filename;\n if (!filename || !loc) {\n return null;\n }\n\n const relPath = path.relative(process.cwd(), filename).replace(/\\\\/g, \"/\");\n return `${relPath}:${loc.line}:${loc.column + 1}`;\n}\n\nfunction buildAssignment(name: string, sourceValue: string) {\n return t.expressionStatement(\n t.assignmentExpression(\n \"=\",\n t.memberExpression(t.identifier(name), t.identifier(SOURCE_PROP)),\n t.stringLiteral(sourceValue),\n ),\n );\n}\n\nfunction visitDeclaration(\n declarationPath: NodePath,\n insertAfterPath: NodePath,\n state: BabelState,\n seen: Set<string>,\n) {\n if (declarationPath.isFunctionDeclaration() || declarationPath.isClassDeclaration()) {\n const name = declarationPath.node.id?.name;\n if (!name || !isComponentName(name) || seen.has(name)) {\n return;\n }\n\n const sourceValue = getSourceValue(state, declarationPath.node.loc?.start);\n if (!sourceValue) {\n return;\n }\n\n seen.add(name);\n insertAfterPath.insertAfter(buildAssignment(name, sourceValue));\n return;\n }\n\n if (!declarationPath.isVariableDeclaration()) {\n return;\n }\n\n const assignments = declarationPath.node.declarations.flatMap((declarator) => {\n if (!t.isIdentifier(declarator.id) || !isComponentName(declarator.id.name) || seen.has(declarator.id.name)) {\n return [];\n }\n\n if (!declarator.init) {\n return [];\n }\n\n if (!isSupportedComponentInit(declarator.init)) {\n return [];\n }\n\n const sourceValue = getSourceValue(state, declarator.loc?.start ?? declarator.init.loc?.start);\n if (!sourceValue) {\n return [];\n }\n\n seen.add(declarator.id.name);\n return [buildAssignment(declarator.id.name, sourceValue)];\n });\n\n if (assignments.length > 0) {\n insertAfterPath.insertAfter(assignments);\n }\n}\n\nexport function babelInjectComponentSource(\n options: BabelInjectComponentSourceOptions = {},\n): PluginObj<BabelState> {\n const { injectJsxSource = false, injectComponentSource = true } = options;\n\n return {\n name: \"babel-inject-component-source\",\n visitor: {\n JSXOpeningElement(pathNode, state) {\n if (!injectJsxSource) {\n return;\n }\n\n const hasSourceProp = pathNode.node.attributes.some(\n (attr) => t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name) && attr.name.name === SOURCE_PROP,\n );\n if (hasSourceProp) {\n return;\n }\n\n const filename = state.file?.opts?.filename;\n const loc = pathNode.node.loc?.start;\n if (!filename || !loc) {\n return;\n }\n\n pathNode.node.attributes.push(\n t.jsxAttribute(\n t.jsxIdentifier(SOURCE_PROP),\n t.stringLiteral(getSourceValue(state, loc) ?? `${filename.replace(/\\\\/g, \"/\")}:${loc.line}:${loc.column + 1}`),\n ),\n );\n },\n Program(programPath, state) {\n if (!injectComponentSource) {\n return;\n }\n\n const seen = new Set<string>();\n\n for (const childPath of programPath.get(\"body\")) {\n if (childPath.isExportNamedDeclaration() || childPath.isExportDefaultDeclaration()) {\n const declarationPath = childPath.get(\"declaration\");\n if (!Array.isArray(declarationPath) && declarationPath.node) {\n visitDeclaration(declarationPath, childPath, state, seen);\n }\n continue;\n }\n\n visitDeclaration(childPath, childPath, state, seen);\n }\n },\n },\n };\n}\n","export const SOURCE_PROP = \"__componentSourceLoc\";\n\n"],"mappings":"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","names":["t","path"]}
|
package/dist/babel.js
CHANGED
|
@@ -9,6 +9,21 @@ var SOURCE_PROP = "__componentSourceLoc";
|
|
|
9
9
|
function isComponentName(name) {
|
|
10
10
|
return /^[A-Z]/.test(name);
|
|
11
11
|
}
|
|
12
|
+
function isSupportedComponentInit(node) {
|
|
13
|
+
if (!node) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
if (t.isArrowFunctionExpression(node) || t.isFunctionExpression(node)) {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
if (!t.isCallExpression(node)) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
if (t.isIdentifier(node.callee) && (node.callee.name === "memo" || node.callee.name === "forwardRef")) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
return t.isMemberExpression(node.callee) && t.isIdentifier(node.callee.object, { name: "React" }) && t.isIdentifier(node.callee.property) && (node.callee.property.name === "memo" || node.callee.property.name === "forwardRef");
|
|
26
|
+
}
|
|
12
27
|
function getSourceValue(state, loc) {
|
|
13
28
|
const filename = state.file?.opts?.filename;
|
|
14
29
|
if (!filename || !loc) {
|
|
@@ -50,7 +65,7 @@ function visitDeclaration(declarationPath, insertAfterPath, state, seen) {
|
|
|
50
65
|
if (!declarator.init) {
|
|
51
66
|
return [];
|
|
52
67
|
}
|
|
53
|
-
if (!
|
|
68
|
+
if (!isSupportedComponentInit(declarator.init)) {
|
|
54
69
|
return [];
|
|
55
70
|
}
|
|
56
71
|
const sourceValue = getSourceValue(state, declarator.loc?.start ?? declarator.init.loc?.start);
|
|
@@ -65,7 +80,7 @@ function visitDeclaration(declarationPath, insertAfterPath, state, seen) {
|
|
|
65
80
|
}
|
|
66
81
|
}
|
|
67
82
|
function babelInjectComponentSource(options = {}) {
|
|
68
|
-
const { injectJsxSource =
|
|
83
|
+
const { injectJsxSource = false, injectComponentSource = true } = options;
|
|
69
84
|
return {
|
|
70
85
|
name: "babel-inject-component-source",
|
|
71
86
|
visitor: {
|
package/dist/babel.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/babelInjectComponentSource.ts","../src/constants.ts"],"sourcesContent":["import path from \"node:path\";\nimport { types as t, type NodePath, type PluginObj } from \"@babel/core\";\nimport { SOURCE_PROP } from \"./constants\";\n\nexport type BabelInjectComponentSourceOptions = {\n injectJsxSource?: boolean;\n injectComponentSource?: boolean;\n};\n\ntype BabelState = {\n file?: {\n opts?: {\n filename?: string;\n };\n };\n};\n\nfunction isComponentName(name: string) {\n return /^[A-Z]/.test(name);\n}\n\nfunction getSourceValue(state: BabelState, loc: { line: number; column: number } | null | undefined) {\n const filename = state.file?.opts?.filename;\n if (!filename || !loc) {\n return null;\n }\n\n const relPath = path.relative(process.cwd(), filename).replace(/\\\\/g, \"/\");\n return `${relPath}:${loc.line}:${loc.column + 1}`;\n}\n\nfunction buildAssignment(name: string, sourceValue: string) {\n return t.expressionStatement(\n t.assignmentExpression(\n \"=\",\n t.memberExpression(t.identifier(name), t.identifier(SOURCE_PROP)),\n t.stringLiteral(sourceValue),\n ),\n );\n}\n\nfunction visitDeclaration(\n declarationPath: NodePath,\n insertAfterPath: NodePath,\n state: BabelState,\n seen: Set<string>,\n) {\n if (declarationPath.isFunctionDeclaration() || declarationPath.isClassDeclaration()) {\n const name = declarationPath.node.id?.name;\n if (!name || !isComponentName(name) || seen.has(name)) {\n return;\n }\n\n const sourceValue = getSourceValue(state, declarationPath.node.loc?.start);\n if (!sourceValue) {\n return;\n }\n\n seen.add(name);\n insertAfterPath.insertAfter(buildAssignment(name, sourceValue));\n return;\n }\n\n if (!declarationPath.isVariableDeclaration()) {\n return;\n }\n\n const assignments = declarationPath.node.declarations.flatMap((declarator) => {\n if (!t.isIdentifier(declarator.id) || !isComponentName(declarator.id.name) || seen.has(declarator.id.name)) {\n return [];\n }\n\n if (!declarator.init) {\n return [];\n }\n\n if (!
|
|
1
|
+
{"version":3,"sources":["../src/babelInjectComponentSource.ts","../src/constants.ts"],"sourcesContent":["import path from \"node:path\";\nimport { types as t, type NodePath, type PluginObj } from \"@babel/core\";\nimport { SOURCE_PROP } from \"./constants\";\n\nexport type BabelInjectComponentSourceOptions = {\n injectJsxSource?: boolean;\n injectComponentSource?: boolean;\n};\n\ntype BabelState = {\n file?: {\n opts?: {\n filename?: string;\n };\n };\n};\n\nfunction isComponentName(name: string) {\n return /^[A-Z]/.test(name);\n}\n\nfunction isSupportedComponentInit(node: t.Expression | null | undefined): boolean {\n if (!node) {\n return false;\n }\n\n if (t.isArrowFunctionExpression(node) || t.isFunctionExpression(node)) {\n return true;\n }\n\n if (!t.isCallExpression(node)) {\n return false;\n }\n\n if (t.isIdentifier(node.callee) && (node.callee.name === \"memo\" || node.callee.name === \"forwardRef\")) {\n return true;\n }\n\n return (\n t.isMemberExpression(node.callee) &&\n t.isIdentifier(node.callee.object, { name: \"React\" }) &&\n t.isIdentifier(node.callee.property) &&\n (node.callee.property.name === \"memo\" || node.callee.property.name === \"forwardRef\")\n );\n}\n\nfunction getSourceValue(state: BabelState, loc: { line: number; column: number } | null | undefined) {\n const filename = state.file?.opts?.filename;\n if (!filename || !loc) {\n return null;\n }\n\n const relPath = path.relative(process.cwd(), filename).replace(/\\\\/g, \"/\");\n return `${relPath}:${loc.line}:${loc.column + 1}`;\n}\n\nfunction buildAssignment(name: string, sourceValue: string) {\n return t.expressionStatement(\n t.assignmentExpression(\n \"=\",\n t.memberExpression(t.identifier(name), t.identifier(SOURCE_PROP)),\n t.stringLiteral(sourceValue),\n ),\n );\n}\n\nfunction visitDeclaration(\n declarationPath: NodePath,\n insertAfterPath: NodePath,\n state: BabelState,\n seen: Set<string>,\n) {\n if (declarationPath.isFunctionDeclaration() || declarationPath.isClassDeclaration()) {\n const name = declarationPath.node.id?.name;\n if (!name || !isComponentName(name) || seen.has(name)) {\n return;\n }\n\n const sourceValue = getSourceValue(state, declarationPath.node.loc?.start);\n if (!sourceValue) {\n return;\n }\n\n seen.add(name);\n insertAfterPath.insertAfter(buildAssignment(name, sourceValue));\n return;\n }\n\n if (!declarationPath.isVariableDeclaration()) {\n return;\n }\n\n const assignments = declarationPath.node.declarations.flatMap((declarator) => {\n if (!t.isIdentifier(declarator.id) || !isComponentName(declarator.id.name) || seen.has(declarator.id.name)) {\n return [];\n }\n\n if (!declarator.init) {\n return [];\n }\n\n if (!isSupportedComponentInit(declarator.init)) {\n return [];\n }\n\n const sourceValue = getSourceValue(state, declarator.loc?.start ?? declarator.init.loc?.start);\n if (!sourceValue) {\n return [];\n }\n\n seen.add(declarator.id.name);\n return [buildAssignment(declarator.id.name, sourceValue)];\n });\n\n if (assignments.length > 0) {\n insertAfterPath.insertAfter(assignments);\n }\n}\n\nexport function babelInjectComponentSource(\n options: BabelInjectComponentSourceOptions = {},\n): PluginObj<BabelState> {\n const { injectJsxSource = false, injectComponentSource = true } = options;\n\n return {\n name: \"babel-inject-component-source\",\n visitor: {\n JSXOpeningElement(pathNode, state) {\n if (!injectJsxSource) {\n return;\n }\n\n const hasSourceProp = pathNode.node.attributes.some(\n (attr) => t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name) && attr.name.name === SOURCE_PROP,\n );\n if (hasSourceProp) {\n return;\n }\n\n const filename = state.file?.opts?.filename;\n const loc = pathNode.node.loc?.start;\n if (!filename || !loc) {\n return;\n }\n\n pathNode.node.attributes.push(\n t.jsxAttribute(\n t.jsxIdentifier(SOURCE_PROP),\n t.stringLiteral(getSourceValue(state, loc) ?? `${filename.replace(/\\\\/g, \"/\")}:${loc.line}:${loc.column + 1}`),\n ),\n );\n },\n Program(programPath, state) {\n if (!injectComponentSource) {\n return;\n }\n\n const seen = new Set<string>();\n\n for (const childPath of programPath.get(\"body\")) {\n if (childPath.isExportNamedDeclaration() || childPath.isExportDefaultDeclaration()) {\n const declarationPath = childPath.get(\"declaration\");\n if (!Array.isArray(declarationPath) && declarationPath.node) {\n visitDeclaration(declarationPath, childPath, state, seen);\n }\n continue;\n }\n\n visitDeclaration(childPath, childPath, state, seen);\n }\n },\n },\n };\n}\n","export const SOURCE_PROP = \"__componentSourceLoc\";\n\n"],"mappings":"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","names":[]}
|
|
@@ -43,6 +43,21 @@ var SOURCE_PROP = "__componentSourceLoc";
|
|
|
43
43
|
function isComponentName(name) {
|
|
44
44
|
return /^[A-Z]/.test(name);
|
|
45
45
|
}
|
|
46
|
+
function isSupportedComponentInit(node) {
|
|
47
|
+
if (!node) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
if (import_core.types.isArrowFunctionExpression(node) || import_core.types.isFunctionExpression(node)) {
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
if (!import_core.types.isCallExpression(node)) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
if (import_core.types.isIdentifier(node.callee) && (node.callee.name === "memo" || node.callee.name === "forwardRef")) {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
return import_core.types.isMemberExpression(node.callee) && import_core.types.isIdentifier(node.callee.object, { name: "React" }) && import_core.types.isIdentifier(node.callee.property) && (node.callee.property.name === "memo" || node.callee.property.name === "forwardRef");
|
|
60
|
+
}
|
|
46
61
|
function getSourceValue(state, loc) {
|
|
47
62
|
const filename = state.file?.opts?.filename;
|
|
48
63
|
if (!filename || !loc) {
|
|
@@ -84,7 +99,7 @@ function visitDeclaration(declarationPath, insertAfterPath, state, seen) {
|
|
|
84
99
|
if (!declarator.init) {
|
|
85
100
|
return [];
|
|
86
101
|
}
|
|
87
|
-
if (!
|
|
102
|
+
if (!isSupportedComponentInit(declarator.init)) {
|
|
88
103
|
return [];
|
|
89
104
|
}
|
|
90
105
|
const sourceValue = getSourceValue(state, declarator.loc?.start ?? declarator.init.loc?.start);
|
|
@@ -99,7 +114,7 @@ function visitDeclaration(declarationPath, insertAfterPath, state, seen) {
|
|
|
99
114
|
}
|
|
100
115
|
}
|
|
101
116
|
function babelInjectComponentSource(options = {}) {
|
|
102
|
-
const { injectJsxSource =
|
|
117
|
+
const { injectJsxSource = false, injectComponentSource = true } = options;
|
|
103
118
|
return {
|
|
104
119
|
name: "babel-inject-component-source",
|
|
105
120
|
visitor: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/babelInjectComponentSource.ts","../src/constants.ts"],"sourcesContent":["import path from \"node:path\";\nimport { types as t, type NodePath, type PluginObj } from \"@babel/core\";\nimport { SOURCE_PROP } from \"./constants\";\n\nexport type BabelInjectComponentSourceOptions = {\n injectJsxSource?: boolean;\n injectComponentSource?: boolean;\n};\n\ntype BabelState = {\n file?: {\n opts?: {\n filename?: string;\n };\n };\n};\n\nfunction isComponentName(name: string) {\n return /^[A-Z]/.test(name);\n}\n\nfunction getSourceValue(state: BabelState, loc: { line: number; column: number } | null | undefined) {\n const filename = state.file?.opts?.filename;\n if (!filename || !loc) {\n return null;\n }\n\n const relPath = path.relative(process.cwd(), filename).replace(/\\\\/g, \"/\");\n return `${relPath}:${loc.line}:${loc.column + 1}`;\n}\n\nfunction buildAssignment(name: string, sourceValue: string) {\n return t.expressionStatement(\n t.assignmentExpression(\n \"=\",\n t.memberExpression(t.identifier(name), t.identifier(SOURCE_PROP)),\n t.stringLiteral(sourceValue),\n ),\n );\n}\n\nfunction visitDeclaration(\n declarationPath: NodePath,\n insertAfterPath: NodePath,\n state: BabelState,\n seen: Set<string>,\n) {\n if (declarationPath.isFunctionDeclaration() || declarationPath.isClassDeclaration()) {\n const name = declarationPath.node.id?.name;\n if (!name || !isComponentName(name) || seen.has(name)) {\n return;\n }\n\n const sourceValue = getSourceValue(state, declarationPath.node.loc?.start);\n if (!sourceValue) {\n return;\n }\n\n seen.add(name);\n insertAfterPath.insertAfter(buildAssignment(name, sourceValue));\n return;\n }\n\n if (!declarationPath.isVariableDeclaration()) {\n return;\n }\n\n const assignments = declarationPath.node.declarations.flatMap((declarator) => {\n if (!t.isIdentifier(declarator.id) || !isComponentName(declarator.id.name) || seen.has(declarator.id.name)) {\n return [];\n }\n\n if (!declarator.init) {\n return [];\n }\n\n if (!
|
|
1
|
+
{"version":3,"sources":["../src/babelInjectComponentSource.ts","../src/constants.ts"],"sourcesContent":["import path from \"node:path\";\nimport { types as t, type NodePath, type PluginObj } from \"@babel/core\";\nimport { SOURCE_PROP } from \"./constants\";\n\nexport type BabelInjectComponentSourceOptions = {\n injectJsxSource?: boolean;\n injectComponentSource?: boolean;\n};\n\ntype BabelState = {\n file?: {\n opts?: {\n filename?: string;\n };\n };\n};\n\nfunction isComponentName(name: string) {\n return /^[A-Z]/.test(name);\n}\n\nfunction isSupportedComponentInit(node: t.Expression | null | undefined): boolean {\n if (!node) {\n return false;\n }\n\n if (t.isArrowFunctionExpression(node) || t.isFunctionExpression(node)) {\n return true;\n }\n\n if (!t.isCallExpression(node)) {\n return false;\n }\n\n if (t.isIdentifier(node.callee) && (node.callee.name === \"memo\" || node.callee.name === \"forwardRef\")) {\n return true;\n }\n\n return (\n t.isMemberExpression(node.callee) &&\n t.isIdentifier(node.callee.object, { name: \"React\" }) &&\n t.isIdentifier(node.callee.property) &&\n (node.callee.property.name === \"memo\" || node.callee.property.name === \"forwardRef\")\n );\n}\n\nfunction getSourceValue(state: BabelState, loc: { line: number; column: number } | null | undefined) {\n const filename = state.file?.opts?.filename;\n if (!filename || !loc) {\n return null;\n }\n\n const relPath = path.relative(process.cwd(), filename).replace(/\\\\/g, \"/\");\n return `${relPath}:${loc.line}:${loc.column + 1}`;\n}\n\nfunction buildAssignment(name: string, sourceValue: string) {\n return t.expressionStatement(\n t.assignmentExpression(\n \"=\",\n t.memberExpression(t.identifier(name), t.identifier(SOURCE_PROP)),\n t.stringLiteral(sourceValue),\n ),\n );\n}\n\nfunction visitDeclaration(\n declarationPath: NodePath,\n insertAfterPath: NodePath,\n state: BabelState,\n seen: Set<string>,\n) {\n if (declarationPath.isFunctionDeclaration() || declarationPath.isClassDeclaration()) {\n const name = declarationPath.node.id?.name;\n if (!name || !isComponentName(name) || seen.has(name)) {\n return;\n }\n\n const sourceValue = getSourceValue(state, declarationPath.node.loc?.start);\n if (!sourceValue) {\n return;\n }\n\n seen.add(name);\n insertAfterPath.insertAfter(buildAssignment(name, sourceValue));\n return;\n }\n\n if (!declarationPath.isVariableDeclaration()) {\n return;\n }\n\n const assignments = declarationPath.node.declarations.flatMap((declarator) => {\n if (!t.isIdentifier(declarator.id) || !isComponentName(declarator.id.name) || seen.has(declarator.id.name)) {\n return [];\n }\n\n if (!declarator.init) {\n return [];\n }\n\n if (!isSupportedComponentInit(declarator.init)) {\n return [];\n }\n\n const sourceValue = getSourceValue(state, declarator.loc?.start ?? declarator.init.loc?.start);\n if (!sourceValue) {\n return [];\n }\n\n seen.add(declarator.id.name);\n return [buildAssignment(declarator.id.name, sourceValue)];\n });\n\n if (assignments.length > 0) {\n insertAfterPath.insertAfter(assignments);\n }\n}\n\nexport function babelInjectComponentSource(\n options: BabelInjectComponentSourceOptions = {},\n): PluginObj<BabelState> {\n const { injectJsxSource = false, injectComponentSource = true } = options;\n\n return {\n name: \"babel-inject-component-source\",\n visitor: {\n JSXOpeningElement(pathNode, state) {\n if (!injectJsxSource) {\n return;\n }\n\n const hasSourceProp = pathNode.node.attributes.some(\n (attr) => t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name) && attr.name.name === SOURCE_PROP,\n );\n if (hasSourceProp) {\n return;\n }\n\n const filename = state.file?.opts?.filename;\n const loc = pathNode.node.loc?.start;\n if (!filename || !loc) {\n return;\n }\n\n pathNode.node.attributes.push(\n t.jsxAttribute(\n t.jsxIdentifier(SOURCE_PROP),\n t.stringLiteral(getSourceValue(state, loc) ?? `${filename.replace(/\\\\/g, \"/\")}:${loc.line}:${loc.column + 1}`),\n ),\n );\n },\n Program(programPath, state) {\n if (!injectComponentSource) {\n return;\n }\n\n const seen = new Set<string>();\n\n for (const childPath of programPath.get(\"body\")) {\n if (childPath.isExportNamedDeclaration() || childPath.isExportDefaultDeclaration()) {\n const declarationPath = childPath.get(\"declaration\");\n if (!Array.isArray(declarationPath) && declarationPath.node) {\n visitDeclaration(declarationPath, childPath, state, seen);\n }\n continue;\n }\n\n visitDeclaration(childPath, childPath, state, seen);\n }\n },\n },\n };\n}\n","export const SOURCE_PROP = \"__componentSourceLoc\";\n\n"],"mappings":"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","names":["t","path"]}
|
|
@@ -9,6 +9,21 @@ var SOURCE_PROP = "__componentSourceLoc";
|
|
|
9
9
|
function isComponentName(name) {
|
|
10
10
|
return /^[A-Z]/.test(name);
|
|
11
11
|
}
|
|
12
|
+
function isSupportedComponentInit(node) {
|
|
13
|
+
if (!node) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
if (t.isArrowFunctionExpression(node) || t.isFunctionExpression(node)) {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
if (!t.isCallExpression(node)) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
if (t.isIdentifier(node.callee) && (node.callee.name === "memo" || node.callee.name === "forwardRef")) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
return t.isMemberExpression(node.callee) && t.isIdentifier(node.callee.object, { name: "React" }) && t.isIdentifier(node.callee.property) && (node.callee.property.name === "memo" || node.callee.property.name === "forwardRef");
|
|
26
|
+
}
|
|
12
27
|
function getSourceValue(state, loc) {
|
|
13
28
|
const filename = state.file?.opts?.filename;
|
|
14
29
|
if (!filename || !loc) {
|
|
@@ -50,7 +65,7 @@ function visitDeclaration(declarationPath, insertAfterPath, state, seen) {
|
|
|
50
65
|
if (!declarator.init) {
|
|
51
66
|
return [];
|
|
52
67
|
}
|
|
53
|
-
if (!
|
|
68
|
+
if (!isSupportedComponentInit(declarator.init)) {
|
|
54
69
|
return [];
|
|
55
70
|
}
|
|
56
71
|
const sourceValue = getSourceValue(state, declarator.loc?.start ?? declarator.init.loc?.start);
|
|
@@ -65,7 +80,7 @@ function visitDeclaration(declarationPath, insertAfterPath, state, seen) {
|
|
|
65
80
|
}
|
|
66
81
|
}
|
|
67
82
|
function babelInjectComponentSource(options = {}) {
|
|
68
|
-
const { injectJsxSource =
|
|
83
|
+
const { injectJsxSource = false, injectComponentSource = true } = options;
|
|
69
84
|
return {
|
|
70
85
|
name: "babel-inject-component-source",
|
|
71
86
|
visitor: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/babelInjectComponentSource.ts","../src/constants.ts"],"sourcesContent":["import path from \"node:path\";\nimport { types as t, type NodePath, type PluginObj } from \"@babel/core\";\nimport { SOURCE_PROP } from \"./constants\";\n\nexport type BabelInjectComponentSourceOptions = {\n injectJsxSource?: boolean;\n injectComponentSource?: boolean;\n};\n\ntype BabelState = {\n file?: {\n opts?: {\n filename?: string;\n };\n };\n};\n\nfunction isComponentName(name: string) {\n return /^[A-Z]/.test(name);\n}\n\nfunction getSourceValue(state: BabelState, loc: { line: number; column: number } | null | undefined) {\n const filename = state.file?.opts?.filename;\n if (!filename || !loc) {\n return null;\n }\n\n const relPath = path.relative(process.cwd(), filename).replace(/\\\\/g, \"/\");\n return `${relPath}:${loc.line}:${loc.column + 1}`;\n}\n\nfunction buildAssignment(name: string, sourceValue: string) {\n return t.expressionStatement(\n t.assignmentExpression(\n \"=\",\n t.memberExpression(t.identifier(name), t.identifier(SOURCE_PROP)),\n t.stringLiteral(sourceValue),\n ),\n );\n}\n\nfunction visitDeclaration(\n declarationPath: NodePath,\n insertAfterPath: NodePath,\n state: BabelState,\n seen: Set<string>,\n) {\n if (declarationPath.isFunctionDeclaration() || declarationPath.isClassDeclaration()) {\n const name = declarationPath.node.id?.name;\n if (!name || !isComponentName(name) || seen.has(name)) {\n return;\n }\n\n const sourceValue = getSourceValue(state, declarationPath.node.loc?.start);\n if (!sourceValue) {\n return;\n }\n\n seen.add(name);\n insertAfterPath.insertAfter(buildAssignment(name, sourceValue));\n return;\n }\n\n if (!declarationPath.isVariableDeclaration()) {\n return;\n }\n\n const assignments = declarationPath.node.declarations.flatMap((declarator) => {\n if (!t.isIdentifier(declarator.id) || !isComponentName(declarator.id.name) || seen.has(declarator.id.name)) {\n return [];\n }\n\n if (!declarator.init) {\n return [];\n }\n\n if (!
|
|
1
|
+
{"version":3,"sources":["../src/babelInjectComponentSource.ts","../src/constants.ts"],"sourcesContent":["import path from \"node:path\";\nimport { types as t, type NodePath, type PluginObj } from \"@babel/core\";\nimport { SOURCE_PROP } from \"./constants\";\n\nexport type BabelInjectComponentSourceOptions = {\n injectJsxSource?: boolean;\n injectComponentSource?: boolean;\n};\n\ntype BabelState = {\n file?: {\n opts?: {\n filename?: string;\n };\n };\n};\n\nfunction isComponentName(name: string) {\n return /^[A-Z]/.test(name);\n}\n\nfunction isSupportedComponentInit(node: t.Expression | null | undefined): boolean {\n if (!node) {\n return false;\n }\n\n if (t.isArrowFunctionExpression(node) || t.isFunctionExpression(node)) {\n return true;\n }\n\n if (!t.isCallExpression(node)) {\n return false;\n }\n\n if (t.isIdentifier(node.callee) && (node.callee.name === \"memo\" || node.callee.name === \"forwardRef\")) {\n return true;\n }\n\n return (\n t.isMemberExpression(node.callee) &&\n t.isIdentifier(node.callee.object, { name: \"React\" }) &&\n t.isIdentifier(node.callee.property) &&\n (node.callee.property.name === \"memo\" || node.callee.property.name === \"forwardRef\")\n );\n}\n\nfunction getSourceValue(state: BabelState, loc: { line: number; column: number } | null | undefined) {\n const filename = state.file?.opts?.filename;\n if (!filename || !loc) {\n return null;\n }\n\n const relPath = path.relative(process.cwd(), filename).replace(/\\\\/g, \"/\");\n return `${relPath}:${loc.line}:${loc.column + 1}`;\n}\n\nfunction buildAssignment(name: string, sourceValue: string) {\n return t.expressionStatement(\n t.assignmentExpression(\n \"=\",\n t.memberExpression(t.identifier(name), t.identifier(SOURCE_PROP)),\n t.stringLiteral(sourceValue),\n ),\n );\n}\n\nfunction visitDeclaration(\n declarationPath: NodePath,\n insertAfterPath: NodePath,\n state: BabelState,\n seen: Set<string>,\n) {\n if (declarationPath.isFunctionDeclaration() || declarationPath.isClassDeclaration()) {\n const name = declarationPath.node.id?.name;\n if (!name || !isComponentName(name) || seen.has(name)) {\n return;\n }\n\n const sourceValue = getSourceValue(state, declarationPath.node.loc?.start);\n if (!sourceValue) {\n return;\n }\n\n seen.add(name);\n insertAfterPath.insertAfter(buildAssignment(name, sourceValue));\n return;\n }\n\n if (!declarationPath.isVariableDeclaration()) {\n return;\n }\n\n const assignments = declarationPath.node.declarations.flatMap((declarator) => {\n if (!t.isIdentifier(declarator.id) || !isComponentName(declarator.id.name) || seen.has(declarator.id.name)) {\n return [];\n }\n\n if (!declarator.init) {\n return [];\n }\n\n if (!isSupportedComponentInit(declarator.init)) {\n return [];\n }\n\n const sourceValue = getSourceValue(state, declarator.loc?.start ?? declarator.init.loc?.start);\n if (!sourceValue) {\n return [];\n }\n\n seen.add(declarator.id.name);\n return [buildAssignment(declarator.id.name, sourceValue)];\n });\n\n if (assignments.length > 0) {\n insertAfterPath.insertAfter(assignments);\n }\n}\n\nexport function babelInjectComponentSource(\n options: BabelInjectComponentSourceOptions = {},\n): PluginObj<BabelState> {\n const { injectJsxSource = false, injectComponentSource = true } = options;\n\n return {\n name: \"babel-inject-component-source\",\n visitor: {\n JSXOpeningElement(pathNode, state) {\n if (!injectJsxSource) {\n return;\n }\n\n const hasSourceProp = pathNode.node.attributes.some(\n (attr) => t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name) && attr.name.name === SOURCE_PROP,\n );\n if (hasSourceProp) {\n return;\n }\n\n const filename = state.file?.opts?.filename;\n const loc = pathNode.node.loc?.start;\n if (!filename || !loc) {\n return;\n }\n\n pathNode.node.attributes.push(\n t.jsxAttribute(\n t.jsxIdentifier(SOURCE_PROP),\n t.stringLiteral(getSourceValue(state, loc) ?? `${filename.replace(/\\\\/g, \"/\")}:${loc.line}:${loc.column + 1}`),\n ),\n );\n },\n Program(programPath, state) {\n if (!injectComponentSource) {\n return;\n }\n\n const seen = new Set<string>();\n\n for (const childPath of programPath.get(\"body\")) {\n if (childPath.isExportNamedDeclaration() || childPath.isExportDefaultDeclaration()) {\n const declarationPath = childPath.get(\"declaration\");\n if (!Array.isArray(declarationPath) && declarationPath.node) {\n visitDeclaration(declarationPath, childPath, state, seen);\n }\n continue;\n }\n\n visitDeclaration(childPath, childPath, state, seen);\n }\n },\n },\n };\n}\n","export const SOURCE_PROP = \"__componentSourceLoc\";\n\n"],"mappings":"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","names":[]}
|
package/dist/vite.cjs
CHANGED
|
@@ -30,13 +30,55 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/vite.ts
|
|
31
31
|
var vite_exports = {};
|
|
32
32
|
__export(vite_exports, {
|
|
33
|
+
babelInjectComponentSource: () => babelInjectComponentSource,
|
|
33
34
|
elementLocatorReact: () => elementLocatorReact,
|
|
34
35
|
reactComponentJump: () => elementLocatorReact
|
|
35
36
|
});
|
|
36
37
|
module.exports = __toCommonJS(vite_exports);
|
|
37
38
|
|
|
38
39
|
// src/elementLocatorReact.ts
|
|
39
|
-
var
|
|
40
|
+
var VIRTUAL_CLIENT_MODULE_ID = "virtual:react-code-locator/client";
|
|
41
|
+
var RESOLVED_VIRTUAL_CLIENT_MODULE_ID = `\0${VIRTUAL_CLIENT_MODULE_ID}`;
|
|
42
|
+
function createClientInjector(locatorOptions = {}) {
|
|
43
|
+
const serialized = JSON.stringify(locatorOptions);
|
|
44
|
+
return {
|
|
45
|
+
name: "element-locator-client-injector",
|
|
46
|
+
apply: "serve",
|
|
47
|
+
resolveId(id) {
|
|
48
|
+
if (id === VIRTUAL_CLIENT_MODULE_ID) {
|
|
49
|
+
return RESOLVED_VIRTUAL_CLIENT_MODULE_ID;
|
|
50
|
+
}
|
|
51
|
+
return null;
|
|
52
|
+
},
|
|
53
|
+
load(id) {
|
|
54
|
+
if (id !== RESOLVED_VIRTUAL_CLIENT_MODULE_ID) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
return `
|
|
58
|
+
import { enableReactComponentJump } from "react-code-locator/client";
|
|
59
|
+
|
|
60
|
+
enableReactComponentJump(${serialized});
|
|
61
|
+
`;
|
|
62
|
+
},
|
|
63
|
+
transformIndexHtml() {
|
|
64
|
+
return [
|
|
65
|
+
{
|
|
66
|
+
tag: "script",
|
|
67
|
+
attrs: {
|
|
68
|
+
type: "module"
|
|
69
|
+
},
|
|
70
|
+
children: `import "${VIRTUAL_CLIENT_MODULE_ID}";`,
|
|
71
|
+
injectTo: "head"
|
|
72
|
+
}
|
|
73
|
+
];
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function elementLocatorReact(options = {}) {
|
|
78
|
+
const { command = "serve", locator = {}, injectClient = true } = options;
|
|
79
|
+
const isServe = command === "serve";
|
|
80
|
+
return [isServe && injectClient ? createClientInjector(locator) : null].filter(Boolean);
|
|
81
|
+
}
|
|
40
82
|
|
|
41
83
|
// src/babelInjectComponentSource.ts
|
|
42
84
|
var import_node_path = __toESM(require("path"), 1);
|
|
@@ -49,6 +91,21 @@ var SOURCE_PROP = "__componentSourceLoc";
|
|
|
49
91
|
function isComponentName(name) {
|
|
50
92
|
return /^[A-Z]/.test(name);
|
|
51
93
|
}
|
|
94
|
+
function isSupportedComponentInit(node) {
|
|
95
|
+
if (!node) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
if (import_core.types.isArrowFunctionExpression(node) || import_core.types.isFunctionExpression(node)) {
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
if (!import_core.types.isCallExpression(node)) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
if (import_core.types.isIdentifier(node.callee) && (node.callee.name === "memo" || node.callee.name === "forwardRef")) {
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
return import_core.types.isMemberExpression(node.callee) && import_core.types.isIdentifier(node.callee.object, { name: "React" }) && import_core.types.isIdentifier(node.callee.property) && (node.callee.property.name === "memo" || node.callee.property.name === "forwardRef");
|
|
108
|
+
}
|
|
52
109
|
function getSourceValue(state, loc) {
|
|
53
110
|
const filename = state.file?.opts?.filename;
|
|
54
111
|
if (!filename || !loc) {
|
|
@@ -90,7 +147,7 @@ function visitDeclaration(declarationPath, insertAfterPath, state, seen) {
|
|
|
90
147
|
if (!declarator.init) {
|
|
91
148
|
return [];
|
|
92
149
|
}
|
|
93
|
-
if (!
|
|
150
|
+
if (!isSupportedComponentInit(declarator.init)) {
|
|
94
151
|
return [];
|
|
95
152
|
}
|
|
96
153
|
const sourceValue = getSourceValue(state, declarator.loc?.start ?? declarator.init.loc?.start);
|
|
@@ -105,7 +162,7 @@ function visitDeclaration(declarationPath, insertAfterPath, state, seen) {
|
|
|
105
162
|
}
|
|
106
163
|
}
|
|
107
164
|
function babelInjectComponentSource(options = {}) {
|
|
108
|
-
const { injectJsxSource =
|
|
165
|
+
const { injectJsxSource = false, injectComponentSource = true } = options;
|
|
109
166
|
return {
|
|
110
167
|
name: "babel-inject-component-source",
|
|
111
168
|
visitor: {
|
|
@@ -150,63 +207,9 @@ function babelInjectComponentSource(options = {}) {
|
|
|
150
207
|
}
|
|
151
208
|
};
|
|
152
209
|
}
|
|
153
|
-
|
|
154
|
-
// src/elementLocatorReact.ts
|
|
155
|
-
function withLocatorBabel(reactOptions = {}) {
|
|
156
|
-
const baseBabel = reactOptions.babel;
|
|
157
|
-
const locatorBabelPlugin = babelInjectComponentSource;
|
|
158
|
-
if (typeof baseBabel === "function") {
|
|
159
|
-
return {
|
|
160
|
-
...reactOptions,
|
|
161
|
-
babel(id, options) {
|
|
162
|
-
const result = baseBabel(id, options);
|
|
163
|
-
const resolved2 = result ?? {};
|
|
164
|
-
const plugins = [...resolved2.plugins ?? [], locatorBabelPlugin];
|
|
165
|
-
return {
|
|
166
|
-
...resolved2,
|
|
167
|
-
plugins
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
const resolved = baseBabel ?? {};
|
|
173
|
-
return {
|
|
174
|
-
...reactOptions,
|
|
175
|
-
babel: {
|
|
176
|
-
...resolved,
|
|
177
|
-
plugins: [...resolved.plugins ?? [], locatorBabelPlugin]
|
|
178
|
-
}
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
function createClientInjector(locatorOptions = {}) {
|
|
182
|
-
const serialized = JSON.stringify(locatorOptions);
|
|
183
|
-
return {
|
|
184
|
-
name: "element-locator-client-injector",
|
|
185
|
-
apply: "serve",
|
|
186
|
-
transformIndexHtml() {
|
|
187
|
-
return [
|
|
188
|
-
{
|
|
189
|
-
tag: "script",
|
|
190
|
-
attrs: {
|
|
191
|
-
type: "module"
|
|
192
|
-
},
|
|
193
|
-
children: `import { enableReactComponentJump } from "react-code-locator/client"; enableReactComponentJump(${serialized});`,
|
|
194
|
-
injectTo: "head"
|
|
195
|
-
}
|
|
196
|
-
];
|
|
197
|
-
}
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
function elementLocatorReact(options = {}) {
|
|
201
|
-
const { command = "serve", react: reactOptions, locator = {}, injectClient = true } = options;
|
|
202
|
-
const isServe = command === "serve";
|
|
203
|
-
return [
|
|
204
|
-
(0, import_plugin_react.default)(isServe ? withLocatorBabel(reactOptions) : reactOptions),
|
|
205
|
-
isServe && injectClient ? createClientInjector(locator) : null
|
|
206
|
-
].filter(Boolean);
|
|
207
|
-
}
|
|
208
210
|
// Annotate the CommonJS export names for ESM import in node:
|
|
209
211
|
0 && (module.exports = {
|
|
212
|
+
babelInjectComponentSource,
|
|
210
213
|
elementLocatorReact,
|
|
211
214
|
reactComponentJump
|
|
212
215
|
});
|
package/dist/vite.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/vite.ts","../src/elementLocatorReact.ts","../src/babelInjectComponentSource.ts","../src/constants.ts"],"sourcesContent":["export { elementLocatorReact as reactComponentJump } from \"./elementLocatorReact\";\nexport { elementLocatorReact } from \"./elementLocatorReact\";\nexport type { ElementLocatorReactOptions } from \"./elementLocatorReact\";\n\n","import type { TransformOptions, PluginItem } from \"@babel/core\";\nimport react, { type Options as ReactOptions } from \"@vitejs/plugin-react\";\nimport type { Plugin, PluginOption } from \"vite\";\nimport { babelInjectComponentSource } from \"./babelInjectComponentSource\";\nimport { type LocatorOptions } from \"./runtime\";\n\nexport type ElementLocatorReactOptions = {\n command?: \"serve\" | \"build\";\n react?: ReactOptions;\n locator?: LocatorOptions;\n injectClient?: boolean;\n};\n\nfunction withLocatorBabel(reactOptions: ReactOptions = {}): ReactOptions {\n const baseBabel = reactOptions.babel;\n const locatorBabelPlugin: PluginItem = babelInjectComponentSource as PluginItem;\n\n if (typeof baseBabel === \"function\") {\n return {\n ...reactOptions,\n babel(id, options) {\n const result = baseBabel(id, options);\n const resolved = (result ?? {}) as TransformOptions;\n const plugins = [...(resolved.plugins ?? []), locatorBabelPlugin];\n\n return {\n ...resolved,\n plugins,\n };\n },\n };\n }\n\n const resolved = (baseBabel ?? {}) as TransformOptions;\n return {\n ...reactOptions,\n babel: {\n ...resolved,\n plugins: [...(resolved.plugins ?? []), locatorBabelPlugin],\n },\n };\n}\n\nfunction createClientInjector(locatorOptions: LocatorOptions = {}): Plugin {\n const serialized = JSON.stringify(locatorOptions);\n\n return {\n name: \"element-locator-client-injector\",\n apply: \"serve\",\n transformIndexHtml() {\n return [\n {\n tag: \"script\",\n attrs: {\n type: \"module\",\n },\n children: `import { enableReactComponentJump } from \"react-code-locator/client\"; enableReactComponentJump(${serialized});`,\n injectTo: \"head\",\n },\n ];\n },\n };\n}\n\nexport function elementLocatorReact(options: ElementLocatorReactOptions = {}): PluginOption[] {\n const { command = \"serve\", react: reactOptions, locator = {}, injectClient = true } = options;\n const isServe = command === \"serve\";\n\n return [\n react(isServe ? withLocatorBabel(reactOptions) : reactOptions),\n isServe && injectClient ? createClientInjector(locator) : null,\n ].filter(Boolean);\n}\n","import path from \"node:path\";\nimport { types as t, type NodePath, type PluginObj } from \"@babel/core\";\nimport { SOURCE_PROP } from \"./constants\";\n\nexport type BabelInjectComponentSourceOptions = {\n injectJsxSource?: boolean;\n injectComponentSource?: boolean;\n};\n\ntype BabelState = {\n file?: {\n opts?: {\n filename?: string;\n };\n };\n};\n\nfunction isComponentName(name: string) {\n return /^[A-Z]/.test(name);\n}\n\nfunction getSourceValue(state: BabelState, loc: { line: number; column: number } | null | undefined) {\n const filename = state.file?.opts?.filename;\n if (!filename || !loc) {\n return null;\n }\n\n const relPath = path.relative(process.cwd(), filename).replace(/\\\\/g, \"/\");\n return `${relPath}:${loc.line}:${loc.column + 1}`;\n}\n\nfunction buildAssignment(name: string, sourceValue: string) {\n return t.expressionStatement(\n t.assignmentExpression(\n \"=\",\n t.memberExpression(t.identifier(name), t.identifier(SOURCE_PROP)),\n t.stringLiteral(sourceValue),\n ),\n );\n}\n\nfunction visitDeclaration(\n declarationPath: NodePath,\n insertAfterPath: NodePath,\n state: BabelState,\n seen: Set<string>,\n) {\n if (declarationPath.isFunctionDeclaration() || declarationPath.isClassDeclaration()) {\n const name = declarationPath.node.id?.name;\n if (!name || !isComponentName(name) || seen.has(name)) {\n return;\n }\n\n const sourceValue = getSourceValue(state, declarationPath.node.loc?.start);\n if (!sourceValue) {\n return;\n }\n\n seen.add(name);\n insertAfterPath.insertAfter(buildAssignment(name, sourceValue));\n return;\n }\n\n if (!declarationPath.isVariableDeclaration()) {\n return;\n }\n\n const assignments = declarationPath.node.declarations.flatMap((declarator) => {\n if (!t.isIdentifier(declarator.id) || !isComponentName(declarator.id.name) || seen.has(declarator.id.name)) {\n return [];\n }\n\n if (!declarator.init) {\n return [];\n }\n\n if (!t.isArrowFunctionExpression(declarator.init) && !t.isFunctionExpression(declarator.init)) {\n return [];\n }\n\n const sourceValue = getSourceValue(state, declarator.loc?.start ?? declarator.init.loc?.start);\n if (!sourceValue) {\n return [];\n }\n\n seen.add(declarator.id.name);\n return [buildAssignment(declarator.id.name, sourceValue)];\n });\n\n if (assignments.length > 0) {\n insertAfterPath.insertAfter(assignments);\n }\n}\n\nexport function babelInjectComponentSource(\n options: BabelInjectComponentSourceOptions = {},\n): PluginObj<BabelState> {\n const { injectJsxSource = true, injectComponentSource = true } = options;\n\n return {\n name: \"babel-inject-component-source\",\n visitor: {\n JSXOpeningElement(pathNode, state) {\n if (!injectJsxSource) {\n return;\n }\n\n const hasSourceProp = pathNode.node.attributes.some(\n (attr) => t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name) && attr.name.name === SOURCE_PROP,\n );\n if (hasSourceProp) {\n return;\n }\n\n const filename = state.file?.opts?.filename;\n const loc = pathNode.node.loc?.start;\n if (!filename || !loc) {\n return;\n }\n\n pathNode.node.attributes.push(\n t.jsxAttribute(\n t.jsxIdentifier(SOURCE_PROP),\n t.stringLiteral(getSourceValue(state, loc) ?? `${filename.replace(/\\\\/g, \"/\")}:${loc.line}:${loc.column + 1}`),\n ),\n );\n },\n Program(programPath, state) {\n if (!injectComponentSource) {\n return;\n }\n\n const seen = new Set<string>();\n\n for (const childPath of programPath.get(\"body\")) {\n if (childPath.isExportNamedDeclaration() || childPath.isExportDefaultDeclaration()) {\n const declarationPath = childPath.get(\"declaration\");\n if (!Array.isArray(declarationPath) && declarationPath.node) {\n visitDeclaration(declarationPath, childPath, state, seen);\n }\n continue;\n }\n\n visitDeclaration(childPath, childPath, state, seen);\n }\n },\n },\n };\n}\n","export const SOURCE_PROP = \"__componentSourceLoc\";\n\n"],"mappings":"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","names":["path","t","resolved","react"]}
|
|
1
|
+
{"version":3,"sources":["../src/vite.ts","../src/elementLocatorReact.ts","../src/babelInjectComponentSource.ts","../src/constants.ts"],"sourcesContent":["export { elementLocatorReact as reactComponentJump } from \"./elementLocatorReact\";\nexport { elementLocatorReact } from \"./elementLocatorReact\";\nexport type { ElementLocatorReactOptions } from \"./elementLocatorReact\";\nexport { babelInjectComponentSource } from \"./babelInjectComponentSource\";\nexport type { BabelInjectComponentSourceOptions } from \"./babelInjectComponentSource\";\n","import type { Plugin, PluginOption } from \"vite\";\nimport { type LocatorOptions } from \"./runtime\";\n\nconst VIRTUAL_CLIENT_MODULE_ID = \"virtual:react-code-locator/client\";\nconst RESOLVED_VIRTUAL_CLIENT_MODULE_ID = `\\0${VIRTUAL_CLIENT_MODULE_ID}`;\n\nexport type ElementLocatorReactOptions = {\n command?: \"serve\" | \"build\";\n locator?: LocatorOptions;\n injectClient?: boolean;\n};\n\nfunction createClientInjector(locatorOptions: LocatorOptions = {}): Plugin {\n const serialized = JSON.stringify(locatorOptions);\n\n return {\n name: \"element-locator-client-injector\",\n apply: \"serve\",\n resolveId(id) {\n if (id === VIRTUAL_CLIENT_MODULE_ID) {\n return RESOLVED_VIRTUAL_CLIENT_MODULE_ID;\n }\n\n return null;\n },\n load(id) {\n if (id !== RESOLVED_VIRTUAL_CLIENT_MODULE_ID) {\n return null;\n }\n\n return `\n import { enableReactComponentJump } from \"react-code-locator/client\";\n\n enableReactComponentJump(${serialized});\n `;\n },\n transformIndexHtml() {\n return [\n {\n tag: \"script\",\n attrs: {\n type: \"module\",\n },\n children: `import \"${VIRTUAL_CLIENT_MODULE_ID}\";`,\n injectTo: \"head\",\n },\n ];\n },\n };\n}\n\nexport function elementLocatorReact(options: ElementLocatorReactOptions = {}): PluginOption[] {\n const { command = \"serve\", locator = {}, injectClient = true } = options;\n const isServe = command === \"serve\";\n\n return [isServe && injectClient ? createClientInjector(locator) : null].filter(Boolean);\n}\n","import path from \"node:path\";\nimport { types as t, type NodePath, type PluginObj } from \"@babel/core\";\nimport { SOURCE_PROP } from \"./constants\";\n\nexport type BabelInjectComponentSourceOptions = {\n injectJsxSource?: boolean;\n injectComponentSource?: boolean;\n};\n\ntype BabelState = {\n file?: {\n opts?: {\n filename?: string;\n };\n };\n};\n\nfunction isComponentName(name: string) {\n return /^[A-Z]/.test(name);\n}\n\nfunction isSupportedComponentInit(node: t.Expression | null | undefined): boolean {\n if (!node) {\n return false;\n }\n\n if (t.isArrowFunctionExpression(node) || t.isFunctionExpression(node)) {\n return true;\n }\n\n if (!t.isCallExpression(node)) {\n return false;\n }\n\n if (t.isIdentifier(node.callee) && (node.callee.name === \"memo\" || node.callee.name === \"forwardRef\")) {\n return true;\n }\n\n return (\n t.isMemberExpression(node.callee) &&\n t.isIdentifier(node.callee.object, { name: \"React\" }) &&\n t.isIdentifier(node.callee.property) &&\n (node.callee.property.name === \"memo\" || node.callee.property.name === \"forwardRef\")\n );\n}\n\nfunction getSourceValue(state: BabelState, loc: { line: number; column: number } | null | undefined) {\n const filename = state.file?.opts?.filename;\n if (!filename || !loc) {\n return null;\n }\n\n const relPath = path.relative(process.cwd(), filename).replace(/\\\\/g, \"/\");\n return `${relPath}:${loc.line}:${loc.column + 1}`;\n}\n\nfunction buildAssignment(name: string, sourceValue: string) {\n return t.expressionStatement(\n t.assignmentExpression(\n \"=\",\n t.memberExpression(t.identifier(name), t.identifier(SOURCE_PROP)),\n t.stringLiteral(sourceValue),\n ),\n );\n}\n\nfunction visitDeclaration(\n declarationPath: NodePath,\n insertAfterPath: NodePath,\n state: BabelState,\n seen: Set<string>,\n) {\n if (declarationPath.isFunctionDeclaration() || declarationPath.isClassDeclaration()) {\n const name = declarationPath.node.id?.name;\n if (!name || !isComponentName(name) || seen.has(name)) {\n return;\n }\n\n const sourceValue = getSourceValue(state, declarationPath.node.loc?.start);\n if (!sourceValue) {\n return;\n }\n\n seen.add(name);\n insertAfterPath.insertAfter(buildAssignment(name, sourceValue));\n return;\n }\n\n if (!declarationPath.isVariableDeclaration()) {\n return;\n }\n\n const assignments = declarationPath.node.declarations.flatMap((declarator) => {\n if (!t.isIdentifier(declarator.id) || !isComponentName(declarator.id.name) || seen.has(declarator.id.name)) {\n return [];\n }\n\n if (!declarator.init) {\n return [];\n }\n\n if (!isSupportedComponentInit(declarator.init)) {\n return [];\n }\n\n const sourceValue = getSourceValue(state, declarator.loc?.start ?? declarator.init.loc?.start);\n if (!sourceValue) {\n return [];\n }\n\n seen.add(declarator.id.name);\n return [buildAssignment(declarator.id.name, sourceValue)];\n });\n\n if (assignments.length > 0) {\n insertAfterPath.insertAfter(assignments);\n }\n}\n\nexport function babelInjectComponentSource(\n options: BabelInjectComponentSourceOptions = {},\n): PluginObj<BabelState> {\n const { injectJsxSource = false, injectComponentSource = true } = options;\n\n return {\n name: \"babel-inject-component-source\",\n visitor: {\n JSXOpeningElement(pathNode, state) {\n if (!injectJsxSource) {\n return;\n }\n\n const hasSourceProp = pathNode.node.attributes.some(\n (attr) => t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name) && attr.name.name === SOURCE_PROP,\n );\n if (hasSourceProp) {\n return;\n }\n\n const filename = state.file?.opts?.filename;\n const loc = pathNode.node.loc?.start;\n if (!filename || !loc) {\n return;\n }\n\n pathNode.node.attributes.push(\n t.jsxAttribute(\n t.jsxIdentifier(SOURCE_PROP),\n t.stringLiteral(getSourceValue(state, loc) ?? `${filename.replace(/\\\\/g, \"/\")}:${loc.line}:${loc.column + 1}`),\n ),\n );\n },\n Program(programPath, state) {\n if (!injectComponentSource) {\n return;\n }\n\n const seen = new Set<string>();\n\n for (const childPath of programPath.get(\"body\")) {\n if (childPath.isExportNamedDeclaration() || childPath.isExportDefaultDeclaration()) {\n const declarationPath = childPath.get(\"declaration\");\n if (!Array.isArray(declarationPath) && declarationPath.node) {\n visitDeclaration(declarationPath, childPath, state, seen);\n }\n continue;\n }\n\n visitDeclaration(childPath, childPath, state, seen);\n }\n },\n },\n };\n}\n","export const SOURCE_PROP = \"__componentSourceLoc\";\n\n"],"mappings":"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","names":["t","path"]}
|
package/dist/vite.d.cts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { Options } from '@vitejs/plugin-react';
|
|
2
1
|
import { PluginOption } from 'vite';
|
|
3
2
|
import { LocatorOptions } from './client.cjs';
|
|
3
|
+
export { BabelInjectComponentSourceOptions, babelInjectComponentSource } from './babelInjectComponentSource.cjs';
|
|
4
|
+
import '@babel/core';
|
|
4
5
|
|
|
5
6
|
type ElementLocatorReactOptions = {
|
|
6
7
|
command?: "serve" | "build";
|
|
7
|
-
react?: Options;
|
|
8
8
|
locator?: LocatorOptions;
|
|
9
9
|
injectClient?: boolean;
|
|
10
10
|
};
|
package/dist/vite.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { Options } from '@vitejs/plugin-react';
|
|
2
1
|
import { PluginOption } from 'vite';
|
|
3
2
|
import { LocatorOptions } from './client.js';
|
|
3
|
+
export { BabelInjectComponentSourceOptions, babelInjectComponentSource } from './babelInjectComponentSource.js';
|
|
4
|
+
import '@babel/core';
|
|
4
5
|
|
|
5
6
|
type ElementLocatorReactOptions = {
|
|
6
7
|
command?: "serve" | "build";
|
|
7
|
-
react?: Options;
|
|
8
8
|
locator?: LocatorOptions;
|
|
9
9
|
injectClient?: boolean;
|
|
10
10
|
};
|
package/dist/vite.js
CHANGED
|
@@ -1,5 +1,46 @@
|
|
|
1
1
|
// src/elementLocatorReact.ts
|
|
2
|
-
|
|
2
|
+
var VIRTUAL_CLIENT_MODULE_ID = "virtual:react-code-locator/client";
|
|
3
|
+
var RESOLVED_VIRTUAL_CLIENT_MODULE_ID = `\0${VIRTUAL_CLIENT_MODULE_ID}`;
|
|
4
|
+
function createClientInjector(locatorOptions = {}) {
|
|
5
|
+
const serialized = JSON.stringify(locatorOptions);
|
|
6
|
+
return {
|
|
7
|
+
name: "element-locator-client-injector",
|
|
8
|
+
apply: "serve",
|
|
9
|
+
resolveId(id) {
|
|
10
|
+
if (id === VIRTUAL_CLIENT_MODULE_ID) {
|
|
11
|
+
return RESOLVED_VIRTUAL_CLIENT_MODULE_ID;
|
|
12
|
+
}
|
|
13
|
+
return null;
|
|
14
|
+
},
|
|
15
|
+
load(id) {
|
|
16
|
+
if (id !== RESOLVED_VIRTUAL_CLIENT_MODULE_ID) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
return `
|
|
20
|
+
import { enableReactComponentJump } from "react-code-locator/client";
|
|
21
|
+
|
|
22
|
+
enableReactComponentJump(${serialized});
|
|
23
|
+
`;
|
|
24
|
+
},
|
|
25
|
+
transformIndexHtml() {
|
|
26
|
+
return [
|
|
27
|
+
{
|
|
28
|
+
tag: "script",
|
|
29
|
+
attrs: {
|
|
30
|
+
type: "module"
|
|
31
|
+
},
|
|
32
|
+
children: `import "${VIRTUAL_CLIENT_MODULE_ID}";`,
|
|
33
|
+
injectTo: "head"
|
|
34
|
+
}
|
|
35
|
+
];
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function elementLocatorReact(options = {}) {
|
|
40
|
+
const { command = "serve", locator = {}, injectClient = true } = options;
|
|
41
|
+
const isServe = command === "serve";
|
|
42
|
+
return [isServe && injectClient ? createClientInjector(locator) : null].filter(Boolean);
|
|
43
|
+
}
|
|
3
44
|
|
|
4
45
|
// src/babelInjectComponentSource.ts
|
|
5
46
|
import path from "path";
|
|
@@ -12,6 +53,21 @@ var SOURCE_PROP = "__componentSourceLoc";
|
|
|
12
53
|
function isComponentName(name) {
|
|
13
54
|
return /^[A-Z]/.test(name);
|
|
14
55
|
}
|
|
56
|
+
function isSupportedComponentInit(node) {
|
|
57
|
+
if (!node) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
if (t.isArrowFunctionExpression(node) || t.isFunctionExpression(node)) {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
if (!t.isCallExpression(node)) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
if (t.isIdentifier(node.callee) && (node.callee.name === "memo" || node.callee.name === "forwardRef")) {
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
return t.isMemberExpression(node.callee) && t.isIdentifier(node.callee.object, { name: "React" }) && t.isIdentifier(node.callee.property) && (node.callee.property.name === "memo" || node.callee.property.name === "forwardRef");
|
|
70
|
+
}
|
|
15
71
|
function getSourceValue(state, loc) {
|
|
16
72
|
const filename = state.file?.opts?.filename;
|
|
17
73
|
if (!filename || !loc) {
|
|
@@ -53,7 +109,7 @@ function visitDeclaration(declarationPath, insertAfterPath, state, seen) {
|
|
|
53
109
|
if (!declarator.init) {
|
|
54
110
|
return [];
|
|
55
111
|
}
|
|
56
|
-
if (!
|
|
112
|
+
if (!isSupportedComponentInit(declarator.init)) {
|
|
57
113
|
return [];
|
|
58
114
|
}
|
|
59
115
|
const sourceValue = getSourceValue(state, declarator.loc?.start ?? declarator.init.loc?.start);
|
|
@@ -68,7 +124,7 @@ function visitDeclaration(declarationPath, insertAfterPath, state, seen) {
|
|
|
68
124
|
}
|
|
69
125
|
}
|
|
70
126
|
function babelInjectComponentSource(options = {}) {
|
|
71
|
-
const { injectJsxSource =
|
|
127
|
+
const { injectJsxSource = false, injectComponentSource = true } = options;
|
|
72
128
|
return {
|
|
73
129
|
name: "babel-inject-component-source",
|
|
74
130
|
visitor: {
|
|
@@ -113,62 +169,8 @@ function babelInjectComponentSource(options = {}) {
|
|
|
113
169
|
}
|
|
114
170
|
};
|
|
115
171
|
}
|
|
116
|
-
|
|
117
|
-
// src/elementLocatorReact.ts
|
|
118
|
-
function withLocatorBabel(reactOptions = {}) {
|
|
119
|
-
const baseBabel = reactOptions.babel;
|
|
120
|
-
const locatorBabelPlugin = babelInjectComponentSource;
|
|
121
|
-
if (typeof baseBabel === "function") {
|
|
122
|
-
return {
|
|
123
|
-
...reactOptions,
|
|
124
|
-
babel(id, options) {
|
|
125
|
-
const result = baseBabel(id, options);
|
|
126
|
-
const resolved2 = result ?? {};
|
|
127
|
-
const plugins = [...resolved2.plugins ?? [], locatorBabelPlugin];
|
|
128
|
-
return {
|
|
129
|
-
...resolved2,
|
|
130
|
-
plugins
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
const resolved = baseBabel ?? {};
|
|
136
|
-
return {
|
|
137
|
-
...reactOptions,
|
|
138
|
-
babel: {
|
|
139
|
-
...resolved,
|
|
140
|
-
plugins: [...resolved.plugins ?? [], locatorBabelPlugin]
|
|
141
|
-
}
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
function createClientInjector(locatorOptions = {}) {
|
|
145
|
-
const serialized = JSON.stringify(locatorOptions);
|
|
146
|
-
return {
|
|
147
|
-
name: "element-locator-client-injector",
|
|
148
|
-
apply: "serve",
|
|
149
|
-
transformIndexHtml() {
|
|
150
|
-
return [
|
|
151
|
-
{
|
|
152
|
-
tag: "script",
|
|
153
|
-
attrs: {
|
|
154
|
-
type: "module"
|
|
155
|
-
},
|
|
156
|
-
children: `import { enableReactComponentJump } from "react-code-locator/client"; enableReactComponentJump(${serialized});`,
|
|
157
|
-
injectTo: "head"
|
|
158
|
-
}
|
|
159
|
-
];
|
|
160
|
-
}
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
function elementLocatorReact(options = {}) {
|
|
164
|
-
const { command = "serve", react: reactOptions, locator = {}, injectClient = true } = options;
|
|
165
|
-
const isServe = command === "serve";
|
|
166
|
-
return [
|
|
167
|
-
react(isServe ? withLocatorBabel(reactOptions) : reactOptions),
|
|
168
|
-
isServe && injectClient ? createClientInjector(locator) : null
|
|
169
|
-
].filter(Boolean);
|
|
170
|
-
}
|
|
171
172
|
export {
|
|
173
|
+
babelInjectComponentSource,
|
|
172
174
|
elementLocatorReact,
|
|
173
175
|
elementLocatorReact as reactComponentJump
|
|
174
176
|
};
|
package/dist/vite.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/elementLocatorReact.ts","../src/babelInjectComponentSource.ts","../src/constants.ts"],"sourcesContent":["import type { TransformOptions, PluginItem } from \"@babel/core\";\nimport react, { type Options as ReactOptions } from \"@vitejs/plugin-react\";\nimport type { Plugin, PluginOption } from \"vite\";\nimport { babelInjectComponentSource } from \"./babelInjectComponentSource\";\nimport { type LocatorOptions } from \"./runtime\";\n\nexport type ElementLocatorReactOptions = {\n command?: \"serve\" | \"build\";\n react?: ReactOptions;\n locator?: LocatorOptions;\n injectClient?: boolean;\n};\n\nfunction withLocatorBabel(reactOptions: ReactOptions = {}): ReactOptions {\n const baseBabel = reactOptions.babel;\n const locatorBabelPlugin: PluginItem = babelInjectComponentSource as PluginItem;\n\n if (typeof baseBabel === \"function\") {\n return {\n ...reactOptions,\n babel(id, options) {\n const result = baseBabel(id, options);\n const resolved = (result ?? {}) as TransformOptions;\n const plugins = [...(resolved.plugins ?? []), locatorBabelPlugin];\n\n return {\n ...resolved,\n plugins,\n };\n },\n };\n }\n\n const resolved = (baseBabel ?? {}) as TransformOptions;\n return {\n ...reactOptions,\n babel: {\n ...resolved,\n plugins: [...(resolved.plugins ?? []), locatorBabelPlugin],\n },\n };\n}\n\nfunction createClientInjector(locatorOptions: LocatorOptions = {}): Plugin {\n const serialized = JSON.stringify(locatorOptions);\n\n return {\n name: \"element-locator-client-injector\",\n apply: \"serve\",\n transformIndexHtml() {\n return [\n {\n tag: \"script\",\n attrs: {\n type: \"module\",\n },\n children: `import { enableReactComponentJump } from \"react-code-locator/client\"; enableReactComponentJump(${serialized});`,\n injectTo: \"head\",\n },\n ];\n },\n };\n}\n\nexport function elementLocatorReact(options: ElementLocatorReactOptions = {}): PluginOption[] {\n const { command = \"serve\", react: reactOptions, locator = {}, injectClient = true } = options;\n const isServe = command === \"serve\";\n\n return [\n react(isServe ? withLocatorBabel(reactOptions) : reactOptions),\n isServe && injectClient ? createClientInjector(locator) : null,\n ].filter(Boolean);\n}\n","import path from \"node:path\";\nimport { types as t, type NodePath, type PluginObj } from \"@babel/core\";\nimport { SOURCE_PROP } from \"./constants\";\n\nexport type BabelInjectComponentSourceOptions = {\n injectJsxSource?: boolean;\n injectComponentSource?: boolean;\n};\n\ntype BabelState = {\n file?: {\n opts?: {\n filename?: string;\n };\n };\n};\n\nfunction isComponentName(name: string) {\n return /^[A-Z]/.test(name);\n}\n\nfunction getSourceValue(state: BabelState, loc: { line: number; column: number } | null | undefined) {\n const filename = state.file?.opts?.filename;\n if (!filename || !loc) {\n return null;\n }\n\n const relPath = path.relative(process.cwd(), filename).replace(/\\\\/g, \"/\");\n return `${relPath}:${loc.line}:${loc.column + 1}`;\n}\n\nfunction buildAssignment(name: string, sourceValue: string) {\n return t.expressionStatement(\n t.assignmentExpression(\n \"=\",\n t.memberExpression(t.identifier(name), t.identifier(SOURCE_PROP)),\n t.stringLiteral(sourceValue),\n ),\n );\n}\n\nfunction visitDeclaration(\n declarationPath: NodePath,\n insertAfterPath: NodePath,\n state: BabelState,\n seen: Set<string>,\n) {\n if (declarationPath.isFunctionDeclaration() || declarationPath.isClassDeclaration()) {\n const name = declarationPath.node.id?.name;\n if (!name || !isComponentName(name) || seen.has(name)) {\n return;\n }\n\n const sourceValue = getSourceValue(state, declarationPath.node.loc?.start);\n if (!sourceValue) {\n return;\n }\n\n seen.add(name);\n insertAfterPath.insertAfter(buildAssignment(name, sourceValue));\n return;\n }\n\n if (!declarationPath.isVariableDeclaration()) {\n return;\n }\n\n const assignments = declarationPath.node.declarations.flatMap((declarator) => {\n if (!t.isIdentifier(declarator.id) || !isComponentName(declarator.id.name) || seen.has(declarator.id.name)) {\n return [];\n }\n\n if (!declarator.init) {\n return [];\n }\n\n if (!t.isArrowFunctionExpression(declarator.init) && !t.isFunctionExpression(declarator.init)) {\n return [];\n }\n\n const sourceValue = getSourceValue(state, declarator.loc?.start ?? declarator.init.loc?.start);\n if (!sourceValue) {\n return [];\n }\n\n seen.add(declarator.id.name);\n return [buildAssignment(declarator.id.name, sourceValue)];\n });\n\n if (assignments.length > 0) {\n insertAfterPath.insertAfter(assignments);\n }\n}\n\nexport function babelInjectComponentSource(\n options: BabelInjectComponentSourceOptions = {},\n): PluginObj<BabelState> {\n const { injectJsxSource = true, injectComponentSource = true } = options;\n\n return {\n name: \"babel-inject-component-source\",\n visitor: {\n JSXOpeningElement(pathNode, state) {\n if (!injectJsxSource) {\n return;\n }\n\n const hasSourceProp = pathNode.node.attributes.some(\n (attr) => t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name) && attr.name.name === SOURCE_PROP,\n );\n if (hasSourceProp) {\n return;\n }\n\n const filename = state.file?.opts?.filename;\n const loc = pathNode.node.loc?.start;\n if (!filename || !loc) {\n return;\n }\n\n pathNode.node.attributes.push(\n t.jsxAttribute(\n t.jsxIdentifier(SOURCE_PROP),\n t.stringLiteral(getSourceValue(state, loc) ?? `${filename.replace(/\\\\/g, \"/\")}:${loc.line}:${loc.column + 1}`),\n ),\n );\n },\n Program(programPath, state) {\n if (!injectComponentSource) {\n return;\n }\n\n const seen = new Set<string>();\n\n for (const childPath of programPath.get(\"body\")) {\n if (childPath.isExportNamedDeclaration() || childPath.isExportDefaultDeclaration()) {\n const declarationPath = childPath.get(\"declaration\");\n if (!Array.isArray(declarationPath) && declarationPath.node) {\n visitDeclaration(declarationPath, childPath, state, seen);\n }\n continue;\n }\n\n visitDeclaration(childPath, childPath, state, seen);\n }\n },\n },\n };\n}\n","export const SOURCE_PROP = \"__componentSourceLoc\";\n\n"],"mappings":"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","names":["resolved"]}
|
|
1
|
+
{"version":3,"sources":["../src/elementLocatorReact.ts","../src/babelInjectComponentSource.ts","../src/constants.ts"],"sourcesContent":["import type { Plugin, PluginOption } from \"vite\";\nimport { type LocatorOptions } from \"./runtime\";\n\nconst VIRTUAL_CLIENT_MODULE_ID = \"virtual:react-code-locator/client\";\nconst RESOLVED_VIRTUAL_CLIENT_MODULE_ID = `\\0${VIRTUAL_CLIENT_MODULE_ID}`;\n\nexport type ElementLocatorReactOptions = {\n command?: \"serve\" | \"build\";\n locator?: LocatorOptions;\n injectClient?: boolean;\n};\n\nfunction createClientInjector(locatorOptions: LocatorOptions = {}): Plugin {\n const serialized = JSON.stringify(locatorOptions);\n\n return {\n name: \"element-locator-client-injector\",\n apply: \"serve\",\n resolveId(id) {\n if (id === VIRTUAL_CLIENT_MODULE_ID) {\n return RESOLVED_VIRTUAL_CLIENT_MODULE_ID;\n }\n\n return null;\n },\n load(id) {\n if (id !== RESOLVED_VIRTUAL_CLIENT_MODULE_ID) {\n return null;\n }\n\n return `\n import { enableReactComponentJump } from \"react-code-locator/client\";\n\n enableReactComponentJump(${serialized});\n `;\n },\n transformIndexHtml() {\n return [\n {\n tag: \"script\",\n attrs: {\n type: \"module\",\n },\n children: `import \"${VIRTUAL_CLIENT_MODULE_ID}\";`,\n injectTo: \"head\",\n },\n ];\n },\n };\n}\n\nexport function elementLocatorReact(options: ElementLocatorReactOptions = {}): PluginOption[] {\n const { command = \"serve\", locator = {}, injectClient = true } = options;\n const isServe = command === \"serve\";\n\n return [isServe && injectClient ? createClientInjector(locator) : null].filter(Boolean);\n}\n","import path from \"node:path\";\nimport { types as t, type NodePath, type PluginObj } from \"@babel/core\";\nimport { SOURCE_PROP } from \"./constants\";\n\nexport type BabelInjectComponentSourceOptions = {\n injectJsxSource?: boolean;\n injectComponentSource?: boolean;\n};\n\ntype BabelState = {\n file?: {\n opts?: {\n filename?: string;\n };\n };\n};\n\nfunction isComponentName(name: string) {\n return /^[A-Z]/.test(name);\n}\n\nfunction isSupportedComponentInit(node: t.Expression | null | undefined): boolean {\n if (!node) {\n return false;\n }\n\n if (t.isArrowFunctionExpression(node) || t.isFunctionExpression(node)) {\n return true;\n }\n\n if (!t.isCallExpression(node)) {\n return false;\n }\n\n if (t.isIdentifier(node.callee) && (node.callee.name === \"memo\" || node.callee.name === \"forwardRef\")) {\n return true;\n }\n\n return (\n t.isMemberExpression(node.callee) &&\n t.isIdentifier(node.callee.object, { name: \"React\" }) &&\n t.isIdentifier(node.callee.property) &&\n (node.callee.property.name === \"memo\" || node.callee.property.name === \"forwardRef\")\n );\n}\n\nfunction getSourceValue(state: BabelState, loc: { line: number; column: number } | null | undefined) {\n const filename = state.file?.opts?.filename;\n if (!filename || !loc) {\n return null;\n }\n\n const relPath = path.relative(process.cwd(), filename).replace(/\\\\/g, \"/\");\n return `${relPath}:${loc.line}:${loc.column + 1}`;\n}\n\nfunction buildAssignment(name: string, sourceValue: string) {\n return t.expressionStatement(\n t.assignmentExpression(\n \"=\",\n t.memberExpression(t.identifier(name), t.identifier(SOURCE_PROP)),\n t.stringLiteral(sourceValue),\n ),\n );\n}\n\nfunction visitDeclaration(\n declarationPath: NodePath,\n insertAfterPath: NodePath,\n state: BabelState,\n seen: Set<string>,\n) {\n if (declarationPath.isFunctionDeclaration() || declarationPath.isClassDeclaration()) {\n const name = declarationPath.node.id?.name;\n if (!name || !isComponentName(name) || seen.has(name)) {\n return;\n }\n\n const sourceValue = getSourceValue(state, declarationPath.node.loc?.start);\n if (!sourceValue) {\n return;\n }\n\n seen.add(name);\n insertAfterPath.insertAfter(buildAssignment(name, sourceValue));\n return;\n }\n\n if (!declarationPath.isVariableDeclaration()) {\n return;\n }\n\n const assignments = declarationPath.node.declarations.flatMap((declarator) => {\n if (!t.isIdentifier(declarator.id) || !isComponentName(declarator.id.name) || seen.has(declarator.id.name)) {\n return [];\n }\n\n if (!declarator.init) {\n return [];\n }\n\n if (!isSupportedComponentInit(declarator.init)) {\n return [];\n }\n\n const sourceValue = getSourceValue(state, declarator.loc?.start ?? declarator.init.loc?.start);\n if (!sourceValue) {\n return [];\n }\n\n seen.add(declarator.id.name);\n return [buildAssignment(declarator.id.name, sourceValue)];\n });\n\n if (assignments.length > 0) {\n insertAfterPath.insertAfter(assignments);\n }\n}\n\nexport function babelInjectComponentSource(\n options: BabelInjectComponentSourceOptions = {},\n): PluginObj<BabelState> {\n const { injectJsxSource = false, injectComponentSource = true } = options;\n\n return {\n name: \"babel-inject-component-source\",\n visitor: {\n JSXOpeningElement(pathNode, state) {\n if (!injectJsxSource) {\n return;\n }\n\n const hasSourceProp = pathNode.node.attributes.some(\n (attr) => t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name) && attr.name.name === SOURCE_PROP,\n );\n if (hasSourceProp) {\n return;\n }\n\n const filename = state.file?.opts?.filename;\n const loc = pathNode.node.loc?.start;\n if (!filename || !loc) {\n return;\n }\n\n pathNode.node.attributes.push(\n t.jsxAttribute(\n t.jsxIdentifier(SOURCE_PROP),\n t.stringLiteral(getSourceValue(state, loc) ?? `${filename.replace(/\\\\/g, \"/\")}:${loc.line}:${loc.column + 1}`),\n ),\n );\n },\n Program(programPath, state) {\n if (!injectComponentSource) {\n return;\n }\n\n const seen = new Set<string>();\n\n for (const childPath of programPath.get(\"body\")) {\n if (childPath.isExportNamedDeclaration() || childPath.isExportDefaultDeclaration()) {\n const declarationPath = childPath.get(\"declaration\");\n if (!Array.isArray(declarationPath) && declarationPath.node) {\n visitDeclaration(declarationPath, childPath, state, seen);\n }\n continue;\n }\n\n visitDeclaration(childPath, childPath, state, seen);\n }\n },\n },\n };\n}\n","export const SOURCE_PROP = \"__componentSourceLoc\";\n\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-code-locator",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -13,12 +13,12 @@
|
|
|
13
13
|
},
|
|
14
14
|
"./client": {
|
|
15
15
|
"types": "./dist/client.d.ts",
|
|
16
|
-
"import": "./dist/client.
|
|
16
|
+
"import": "./dist/client.js",
|
|
17
17
|
"require": "./dist/client.cjs"
|
|
18
18
|
},
|
|
19
19
|
"./babel": {
|
|
20
20
|
"types": "./dist/babel.d.ts",
|
|
21
|
-
"import": "./dist/babel.
|
|
21
|
+
"import": "./dist/babel.js",
|
|
22
22
|
"require": "./dist/babel.cjs"
|
|
23
23
|
},
|
|
24
24
|
"./vite": {
|
|
@@ -42,21 +42,16 @@
|
|
|
42
42
|
"typecheck": "tsc --noEmit"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
|
-
"@babel/core": ">=7"
|
|
46
|
-
"@vitejs/plugin-react": ">=4"
|
|
45
|
+
"@babel/core": ">=7"
|
|
47
46
|
},
|
|
48
47
|
"peerDependenciesMeta": {
|
|
49
48
|
"@babel/core": {
|
|
50
49
|
"optional": true
|
|
51
|
-
},
|
|
52
|
-
"@vitejs/plugin-react": {
|
|
53
|
-
"optional": true
|
|
54
50
|
}
|
|
55
51
|
},
|
|
56
52
|
"devDependencies": {
|
|
57
53
|
"@babel/core": "^7.26.0",
|
|
58
54
|
"@types/node": "^22.13.10",
|
|
59
|
-
"@vitejs/plugin-react": "^4.3.4",
|
|
60
55
|
"tsup": "^8.5.1",
|
|
61
56
|
"typescript": "^5.8.2",
|
|
62
57
|
"vite": "^5.4.14"
|