trickle-observe 0.2.85 → 0.2.86
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/dist/observe-register.js +107 -25
- package/dist/vite-plugin.js +5 -0
- package/dist-esm/vite-plugin.js +5 -0
- package/package.json +1 -1
- package/src/observe-register.ts +114 -28
- package/src/vite-plugin.ts +3 -0
package/dist/observe-register.js
CHANGED
|
@@ -70,25 +70,75 @@ function decodeVLQ(encoded) {
|
|
|
70
70
|
}
|
|
71
71
|
return values;
|
|
72
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* Resolve a source map source path to a real filesystem path.
|
|
75
|
+
* Handles webpack:// URLs, file:// URLs, and relative paths.
|
|
76
|
+
*/
|
|
77
|
+
function resolveSourcePath(source, mapDir, sourceRoot, jsFilePath) {
|
|
78
|
+
// Handle webpack:// URLs: webpack://package-name/./src/file.ts
|
|
79
|
+
if (source.startsWith('webpack://')) {
|
|
80
|
+
// Strip webpack://package-name/ prefix
|
|
81
|
+
const withoutProtocol = source.replace(/^webpack:\/\/[^/]*\//, '');
|
|
82
|
+
// Resolve relative to the project root (parent of dist/ or the JS file's directory)
|
|
83
|
+
const projectRoot = findProjectRoot(jsFilePath);
|
|
84
|
+
return path_1.default.resolve(projectRoot, withoutProtocol);
|
|
85
|
+
}
|
|
86
|
+
// Handle file:// URLs
|
|
87
|
+
if (source.startsWith('file://')) {
|
|
88
|
+
return source.replace(/^file:\/\//, '');
|
|
89
|
+
}
|
|
90
|
+
// Regular relative path
|
|
91
|
+
return path_1.default.resolve(mapDir, sourceRoot, source);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Find the project root by looking for package.json or tsconfig.json
|
|
95
|
+
* starting from the JS file's directory and walking up.
|
|
96
|
+
*/
|
|
97
|
+
function findProjectRoot(jsFilePath) {
|
|
98
|
+
let dir = path_1.default.dirname(jsFilePath);
|
|
99
|
+
for (let i = 0; i < 10; i++) {
|
|
100
|
+
if (fs_1.default.existsSync(path_1.default.join(dir, 'package.json')) || fs_1.default.existsSync(path_1.default.join(dir, 'tsconfig.json'))) {
|
|
101
|
+
return dir;
|
|
102
|
+
}
|
|
103
|
+
const parent = path_1.default.dirname(dir);
|
|
104
|
+
if (parent === dir)
|
|
105
|
+
break;
|
|
106
|
+
dir = parent;
|
|
107
|
+
}
|
|
108
|
+
return path_1.default.dirname(jsFilePath);
|
|
109
|
+
}
|
|
73
110
|
/**
|
|
74
111
|
* Parse a source map JSON and build a compiled→original line mapping.
|
|
75
|
-
*
|
|
112
|
+
* Supports both single-source (tsc) and multi-source (webpack/rollup) maps.
|
|
76
113
|
*/
|
|
77
|
-
function parseSourceMap(mapJson, mapFilePath) {
|
|
114
|
+
function parseSourceMap(mapJson, mapFilePath, jsFilePath) {
|
|
78
115
|
try {
|
|
79
116
|
const map = JSON.parse(mapJson);
|
|
80
117
|
if (!map.mappings || !map.sources || map.sources.length === 0)
|
|
81
118
|
return null;
|
|
82
|
-
// Resolve original source path relative to the .map file location
|
|
83
119
|
const mapDir = path_1.default.dirname(mapFilePath);
|
|
84
120
|
const sourceRoot = map.sourceRoot || '';
|
|
85
|
-
|
|
121
|
+
// Resolve all source paths, filtering out non-user sources (webpack internals)
|
|
122
|
+
const resolvedSources = map.sources.map((s) => resolveSourcePath(s, mapDir, sourceRoot, jsFilePath));
|
|
123
|
+
// Find the first user source file (skip webpack bootstrap/runtime)
|
|
124
|
+
let primarySourceIdx = 0;
|
|
125
|
+
for (let i = 0; i < resolvedSources.length; i++) {
|
|
126
|
+
const s = map.sources[i];
|
|
127
|
+
if (!s.includes('webpack/bootstrap') && !s.includes('webpack/runtime') && fs_1.default.existsSync(resolvedSources[i])) {
|
|
128
|
+
primarySourceIdx = i;
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
const originalFile = resolvedSources[primarySourceIdx];
|
|
86
133
|
// Decode mappings: semicolons separate lines, commas separate segments
|
|
87
|
-
// VLQ values are cumulative across ALL segments (not just first per line)
|
|
88
|
-
//
|
|
134
|
+
// VLQ values are cumulative across ALL segments (not just first per line).
|
|
135
|
+
// For multi-source maps, also track the source index to map lines to different files.
|
|
89
136
|
const lineMap = new Map();
|
|
137
|
+
/** Map from generated line → resolved source file path */
|
|
138
|
+
const lineSourceMap = new Map();
|
|
90
139
|
const lines = map.mappings.split(';');
|
|
91
|
-
let sourceLine = 0;
|
|
140
|
+
let sourceLine = 0;
|
|
141
|
+
let sourceIdx = 0;
|
|
92
142
|
for (let genLine = 0; genLine < lines.length; genLine++) {
|
|
93
143
|
const line = lines[genLine];
|
|
94
144
|
if (!line)
|
|
@@ -99,18 +149,22 @@ function parseSourceMap(mapJson, mapFilePath) {
|
|
|
99
149
|
if (!seg)
|
|
100
150
|
continue;
|
|
101
151
|
const decoded = decodeVLQ(seg);
|
|
102
|
-
// decoded: [genCol, sourceIdx, sourceLine, sourceCol, ...]
|
|
103
152
|
if (decoded.length >= 3) {
|
|
104
|
-
|
|
105
|
-
|
|
153
|
+
if (decoded.length >= 2)
|
|
154
|
+
sourceIdx += decoded[1]; // Update source index
|
|
155
|
+
sourceLine += decoded[2];
|
|
106
156
|
if (!firstSegmentMapped) {
|
|
107
|
-
lineMap.set(genLine + 1, sourceLine + 1);
|
|
157
|
+
lineMap.set(genLine + 1, sourceLine + 1);
|
|
158
|
+
// Track which source file this line belongs to
|
|
159
|
+
if (sourceIdx >= 0 && sourceIdx < resolvedSources.length) {
|
|
160
|
+
lineSourceMap.set(genLine + 1, resolvedSources[sourceIdx]);
|
|
161
|
+
}
|
|
108
162
|
firstSegmentMapped = true;
|
|
109
163
|
}
|
|
110
164
|
}
|
|
111
165
|
}
|
|
112
166
|
}
|
|
113
|
-
return { lineMap, originalFile, sources: map.sources };
|
|
167
|
+
return { lineMap, originalFile, sources: map.sources, lineSourceMap };
|
|
114
168
|
}
|
|
115
169
|
catch {
|
|
116
170
|
return null;
|
|
@@ -133,14 +187,14 @@ function loadSourceMap(jsFilePath, source) {
|
|
|
133
187
|
const mapPath = path_1.default.resolve(path_1.default.dirname(jsFilePath), url);
|
|
134
188
|
if (fs_1.default.existsSync(mapPath)) {
|
|
135
189
|
const mapJson = fs_1.default.readFileSync(mapPath, 'utf8');
|
|
136
|
-
return parseSourceMap(mapJson, mapPath);
|
|
190
|
+
return parseSourceMap(mapJson, mapPath, jsFilePath);
|
|
137
191
|
}
|
|
138
192
|
}
|
|
139
193
|
// Fallback: check for .map file alongside .js
|
|
140
194
|
const mapPath = jsFilePath + '.map';
|
|
141
195
|
if (fs_1.default.existsSync(mapPath)) {
|
|
142
196
|
const mapJson = fs_1.default.readFileSync(mapPath, 'utf8');
|
|
143
|
-
return parseSourceMap(mapJson, mapPath);
|
|
197
|
+
return parseSourceMap(mapJson, mapPath, jsFilePath);
|
|
144
198
|
}
|
|
145
199
|
return null;
|
|
146
200
|
}
|
|
@@ -322,6 +376,11 @@ function findVarDeclarations(source, lineOffset = 0) {
|
|
|
322
376
|
continue;
|
|
323
377
|
if (varName === '__commonJS' || varName === '__toCommonJS' || varName === '__export' || varName === '__copyProps')
|
|
324
378
|
continue;
|
|
379
|
+
// Skip webpack internals
|
|
380
|
+
if (varName.startsWith('__webpack_'))
|
|
381
|
+
continue;
|
|
382
|
+
if (varName === '__unused_webpack_module')
|
|
383
|
+
continue;
|
|
325
384
|
// Skip React Refresh / HMR internals
|
|
326
385
|
if (varName === 'prevRefreshReg' || varName === 'prevRefreshSig' || varName === 'inWebWorker' || varName === 'invalidateMessage')
|
|
327
386
|
continue;
|
|
@@ -619,6 +678,14 @@ function transformCjsSource(source, filename, moduleName, env, sourceMap) {
|
|
|
619
678
|
const remapLine = sourceMap
|
|
620
679
|
? (line) => mapLineToOriginal(sourceMap, line)
|
|
621
680
|
: (line) => line;
|
|
681
|
+
// Helper: get source file for a compiled line (for multi-source bundles)
|
|
682
|
+
const hasMultiSource = sourceMap?.lineSourceMap && sourceMap.lineSourceMap.size > 0;
|
|
683
|
+
const getSourceFile = hasMultiSource
|
|
684
|
+
? (compiledLine) => {
|
|
685
|
+
const file = sourceMap.lineSourceMap.get(compiledLine);
|
|
686
|
+
return file && !file.includes('webpack/bootstrap') && !file.includes('webpack/runtime') ? file : undefined;
|
|
687
|
+
}
|
|
688
|
+
: (_) => undefined;
|
|
622
689
|
if (varTraceEnabled) {
|
|
623
690
|
const isTsFile = /\.[mc]?tsx?$/.test(filename);
|
|
624
691
|
if (isTsFile && !sourceMap) {
|
|
@@ -682,10 +749,12 @@ function transformCjsSource(source, filename, moduleName, env, sourceMap) {
|
|
|
682
749
|
// Plain JS or source-map-assisted: parse compiled source, then remap lines
|
|
683
750
|
varInsertions = findVarDeclarations(source).map(ins => ({
|
|
684
751
|
...ins,
|
|
752
|
+
sourceFile: getSourceFile(ins.lineNo),
|
|
685
753
|
lineNo: remapLine(ins.lineNo),
|
|
686
754
|
}));
|
|
687
755
|
destructInsertions = findDestructuredDeclarations(source).map(ins => ({
|
|
688
756
|
...ins,
|
|
757
|
+
sourceFile: getSourceFile(ins.lineNo),
|
|
689
758
|
lineNo: remapLine(ins.lineNo),
|
|
690
759
|
}));
|
|
691
760
|
}
|
|
@@ -696,14 +765,17 @@ function transformCjsSource(source, filename, moduleName, env, sourceMap) {
|
|
|
696
765
|
// Apply source map remapping to these too.
|
|
697
766
|
const reassignInsertions = (0, vite_plugin_1.findReassignments)(source).map(ins => ({
|
|
698
767
|
...ins,
|
|
768
|
+
sourceFile: getSourceFile(ins.lineNo),
|
|
699
769
|
lineNo: remapLine(ins.lineNo),
|
|
700
770
|
}));
|
|
701
771
|
const forLoopInsertions = (0, vite_plugin_1.findForLoopVars)(source).map(ins => ({
|
|
702
772
|
...ins,
|
|
773
|
+
sourceFile: getSourceFile(ins.lineNo),
|
|
703
774
|
lineNo: remapLine(ins.lineNo),
|
|
704
775
|
}));
|
|
705
776
|
const catchInsertions = (0, vite_plugin_1.findCatchVars)(source).map(ins => ({
|
|
706
777
|
...ins,
|
|
778
|
+
sourceFile: getSourceFile(ins.lineNo),
|
|
707
779
|
lineNo: remapLine(ins.lineNo),
|
|
708
780
|
}));
|
|
709
781
|
if (insertions.length === 0 && varInsertions.length === 0 && destructInsertions.length === 0 && reassignInsertions.length === 0 && forLoopInsertions.length === 0 && catchInsertions.length === 0 && classInsertions.length === 0)
|
|
@@ -740,7 +812,7 @@ function transformCjsSource(source, filename, moduleName, env, sourceMap) {
|
|
|
740
812
|
const traceModuleName = sourceMap
|
|
741
813
|
? path_1.default.basename(sourceMap.originalFile).replace(/\.[jt]sx?$/, '')
|
|
742
814
|
: moduleName;
|
|
743
|
-
prefixLines.push(`var __trickle_tv_mod = require(${JSON.stringify(traceVarPath)});`, `var __trickle_tv = function(v, n, l) { try { __trickle_tv_mod.traceVar(v, n, l, ${JSON.stringify(traceModuleName)}, ${JSON.stringify(traceFilePath)}); } catch(e){} };`);
|
|
815
|
+
prefixLines.push(`var __trickle_tv_mod = require(${JSON.stringify(traceVarPath)});`, `var __trickle_tv = function(v, n, l, m, f) { try { __trickle_tv_mod.traceVar(v, n, l, m || ${JSON.stringify(traceModuleName)}, f || ${JSON.stringify(traceFilePath)}); } catch(e){} };`);
|
|
744
816
|
}
|
|
745
817
|
prefixLines.push('');
|
|
746
818
|
const prefix = prefixLines.join('\n');
|
|
@@ -752,37 +824,47 @@ function transformCjsSource(source, filename, moduleName, env, sourceMap) {
|
|
|
752
824
|
code: `\ntry{${name}=__trickle_wrap(${name},'${name}',${paramNamesArg})}catch(__e){}\n`,
|
|
753
825
|
});
|
|
754
826
|
}
|
|
755
|
-
|
|
827
|
+
// Helper to generate source file args for __trickle_tv calls (for multi-source bundles)
|
|
828
|
+
const sfArgs = (sourceFile) => {
|
|
829
|
+
if (!sourceFile)
|
|
830
|
+
return '';
|
|
831
|
+
const mod = path_1.default.basename(sourceFile).replace(/\.[jt]sx?$/, '');
|
|
832
|
+
return `,${JSON.stringify(mod)},${JSON.stringify(sourceFile)}`;
|
|
833
|
+
};
|
|
834
|
+
for (const { lineEnd, varName, lineNo, sourceFile } of varInsertions) {
|
|
756
835
|
allInsertions.push({
|
|
757
836
|
position: lineEnd,
|
|
758
|
-
code: `\ntry{__trickle_tv(${varName},${JSON.stringify(varName)},${lineNo})}catch(__e){}\n`,
|
|
837
|
+
code: `\ntry{__trickle_tv(${varName},${JSON.stringify(varName)},${lineNo}${sfArgs(sourceFile)})}catch(__e){}\n`,
|
|
759
838
|
});
|
|
760
839
|
}
|
|
761
|
-
for (const { lineEnd, varNames, lineNo } of destructInsertions) {
|
|
762
|
-
const
|
|
840
|
+
for (const { lineEnd, varNames, lineNo, sourceFile } of destructInsertions) {
|
|
841
|
+
const sf = sfArgs(sourceFile);
|
|
842
|
+
const calls = varNames.map(n => `__trickle_tv(${n},${JSON.stringify(n)},${lineNo}${sf})`).join(';');
|
|
763
843
|
allInsertions.push({
|
|
764
844
|
position: lineEnd,
|
|
765
845
|
code: `\n;try{${calls}}catch(__e){}\n`,
|
|
766
846
|
});
|
|
767
847
|
}
|
|
768
848
|
// Reassignment insertions
|
|
769
|
-
for (const { lineEnd, varName, lineNo } of reassignInsertions) {
|
|
849
|
+
for (const { lineEnd, varName, lineNo, sourceFile } of reassignInsertions) {
|
|
770
850
|
allInsertions.push({
|
|
771
851
|
position: lineEnd,
|
|
772
|
-
code: `\n;try{__trickle_tv(${varName},${JSON.stringify(varName)},${lineNo})}catch(__e){}\n`,
|
|
852
|
+
code: `\n;try{__trickle_tv(${varName},${JSON.stringify(varName)},${lineNo}${sfArgs(sourceFile)})}catch(__e){}\n`,
|
|
773
853
|
});
|
|
774
854
|
}
|
|
775
855
|
// For-loop variable insertions
|
|
776
|
-
for (const { bodyStart, varNames, lineNo } of forLoopInsertions) {
|
|
777
|
-
const
|
|
856
|
+
for (const { bodyStart, varNames, lineNo, sourceFile } of forLoopInsertions) {
|
|
857
|
+
const sf = sfArgs(sourceFile);
|
|
858
|
+
const calls = varNames.map(n => `__trickle_tv(${n},${JSON.stringify(n)},${lineNo}${sf})`).join(';');
|
|
778
859
|
allInsertions.push({
|
|
779
860
|
position: bodyStart,
|
|
780
861
|
code: `\ntry{${calls}}catch(__e){}\n`,
|
|
781
862
|
});
|
|
782
863
|
}
|
|
783
864
|
// Catch clause insertions
|
|
784
|
-
for (const { bodyStart, varNames, lineNo } of catchInsertions) {
|
|
785
|
-
const
|
|
865
|
+
for (const { bodyStart, varNames, lineNo, sourceFile } of catchInsertions) {
|
|
866
|
+
const sf = sfArgs(sourceFile);
|
|
867
|
+
const calls = varNames.map(n => `__trickle_tv(${n},${JSON.stringify(n)},${lineNo}${sf})`).join(';');
|
|
786
868
|
allInsertions.push({
|
|
787
869
|
position: bodyStart,
|
|
788
870
|
code: `\ntry{${calls}}catch(__e2){}\n`,
|
package/dist/vite-plugin.js
CHANGED
|
@@ -278,6 +278,11 @@ function findVarDeclarations(source) {
|
|
|
278
278
|
continue;
|
|
279
279
|
if (varName === '__commonJS' || varName === '__toCommonJS' || varName === '__export' || varName === '__copyProps')
|
|
280
280
|
continue;
|
|
281
|
+
// Skip webpack internals
|
|
282
|
+
if (varName.startsWith('__webpack_'))
|
|
283
|
+
continue;
|
|
284
|
+
if (varName === '__unused_webpack_module')
|
|
285
|
+
continue;
|
|
281
286
|
// Skip React Refresh / HMR internals (Vite, webpack, Next.js inject these)
|
|
282
287
|
if (varName === 'prevRefreshReg' || varName === 'prevRefreshSig' || varName === 'inWebWorker' || varName === 'invalidateMessage')
|
|
283
288
|
continue;
|
package/dist-esm/vite-plugin.js
CHANGED
|
@@ -265,6 +265,11 @@ function findVarDeclarations(source) {
|
|
|
265
265
|
continue;
|
|
266
266
|
if (varName === '__commonJS' || varName === '__toCommonJS' || varName === '__export' || varName === '__copyProps')
|
|
267
267
|
continue;
|
|
268
|
+
// Skip webpack internals
|
|
269
|
+
if (varName.startsWith('__webpack_'))
|
|
270
|
+
continue;
|
|
271
|
+
if (varName === '__unused_webpack_module')
|
|
272
|
+
continue;
|
|
268
273
|
// Skip React Refresh / HMR internals (Vite, webpack, Next.js inject these)
|
|
269
274
|
if (varName === 'prevRefreshReg' || varName === 'prevRefreshSig' || varName === 'inWebWorker' || varName === 'invalidateMessage')
|
|
270
275
|
continue;
|
package/package.json
CHANGED
package/src/observe-register.ts
CHANGED
|
@@ -76,32 +76,87 @@ function decodeVLQ(encoded: string): number[] {
|
|
|
76
76
|
interface SourceMapData {
|
|
77
77
|
/** Map from compiled 1-based line → original 1-based line */
|
|
78
78
|
lineMap: Map<number, number>;
|
|
79
|
-
/** Original source file path (resolved to absolute) */
|
|
79
|
+
/** Original source file path (resolved to absolute) — primary/first user source */
|
|
80
80
|
originalFile: string;
|
|
81
81
|
/** All source files listed in the source map */
|
|
82
82
|
sources: string[];
|
|
83
|
+
/** Map from compiled line → resolved source file path (for multi-source bundles) */
|
|
84
|
+
lineSourceMap?: Map<number, string>;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Resolve a source map source path to a real filesystem path.
|
|
89
|
+
* Handles webpack:// URLs, file:// URLs, and relative paths.
|
|
90
|
+
*/
|
|
91
|
+
function resolveSourcePath(source: string, mapDir: string, sourceRoot: string, jsFilePath: string): string {
|
|
92
|
+
// Handle webpack:// URLs: webpack://package-name/./src/file.ts
|
|
93
|
+
if (source.startsWith('webpack://')) {
|
|
94
|
+
// Strip webpack://package-name/ prefix
|
|
95
|
+
const withoutProtocol = source.replace(/^webpack:\/\/[^/]*\//, '');
|
|
96
|
+
// Resolve relative to the project root (parent of dist/ or the JS file's directory)
|
|
97
|
+
const projectRoot = findProjectRoot(jsFilePath);
|
|
98
|
+
return path.resolve(projectRoot, withoutProtocol);
|
|
99
|
+
}
|
|
100
|
+
// Handle file:// URLs
|
|
101
|
+
if (source.startsWith('file://')) {
|
|
102
|
+
return source.replace(/^file:\/\//, '');
|
|
103
|
+
}
|
|
104
|
+
// Regular relative path
|
|
105
|
+
return path.resolve(mapDir, sourceRoot, source);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Find the project root by looking for package.json or tsconfig.json
|
|
110
|
+
* starting from the JS file's directory and walking up.
|
|
111
|
+
*/
|
|
112
|
+
function findProjectRoot(jsFilePath: string): string {
|
|
113
|
+
let dir = path.dirname(jsFilePath);
|
|
114
|
+
for (let i = 0; i < 10; i++) {
|
|
115
|
+
if (fs.existsSync(path.join(dir, 'package.json')) || fs.existsSync(path.join(dir, 'tsconfig.json'))) {
|
|
116
|
+
return dir;
|
|
117
|
+
}
|
|
118
|
+
const parent = path.dirname(dir);
|
|
119
|
+
if (parent === dir) break;
|
|
120
|
+
dir = parent;
|
|
121
|
+
}
|
|
122
|
+
return path.dirname(jsFilePath);
|
|
83
123
|
}
|
|
84
124
|
|
|
85
125
|
/**
|
|
86
126
|
* Parse a source map JSON and build a compiled→original line mapping.
|
|
87
|
-
*
|
|
127
|
+
* Supports both single-source (tsc) and multi-source (webpack/rollup) maps.
|
|
88
128
|
*/
|
|
89
|
-
function parseSourceMap(mapJson: string, mapFilePath: string): SourceMapData | null {
|
|
129
|
+
function parseSourceMap(mapJson: string, mapFilePath: string, jsFilePath: string): SourceMapData | null {
|
|
90
130
|
try {
|
|
91
131
|
const map = JSON.parse(mapJson);
|
|
92
132
|
if (!map.mappings || !map.sources || map.sources.length === 0) return null;
|
|
93
133
|
|
|
94
|
-
// Resolve original source path relative to the .map file location
|
|
95
134
|
const mapDir = path.dirname(mapFilePath);
|
|
96
135
|
const sourceRoot = map.sourceRoot || '';
|
|
97
|
-
|
|
136
|
+
|
|
137
|
+
// Resolve all source paths, filtering out non-user sources (webpack internals)
|
|
138
|
+
const resolvedSources = map.sources.map((s: string) => resolveSourcePath(s, mapDir, sourceRoot, jsFilePath));
|
|
139
|
+
|
|
140
|
+
// Find the first user source file (skip webpack bootstrap/runtime)
|
|
141
|
+
let primarySourceIdx = 0;
|
|
142
|
+
for (let i = 0; i < resolvedSources.length; i++) {
|
|
143
|
+
const s = map.sources[i] as string;
|
|
144
|
+
if (!s.includes('webpack/bootstrap') && !s.includes('webpack/runtime') && fs.existsSync(resolvedSources[i])) {
|
|
145
|
+
primarySourceIdx = i;
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const originalFile = resolvedSources[primarySourceIdx];
|
|
98
150
|
|
|
99
151
|
// Decode mappings: semicolons separate lines, commas separate segments
|
|
100
|
-
// VLQ values are cumulative across ALL segments (not just first per line)
|
|
101
|
-
//
|
|
152
|
+
// VLQ values are cumulative across ALL segments (not just first per line).
|
|
153
|
+
// For multi-source maps, also track the source index to map lines to different files.
|
|
102
154
|
const lineMap = new Map<number, number>();
|
|
155
|
+
/** Map from generated line → resolved source file path */
|
|
156
|
+
const lineSourceMap = new Map<number, string>();
|
|
103
157
|
const lines = map.mappings.split(';');
|
|
104
|
-
let sourceLine = 0;
|
|
158
|
+
let sourceLine = 0;
|
|
159
|
+
let sourceIdx = 0;
|
|
105
160
|
|
|
106
161
|
for (let genLine = 0; genLine < lines.length; genLine++) {
|
|
107
162
|
const line = lines[genLine];
|
|
@@ -112,19 +167,22 @@ function parseSourceMap(mapJson: string, mapFilePath: string): SourceMapData | n
|
|
|
112
167
|
for (const seg of segments) {
|
|
113
168
|
if (!seg) continue;
|
|
114
169
|
const decoded = decodeVLQ(seg);
|
|
115
|
-
// decoded: [genCol, sourceIdx, sourceLine, sourceCol, ...]
|
|
116
170
|
if (decoded.length >= 3) {
|
|
117
|
-
|
|
118
|
-
|
|
171
|
+
if (decoded.length >= 2) sourceIdx += decoded[1]; // Update source index
|
|
172
|
+
sourceLine += decoded[2];
|
|
119
173
|
if (!firstSegmentMapped) {
|
|
120
|
-
lineMap.set(genLine + 1, sourceLine + 1);
|
|
174
|
+
lineMap.set(genLine + 1, sourceLine + 1);
|
|
175
|
+
// Track which source file this line belongs to
|
|
176
|
+
if (sourceIdx >= 0 && sourceIdx < resolvedSources.length) {
|
|
177
|
+
lineSourceMap.set(genLine + 1, resolvedSources[sourceIdx]);
|
|
178
|
+
}
|
|
121
179
|
firstSegmentMapped = true;
|
|
122
180
|
}
|
|
123
181
|
}
|
|
124
182
|
}
|
|
125
183
|
}
|
|
126
184
|
|
|
127
|
-
return { lineMap, originalFile, sources: map.sources };
|
|
185
|
+
return { lineMap, originalFile, sources: map.sources, lineSourceMap };
|
|
128
186
|
} catch {
|
|
129
187
|
return null;
|
|
130
188
|
}
|
|
@@ -146,7 +204,7 @@ function loadSourceMap(jsFilePath: string, source: string): SourceMapData | null
|
|
|
146
204
|
const mapPath = path.resolve(path.dirname(jsFilePath), url);
|
|
147
205
|
if (fs.existsSync(mapPath)) {
|
|
148
206
|
const mapJson = fs.readFileSync(mapPath, 'utf8');
|
|
149
|
-
return parseSourceMap(mapJson, mapPath);
|
|
207
|
+
return parseSourceMap(mapJson, mapPath, jsFilePath);
|
|
150
208
|
}
|
|
151
209
|
}
|
|
152
210
|
|
|
@@ -154,7 +212,7 @@ function loadSourceMap(jsFilePath: string, source: string): SourceMapData | null
|
|
|
154
212
|
const mapPath = jsFilePath + '.map';
|
|
155
213
|
if (fs.existsSync(mapPath)) {
|
|
156
214
|
const mapJson = fs.readFileSync(mapPath, 'utf8');
|
|
157
|
-
return parseSourceMap(mapJson, mapPath);
|
|
215
|
+
return parseSourceMap(mapJson, mapPath, jsFilePath);
|
|
158
216
|
}
|
|
159
217
|
|
|
160
218
|
return null;
|
|
@@ -322,6 +380,9 @@ function findVarDeclarations(source: string, lineOffset: number = 0): Array<{ li
|
|
|
322
380
|
// Skip esbuild helpers
|
|
323
381
|
if (varName === '__defProp' || varName === '__defNormalProp' || varName === '__publicField' || varName === '__getOwnPropNames') continue;
|
|
324
382
|
if (varName === '__commonJS' || varName === '__toCommonJS' || varName === '__export' || varName === '__copyProps') continue;
|
|
383
|
+
// Skip webpack internals
|
|
384
|
+
if (varName.startsWith('__webpack_')) continue;
|
|
385
|
+
if (varName === '__unused_webpack_module') continue;
|
|
325
386
|
// Skip React Refresh / HMR internals
|
|
326
387
|
if (varName === 'prevRefreshReg' || varName === 'prevRefreshSig' || varName === 'inWebWorker' || varName === 'invalidateMessage') continue;
|
|
327
388
|
if (varName === '_s' || varName === '_c2' || varName === '_s2') continue;
|
|
@@ -594,14 +655,23 @@ function transformCjsSource(source: string, filename: string, moduleName: string
|
|
|
594
655
|
// are stripped from the compiled JS, shifting line numbers. The only accurate way to get correct
|
|
595
656
|
// line numbers is to read the original .ts source file and parse it directly.
|
|
596
657
|
// For plain JS files, we parse the source directly.
|
|
597
|
-
let varInsertions: Array<{ lineEnd: number; varName: string; lineNo: number }> = [];
|
|
598
|
-
let destructInsertions: Array<{ lineEnd: number; varNames: string[]; lineNo: number }> = [];
|
|
658
|
+
let varInsertions: Array<{ lineEnd: number; varName: string; lineNo: number; sourceFile?: string }> = [];
|
|
659
|
+
let destructInsertions: Array<{ lineEnd: number; varNames: string[]; lineNo: number; sourceFile?: string }> = [];
|
|
599
660
|
|
|
600
661
|
// Helper: remap line numbers using source map if available
|
|
601
662
|
const remapLine = sourceMap
|
|
602
663
|
? (line: number) => mapLineToOriginal(sourceMap, line)
|
|
603
664
|
: (line: number) => line;
|
|
604
665
|
|
|
666
|
+
// Helper: get source file for a compiled line (for multi-source bundles)
|
|
667
|
+
const hasMultiSource = sourceMap?.lineSourceMap && sourceMap.lineSourceMap.size > 0;
|
|
668
|
+
const getSourceFile = hasMultiSource
|
|
669
|
+
? (compiledLine: number) => {
|
|
670
|
+
const file = sourceMap!.lineSourceMap!.get(compiledLine);
|
|
671
|
+
return file && !file.includes('webpack/bootstrap') && !file.includes('webpack/runtime') ? file : undefined;
|
|
672
|
+
}
|
|
673
|
+
: (_: number) => undefined as string | undefined;
|
|
674
|
+
|
|
605
675
|
if (varTraceEnabled) {
|
|
606
676
|
const isTsFile = /\.[mc]?tsx?$/.test(filename);
|
|
607
677
|
if (isTsFile && !sourceMap) {
|
|
@@ -663,10 +733,12 @@ function transformCjsSource(source: string, filename: string, moduleName: string
|
|
|
663
733
|
// Plain JS or source-map-assisted: parse compiled source, then remap lines
|
|
664
734
|
varInsertions = findVarDeclarations(source).map(ins => ({
|
|
665
735
|
...ins,
|
|
736
|
+
sourceFile: getSourceFile(ins.lineNo),
|
|
666
737
|
lineNo: remapLine(ins.lineNo),
|
|
667
738
|
}));
|
|
668
739
|
destructInsertions = findDestructuredDeclarations(source).map(ins => ({
|
|
669
740
|
...ins,
|
|
741
|
+
sourceFile: getSourceFile(ins.lineNo),
|
|
670
742
|
lineNo: remapLine(ins.lineNo),
|
|
671
743
|
}));
|
|
672
744
|
}
|
|
@@ -678,14 +750,17 @@ function transformCjsSource(source: string, filename: string, moduleName: string
|
|
|
678
750
|
// Apply source map remapping to these too.
|
|
679
751
|
const reassignInsertions = findReassignments(source).map(ins => ({
|
|
680
752
|
...ins,
|
|
753
|
+
sourceFile: getSourceFile(ins.lineNo),
|
|
681
754
|
lineNo: remapLine(ins.lineNo),
|
|
682
755
|
}));
|
|
683
756
|
const forLoopInsertions = findForLoopVars(source).map(ins => ({
|
|
684
757
|
...ins,
|
|
758
|
+
sourceFile: getSourceFile(ins.lineNo),
|
|
685
759
|
lineNo: remapLine(ins.lineNo),
|
|
686
760
|
}));
|
|
687
761
|
const catchInsertions = findCatchVars(source).map(ins => ({
|
|
688
762
|
...ins,
|
|
763
|
+
sourceFile: getSourceFile(ins.lineNo),
|
|
689
764
|
lineNo: remapLine(ins.lineNo),
|
|
690
765
|
}));
|
|
691
766
|
|
|
@@ -726,9 +801,10 @@ function transformCjsSource(source: string, filename: string, moduleName: string
|
|
|
726
801
|
const traceModuleName = sourceMap
|
|
727
802
|
? path.basename(sourceMap.originalFile).replace(/\.[jt]sx?$/, '')
|
|
728
803
|
: moduleName;
|
|
804
|
+
|
|
729
805
|
prefixLines.push(
|
|
730
806
|
`var __trickle_tv_mod = require(${JSON.stringify(traceVarPath)});`,
|
|
731
|
-
`var __trickle_tv = function(v, n, l) { try { __trickle_tv_mod.traceVar(v, n, l, ${JSON.stringify(traceModuleName)}, ${JSON.stringify(traceFilePath)}); } catch(e){} };`,
|
|
807
|
+
`var __trickle_tv = function(v, n, l, m, f) { try { __trickle_tv_mod.traceVar(v, n, l, m || ${JSON.stringify(traceModuleName)}, f || ${JSON.stringify(traceFilePath)}); } catch(e){} };`,
|
|
732
808
|
);
|
|
733
809
|
}
|
|
734
810
|
|
|
@@ -747,15 +823,23 @@ function transformCjsSource(source: string, filename: string, moduleName: string
|
|
|
747
823
|
});
|
|
748
824
|
}
|
|
749
825
|
|
|
750
|
-
|
|
826
|
+
// Helper to generate source file args for __trickle_tv calls (for multi-source bundles)
|
|
827
|
+
const sfArgs = (sourceFile?: string) => {
|
|
828
|
+
if (!sourceFile) return '';
|
|
829
|
+
const mod = path.basename(sourceFile).replace(/\.[jt]sx?$/, '');
|
|
830
|
+
return `,${JSON.stringify(mod)},${JSON.stringify(sourceFile)}`;
|
|
831
|
+
};
|
|
832
|
+
|
|
833
|
+
for (const { lineEnd, varName, lineNo, sourceFile } of varInsertions) {
|
|
751
834
|
allInsertions.push({
|
|
752
835
|
position: lineEnd,
|
|
753
|
-
code: `\ntry{__trickle_tv(${varName},${JSON.stringify(varName)},${lineNo})}catch(__e){}\n`,
|
|
836
|
+
code: `\ntry{__trickle_tv(${varName},${JSON.stringify(varName)},${lineNo}${sfArgs(sourceFile)})}catch(__e){}\n`,
|
|
754
837
|
});
|
|
755
838
|
}
|
|
756
839
|
|
|
757
|
-
for (const { lineEnd, varNames, lineNo } of destructInsertions) {
|
|
758
|
-
const
|
|
840
|
+
for (const { lineEnd, varNames, lineNo, sourceFile } of destructInsertions) {
|
|
841
|
+
const sf = sfArgs(sourceFile);
|
|
842
|
+
const calls = varNames.map(n => `__trickle_tv(${n},${JSON.stringify(n)},${lineNo}${sf})`).join(';');
|
|
759
843
|
allInsertions.push({
|
|
760
844
|
position: lineEnd,
|
|
761
845
|
code: `\n;try{${calls}}catch(__e){}\n`,
|
|
@@ -763,16 +847,17 @@ function transformCjsSource(source: string, filename: string, moduleName: string
|
|
|
763
847
|
}
|
|
764
848
|
|
|
765
849
|
// Reassignment insertions
|
|
766
|
-
for (const { lineEnd, varName, lineNo } of reassignInsertions) {
|
|
850
|
+
for (const { lineEnd, varName, lineNo, sourceFile } of reassignInsertions) {
|
|
767
851
|
allInsertions.push({
|
|
768
852
|
position: lineEnd,
|
|
769
|
-
code: `\n;try{__trickle_tv(${varName},${JSON.stringify(varName)},${lineNo})}catch(__e){}\n`,
|
|
853
|
+
code: `\n;try{__trickle_tv(${varName},${JSON.stringify(varName)},${lineNo}${sfArgs(sourceFile)})}catch(__e){}\n`,
|
|
770
854
|
});
|
|
771
855
|
}
|
|
772
856
|
|
|
773
857
|
// For-loop variable insertions
|
|
774
|
-
for (const { bodyStart, varNames, lineNo } of forLoopInsertions) {
|
|
775
|
-
const
|
|
858
|
+
for (const { bodyStart, varNames, lineNo, sourceFile } of forLoopInsertions) {
|
|
859
|
+
const sf = sfArgs(sourceFile);
|
|
860
|
+
const calls = varNames.map(n => `__trickle_tv(${n},${JSON.stringify(n)},${lineNo}${sf})`).join(';');
|
|
776
861
|
allInsertions.push({
|
|
777
862
|
position: bodyStart,
|
|
778
863
|
code: `\ntry{${calls}}catch(__e){}\n`,
|
|
@@ -780,8 +865,9 @@ function transformCjsSource(source: string, filename: string, moduleName: string
|
|
|
780
865
|
}
|
|
781
866
|
|
|
782
867
|
// Catch clause insertions
|
|
783
|
-
for (const { bodyStart, varNames, lineNo } of catchInsertions) {
|
|
784
|
-
const
|
|
868
|
+
for (const { bodyStart, varNames, lineNo, sourceFile } of catchInsertions) {
|
|
869
|
+
const sf = sfArgs(sourceFile);
|
|
870
|
+
const calls = varNames.map(n => `__trickle_tv(${n},${JSON.stringify(n)},${lineNo}${sf})`).join(';');
|
|
785
871
|
allInsertions.push({
|
|
786
872
|
position: bodyStart,
|
|
787
873
|
code: `\ntry{${calls}}catch(__e2){}\n`,
|
package/src/vite-plugin.ts
CHANGED
|
@@ -263,6 +263,9 @@ function findVarDeclarations(source: string): Array<{ lineEnd: number; varName:
|
|
|
263
263
|
// Skip esbuild helpers
|
|
264
264
|
if (varName === '__defProp' || varName === '__defNormalProp' || varName === '__publicField' || varName === '__getOwnPropNames') continue;
|
|
265
265
|
if (varName === '__commonJS' || varName === '__toCommonJS' || varName === '__export' || varName === '__copyProps') continue;
|
|
266
|
+
// Skip webpack internals
|
|
267
|
+
if (varName.startsWith('__webpack_')) continue;
|
|
268
|
+
if (varName === '__unused_webpack_module') continue;
|
|
266
269
|
// Skip React Refresh / HMR internals (Vite, webpack, Next.js inject these)
|
|
267
270
|
if (varName === 'prevRefreshReg' || varName === 'prevRefreshSig' || varName === 'inWebWorker' || varName === 'invalidateMessage') continue;
|
|
268
271
|
if (varName === '_s' || varName === '_c2' || varName === '_s2') continue;
|