vite-plugin-jsx-source-attrs 0.1.0 → 0.1.1-dev.mr.2.23.8ec07595

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.llm.txt ADDED
@@ -0,0 +1,467 @@
1
+ # LLM 설치 가이드: vite-plugin-jsx-source-attrs
2
+
3
+ 이 문서는 LLM/AI 에이전트가 Vite React 프로젝트의 `vite.config.ts`에 `vite-plugin-jsx-source-attrs`를 설치하고 설정할 때 참고하기 위한 최소 실행 지침입니다.
4
+
5
+ ## 목적
6
+
7
+ `vite-plugin-jsx-source-attrs`는 Vite React 개발 환경에서 JSX 요소에 소스 위치 attribute를 자동 주입하는 Vite 플러그인과 Babel 플러그인 유틸리티입니다.
8
+
9
+ 기본 목표:
10
+
11
+ - 개발 모드에서 intrinsic JSX 요소에 `data-source-location`을 주입한다.
12
+ - 프로덕션 빌드에서는 주입하지 않는다.
13
+ - 사용자 컴포넌트에는 기본적으로 주입하지 않는다.
14
+ - 기존 attribute가 있으면 중복 주입하지 않는다.
15
+ - Vite 가상 모듈 prefix, query/hash, Windows 경로 구분자를 정규화한다.
16
+
17
+ 예상 변환 결과:
18
+
19
+ 입력:
20
+
21
+ ```tsx
22
+ const view = <div />;
23
+ ```
24
+
25
+ 개발 모드 출력 예시:
26
+
27
+ ```tsx
28
+ const view = <div data-source-location="src/App.tsx:1:14" />;
29
+ ```
30
+
31
+ ## 자동 적용 프롬프트
32
+
33
+ 다른 프로젝트에 이 패키지를 설치하고 `vite.config.ts`까지 자동 반영하려면 아래 프롬프트를 LLM/AI 에이전트에게 그대로 전달한다.
34
+
35
+ ````text
36
+ Vite React 프로젝트에 `vite-plugin-jsx-source-attrs`를 설치하고 설정해줘.
37
+
38
+ 요구사항:
39
+ 1. 프로젝트 루트에서 패키지 매니저를 확인해줘.
40
+ - `packageManager` 필드가 있으면 해당 도구를 우선 사용해줘.
41
+ - `packageManager`가 없으면 lockfile 기준으로 판단해줘: `bun.lock` → Bun, `package-lock.json` → npm, `pnpm-lock.yaml` → pnpm, `yarn.lock` → Yarn.
42
+ - 그래도 판단할 수 없으면 npm을 사용해줘.
43
+ 2. `vite-plugin-jsx-source-attrs`를 devDependency로 설치해줘.
44
+ - stable 검증이면 `vite-plugin-jsx-source-attrs` 또는 `vite-plugin-jsx-source-attrs@latest`를 설치해줘.
45
+ - MR 검증본이 필요하면 `vite-plugin-jsx-source-attrs@dev`를 설치해줘.
46
+ - main prerelease 검증본이 필요하면 `vite-plugin-jsx-source-attrs@pre`를 설치해줘.
47
+ 3. `vite.config.ts` 또는 `vite.config.mts`를 찾아 `@vitejs/plugin-react`보다 앞에 `vitePluginJsxSourceAttrs()`를 추가해줘.
48
+ 4. 기존 `react()` 설정, 기존 Babel plugin, alias, server, build 설정은 보존해줘.
49
+ 5. 이미 `react({ babel: { plugins: [...] } })`를 지원하는 구버전 조합이면 `jsxSourceAttrs()`를 기존 Babel 플러그인 배열 끝에 추가해도 돼.
50
+ 6. Vite 8 + `@vitejs/plugin-react` 6처럼 `babel` 옵션이 없는 조합에서는 `react({ babel: ... })` 형태로 바꾸지 말고 Vite 플러그인 방식을 사용해줘.
51
+ 7. `defineConfig`가 객체 형태이고 `mode`가 필요하면 `defineConfig(({ mode }) => ({ ... }))` 형태로 바꿔줘. 단, 기존 설정 의미는 보존해줘.
52
+ 8. 기본 설정은 아래 값을 사용해줘.
53
+
54
+ ```ts
55
+ vitePluginJsxSourceAttrs({
56
+ enabled: mode === 'development',
57
+ attributeName: 'data-source-location',
58
+ componentAttributeName: null,
59
+ })
60
+ ```
61
+
62
+ 9. import를 추가해줘.
63
+
64
+ ```ts
65
+ import { vitePluginJsxSourceAttrs } from 'vite-plugin-jsx-source-attrs';
66
+ ```
67
+
68
+ 10. 기존 로컬 JSX source attrs 플러그인이 있으면 새 패키지로 대체해줘.
69
+ - 제거 후보: `resources/scripts/jsx-source-attrs.js`, `resources/babel/jsx-source-attrs.js`, `src/resources/babel/jsx-source-attrs.js`, `@wix/babel-plugin-jsx-source-attrs` 직접 설정.
70
+ - 단, 실제 import가 제거된 뒤에만 파일/의존성을 삭제해줘.
71
+ 11. 빌드 단계에서는 devDependencies가 설치되어야 한다는 점을 CI 설정에서 확인해줘.
72
+ 12. 변경 후 프로젝트의 기존 검증 스크립트를 실행해줘.
73
+ - 가능한 경우: lint, typecheck, test, build 순서.
74
+ - 없는 스크립트는 억지로 만들지 말고 건너뛰었다고 보고해줘.
75
+ 13. 최종 보고에는 설치한 버전/tag, 수정한 파일, 실행한 검증 명령, 남은 주의사항을 요약해줘.
76
+
77
+ 주의사항:
78
+ - unrelated 파일은 수정하지 마.
79
+ - 기존 변경사항을 되돌리지 마.
80
+ - 문자열/주석에 있는 예시는 실제 import나 실행 코드가 아니면 함부로 삭제하지 마.
81
+ - 프로덕션 빌드 산출물에는 `data-source-location`이 없어야 해.
82
+ ````
83
+
84
+ ### 자동 적용 후 기대되는 `vite.config.ts` 예시
85
+
86
+ ```ts
87
+ import react from '@vitejs/plugin-react';
88
+ import { vitePluginJsxSourceAttrs } from 'vite-plugin-jsx-source-attrs';
89
+ import { defineConfig } from 'vite';
90
+
91
+ export default defineConfig(({ mode }) => ({
92
+ plugins: [
93
+ vitePluginJsxSourceAttrs({
94
+ enabled: mode === 'development',
95
+ attributeName: 'data-source-location',
96
+ componentAttributeName: null,
97
+ }),
98
+ react(),
99
+ ],
100
+ }));
101
+ ```
102
+
103
+ ## 설치 명령
104
+
105
+ 대상 프로젝트의 패키지 매니저를 먼저 확인한다.
106
+
107
+ - `bun.lock`이 있으면 Bun 사용
108
+ - `package-lock.json`이 있으면 npm 사용
109
+ - `pnpm-lock.yaml`이 있으면 pnpm 사용
110
+ - `yarn.lock`이 있으면 Yarn 사용
111
+
112
+ 패키지 설치:
113
+
114
+ ```bash
115
+ bun add -D vite-plugin-jsx-source-attrs
116
+ ```
117
+
118
+ ```bash
119
+ npm install -D vite-plugin-jsx-source-attrs
120
+ ```
121
+
122
+ ```bash
123
+ pnpm add -D vite-plugin-jsx-source-attrs
124
+ ```
125
+
126
+ ```bash
127
+ yarn add -D vite-plugin-jsx-source-attrs
128
+ ```
129
+
130
+ ## devDependency 설치 기준
131
+
132
+ 이 패키지는 Vite의 `@vitejs/plugin-react` Babel 설정에서 실행되는 빌드 타임 도구다. 브라우저 런타임 코드에서 직접 import하지 않으므로 일반 Vite 애플리케이션에서는 `devDependencies`에 설치한다.
133
+
134
+ 프로덕션 배포를 만들 때도 `vite.config.ts`가 평가되므로 빌드 단계에서는 devDependency가 설치되어 있어야 한다. 즉, 프로덕션에 배포하는 앱이어도 이 패키지는 devDependency여도 괜찮다. 단, CI가 `npm ci --omit=dev`, `pnpm install --prod`, `bun install --production`처럼 devDependency를 제외하고 빌드하면 Vite config import가 실패한다.
135
+
136
+ 권장 운영 방식:
137
+
138
+ 1. 빌드 job/stage에서는 devDependency 포함 설치를 사용한다.
139
+ 2. 빌드 산출물만 런타임 이미지나 서버로 복사한다.
140
+ 3. 런타임 이미지에서 별도 Node 의존성을 설치해야 한다면 그 단계에서만 production dependency 설치를 사용한다.
141
+
142
+
143
+ ## MR/main 테스트 배포 버전 설치
144
+
145
+ 이 저장소의 CI는 레퍼런스 코드베이스처럼 main/MR마다 테스트 가능한 prerelease 패키지를 배포한다.
146
+
147
+ - main pipeline: `pre` dist-tag로 `0.1.1-pre.main.<pipeline>.<shortsha>` 형태를 배포한다.
148
+ - MR pipeline: `dev` dist-tag로 `0.1.1-dev.mr.<mr_iid>.<pipeline>.<shortsha>` 형태를 배포한다.
149
+ - stable release/main 버전은 `latest` dist-tag를 사용한다.
150
+
151
+ 대상 프로젝트에서 최신 MR 검증본을 설치해야 하면 `dev` tag를 사용한다.
152
+
153
+ ```bash
154
+ bun add -D vite-plugin-jsx-source-attrs@dev
155
+ ```
156
+
157
+ ```bash
158
+ npm install -D vite-plugin-jsx-source-attrs@dev
159
+ ```
160
+
161
+ main 최신 사전 검증본을 설치해야 하면 `pre` tag를 사용한다.
162
+
163
+ ```bash
164
+ bun add -D vite-plugin-jsx-source-attrs@pre
165
+ ```
166
+
167
+ ```bash
168
+ npm install -D vite-plugin-jsx-source-attrs@pre
169
+ ```
170
+
171
+ GitLab Package Registry에서 설치해야 하는 경우에는 프로젝트 npm registry를 지정한다.
172
+
173
+ ```bash
174
+ npm install -D vite-plugin-jsx-source-attrs@dev \
175
+ --registry https://git.hyeon.pro/api/v4/projects/112/packages/npm/
176
+ ```
177
+
178
+ GitLab registry 인증이 필요한 프로젝트에서는 `.npmrc`에 다음 형태의 토큰 설정이 있어야 한다.
179
+
180
+ ```text
181
+ //git.hyeon.pro/api/v4/projects/112/packages/npm/:_authToken=${GITLAB_NPM_TOKEN}
182
+ ```
183
+
184
+ LLM이 특정 MR에서 배포된 정확한 버전을 알고 있다면 tag 대신 명시 버전을 설치한다.
185
+
186
+ ```bash
187
+ bun add -D vite-plugin-jsx-source-attrs@0.1.1-dev.mr.12.345.abcdef1
188
+ ```
189
+
190
+ ## vite.config.ts 기본 적용
191
+
192
+ `@vitejs/plugin-react`의 `react({ babel: { plugins: [...] } })` 설정에 `jsxSourceAttrs()`를 추가한다.
193
+
194
+ 기본 권장 설정:
195
+
196
+ ```ts
197
+ import react from '@vitejs/plugin-react';
198
+ import { jsxSourceAttrs } from 'vite-plugin-jsx-source-attrs';
199
+ import { defineConfig } from 'vite';
200
+
201
+ export default defineConfig(({ mode }) => ({
202
+ plugins: [
203
+ react({
204
+ babel: {
205
+ plugins: [
206
+ jsxSourceAttrs({
207
+ enabled: mode === 'development',
208
+ attributeName: 'data-source-location',
209
+ componentAttributeName: null,
210
+ }),
211
+ ],
212
+ },
213
+ }),
214
+ ],
215
+ }));
216
+ ```
217
+
218
+ ## 기존 vite.config.ts에 react()가 이미 있는 경우
219
+
220
+ 기존 설정을 유지하면서 `babel.plugins` 배열에만 추가한다.
221
+
222
+ 변경 전 예시:
223
+
224
+ ```ts
225
+ import react from '@vitejs/plugin-react';
226
+ import { defineConfig } from 'vite';
227
+
228
+ export default defineConfig({
229
+ plugins: [react()],
230
+ });
231
+ ```
232
+
233
+ 변경 후 예시:
234
+
235
+ ```ts
236
+ import react from '@vitejs/plugin-react';
237
+ import { jsxSourceAttrs } from 'vite-plugin-jsx-source-attrs';
238
+ import { defineConfig } from 'vite';
239
+
240
+ export default defineConfig(({ mode }) => ({
241
+ plugins: [
242
+ react({
243
+ babel: {
244
+ plugins: [
245
+ jsxSourceAttrs({
246
+ enabled: mode === 'development',
247
+ componentAttributeName: null,
248
+ }),
249
+ ],
250
+ },
251
+ }),
252
+ ],
253
+ }));
254
+ ```
255
+
256
+ ## 기존 babel.plugins가 이미 있는 경우
257
+
258
+ 기존 Babel 플러그인 목록을 보존하고 `jsxSourceAttrs()`만 추가한다. 기존 플러그인을 삭제하거나 순서를 임의로 바꾸지 않는다.
259
+
260
+ 변경 전 예시:
261
+
262
+ ```ts
263
+ react({
264
+ babel: {
265
+ plugins: [existingPlugin],
266
+ },
267
+ });
268
+ ```
269
+
270
+ 변경 후 예시:
271
+
272
+ ```ts
273
+ react({
274
+ babel: {
275
+ plugins: [
276
+ existingPlugin,
277
+ jsxSourceAttrs({
278
+ enabled: mode === 'development',
279
+ componentAttributeName: null,
280
+ }),
281
+ ],
282
+ },
283
+ });
284
+ ```
285
+
286
+ ## defineConfig가 객체 형태인 경우
287
+
288
+ `mode`를 사용해 개발 모드에서만 활성화해야 하므로 가능하면 함수 형태로 바꾼다.
289
+
290
+ ```ts
291
+ export default defineConfig(({ mode }) => ({
292
+ plugins: [
293
+ react({
294
+ babel: {
295
+ plugins: [
296
+ jsxSourceAttrs({
297
+ enabled: mode === 'development',
298
+ componentAttributeName: null,
299
+ }),
300
+ ],
301
+ },
302
+ }),
303
+ ],
304
+ }));
305
+ ```
306
+
307
+ 객체 형태를 꼭 유지해야 하면 `process.env.NODE_ENV` 기반 기본값을 사용할 수 있다.
308
+
309
+ ```ts
310
+ react({
311
+ babel: {
312
+ plugins: [jsxSourceAttrs()],
313
+ },
314
+ });
315
+ ```
316
+
317
+ 단, Vite 설정에서는 `mode === 'development'`를 명시하는 방식을 우선한다.
318
+
319
+ ## 사용자 컴포넌트에도 attribute가 필요한 경우
320
+
321
+ 기본값은 사용자 컴포넌트에 attribute를 주입하지 않는다. 사용자 컴포넌트까지 추적해야 하면 `componentAttributeName`을 지정한다.
322
+
323
+ ```ts
324
+ jsxSourceAttrs({
325
+ enabled: mode === 'development',
326
+ attributeName: 'data-source-location',
327
+ componentAttributeName: 'data-component-source-location',
328
+ });
329
+ ```
330
+
331
+ ## 경로 기준 root 지정
332
+
333
+ 모노레포나 Vite root가 다를 때 attribute 값의 상대 경로 기준을 맞추려면 `root`를 지정한다.
334
+
335
+ ```ts
336
+ jsxSourceAttrs({
337
+ enabled: mode === 'development',
338
+ root: process.cwd(),
339
+ componentAttributeName: null,
340
+ });
341
+ ```
342
+
343
+ ## 커스텀 활성화 조건
344
+
345
+ 특정 파일만 처리하려면 `enabled`에 함수를 사용할 수 있다.
346
+
347
+ ```ts
348
+ jsxSourceAttrs({
349
+ enabled: ({ filename }) => {
350
+ return filename.endsWith('.tsx') && !filename.includes('/node_modules/');
351
+ },
352
+ componentAttributeName: null,
353
+ });
354
+ ```
355
+
356
+ ## 마이그레이션 지침
357
+
358
+ 기존 프로젝트에 아래 파일 또는 패키지 설정이 있다면 이 패키지로 대체한다.
359
+
360
+ 제거 후보:
361
+
362
+ - `resources/scripts/jsx-source-attrs.js`
363
+ - `resources/babel/jsx-source-attrs.js`
364
+ - `src/resources/babel/jsx-source-attrs.js`
365
+ - `@wix/babel-plugin-jsx-source-attrs` 직접 설정
366
+
367
+ 대체 원칙:
368
+
369
+ 1. `vite-plugin-jsx-source-attrs`를 devDependency로 설치한다.
370
+ 2. `vite.config.ts`에서 로컬 `jsx-source-attrs.js` import를 제거한다.
371
+ 3. `import { jsxSourceAttrs } from 'vite-plugin-jsx-source-attrs';`를 추가한다.
372
+ 4. `react({ babel: { plugins: [...] } })` 안에 `jsxSourceAttrs({ enabled: mode === 'development', componentAttributeName: null })`를 추가한다.
373
+ 5. 기존 로컬 플러그인 파일은 import가 사라진 뒤 삭제한다.
374
+ 6. `@wix/babel-plugin-jsx-source-attrs`가 더 이상 필요 없다면 dependency/devDependency에서 제거한다.
375
+
376
+ ## 이 저장소에서 패키지 자체 검증
377
+
378
+ 이 저장소는 단일 npm 패키지 repo입니다. LLM이 패키지 자체를 수정했다면 루트에서 다음 순서로 확인한다.
379
+
380
+ ```bash
381
+ bun install
382
+ bun run verify
383
+ bun run pack:dry
384
+ ```
385
+
386
+ `bun run verify`는 lint/typecheck/test/build를 함께 수행한다.
387
+
388
+ ## 적용 후 검증
389
+
390
+ 대상 프로젝트의 기존 스크립트를 사용한다.
391
+
392
+ 예시:
393
+
394
+ ```bash
395
+ bun run lint
396
+ bun run typecheck
397
+ bun run build
398
+ ```
399
+
400
+ 또는 npm 프로젝트:
401
+
402
+ ```bash
403
+ npm run lint
404
+ npm run typecheck
405
+ npm run build
406
+ ```
407
+
408
+ 브라우저 개발 모드에서 확인할 것:
409
+
410
+ - `div`, `span` 같은 intrinsic JSX 요소에 `data-source-location`이 보인다.
411
+ - 값 형식은 `상대/파일경로.tsx:라인:컬럼`이다.
412
+ - 동일 attribute가 중복으로 생기지 않는다.
413
+ - 프로덕션 빌드 산출물에는 `data-source-location`이 없어야 한다.
414
+
415
+ ## 흔한 실패와 대응
416
+
417
+ ### `jsxSourceAttrs` import 오류
418
+
419
+ 패키지가 설치되어 있는지 확인한다.
420
+
421
+ ```bash
422
+ bun pm ls vite-plugin-jsx-source-attrs
423
+ ```
424
+
425
+ 또는:
426
+
427
+ ```bash
428
+ npm ls vite-plugin-jsx-source-attrs
429
+ ```
430
+
431
+ ### `react()` 설정이 두 번 생김
432
+
433
+ 기존 `react()` 플러그인을 새로 추가하지 말고 기존 설정에 `babel.plugins`만 병합한다.
434
+
435
+ ### 프로덕션에도 attribute가 들어감
436
+
437
+ `enabled: mode === 'development'`가 적용되어 있는지 확인한다.
438
+
439
+ ### 사용자 컴포넌트에 attribute가 안 들어감
440
+
441
+ 정상 기본 동작이다. 필요하면 `componentAttributeName`을 문자열로 지정한다.
442
+
443
+ ## 최종 권장 스니펫
444
+
445
+ LLM이 별도 요구사항을 받지 않았다면 아래 설정을 최종값으로 사용한다.
446
+
447
+ ```ts
448
+ import react from '@vitejs/plugin-react';
449
+ import { jsxSourceAttrs } from 'vite-plugin-jsx-source-attrs';
450
+ import { defineConfig } from 'vite';
451
+
452
+ export default defineConfig(({ mode }) => ({
453
+ plugins: [
454
+ react({
455
+ babel: {
456
+ plugins: [
457
+ jsxSourceAttrs({
458
+ enabled: mode === 'development',
459
+ attributeName: 'data-source-location',
460
+ componentAttributeName: null,
461
+ }),
462
+ ],
463
+ },
464
+ }),
465
+ ],
466
+ }));
467
+ ```
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # vite-plugin-jsx-source-attrs
2
2
 
