swc-plugin-source-tracker 0.1.0
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/LICENSE +21 -0
- package/README.md +137 -0
- package/index.js +120 -0
- package/package.json +38 -0
- package/wasm/v35.wasm +0 -0
- package/wasm/v54.wasm +0 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# swc-plugin-source-tracker
|
|
2
|
+
|
|
3
|
+
SWC plugin that injects `data-source` attributes into JSX DOM elements at compile time.
|
|
4
|
+
|
|
5
|
+
Each attribute contains the **file path, line number, and column** where the element was defined in source code.
|
|
6
|
+
|
|
7
|
+
```html
|
|
8
|
+
<!-- Before -->
|
|
9
|
+
<div className="card">
|
|
10
|
+
<h2>Title</h2>
|
|
11
|
+
<p>Content</p>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<!-- After -->
|
|
15
|
+
<div className="card" data-source="app/page.tsx:12:5">
|
|
16
|
+
<h2 data-source="app/page.tsx:13:7">Title</h2>
|
|
17
|
+
<p data-source="app/page.tsx:14:7">Content</p>
|
|
18
|
+
</div>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Why?
|
|
22
|
+
|
|
23
|
+
React 19 removed `fiber._debugSource`, breaking all click-to-source tools (react-dev-inspector, LocatorJS, click-to-component, etc.).
|
|
24
|
+
|
|
25
|
+
This plugin provides a build-time alternative that works with **Next.js 15, 16, Turbopack, and standalone `@swc/core`**.
|
|
26
|
+
|
|
27
|
+
## Install
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm install swc-plugin-source-tracker
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Usage with Next.js
|
|
34
|
+
|
|
35
|
+
```js
|
|
36
|
+
// next.config.mjs
|
|
37
|
+
import { withSourceTracker } from "swc-plugin-source-tracker";
|
|
38
|
+
|
|
39
|
+
const nextConfig = {
|
|
40
|
+
experimental: {
|
|
41
|
+
swcPlugins: [withSourceTracker()],
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export default nextConfig;
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
That's it. The plugin auto-detects your Next.js version and selects the compatible WASM binary.
|
|
49
|
+
|
|
50
|
+
### Dev-only
|
|
51
|
+
|
|
52
|
+
You probably want this in development only:
|
|
53
|
+
|
|
54
|
+
```js
|
|
55
|
+
// next.config.mjs
|
|
56
|
+
import { withSourceTracker } from "swc-plugin-source-tracker";
|
|
57
|
+
|
|
58
|
+
const nextConfig = {
|
|
59
|
+
experimental: {
|
|
60
|
+
swcPlugins: [
|
|
61
|
+
...(process.env.NODE_ENV === "development" ? [withSourceTracker()] : []),
|
|
62
|
+
],
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export default nextConfig;
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Options
|
|
70
|
+
|
|
71
|
+
```js
|
|
72
|
+
withSourceTracker(
|
|
73
|
+
// Plugin config (passed to SWC)
|
|
74
|
+
{ attr: "data-source" }, // Custom attribute name (default: "data-source")
|
|
75
|
+
|
|
76
|
+
// JS options
|
|
77
|
+
{ version: "v54" }, // Force specific swc_core version: "v35" | "v54"
|
|
78
|
+
);
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Usage with `@swc/core` (standalone)
|
|
82
|
+
|
|
83
|
+
```js
|
|
84
|
+
import { getWasmPath } from "swc-plugin-source-tracker";
|
|
85
|
+
|
|
86
|
+
const result = await swc.transform(code, {
|
|
87
|
+
filename: "src/App.tsx",
|
|
88
|
+
jsc: {
|
|
89
|
+
parser: { syntax: "typescript", tsx: true },
|
|
90
|
+
experimental: {
|
|
91
|
+
plugins: [[getWasmPath(), {}]],
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Compatibility
|
|
98
|
+
|
|
99
|
+
| WASM | swc_core | Next.js | Bundler |
|
|
100
|
+
|------|----------|---------|---------|
|
|
101
|
+
| v35 | 35.0.0 | 15.x | Webpack |
|
|
102
|
+
| v54 | 54.0.0 | 16.x | Turbopack |
|
|
103
|
+
|
|
104
|
+
`withSourceTracker()` auto-selects the correct version based on your installed Next.js.
|
|
105
|
+
|
|
106
|
+
## How it works
|
|
107
|
+
|
|
108
|
+
1. An SWC WASM plugin visits every JSX opening element during compilation
|
|
109
|
+
2. For intrinsic DOM elements (`div`, `span`, `button`, etc.), it injects a `data-source` attribute
|
|
110
|
+
3. The value contains `filename:line:column` resolved from SWC's source map
|
|
111
|
+
4. Custom components (`<MyComponent>`) are skipped — they render DOM elements that get annotated
|
|
112
|
+
|
|
113
|
+
## Reading source info at runtime
|
|
114
|
+
|
|
115
|
+
Open DevTools and inspect any element to see its `data-source` attribute, or:
|
|
116
|
+
|
|
117
|
+
```js
|
|
118
|
+
// Paste in browser console
|
|
119
|
+
document.addEventListener("click", (e) => {
|
|
120
|
+
const src =
|
|
121
|
+
e.target.getAttribute("data-source") ||
|
|
122
|
+
e.target.closest("[data-source]")?.getAttribute("data-source");
|
|
123
|
+
if (src) console.log(e.target.tagName, "→", src);
|
|
124
|
+
}, true);
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## .gitignore
|
|
128
|
+
|
|
129
|
+
The plugin copies a WASM file to your project root for Turbopack compatibility. Add this to `.gitignore`:
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
swc_plugin_source_tracker.wasm
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## License
|
|
136
|
+
|
|
137
|
+
MIT
|
package/index.js
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
const path = require("path");
|
|
2
|
+
const fs = require("fs");
|
|
3
|
+
|
|
4
|
+
const WASM_DIR = path.join(__dirname, "wasm");
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* swc_core 버전별 WASM 경로 매핑
|
|
8
|
+
*
|
|
9
|
+
* Next.js 15.x → swc_common 14.x → swc_core 35
|
|
10
|
+
* Next.js 16.x → swc_common 18.x → swc_core 54
|
|
11
|
+
*/
|
|
12
|
+
const VERSION_MAP = {
|
|
13
|
+
v35: path.join(WASM_DIR, "v35.wasm"),
|
|
14
|
+
v54: path.join(WASM_DIR, "v54.wasm"),
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Next.js major 버전 → WASM 버전 매핑
|
|
19
|
+
*/
|
|
20
|
+
const NEXTJS_MAP = {
|
|
21
|
+
15: "v35",
|
|
22
|
+
16: "v54",
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 설치된 Next.js 버전을 감지한다.
|
|
27
|
+
*/
|
|
28
|
+
function detectNextVersion() {
|
|
29
|
+
try {
|
|
30
|
+
const nextPkg = require("next/package.json");
|
|
31
|
+
return parseInt(nextPkg.version.split(".")[0], 10);
|
|
32
|
+
} catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 현재 환경에 맞는 WASM 파일의 절대 경로를 반환한다.
|
|
39
|
+
*
|
|
40
|
+
* @param {Object} [options]
|
|
41
|
+
* @param {"v35"|"v54"} [options.version] - 수동으로 swc_core 버전 지정
|
|
42
|
+
* @returns {string} WASM 파일의 절대 경로
|
|
43
|
+
*/
|
|
44
|
+
function getWasmPath(options = {}) {
|
|
45
|
+
let version = options.version;
|
|
46
|
+
|
|
47
|
+
if (!version) {
|
|
48
|
+
const nextMajor = detectNextVersion();
|
|
49
|
+
if (nextMajor && NEXTJS_MAP[nextMajor]) {
|
|
50
|
+
version = NEXTJS_MAP[nextMajor];
|
|
51
|
+
} else {
|
|
52
|
+
version = "v54";
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const wasmPath = VERSION_MAP[version];
|
|
57
|
+
if (!wasmPath || !fs.existsSync(wasmPath)) {
|
|
58
|
+
throw new Error(
|
|
59
|
+
`[swc-plugin-source-tracker] WASM not found for version "${version}". ` +
|
|
60
|
+
`Available: ${Object.keys(VERSION_MAP).join(", ")}`
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return wasmPath;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* WASM 파일을 프로젝트 루트에 복사하고 상대 경로를 반환한다.
|
|
69
|
+
* Turbopack은 node_modules 안의 WASM을 resolve하지 못하므로
|
|
70
|
+
* 프로젝트 루트에 복사해야 한다.
|
|
71
|
+
*
|
|
72
|
+
* @param {string} srcWasmPath - 원본 WASM 절대 경로
|
|
73
|
+
* @param {string} projectRoot - 프로젝트 루트 (process.cwd())
|
|
74
|
+
* @returns {string} "./파일명.wasm" 형태의 상대 경로
|
|
75
|
+
*/
|
|
76
|
+
function ensureLocalWasm(srcWasmPath, projectRoot) {
|
|
77
|
+
const filename = "swc_plugin_source_tracker.wasm";
|
|
78
|
+
const destPath = path.join(projectRoot, filename);
|
|
79
|
+
|
|
80
|
+
// 이미 존재하고 크기가 같으면 복사 생략
|
|
81
|
+
if (fs.existsSync(destPath)) {
|
|
82
|
+
const srcStat = fs.statSync(srcWasmPath);
|
|
83
|
+
const destStat = fs.statSync(destPath);
|
|
84
|
+
if (srcStat.size === destStat.size) {
|
|
85
|
+
return "./" + filename;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
fs.copyFileSync(srcWasmPath, destPath);
|
|
90
|
+
return "./" + filename;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* next.config.js의 swcPlugins에 바로 넣을 수 있는 튜플을 반환한다.
|
|
95
|
+
*
|
|
96
|
+
* WASM 파일을 프로젝트 루트에 자동 복사하여 Turbopack/Webpack 모두에서 동작한다.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* // next.config.mjs
|
|
100
|
+
* import { withSourceTracker } from "swc-plugin-source-tracker";
|
|
101
|
+
*
|
|
102
|
+
* const nextConfig = {
|
|
103
|
+
* experimental: {
|
|
104
|
+
* swcPlugins: [withSourceTracker()],
|
|
105
|
+
* },
|
|
106
|
+
* };
|
|
107
|
+
*
|
|
108
|
+
* @param {Object} [pluginConfig={}] - 플러그인 설정
|
|
109
|
+
* @param {string} [pluginConfig.attr="data-source"] - DOM에 주입할 속성명
|
|
110
|
+
* @param {Object} [options]
|
|
111
|
+
* @param {"v35"|"v54"} [options.version] - swc_core 버전 수동 지정
|
|
112
|
+
* @returns {[string, Object]} swcPlugins 튜플
|
|
113
|
+
*/
|
|
114
|
+
function withSourceTracker(pluginConfig = {}, options = {}) {
|
|
115
|
+
const srcWasmPath = getWasmPath(options);
|
|
116
|
+
const localPath = ensureLocalWasm(srcWasmPath, process.cwd());
|
|
117
|
+
return [localPath, pluginConfig];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
module.exports = { getWasmPath, withSourceTracker, VERSION_MAP, NEXTJS_MAP };
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "swc-plugin-source-tracker",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "SWC plugin that injects data-source attributes with file:line:col into JSX DOM elements. Replaces React 19's removed __debugSource. Works with Next.js 15/16 + Turbopack.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": "./index.js",
|
|
8
|
+
"./wasm/v35": "./wasm/v35.wasm",
|
|
9
|
+
"./wasm/v54": "./wasm/v54.wasm"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"index.js",
|
|
13
|
+
"wasm/*.wasm"
|
|
14
|
+
],
|
|
15
|
+
"keywords": [
|
|
16
|
+
"swc",
|
|
17
|
+
"swc-plugin",
|
|
18
|
+
"react",
|
|
19
|
+
"react-19",
|
|
20
|
+
"next.js",
|
|
21
|
+
"turbopack",
|
|
22
|
+
"debug",
|
|
23
|
+
"debugSource",
|
|
24
|
+
"source-tracking",
|
|
25
|
+
"click-to-source",
|
|
26
|
+
"jsx",
|
|
27
|
+
"data-source"
|
|
28
|
+
],
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"peerDependencies": {
|
|
31
|
+
"next": ">=15.0.0"
|
|
32
|
+
},
|
|
33
|
+
"peerDependenciesMeta": {
|
|
34
|
+
"next": {
|
|
35
|
+
"optional": true
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
package/wasm/v35.wasm
ADDED
|
Binary file
|
package/wasm/v54.wasm
ADDED
|
Binary file
|