react-code-locator 0.1.6 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/client.cjs +96 -0
- package/dist/client.cjs.map +1 -1
- package/dist/client.js +96 -0
- package/dist/client.js.map +1 -1
- package/dist/index.cjs +96 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +96 -0
- package/dist/index.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/dist/webpackRuntimeEntry.cjs +96 -0
- package/dist/webpackRuntimeEntry.cjs.map +1 -1
- package/dist/webpackRuntimeEntry.js +96 -0
- package/dist/webpackRuntimeEntry.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/client.cjs
CHANGED
|
@@ -110,6 +110,86 @@ function getDebugSource(fiber) {
|
|
|
110
110
|
}
|
|
111
111
|
return null;
|
|
112
112
|
}
|
|
113
|
+
function createStatusOverlay(triggerKey) {
|
|
114
|
+
if (typeof document === "undefined") {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
const element = document.createElement("div");
|
|
118
|
+
let currentText = "";
|
|
119
|
+
let copyValue = null;
|
|
120
|
+
let hideTimer = null;
|
|
121
|
+
element.setAttribute("data-react-code-locator", "true");
|
|
122
|
+
Object.assign(element.style, {
|
|
123
|
+
position: "fixed",
|
|
124
|
+
right: "12px",
|
|
125
|
+
bottom: "12px",
|
|
126
|
+
zIndex: "2147483647",
|
|
127
|
+
padding: "8px 10px",
|
|
128
|
+
borderRadius: "8px",
|
|
129
|
+
background: "rgba(17, 24, 39, 0.92)",
|
|
130
|
+
color: "#fff",
|
|
131
|
+
fontSize: "12px",
|
|
132
|
+
lineHeight: "1.4",
|
|
133
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",
|
|
134
|
+
boxShadow: "0 8px 30px rgba(0, 0, 0, 0.25)",
|
|
135
|
+
pointerEvents: "auto",
|
|
136
|
+
cursor: "pointer",
|
|
137
|
+
maxWidth: "min(70vw, 720px)",
|
|
138
|
+
wordBreak: "break-all",
|
|
139
|
+
opacity: "0",
|
|
140
|
+
transition: "opacity 120ms ease"
|
|
141
|
+
});
|
|
142
|
+
const show = (message, tone) => {
|
|
143
|
+
currentText = message;
|
|
144
|
+
element.textContent = message;
|
|
145
|
+
element.style.background = tone === "success" ? "rgba(6, 95, 70, 0.92)" : tone === "error" ? "rgba(153, 27, 27, 0.94)" : "rgba(17, 24, 39, 0.92)";
|
|
146
|
+
element.style.opacity = "1";
|
|
147
|
+
element.style.pointerEvents = "auto";
|
|
148
|
+
if (hideTimer) {
|
|
149
|
+
clearTimeout(hideTimer);
|
|
150
|
+
}
|
|
151
|
+
hideTimer = setTimeout(() => {
|
|
152
|
+
element.style.opacity = "0";
|
|
153
|
+
element.style.pointerEvents = "none";
|
|
154
|
+
}, 1500);
|
|
155
|
+
};
|
|
156
|
+
element.addEventListener("click", async () => {
|
|
157
|
+
if (!copyValue) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
try {
|
|
161
|
+
await navigator.clipboard.writeText(copyValue);
|
|
162
|
+
show(`[react-code-locator] copied`, "success");
|
|
163
|
+
} catch {
|
|
164
|
+
show(`[react-code-locator] copy failed`, "error");
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
show(`[react-code-locator] enabled (${triggerKey}+click)`, "idle");
|
|
168
|
+
const mount = () => {
|
|
169
|
+
if (!element.isConnected && document.body) {
|
|
170
|
+
document.body.appendChild(element);
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
if (document.body) {
|
|
174
|
+
mount();
|
|
175
|
+
} else {
|
|
176
|
+
document.addEventListener("DOMContentLoaded", mount, { once: true });
|
|
177
|
+
}
|
|
178
|
+
return {
|
|
179
|
+
setStatus(message, tone = "idle") {
|
|
180
|
+
show(message, tone);
|
|
181
|
+
},
|
|
182
|
+
setCopyValue(value) {
|
|
183
|
+
copyValue = value;
|
|
184
|
+
},
|
|
185
|
+
remove() {
|
|
186
|
+
if (hideTimer) {
|
|
187
|
+
clearTimeout(hideTimer);
|
|
188
|
+
}
|
|
189
|
+
element.remove();
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
}
|
|
113
193
|
function locateComponentSource(target) {
|
|
114
194
|
const elementTarget = target instanceof Element ? target : target instanceof Node ? target.parentElement : null;
|
|
115
195
|
const fiber = getClosestReactFiber(elementTarget);
|
|
@@ -133,16 +213,31 @@ function locateComponentSource(target) {
|
|
|
133
213
|
};
|
|
134
214
|
}
|
|
135
215
|
function enableReactComponentJump(options = {}) {
|
|
216
|
+
const overlay = createStatusOverlay(options.triggerKey ?? "shift");
|
|
136
217
|
const {
|
|
137
218
|
triggerKey = "shift",
|
|
138
219
|
onLocate = (result) => {
|
|
139
220
|
console.log(`[react-code-locator] ${result.source}`);
|
|
221
|
+
overlay?.setCopyValue(result.source);
|
|
222
|
+
overlay?.setStatus(`[react-code-locator] ${result.source}`, "success");
|
|
140
223
|
},
|
|
141
224
|
onError = (error) => {
|
|
142
225
|
console.error("[react-code-locator]", error);
|
|
226
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
227
|
+
overlay?.setCopyValue(null);
|
|
228
|
+
overlay?.setStatus(`[react-code-locator] ${message}`, "error");
|
|
143
229
|
}
|
|
144
230
|
} = options;
|
|
231
|
+
console.log("[react-code-locator] enabled", { triggerKey });
|
|
145
232
|
const handler = (event) => {
|
|
233
|
+
console.log("[react-code-locator] click", {
|
|
234
|
+
triggerKey,
|
|
235
|
+
shiftKey: event.shiftKey,
|
|
236
|
+
altKey: event.altKey,
|
|
237
|
+
ctrlKey: event.ctrlKey,
|
|
238
|
+
metaKey: event.metaKey,
|
|
239
|
+
target: event.target
|
|
240
|
+
});
|
|
146
241
|
if (!isTriggerPressed(event, triggerKey)) {
|
|
147
242
|
return;
|
|
148
243
|
}
|
|
@@ -158,6 +253,7 @@ function enableReactComponentJump(options = {}) {
|
|
|
158
253
|
document.addEventListener("click", handler, true);
|
|
159
254
|
return () => {
|
|
160
255
|
document.removeEventListener("click", handler, true);
|
|
256
|
+
overlay?.remove();
|
|
161
257
|
};
|
|
162
258
|
}
|
|
163
259
|
// Annotate the CommonJS export names for ESM import in node:
|