3
- Vite React 개발 환경에서 JSX 요소에 소스 위치 정보를 담은 attribute를 주입하기 위한 Babel 플러그인 유틸리티입니다.
3
+ Vite React 개발 환경에서 JSX 요소에 소스 위치 정보를 담은 attribute를 주입하기 위한 Vite 플러그인과 Babel 플러그인 유틸리티입니다.
4
4
 
5
5
  여러 Analytics MFE 프로젝트에 반복되어 있던 `@wix/babel-plugin-jsx-source-attrs` 설정과 로컬 Babel 플러그인 구현을 공통 패키지로 분리해, 동일한 동작을 한 곳에서 관리하는 것을 목표로 합니다.
6
6
 
@@ -25,9 +25,35 @@ npm을 사용한다면 다음처럼 설치합니다.
25
25
  npm install -D vite-plugin-jsx-source-attrs
26
26
  ```
27
27
 
28
+ ### devDependency로 설치하는 이유
29
+
30
+ 이 패키지는 Vite의 `@vitejs/plugin-react` Babel 설정에서 **빌드 타임에만 실행**됩니다. 런타임 브라우저 코드에서 import되는 패키지가 아니므로 일반 Vite 애플리케이션에서는 `devDependencies`에 설치하는 것이 권장값입니다.
31
+
32
+ 프로덕션 빌드에서도 `vite.config.ts`를 평가해야 하므로 CI/CD가 `devDependencies`를 포함해 설치해야 합니다. `npm ci --omit=dev`처럼 devDependency를 제외한 상태에서 빌드를 실행하는 환경이라면 이 패키지를 찾을 수 없습니다. 이 경우 빌드 단계에서는 devDependency를 포함해 설치하고, 배포 런타임 이미지나 서버 산출물에서만 devDependency를 제외하세요.
33
+
34
+
28
35
  ## 사용법
29
36
 
30
- `@vitejs/plugin-react`의 Babel 플러그인 배열에 `jsxSourceAttrs()`를 추가합니다.
37
+ Vite 8 + `@vitejs/plugin-react` 6처럼 React 플러그인의 `babel.plugins` 옵션을 사용할 수 없는 조합에서는 독립 Vite 플러그인으로 추가합니다.
38
+
39
+ ```ts
40
+ import react from '@vitejs/plugin-react';
41
+ import { vitePluginJsxSourceAttrs } from 'vite-plugin-jsx-source-attrs';
42
+ import { defineConfig } from 'vite';
43
+
44
+ export default defineConfig(({ mode }) => ({
45
+ plugins: [
46
+ vitePluginJsxSourceAttrs({
47
+ enabled: mode === 'development',
48
+ attributeName: 'data-source-location',
49
+ componentAttributeName: null,
50
+ }),
51
+ react(),
52
+ ],
53
+ }));
54
+ ```
55
+
56
+ `@vitejs/plugin-react` 4.x처럼 `babel.plugins` 옵션을 지원하는 조합에서는 Babel 플러그인 유틸리티를 직접 사용할 수도 있습니다.
31
57
 
32
58
  ```ts
