react-code-locator 0.1.4
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 +243 -0
- package/dist/babel.cjs +152 -0
- package/dist/babel.cjs.map +1 -0
- package/dist/babel.d.cts +2 -0
- package/dist/babel.d.ts +2 -0
- package/dist/babel.js +115 -0
- package/dist/babel.js.map +1 -0
- package/dist/babelInjectComponentSource.cjs +150 -0
- package/dist/babelInjectComponentSource.cjs.map +1 -0
- package/dist/babelInjectComponentSource.d.cts +12 -0
- package/dist/babelInjectComponentSource.d.ts +12 -0
- package/dist/babelInjectComponentSource.js +115 -0
- package/dist/babelInjectComponentSource.js.map +1 -0
- package/dist/client.cjs +153 -0
- package/dist/client.cjs.map +1 -0
- package/dist/client.d.cts +14 -0
- package/dist/client.d.ts +14 -0
- package/dist/client.js +125 -0
- package/dist/client.js.map +1 -0
- package/dist/index.cjs +153 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +125 -0
- package/dist/index.js.map +1 -0
- package/dist/vite.cjs +211 -0
- package/dist/vite.cjs.map +1 -0
- package/dist/vite.d.cts +13 -0
- package/dist/vite.d.ts +13 -0
- package/dist/vite.js +173 -0
- package/dist/vite.js.map +1 -0
- package/dist/webpack.cjs +68 -0
- package/dist/webpack.cjs.map +1 -0
- package/dist/webpack.d.cts +2 -0
- package/dist/webpack.d.ts +2 -0
- package/dist/webpack.js +82 -0
- package/dist/webpack.js.map +1 -0
- package/dist/webpackRuntimeEntry.cjs +128 -0
- package/dist/webpackRuntimeEntry.cjs.map +1 -0
- package/dist/webpackRuntimeEntry.d.cts +2 -0
- package/dist/webpackRuntimeEntry.d.ts +2 -0
- package/dist/webpackRuntimeEntry.js +126 -0
- package/dist/webpackRuntimeEntry.js.map +1 -0
- package/package.json +64 -0
package/README.md
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
# React Component Jump
|
|
2
|
+
|
|
3
|
+
개발 중인 React 앱에서 요소를 `Shift + Click`하면 해당 UI와 연결된 소스 위치를 찾는 패키지입니다.
|
|
4
|
+
|
|
5
|
+
- JSX 태그의 `__source`를 우선 사용해서 정확한 JSX 위치를 찾습니다.
|
|
6
|
+
- JSX 정보가 없으면 컴포넌트 정의 위치로 fallback 합니다.
|
|
7
|
+
- Vite, Webpack, Babel, 브라우저 런타임을 각각 분리해서 사용할 수 있습니다.
|
|
8
|
+
|
|
9
|
+
## 설치
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm i -D react-component-jump
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
로컬 패키지로 연결할 때는:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm i -D /absolute/path/to/react-component-jump
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## 빠른 시작
|
|
22
|
+
|
|
23
|
+
Vite 환경이면 `react-component-jump/vite`만 붙이면 됩니다.
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { defineConfig } from "vite";
|
|
27
|
+
import { reactComponentJump } from "react-component-jump/vite";
|
|
28
|
+
|
|
29
|
+
export default defineConfig(({ command }) => ({
|
|
30
|
+
plugins: reactComponentJump({
|
|
31
|
+
command,
|
|
32
|
+
locator: {
|
|
33
|
+
triggerKey: "shift",
|
|
34
|
+
},
|
|
35
|
+
}),
|
|
36
|
+
}));
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
개발 서버에서 `Shift + Click`하면 브라우저 콘솔에 이런 식으로 출력됩니다.
|
|
40
|
+
|
|
41
|
+
```text
|
|
42
|
+
[react-component-jump] src/components/Button.tsx:14:1 [jsx]
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## 제공 엔트리
|
|
46
|
+
|
|
47
|
+
### `react-component-jump/vite`
|
|
48
|
+
|
|
49
|
+
Vite + React 프로젝트용 기본 진입점입니다.
|
|
50
|
+
|
|
51
|
+
- 개발 서버에서만 Babel 플러그인을 주입합니다.
|
|
52
|
+
- 기본값으로 브라우저 클라이언트도 자동 주입합니다.
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import { defineConfig } from "vite";
|
|
56
|
+
import { reactComponentJump } from "react-component-jump/vite";
|
|
57
|
+
|
|
58
|
+
export default defineConfig(({ command }) => ({
|
|
59
|
+
plugins: reactComponentJump({
|
|
60
|
+
command,
|
|
61
|
+
locator: {
|
|
62
|
+
triggerKey: "shift",
|
|
63
|
+
},
|
|
64
|
+
injectClient: true,
|
|
65
|
+
}),
|
|
66
|
+
}));
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
옵션:
|
|
70
|
+
|
|
71
|
+
- `command`: `"serve" | "build"` , 보통 Vite의 `command` 그대로 전달
|
|
72
|
+
- `locator.triggerKey`: `"alt" | "meta" | "ctrl" | "shift" | "none"`
|
|
73
|
+
- `locator.onLocate(result)`: 위치를 찾았을 때 커스텀 처리
|
|
74
|
+
- `locator.onError(error)`: 위치를 못 찾았을 때 커스텀 처리
|
|
75
|
+
- `injectClient`: `false`로 두면 브라우저 런타임 자동 주입 비활성화
|
|
76
|
+
|
|
77
|
+
### `react-component-jump/client`
|
|
78
|
+
|
|
79
|
+
브라우저 런타임만 수동으로 붙이고 싶을 때 사용합니다.
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
import { enableReactComponentJump } from "react-component-jump/client";
|
|
83
|
+
|
|
84
|
+
const dispose = enableReactComponentJump({
|
|
85
|
+
triggerKey: "shift",
|
|
86
|
+
onLocate(result) {
|
|
87
|
+
console.log("located:", result.source, result.mode);
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// 필요 시 해제
|
|
92
|
+
dispose();
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
자동 주입을 끄고 직접 붙이는 예시:
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
import { defineConfig } from "vite";
|
|
99
|
+
import { reactComponentJump } from "react-component-jump/vite";
|
|
100
|
+
|
|
101
|
+
export default defineConfig(({ command }) => ({
|
|
102
|
+
plugins: reactComponentJump({
|
|
103
|
+
command,
|
|
104
|
+
injectClient: false,
|
|
105
|
+
}),
|
|
106
|
+
}));
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
그 다음 앱 엔트리에서:
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
import { enableReactComponentJump } from "react-component-jump/client";
|
|
113
|
+
|
|
114
|
+
if (import.meta.env.DEV) {
|
|
115
|
+
enableReactComponentJump();
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### `react-component-jump/babel`
|
|
120
|
+
|
|
121
|
+
Babel 플러그인만 따로 사용할 때 사용합니다.
|
|
122
|
+
|
|
123
|
+
이 플러그인은 두 가지를 주입합니다.
|
|
124
|
+
|
|
125
|
+
- JSX 요소에 `__source`
|
|
126
|
+
- React 컴포넌트 함수/클래스에 `__componentSourceLoc`
|
|
127
|
+
|
|
128
|
+
```js
|
|
129
|
+
const { babelInjectComponentSource } = require("react-component-jump/babel");
|
|
130
|
+
|
|
131
|
+
module.exports = {
|
|
132
|
+
plugins: [babelInjectComponentSource],
|
|
133
|
+
};
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
ESM 설정 예시:
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
import { babelInjectComponentSource } from "react-component-jump/babel";
|
|
140
|
+
|
|
141
|
+
export default {
|
|
142
|
+
plugins: [babelInjectComponentSource],
|
|
143
|
+
};
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### `react-component-jump/webpack`
|
|
147
|
+
|
|
148
|
+
Webpack 설정에 Babel 플러그인과 런타임 엔트리를 함께 주입합니다.
|
|
149
|
+
|
|
150
|
+
```js
|
|
151
|
+
const { withReactComponentJump } = require("react-component-jump/webpack");
|
|
152
|
+
const config = createExistingWebpackConfig();
|
|
153
|
+
|
|
154
|
+
module.exports = withReactComponentJump(config, {
|
|
155
|
+
env: process.env.NODE_ENV,
|
|
156
|
+
});
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
동작 방식:
|
|
160
|
+
|
|
161
|
+
- `env !== "development"`이면 원본 config를 그대로 반환
|
|
162
|
+
- `babel-loader`를 찾아 `babelInjectComponentSource`를 추가
|
|
163
|
+
- 엔트리 앞에 런타임 스크립트를 prepend
|
|
164
|
+
|
|
165
|
+
전제:
|
|
166
|
+
|
|
167
|
+
- React 앱이 Babel을 통해 트랜스파일되어야 합니다.
|
|
168
|
+
- `module.rules` 안에 `babel-loader`가 있어야 자동 주입이 동작합니다.
|
|
169
|
+
|
|
170
|
+
### `react-component-jump`
|
|
171
|
+
|
|
172
|
+
런타임 유틸만 직접 사용할 때의 기본 엔트리입니다.
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
import { enableReactComponentJump, locateComponentSource } from "react-component-jump";
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
제공 API:
|
|
179
|
+
|
|
180
|
+
- `enableReactComponentJump(options)`
|
|
181
|
+
- `locateComponentSource(target)`
|
|
182
|
+
|
|
183
|
+
`locateComponentSource` 반환값:
|
|
184
|
+
|
|
185
|
+
```ts
|
|
186
|
+
type LocatorResult = {
|
|
187
|
+
source: string;
|
|
188
|
+
mode: "jsx" | "component";
|
|
189
|
+
};
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## 사용 흐름
|
|
193
|
+
|
|
194
|
+
정상 동작하려면 보통 아래 두 단계가 같이 필요합니다.
|
|
195
|
+
|
|
196
|
+
1. 빌드 단계에서 Babel 플러그인으로 소스 메타데이터 주입
|
|
197
|
+
2. 브라우저에서 클릭 이벤트를 가로채 React Fiber를 따라가며 위치 계산
|
|
198
|
+
|
|
199
|
+
Vite/webpack 어댑터를 쓰면 이 둘을 한 번에 붙일 수 있습니다.
|
|
200
|
+
|
|
201
|
+
## 개발/배포 스크립트
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
npm run build
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
- 라이브러리만 `dist/`로 빌드합니다.
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
npm run deploy --otp=123456
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
- `build`
|
|
214
|
+
- `npm pack --dry-run`
|
|
215
|
+
- `npm publish --access public`
|
|
216
|
+
|
|
217
|
+
순서로 실행합니다.
|
|
218
|
+
|
|
219
|
+
## peerDependencies
|
|
220
|
+
|
|
221
|
+
- `@babel/core >= 7`
|
|
222
|
+
- `@vitejs/plugin-react >= 4`
|
|
223
|
+
|
|
224
|
+
둘 다 optional peer dependency입니다.
|
|
225
|
+
|
|
226
|
+
- Babel 플러그인을 직접 쓰거나 Vite 어댑터를 쓸 때 필요할 수 있습니다.
|
|
227
|
+
- Webpack 런타임만 소비하는 쪽에서는 사용 방식에 따라 일부만 필요할 수 있습니다.
|
|
228
|
+
|
|
229
|
+
## 주의점
|
|
230
|
+
|
|
231
|
+
- 개발 모드 전용입니다.
|
|
232
|
+
- React 내부 필드인 Fiber와 `_debugSource`에 의존합니다.
|
|
233
|
+
- production build에서는 JSX 디버그 정보가 없어질 수 있습니다.
|
|
234
|
+
- 클릭 이벤트를 capture 단계에서 가로채므로, modifier key가 눌리면 기본 클릭 동작이 막힐 수 있습니다.
|
|
235
|
+
- `triggerKey: "none"`이면 모든 클릭에서 동작하므로 일반적으로 권장하지 않습니다.
|
|
236
|
+
|
|
237
|
+
## 주요 파일
|
|
238
|
+
|
|
239
|
+
- `src/runtime.ts`: 클릭 이벤트 처리와 React Fiber 기반 소스 탐색
|
|
240
|
+
- `src/babelInjectComponentSource.ts`: Babel 메타데이터 주입
|
|
241
|
+
- `src/vite.ts`: Vite 어댑터 export
|
|
242
|
+
- `src/webpack.cts`: Webpack 어댑터 export
|
|
243
|
+
- `src/client.ts`: 브라우저 런타임 export
|
package/dist/babel.cjs
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/babel.ts
|
|
31
|
+
var babel_exports = {};
|
|
32
|
+
__export(babel_exports, {
|
|
33
|
+
babelInjectComponentSource: () => babelInjectComponentSource
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(babel_exports);
|
|
36
|
+
|
|
37
|
+
// src/babelInjectComponentSource.ts
|
|
38
|
+
var import_node_path = __toESM(require("path"), 1);
|
|
39
|
+
var import_core = require("@babel/core");
|
|
40
|
+
|
|
41
|
+
// src/constants.ts
|
|
42
|
+
var SOURCE_PROP = "__componentSourceLoc";
|
|
43
|
+
|
|
44
|
+
// src/babelInjectComponentSource.ts
|
|
45
|
+
function isComponentName(name) {
|
|
46
|
+
return /^[A-Z]/.test(name);
|
|
47
|
+
}
|
|
48
|
+
function getSourceValue(state, loc) {
|
|
49
|
+
const filename = state.file?.opts?.filename;
|
|
50
|
+
if (!filename || !loc) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
const relPath = import_node_path.default.relative(process.cwd(), filename).replace(/\\/g, "/");
|
|
54
|
+
return `${relPath}:${loc.line}:${loc.column + 1}`;
|
|
55
|
+
}
|
|
56
|
+
function buildAssignment(name, sourceValue) {
|
|
57
|
+
return import_core.types.expressionStatement(
|
|
58
|
+
import_core.types.assignmentExpression(
|
|
59
|
+
"=",
|
|
60
|
+
import_core.types.memberExpression(import_core.types.identifier(name), import_core.types.identifier(SOURCE_PROP)),
|
|
61
|
+
import_core.types.stringLiteral(sourceValue)
|
|
62
|
+
)
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
function visitDeclaration(declarationPath, insertAfterPath, state, seen) {
|
|
66
|
+
if (declarationPath.isFunctionDeclaration() || declarationPath.isClassDeclaration()) {
|
|
67
|
+
const name = declarationPath.node.id?.name;
|
|
68
|
+
if (!name || !isComponentName(name) || seen.has(name)) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const sourceValue = getSourceValue(state, declarationPath.node.loc?.start);
|
|
72
|
+
if (!sourceValue) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
seen.add(name);
|
|
76
|
+
insertAfterPath.insertAfter(buildAssignment(name, sourceValue));
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (!declarationPath.isVariableDeclaration()) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const assignments = declarationPath.node.declarations.flatMap((declarator) => {
|
|
83
|
+
if (!import_core.types.isIdentifier(declarator.id) || !isComponentName(declarator.id.name) || seen.has(declarator.id.name)) {
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
if (!declarator.init) {
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
if (!import_core.types.isArrowFunctionExpression(declarator.init) && !import_core.types.isFunctionExpression(declarator.init)) {
|
|
90
|
+
return [];
|
|
91
|
+
}
|
|
92
|
+
const sourceValue = getSourceValue(state, declarator.loc?.start ?? declarator.init.loc?.start);
|
|
93
|
+
if (!sourceValue) {
|
|
94
|
+
return [];
|
|
95
|
+
}
|
|
96
|
+
seen.add(declarator.id.name);
|
|
97
|
+
return [buildAssignment(declarator.id.name, sourceValue)];
|
|
98
|
+
});
|
|
99
|
+
if (assignments.length > 0) {
|
|
100
|
+
insertAfterPath.insertAfter(assignments);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function babelInjectComponentSource() {
|
|
104
|
+
return {
|
|
105
|
+
name: "babel-inject-component-source",
|
|
106
|
+
visitor: {
|
|
107
|
+
JSXOpeningElement(pathNode, state) {
|
|
108
|
+
const hasSourceProp = pathNode.node.attributes.some(
|
|
109
|
+
(attr) => import_core.types.isJSXAttribute(attr) && import_core.types.isJSXIdentifier(attr.name) && attr.name.name === "__source"
|
|
110
|
+
);
|
|
111
|
+
if (hasSourceProp) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const filename = state.file?.opts?.filename;
|
|
115
|
+
const loc = pathNode.node.loc?.start;
|
|
116
|
+
if (!filename || !loc) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
pathNode.node.attributes.push(
|
|
120
|
+
import_core.types.jsxAttribute(
|
|
121
|
+
import_core.types.jsxIdentifier("__source"),
|
|
122
|
+
import_core.types.jsxExpressionContainer(
|
|
123
|
+
import_core.types.objectExpression([
|
|
124
|
+
import_core.types.objectProperty(import_core.types.identifier("fileName"), import_core.types.stringLiteral(filename)),
|
|
125
|
+
import_core.types.objectProperty(import_core.types.identifier("lineNumber"), import_core.types.numericLiteral(loc.line)),
|
|
126
|
+
import_core.types.objectProperty(import_core.types.identifier("columnNumber"), import_core.types.numericLiteral(loc.column + 1))
|
|
127
|
+
])
|
|
128
|
+
)
|
|
129
|
+
)
|
|
130
|
+
);
|
|
131
|
+
},
|
|
132
|
+
Program(programPath, state) {
|
|
133
|
+
const seen = /* @__PURE__ */ new Set();
|
|
134
|
+
for (const childPath of programPath.get("body")) {
|
|
135
|
+
if (childPath.isExportNamedDeclaration() || childPath.isExportDefaultDeclaration()) {
|
|
136
|
+
const declarationPath = childPath.get("declaration");
|
|
137
|
+
if (!Array.isArray(declarationPath) && declarationPath.node) {
|
|
138
|
+
visitDeclaration(declarationPath, childPath, state, seen);
|
|
139
|
+
}
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
visitDeclaration(childPath, childPath, state, seen);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
149
|
+
0 && (module.exports = {
|
|
150
|
+
babelInjectComponentSource
|
|
151
|
+
});
|
|
152
|
+
//# sourceMappingURL=babel.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/babel.ts","../src/babelInjectComponentSource.ts","../src/constants.ts"],"sourcesContent":["export { babelInjectComponentSource } 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\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(): PluginObj<BabelState> {\n return {\n name: \"babel-inject-component-source\",\n visitor: {\n JSXOpeningElement(pathNode, state) {\n const hasSourceProp = pathNode.node.attributes.some(\n (attr) => t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name) && attr.name.name === \"__source\",\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\"),\n t.jsxExpressionContainer(\n t.objectExpression([\n t.objectProperty(t.identifier(\"fileName\"), t.stringLiteral(filename)),\n t.objectProperty(t.identifier(\"lineNumber\"), t.numericLiteral(loc.line)),\n t.objectProperty(t.identifier(\"columnNumber\"), t.numericLiteral(loc.column + 1)),\n ]),\n ),\n ),\n );\n },\n Program(programPath, state) {\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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,uBAAiB;AACjB,kBAA0D;;;ACDnD,IAAM,cAAc;;;ADY3B,SAAS,gBAAgB,MAAc;AACrC,SAAO,SAAS,KAAK,IAAI;AAC3B;AAEA,SAAS,eAAe,OAAmB,KAA0D;AACnG,QAAM,WAAW,MAAM,MAAM,MAAM;AACnC,MAAI,CAAC,YAAY,CAAC,KAAK;AACrB,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,iBAAAA,QAAK,SAAS,QAAQ,IAAI,GAAG,QAAQ,EAAE,QAAQ,OAAO,GAAG;AACzE,SAAO,GAAG,OAAO,IAAI,IAAI,IAAI,IAAI,IAAI,SAAS,CAAC;AACjD;AAEA,SAAS,gBAAgB,MAAc,aAAqB;AAC1D,SAAO,YAAAC,MAAE;AAAA,IACP,YAAAA,MAAE;AAAA,MACA;AAAA,MACA,YAAAA,MAAE,iBAAiB,YAAAA,MAAE,WAAW,IAAI,GAAG,YAAAA,MAAE,WAAW,WAAW,CAAC;AAAA,MAChE,YAAAA,MAAE,cAAc,WAAW;AAAA,IAC7B;AAAA,EACF;AACF;AAEA,SAAS,iBACP,iBACA,iBACA,OACA,MACA;AACA,MAAI,gBAAgB,sBAAsB,KAAK,gBAAgB,mBAAmB,GAAG;AACnF,UAAM,OAAO,gBAAgB,KAAK,IAAI;AACtC,QAAI,CAAC,QAAQ,CAAC,gBAAgB,IAAI,KAAK,KAAK,IAAI,IAAI,GAAG;AACrD;AAAA,IACF;AAEA,UAAM,cAAc,eAAe,OAAO,gBAAgB,KAAK,KAAK,KAAK;AACzE,QAAI,CAAC,aAAa;AAChB;AAAA,IACF;AAEA,SAAK,IAAI,IAAI;AACb,oBAAgB,YAAY,gBAAgB,MAAM,WAAW,CAAC;AAC9D;AAAA,EACF;AAEA,MAAI,CAAC,gBAAgB,sBAAsB,GAAG;AAC5C;AAAA,EACF;AAEA,QAAM,cAAc,gBAAgB,KAAK,aAAa,QAAQ,CAAC,eAAe;AAC5E,QAAI,CAAC,YAAAA,MAAE,aAAa,WAAW,EAAE,KAAK,CAAC,gBAAgB,WAAW,GAAG,IAAI,KAAK,KAAK,IAAI,WAAW,GAAG,IAAI,GAAG;AAC1G,aAAO,CAAC;AAAA,IACV;AAEA,QAAI,CAAC,WAAW,MAAM;AACpB,aAAO,CAAC;AAAA,IACV;AAEA,QAAI,CAAC,YAAAA,MAAE,0BAA0B,WAAW,IAAI,KAAK,CAAC,YAAAA,MAAE,qBAAqB,WAAW,IAAI,GAAG;AAC7F,aAAO,CAAC;AAAA,IACV;AAEA,UAAM,cAAc,eAAe,OAAO,WAAW,KAAK,SAAS,WAAW,KAAK,KAAK,KAAK;AAC7F,QAAI,CAAC,aAAa;AAChB,aAAO,CAAC;AAAA,IACV;AAEA,SAAK,IAAI,WAAW,GAAG,IAAI;AAC3B,WAAO,CAAC,gBAAgB,WAAW,GAAG,MAAM,WAAW,CAAC;AAAA,EAC1D,CAAC;AAED,MAAI,YAAY,SAAS,GAAG;AAC1B,oBAAgB,YAAY,WAAW;AAAA,EACzC;AACF;AAEO,SAAS,6BAAoD;AAClE,SAAO;AAAA,IACL,MAAM;AAAA,IACN,SAAS;AAAA,MACP,kBAAkB,UAAU,OAAO;AACjC,cAAM,gBAAgB,SAAS,KAAK,WAAW;AAAA,UAC7C,CAAC,SAAS,YAAAA,MAAE,eAAe,IAAI,KAAK,YAAAA,MAAE,gBAAgB,KAAK,IAAI,KAAK,KAAK,KAAK,SAAS;AAAA,QACzF;AACA,YAAI,eAAe;AACjB;AAAA,QACF;AAEA,cAAM,WAAW,MAAM,MAAM,MAAM;AACnC,cAAM,MAAM,SAAS,KAAK,KAAK;AAC/B,YAAI,CAAC,YAAY,CAAC,KAAK;AACrB;AAAA,QACF;AAEA,iBAAS,KAAK,WAAW;AAAA,UACvB,YAAAA,MAAE;AAAA,YACA,YAAAA,MAAE,cAAc,UAAU;AAAA,YAC1B,YAAAA,MAAE;AAAA,cACA,YAAAA,MAAE,iBAAiB;AAAA,gBACjB,YAAAA,MAAE,eAAe,YAAAA,MAAE,WAAW,UAAU,GAAG,YAAAA,MAAE,cAAc,QAAQ,CAAC;AAAA,gBACpE,YAAAA,MAAE,eAAe,YAAAA,MAAE,WAAW,YAAY,GAAG,YAAAA,MAAE,eAAe,IAAI,IAAI,CAAC;AAAA,gBACvE,YAAAA,MAAE,eAAe,YAAAA,MAAE,WAAW,cAAc,GAAG,YAAAA,MAAE,eAAe,IAAI,SAAS,CAAC,CAAC;AAAA,cACjF,CAAC;AAAA,YACH;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,MACA,QAAQ,aAAa,OAAO;AAC1B,cAAM,OAAO,oBAAI,IAAY;AAE7B,mBAAW,aAAa,YAAY,IAAI,MAAM,GAAG;AAC/C,cAAI,UAAU,yBAAyB,KAAK,UAAU,2BAA2B,GAAG;AAClF,kBAAM,kBAAkB,UAAU,IAAI,aAAa;AACnD,gBAAI,CAAC,MAAM,QAAQ,eAAe,KAAK,gBAAgB,MAAM;AAC3D,+BAAiB,iBAAiB,WAAW,OAAO,IAAI;AAAA,YAC1D;AACA;AAAA,UACF;AAEA,2BAAiB,WAAW,WAAW,OAAO,IAAI;AAAA,QACpD;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;","names":["path","t"]}
|
package/dist/babel.d.cts
ADDED
package/dist/babel.d.ts
ADDED
package/dist/babel.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// src/babelInjectComponentSource.ts
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { types as t } from "@babel/core";
|
|
4
|
+
|
|
5
|
+
// src/constants.ts
|
|
6
|
+
var SOURCE_PROP = "__componentSourceLoc";
|
|
7
|
+
|
|
8
|
+
// src/babelInjectComponentSource.ts
|
|
9
|
+
function isComponentName(name) {
|
|
10
|
+
return /^[A-Z]/.test(name);
|
|
11
|
+
}
|
|
12
|
+
function getSourceValue(state, loc) {
|
|
13
|
+
const filename = state.file?.opts?.filename;
|
|
14
|
+
if (!filename || !loc) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
const relPath = path.relative(process.cwd(), filename).replace(/\\/g, "/");
|
|
18
|
+
return `${relPath}:${loc.line}:${loc.column + 1}`;
|
|
19
|
+
}
|
|
20
|
+
function buildAssignment(name, sourceValue) {
|
|
21
|
+
return t.expressionStatement(
|
|
22
|
+
t.assignmentExpression(
|
|
23
|
+
"=",
|
|
24
|
+
t.memberExpression(t.identifier(name), t.identifier(SOURCE_PROP)),
|
|
25
|
+
t.stringLiteral(sourceValue)
|
|
26
|
+
)
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
function visitDeclaration(declarationPath, insertAfterPath, state, seen) {
|
|
30
|
+
if (declarationPath.isFunctionDeclaration() || declarationPath.isClassDeclaration()) {
|
|
31
|
+
const name = declarationPath.node.id?.name;
|
|
32
|
+
if (!name || !isComponentName(name) || seen.has(name)) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const sourceValue = getSourceValue(state, declarationPath.node.loc?.start);
|
|
36
|
+
if (!sourceValue) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
seen.add(name);
|
|
40
|
+
insertAfterPath.insertAfter(buildAssignment(name, sourceValue));
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (!declarationPath.isVariableDeclaration()) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const assignments = declarationPath.node.declarations.flatMap((declarator) => {
|
|
47
|
+
if (!t.isIdentifier(declarator.id) || !isComponentName(declarator.id.name) || seen.has(declarator.id.name)) {
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
if (!declarator.init) {
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
if (!t.isArrowFunctionExpression(declarator.init) && !t.isFunctionExpression(declarator.init)) {
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
const sourceValue = getSourceValue(state, declarator.loc?.start ?? declarator.init.loc?.start);
|
|
57
|
+
if (!sourceValue) {
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
60
|
+
seen.add(declarator.id.name);
|
|
61
|
+
return [buildAssignment(declarator.id.name, sourceValue)];
|
|
62
|
+
});
|
|
63
|
+
if (assignments.length > 0) {
|
|
64
|
+
insertAfterPath.insertAfter(assignments);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function babelInjectComponentSource() {
|
|
68
|
+
return {
|
|
69
|
+
name: "babel-inject-component-source",
|
|
70
|
+
visitor: {
|
|
71
|
+
JSXOpeningElement(pathNode, state) {
|
|
72
|
+
const hasSourceProp = pathNode.node.attributes.some(
|
|
73
|
+
(attr) => t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name) && attr.name.name === "__source"
|
|
74
|
+
);
|
|
75
|
+
if (hasSourceProp) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const filename = state.file?.opts?.filename;
|
|
79
|
+
const loc = pathNode.node.loc?.start;
|
|
80
|
+
if (!filename || !loc) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
pathNode.node.attributes.push(
|
|
84
|
+
t.jsxAttribute(
|
|
85
|
+
t.jsxIdentifier("__source"),
|
|
86
|
+
t.jsxExpressionContainer(
|
|
87
|
+
t.objectExpression([
|
|
88
|
+
t.objectProperty(t.identifier("fileName"), t.stringLiteral(filename)),
|
|
89
|
+
t.objectProperty(t.identifier("lineNumber"), t.numericLiteral(loc.line)),
|
|
90
|
+
t.objectProperty(t.identifier("columnNumber"), t.numericLiteral(loc.column + 1))
|
|
91
|
+
])
|
|
92
|
+
)
|
|
93
|
+
)
|
|
94
|
+
);
|
|
95
|
+
},
|
|
96
|
+
Program(programPath, state) {
|
|
97
|
+
const seen = /* @__PURE__ */ new Set();
|
|
98
|
+
for (const childPath of programPath.get("body")) {
|
|
99
|
+
if (childPath.isExportNamedDeclaration() || childPath.isExportDefaultDeclaration()) {
|
|
100
|
+
const declarationPath = childPath.get("declaration");
|
|
101
|
+
if (!Array.isArray(declarationPath) && declarationPath.node) {
|
|
102
|
+
visitDeclaration(declarationPath, childPath, state, seen);
|
|
103
|
+
}
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
visitDeclaration(childPath, childPath, state, seen);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
export {
|
|
113
|
+
babelInjectComponentSource
|
|
114
|
+
};
|
|
115
|
+
//# sourceMappingURL=babel.js.map
|
|
@@ -0,0 +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\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(): PluginObj<BabelState> {\n return {\n name: \"babel-inject-component-source\",\n visitor: {\n JSXOpeningElement(pathNode, state) {\n const hasSourceProp = pathNode.node.attributes.some(\n (attr) => t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name) && attr.name.name === \"__source\",\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\"),\n t.jsxExpressionContainer(\n t.objectExpression([\n t.objectProperty(t.identifier(\"fileName\"), t.stringLiteral(filename)),\n t.objectProperty(t.identifier(\"lineNumber\"), t.numericLiteral(loc.line)),\n t.objectProperty(t.identifier(\"columnNumber\"), t.numericLiteral(loc.column + 1)),\n ]),\n ),\n ),\n );\n },\n Program(programPath, state) {\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":[]}
|