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 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