33
59
  import react from '@vitejs/plugin-react';
@@ -67,11 +93,14 @@ const view = <div data-source-location="src/App.tsx:1:14" />;
67
93
 
68
94
  | 옵션 | 기본값 | 설명 |
69
95
  | --- | --- | --- |
70
- | `enabled` | `process.env.NODE_ENV !== 'production'` | attribute 주입 여부입니다. boolean 또는 context 기반 함수로 지정할 수 있습니다. |
96
+ | `enabled` | Babel: `process.env.NODE_ENV !== 'production'`, Vite: `command === 'serve'` | attribute 주입 여부입니다. boolean 또는 context 기반 함수로 지정할 수 있습니다. |
71
97
  | `attributeName` | `data-source-location` | intrinsic JSX 요소에 주입할 attribute 이름입니다. |
72
98
  | `componentAttributeName` | `null` | 사용자 컴포넌트에 별도로 주입할 attribute 이름입니다. `null`이면 사용자 컴포넌트에는 주입하지 않습니다. |
73
- | `root` | `process.cwd()` | 절대 경로를 상대 경로로 바꿀 기준 루트입니다. |
99
+ | `root` | Babel: `process.cwd()`, Vite: `config.root` | 절대 경로를 상대 경로로 바꿀 기준 루트입니다. |
74
100
  | `normalizePath` | 내장 정규화 함수 | 파일 경로를 attribute 값에 맞게 정규화하는 함수입니다. |
101
+ | `include` | `/\.[jt]sx(?:\?.*)?$/` | `vitePluginJsxSourceAttrs`에서만 사용합니다. 변환할 Vite module id 패턴입니다. |
102
+ | `exclude` | `/\/node_modules\//` | `vitePluginJsxSourceAttrs`에서만 사용합니다. 변환에서 제외할 Vite module id 패턴입니다. |
103
+ | `parserPlugins` | `[]` | `vitePluginJsxSourceAttrs`에서만 사용합니다. 프로젝트 문법에 추가 Babel parser plugin이 필요할 때 지정합니다. |
75
104
 
76
105
  ### enabled 함수 예시
77
106
 
@@ -99,8 +128,22 @@ jsxSourceAttrs({
99
128
  - Windows 경로 구분자 `\\`를 `/`로 정규화
100
129
  - `root` 하위 절대 경로를 상대 경로로 변환
101
130
 
131
+ ## 저장소 구조
132
+
133
+ 이 저장소는 단일 npm 패키지 repo입니다.
134
+
135
+ ```text
136
+ .
137
+ ├── src/ # 배포되는 라이브러리 소스
138
+ ├── test/ # Vitest 테스트
139
+ ├── pipeline/ # GitLab CI publish/release 구성
140
+ └── docs/ # 배포와 마이그레이션 보조 문서
141
+ ```
142
+
102
143
  ## 개발
103
144
 
145
+ 아래 명령은 저장소 루트에서 실행합니다.
146
+
104
147
  ```bash
105
148
  bun install
106
149
  bun run lint
package/dist/index.cjs CHANGED
@@ -32,10 +32,12 @@ var index_exports = {};
32
32
  __export(index_exports, {
33
33
  default: () => index_default,
34
34
  jsxSourceAttrs: () => jsxSourceAttrs,
35
- normalizeSourcePath: () => normalizeSourcePath
35
+ normalizeSourcePath: () => normalizeSourcePath,
36
+ vitePluginJsxSourceAttrs: () => vitePluginJsxSourceAttrs
36
37
  });
37
38
  module.exports = __toCommonJS(index_exports);
38
39
  var import_node_path2 = __toESM(require("path"), 1);
40
+ var import_core = require("@babel/core");
39
41
  var t = __toESM(require("@babel/types"), 1);
40
42
 
41
43
  // src/normalize.ts
@@ -77,6 +79,8 @@ function normalizeSourcePath(filename, context) {
77
79
 
78
80
  // src/index.ts
79
81
  var DEFAULT_ATTRIBUTE_NAME = "data-source-location";
82
+ var DEFAULT_INCLUDE_RE = /\.[jt]sx(?:\?.*)?$/;
83
+ var DEFAULT_EXCLUDE_RE = /\/node_modules\//;
80
84
  function resolveOptions(options) {
81
85
  return {
82
86
  enabled: options.enabled ?? process.env.NODE_ENV !== "production",
@@ -128,6 +132,42 @@ function createLocationValue(pathToOpeningElement, state, options) {
128
132
  });
129
133
  return `${filename}:${location.line}:${location.column + 1}`;
130
134
  }
135
+ function toPatterns(pattern) {
136
+ if (!pattern) {
137
+ return [];
138
+ }
139
+ return Array.isArray(pattern) ? pattern : [pattern];
140
+ }
141
+ function matchesPattern(id, pattern) {
142
+ if (typeof pattern === "string") {
143
+ return id.includes(pattern);
144
+ }
145
+ return pattern.test(id);
146
+ }
147
+ function createFilter(include, exclude) {
148
+ const includePatterns = toPatterns(include ?? DEFAULT_INCLUDE_RE);
149
+ const excludePatterns = toPatterns(exclude ?? DEFAULT_EXCLUDE_RE);
150
+ return (id) => {
151
+ if (excludePatterns.some((pattern) => matchesPattern(id, pattern))) {
152
+ return false;
153
+ }
154
+ return includePatterns.some((pattern) => matchesPattern(id, pattern));
155
+ };
156
+ }
157
+ function getParserPlugins(id, parserPlugins = []) {
158
+ const filename = removeQueryAndHash2(id);
159
+ const plugins = [...parserPlugins];
160
+ if (/\.tsx$/i.test(filename)) {
161
+ plugins.push("typescript", "jsx");
162
+ return plugins;
163
+ }
164
+ if (/\.ts$/i.test(filename)) {
165
+ plugins.push("typescript");
166
+ return plugins;
167
+ }
168
+ plugins.push("jsx");
169
+ return plugins;
170
+ }
131
171
  function jsxSourceAttrs(options = {}) {
132
172
  const resolvedOptions = resolveOptions(options);
133
173
  return {
@@ -153,8 +193,57 @@ function jsxSourceAttrs(options = {}) {
153
193
  };
154
194
  }
155
195
  var index_default = jsxSourceAttrs;
196
+ function removeQueryAndHash2(filename) {
197
+ return filename.split(/[?#]/, 1)[0] ?? filename;
198
+ }
199
+ function vitePluginJsxSourceAttrs(options = {}) {
200
+ const filter = createFilter(options.include, options.exclude);
201
+ let config = {
202
+ command: process.env.NODE_ENV === "production" ? "build" : "serve",
203
+ mode: process.env.NODE_ENV ?? "development",
204
+ root: process.cwd()
205
+ };
206
+ return {
207
+ name: "vite-plugin-jsx-source-attrs:vite",
208
+ enforce: "pre",
209
+ configResolved(resolvedConfig) {
210
+ config = resolvedConfig;
211
+ },
212
+ async transform(code, id) {
213
+ if (!filter(id)) {
214
+ return null;
215
+ }
216
+ const result = await (0, import_core.transformAsync)(code, {
217
+ ast: false,
218
+ babelrc: false,
219
+ configFile: false,
220
+ filename: id,
221
+ parserOpts: {
222
+ sourceType: "module",
223
+ plugins: getParserPlugins(id, options.parserPlugins)
224
+ },
225
+ plugins: [
226
+ jsxSourceAttrs({
227
+ ...options,
228
+ enabled: options.enabled ?? config.command === "serve",
229
+ root: options.root ?? config.root
230
+ })
231
+ ],
232
+ sourceMaps: true
233
+ });
234
+ if (!result?.code) {
235
+ return null;
236
+ }
237
+ return {
238
+ code: result.code,
239
+ map: result.map
240
+ };
241
+ }
242
+ };
243
+ }
156
244
  // Annotate the CommonJS export names for ESM import in node:
157
245
  0 && (module.exports = {
158
246
  jsxSourceAttrs,
159
- normalizeSourcePath
247
+ normalizeSourcePath,
248
+ vitePluginJsxSourceAttrs
160
249
  });
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { PluginObj, PluginPass } from '@babel/core';
1
+ import { ParserOptions, PluginObj, PluginPass } from '@babel/core';
2
2
 
3
3
  interface JsxSourceAttrsContext {
4
4
  /** Babel이 전달한 파일명입니다. */
@@ -38,9 +38,50 @@ interface JsxSourceAttrsPluginState extends PluginPass {
38
38
  };
39
39
  };
40
40
  }
41
+ type FilterPattern = string | RegExp | Array<string | RegExp>;
42
+ interface ViteResolvedConfigLike {
43
+ command?: 'serve' | 'build';
44
+ mode?: string;
45
+ root: string;
46
+ }
47
+ interface SourceMapLike {
48
+ file?: string;
49
+ mappings: string;
50
+ names: string[];
51
+ sourceRoot?: string;
52
+ sources: string[];
53
+ sourcesContent?: string[];
54
+ version: number;
55
+ }
56
+ interface ViteTransformResult {
57
+ code: string;
58
+ map?: SourceMapLike | null;
59
+ }
60
+ interface VitePluginLike {
61
+ name: string;
62
+ enforce?: 'pre' | 'post';
63
+ configResolved?: (config: ViteResolvedConfigLike) => void;
64
+ transform?: (code: string, id: string) => Promise<ViteTransformResult | null>;
65
+ }
66
+ interface VitePluginJsxSourceAttrsOptions extends JsxSourceAttrsOptions {
67
+ /** 변환할 파일 패턴입니다. 기본값은 JSX/TSX 파일입니다. */
68
+ include?: FilterPattern;
69
+ /** 변환에서 제외할 파일 패턴입니다. 기본값은 node_modules 제외입니다. */
70
+ exclude?: FilterPattern;
71
+ /** 프로젝트 문법에 추가 Babel parser plugin이 필요할 때 지정합니다. */
72
+ parserPlugins?: ParserOptions['plugins'];
73
+ }
41
74
  /**
42
75
  * Vite의 `@vitejs/plugin-react` Babel 플러그인 배열에 넣어 사용하는 JSX source attribute 플러그인입니다.
43
76
  */
44
77
  declare function jsxSourceAttrs(options?: JsxSourceAttrsOptions): PluginObj<JsxSourceAttrsPluginState>;
45
78
 
46
- export { type JsxSourceAttrsContext, type JsxSourceAttrsOptions, type NormalizePathContext, jsxSourceAttrs as default, jsxSourceAttrs, normalizeSourcePath };
79
+ /**
80
+ * `@vitejs/plugin-react`의 `babel.plugins` 옵션을 사용할 수 없는 Vite/React 플러그인 조합에서
81
+ * 독립 Vite transform으로 JSX source attribute를 주입합니다.
82
+ *
83
+ * `plugins: [vitePluginJsxSourceAttrs(), react()]`처럼 React 플러그인보다 앞에 둡니다.
84
+ */
85
+ declare function vitePluginJsxSourceAttrs(options?: VitePluginJsxSourceAttrsOptions): VitePluginLike;
86
+
87
+ export { type JsxSourceAttrsContext, type JsxSourceAttrsOptions, type NormalizePathContext, type VitePluginJsxSourceAttrsOptions, jsxSourceAttrs as default, jsxSourceAttrs, normalizeSourcePath, vitePluginJsxSourceAttrs };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { PluginObj, PluginPass } from '@babel/core';
1
+ import { ParserOptions, PluginObj, PluginPass } from '@babel/core';
2
2
 
3
3
  interface JsxSourceAttrsContext {
4
4
  /** Babel이 전달한 파일명입니다. */
@@ -38,9 +38,50 @@ interface JsxSourceAttrsPluginState extends PluginPass {
38
38
  };
39
39
  };
40
40
  }
41
+ type FilterPattern = string | RegExp | Array<string | RegExp>;
42
+ interface ViteResolvedConfigLike {
43
+ command?: 'serve' | 'build';
44
+ mode?: string;
45
+ root: string;
46
+ }
47
+ interface SourceMapLike {
48
+ file?: string;
49
+ mappings: string;
50
+ names: string[];
51
+ sourceRoot?: string;
52
+ sources: string[];
53
+ sourcesContent?: string[];
54
+ version: number;
55
+ }
56
+ interface ViteTransformResult {
57
+ code: string;
58
+ map?: SourceMapLike | null;
59
+ }
60
+ interface VitePluginLike {
61
+ name: string;
62
+ enforce?: 'pre' | 'post';
63
+ configResolved?: (config: ViteResolvedConfigLike) => void;
64
+ transform?: (code: string, id: string) => Promise<ViteTransformResult | null>;
65
+ }
66
+ interface VitePluginJsxSourceAttrsOptions extends JsxSourceAttrsOptions {
67
+ /** 변환할 파일 패턴입니다. 기본값은 JSX/TSX 파일입니다. */
68
+ include?: FilterPattern;
69
+ /** 변환에서 제외할 파일 패턴입니다. 기본값은 node_modules 제외입니다. */
70
+ exclude?: FilterPattern;
71
+ /** 프로젝트 문법에 추가 Babel parser plugin이 필요할 때 지정합니다. */
72
+ parserPlugins?: ParserOptions['plugins'];
73
+ }
41
74
  /**
42
75
  * Vite의 `@vitejs/plugin-react` Babel 플러그인 배열에 넣어 사용하는 JSX source attribute 플러그인입니다.
43
76
  */
44
77
  declare function jsxSourceAttrs(options?: JsxSourceAttrsOptions): PluginObj<JsxSourceAttrsPluginState>;
45
78
 
46
- export { type JsxSourceAttrsContext, type JsxSourceAttrsOptions, type NormalizePathContext, jsxSourceAttrs as default, jsxSourceAttrs, normalizeSourcePath };
79
+ /**
80
+ * `@vitejs/plugin-react`의 `babel.plugins` 옵션을 사용할 수 없는 Vite/React 플러그인 조합에서
81
+ * 독립 Vite transform으로 JSX source attribute를 주입합니다.
82
+ *
83
+ * `plugins: [vitePluginJsxSourceAttrs(), react()]`처럼 React 플러그인보다 앞에 둡니다.
84
+ */
85
+ declare function vitePluginJsxSourceAttrs(options?: VitePluginJsxSourceAttrsOptions): VitePluginLike;
86
+
87
+ export { type JsxSourceAttrsContext, type JsxSourceAttrsOptions, type NormalizePathContext, type VitePluginJsxSourceAttrsOptions, jsxSourceAttrs as default, jsxSourceAttrs, normalizeSourcePath, vitePluginJsxSourceAttrs };
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  // src/index.ts
2
2
  import path2 from "path";
3
+ import { transformAsync } from "@babel/core";
3
4
  import * as t from "@babel/types";
4
5
 
5
6
  // src/normalize.ts
@@ -41,6 +42,8 @@ function normalizeSourcePath(filename, context) {
41
42
 
42
43
  // src/index.ts
43
44
  var DEFAULT_ATTRIBUTE_NAME = "data-source-location";
45
+ var DEFAULT_INCLUDE_RE = /\.[jt]sx(?:\?.*)?$/;
46
+ var DEFAULT_EXCLUDE_RE = /\/node_modules\//;
44
47
  function resolveOptions(options) {
45
48
  return {
46
49
  enabled: options.enabled ?? process.env.NODE_ENV !== "production",
@@ -92,6 +95,42 @@ function createLocationValue(pathToOpeningElement, state, options) {
92
95
  });
93
96
  return `${filename}:${location.line}:${location.column + 1}`;
94
97
  }
98
+ function toPatterns(pattern) {
99
+ if (!pattern) {
100
+ return [];
101
+ }
102
+ return Array.isArray(pattern) ? pattern : [pattern];
103
+ }
104
+ function matchesPattern(id, pattern) {
105
+ if (typeof pattern === "string") {
106
+ return id.includes(pattern);
107
+ }
108
+ return pattern.test(id);
109
+ }
110
+ function createFilter(include, exclude) {
111
+ const includePatterns = toPatterns(include ?? DEFAULT_INCLUDE_RE);
112
+ const excludePatterns = toPatterns(exclude ?? DEFAULT_EXCLUDE_RE);
113
+ return (id) => {
114
+ if (excludePatterns.some((pattern) => matchesPattern(id, pattern))) {
115
+ return false;
116
+ }
117
+ return includePatterns.some((pattern) => matchesPattern(id, pattern));
118
+ };
119
+ }
120
+ function getParserPlugins(id, parserPlugins = []) {
121
+ const filename = removeQueryAndHash2(id);
122
+ const plugins = [...parserPlugins];
123
+ if (/\.tsx$/i.test(filename)) {
124
+ plugins.push("typescript", "jsx");
125
+ return plugins;
126
+ }
127
+ if (/\.ts$/i.test(filename)) {
128
+ plugins.push("typescript");
129
+ return plugins;
130
+ }
131
+ plugins.push("jsx");
132
+ return plugins;
133
+ }
95
134
  function jsxSourceAttrs(options = {}) {
96
135
  const resolvedOptions = resolveOptions(options);
97
136
  return {
@@ -117,8 +156,57 @@ function jsxSourceAttrs(options = {}) {
117
156
  };
118
157
  }
119
158
  var index_default = jsxSourceAttrs;
159
+ function removeQueryAndHash2(filename) {
160
+ return filename.split(/[?#]/, 1)[0] ?? filename;
161
+ }
162
+ function vitePluginJsxSourceAttrs(options = {}) {
163
+ const filter = createFilter(options.include, options.exclude);
164
+ let config = {
165
+ command: process.env.NODE_ENV === "production" ? "build" : "serve",
166
+ mode: process.env.NODE_ENV ?? "development",
167
+ root: process.cwd()
168
+ };
169
+ return {
170
+ name: "vite-plugin-jsx-source-attrs:vite",
171
+ enforce: "pre",
172
+ configResolved(resolvedConfig) {
173
+ config = resolvedConfig;
174
+ },
175
+ async transform(code, id) {
176
+ if (!filter(id)) {
177
+ return null;
178
+ }
179
+ const result = await transformAsync(code, {
180
+ ast: false,
181
+ babelrc: false,
182
+ configFile: false,
183
+ filename: id,
184
+ parserOpts: {
185
+ sourceType: "module",
186
+ plugins: getParserPlugins(id, options.parserPlugins)
187
+ },
188
+ plugins: [
189
+ jsxSourceAttrs({
190
+ ...options,
191
+ enabled: options.enabled ?? config.command === "serve",
192
+ root: options.root ?? config.root
193
+ })
194
+ ],
195
+ sourceMaps: true
196
+ });
197
+ if (!result?.code) {
198
+ return null;
199
+ }
200
+ return {
201
+ code: result.code,
202
+ map: result.map
203
+ };
204
+ }
205
+ };
206
+ }
120
207
  export {
121
208
  index_default as default,
122
209
  jsxSourceAttrs,
123
- normalizeSourcePath
210
+ normalizeSourcePath,
211
+ vitePluginJsxSourceAttrs
124
212
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "vite-plugin-jsx-source-attrs",
3
- "version": "0.1.0",
4
- "description": "Vite React 개발 환경에서 JSX 요소에 소스 위치 attribute를 주입하는 Babel 플러그인 유틸리티",
3
+ "version": "0.1.1-dev.mr.2.23.8ec07595",
4
+ "description": "Vite React 개발 환경에서 JSX 요소에 소스 위치 attribute를 주입하는 Vite/Babel 플러그인",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "author": "Hansanghyeon",
@@ -24,6 +24,7 @@
24
24
  "files": [
25
25
  "dist",
26
26
  "README.md",
27
+ "README.llm.txt",
27
28
  "LICENSE"
28
29
  ],
29
30
  "exports": {
@@ -37,9 +38,11 @@
37
38
  "module": "./dist/index.js",
38
39
  "types": "./dist/index.d.ts",
39
40
  "scripts": {
41
+ "lint": "biome ci .",
42
+ "lint:fix": "biome check --write .",
43
+ "lint:fix:unsafe": "biome check --write --unsafe .",
40
44
  "build": "tsup src/index.ts --format esm,cjs --dts --clean",
41
45
  "typecheck": "tsc --noEmit",
42
- "lint": "eslint .",
43
46
  "test": "vitest run",
44
47
  "verify": "bun run lint && bun run typecheck && bun run test && bun run build",
45
48
  "pack:dry": "bun pm pack --dry-run"
@@ -53,13 +56,12 @@
53
56
  "devDependencies": {
54
57
  "@babel/core": "^7.26.0",
55
58
  "@babel/preset-react": "^7.25.9",
56
- "@eslint/js": "^9.17.0",
59
+ "@biomejs/biome": "^2.4.14",
60
+ "@hyeon/linter": "^11.0.4",
57
61
  "@types/babel__core": "^7.20.5",
58
62
  "@types/bun": "^1.1.14",
59
- "eslint": "^9.17.0",
60
63
  "tsup": "^8.3.5",
61
64
  "typescript": "^5.7.2",
62
- "typescript-eslint": "^8.18.2",
63
65
  "vitest": "^2.1.8"
64
66
  },
65
67
  "publishConfig": {