mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-11-05 13:26:48 +08:00
fix(webpack): es module source mapping improvements
This commit is contained in:
@@ -23,7 +23,8 @@ function getConsumer(mapPath: string, sourceMap: any) {
|
|||||||
c = new SourceMapConsumer(sourceMap);
|
c = new SourceMapConsumer(sourceMap);
|
||||||
consumerCache.set(mapPath, c);
|
consumerCache.set(mapPath, c);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Failed to create SourceMapConsumer for ${mapPath}:`, error);
|
// Keep quiet in production-like console; failures just fall back to original stack
|
||||||
|
console.debug && console.debug(`SourceMapConsumer failed for ${mapPath}:`, error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -67,7 +68,7 @@ function loadAndExtractMap(mapPath: string) {
|
|||||||
mapText = binary;
|
mapText = binary;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Failed to load source map ${mapPath}:`, error);
|
console.debug && console.debug(`Failed to load source map ${mapPath}:`, error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -108,7 +109,7 @@ function remapFrame(file: string, line: number, column: number) {
|
|||||||
try {
|
try {
|
||||||
return consumer.originalPositionFor({ line, column });
|
return consumer.originalPositionFor({ line, column });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Failed to get original position for ${file}:${line}:${column}:`, error);
|
console.debug && console.debug(`Remap failed for ${file}:${line}:${column}:`, error);
|
||||||
return { source: null, line: 0, column: 0 };
|
return { source: null, line: 0, column: 0 };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -116,18 +117,36 @@ function remapFrame(file: string, line: number, column: number) {
|
|||||||
function remapStack(raw: string): string {
|
function remapStack(raw: string): string {
|
||||||
const lines = raw.split('\n');
|
const lines = raw.split('\n');
|
||||||
const out = lines.map((line) => {
|
const out = lines.map((line) => {
|
||||||
const m = /\((.+):(\d+):(\d+)\)/.exec(line);
|
// 1) Parenthesized frame: at fn (file:...:L:C)
|
||||||
if (!m) return line;
|
let m = /\((.+):(\d+):(\d+)\)/.exec(line);
|
||||||
|
if (m) {
|
||||||
try {
|
try {
|
||||||
const [_, file, l, c] = m;
|
const [_, file, l, c] = m;
|
||||||
const orig = remapFrame(file, +l, +c);
|
const orig = remapFrame(file, +l, +c);
|
||||||
if (!orig.source) return line;
|
if (!orig.source) return line;
|
||||||
return line.replace(/\(.+\)/, `(${orig.source}:${orig.line}:${orig.column})`);
|
return line.replace(/\(.+\)/, `(${orig.source}:${orig.line}:${orig.column})`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to remap stack frame:', line, error);
|
console.debug && console.debug('Remap failed for frame:', line, error);
|
||||||
return line; // return original line if remapping fails
|
return line;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 2) Bare frame: at file:///app/vendor.js:L:C (no parentheses)
|
||||||
|
const bare = /(\s+at\s+)([^\s()]+):(\d+):(\d+)/.exec(line);
|
||||||
|
if (bare) {
|
||||||
|
try {
|
||||||
|
const [, prefix, file, l, c] = bare;
|
||||||
|
const orig = remapFrame(file, +l, +c);
|
||||||
|
if (!orig.source) return line;
|
||||||
|
const replacement = `${prefix}${orig.source}:${orig.line}:${orig.column}`;
|
||||||
|
return line.replace(bare[0], replacement);
|
||||||
|
} catch (error) {
|
||||||
|
console.debug && console.debug('Remap failed for bare frame:', line, error);
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return line;
|
||||||
});
|
});
|
||||||
return out.join('\n');
|
return out.join('\n');
|
||||||
}
|
}
|
||||||
@@ -141,7 +160,7 @@ function remapStack(raw: string): string {
|
|||||||
try {
|
try {
|
||||||
return remapStack(rawStack);
|
return remapStack(rawStack);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to remap stack trace, returning original:', error);
|
console.debug && console.debug('Remap failed, returning original:', error);
|
||||||
return rawStack; // fallback to original stack trace
|
return rawStack; // fallback to original stack trace
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import { applyDotEnvPlugin } from '../helpers/dotEnv';
|
|||||||
import { env as _env, IWebpackEnv } from '../index';
|
import { env as _env, IWebpackEnv } from '../index';
|
||||||
import { getValue } from '../helpers/config';
|
import { getValue } from '../helpers/config';
|
||||||
import { getIPS } from '../helpers/host';
|
import { getIPS } from '../helpers/host';
|
||||||
|
import FixSourceMapUrlPlugin from '../plugins/FixSourceMapUrlPlugin';
|
||||||
import {
|
import {
|
||||||
getAvailablePlatforms,
|
getAvailablePlatforms,
|
||||||
getAbsoluteDistPath,
|
getAbsoluteDistPath,
|
||||||
@@ -178,32 +179,9 @@ export default function (config: Config, env: IWebpackEnv = _env): Config {
|
|||||||
|
|
||||||
// For ESM builds, fix the sourceMappingURL to use correct paths
|
// For ESM builds, fix the sourceMappingURL to use correct paths
|
||||||
if (!env.commonjs && sourceMapType && sourceMapType !== 'hidden-source-map') {
|
if (!env.commonjs && sourceMapType && sourceMapType !== 'hidden-source-map') {
|
||||||
class FixSourceMapUrlPlugin {
|
config
|
||||||
apply(compiler) {
|
.plugin('FixSourceMapUrlPlugin')
|
||||||
compiler.hooks.emit.tap('FixSourceMapUrlPlugin', (compilation) => {
|
.use(FixSourceMapUrlPlugin as any, [{ outputPath }]);
|
||||||
const leadingCharacter = process.platform === 'win32' ? '/' : '';
|
|
||||||
Object.keys(compilation.assets).forEach((filename) => {
|
|
||||||
if (filename.endsWith('.mjs') || filename.endsWith('.js')) {
|
|
||||||
const asset = compilation.assets[filename];
|
|
||||||
let source = asset.source();
|
|
||||||
|
|
||||||
// Replace sourceMappingURL to use file:// protocol pointing to actual location
|
|
||||||
source = source.replace(
|
|
||||||
/\/\/# sourceMappingURL=(.+\.map)/g,
|
|
||||||
`//# sourceMappingURL=file://${leadingCharacter}${outputPath}/$1`,
|
|
||||||
);
|
|
||||||
|
|
||||||
compilation.assets[filename] = {
|
|
||||||
source: () => source,
|
|
||||||
size: () => source.length,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
config.plugin('FixSourceMapUrlPlugin').use(FixSourceMapUrlPlugin);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// when using hidden-source-map, output source maps to the `platforms/{platformName}-sourceMaps` folder
|
// when using hidden-source-map, output source maps to the `platforms/{platformName}-sourceMaps` folder
|
||||||
|
|||||||
112
packages/webpack5/src/plugins/FixSourceMapUrlPlugin.ts
Normal file
112
packages/webpack5/src/plugins/FixSourceMapUrlPlugin.ts
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
import type { Compiler } from 'webpack';
|
||||||
|
import { sources } from 'webpack';
|
||||||
|
|
||||||
|
export interface FixSourceMapUrlPluginOptions {
|
||||||
|
outputPath: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures sourceMappingURL points to the actual file:// location on device/emulator.
|
||||||
|
* Handles Webpack 5 asset sources (string/Buffer/Source objects).
|
||||||
|
*/
|
||||||
|
export default class FixSourceMapUrlPlugin {
|
||||||
|
constructor(private readonly options: FixSourceMapUrlPluginOptions) {}
|
||||||
|
|
||||||
|
apply(compiler: Compiler) {
|
||||||
|
const wp: any = (compiler as any).webpack;
|
||||||
|
const hasProcessAssets =
|
||||||
|
!!wp?.Compilation?.PROCESS_ASSETS_STAGE_DEV_TOOLING &&
|
||||||
|
!!(compiler as any).hooks?.thisCompilation;
|
||||||
|
|
||||||
|
const leadingCharacter = process.platform === 'win32' ? '/' : '';
|
||||||
|
|
||||||
|
const toStringContent = (content: any): string => {
|
||||||
|
if (typeof content === 'string') return content;
|
||||||
|
if (Buffer.isBuffer(content)) return content.toString('utf-8');
|
||||||
|
if (content && typeof content.source === 'function') {
|
||||||
|
const inner = content.source();
|
||||||
|
if (typeof inner === 'string') return inner;
|
||||||
|
if (Buffer.isBuffer(inner)) return inner.toString('utf-8');
|
||||||
|
try {
|
||||||
|
return String(inner);
|
||||||
|
} catch {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return String(content);
|
||||||
|
} catch {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const processFile = (filename: string, compilation: any) => {
|
||||||
|
if (!(filename.endsWith('.mjs') || filename.endsWith('.js'))) return;
|
||||||
|
|
||||||
|
// Support both legacy compilation.assets and v5 Asset API
|
||||||
|
let rawSource: any;
|
||||||
|
if (typeof (compilation as any).getAsset === 'function') {
|
||||||
|
const assetObj = (compilation as any).getAsset(filename);
|
||||||
|
if (assetObj && assetObj.source) {
|
||||||
|
rawSource = (assetObj.source as any).source
|
||||||
|
? (assetObj.source as any).source()
|
||||||
|
: (assetObj.source as any)();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
rawSource === undefined &&
|
||||||
|
(compilation as any).assets &&
|
||||||
|
(compilation as any).assets[filename]
|
||||||
|
) {
|
||||||
|
const asset = (compilation as any).assets[filename];
|
||||||
|
rawSource = typeof asset.source === 'function' ? asset.source() : asset;
|
||||||
|
}
|
||||||
|
|
||||||
|
let source = toStringContent(rawSource);
|
||||||
|
// Replace sourceMappingURL to use file:// protocol pointing to actual location
|
||||||
|
source = source.replace(
|
||||||
|
/\/\/\# sourceMappingURL=(.+\.map)/g,
|
||||||
|
`//# sourceMappingURL=file://${leadingCharacter}${this.options.outputPath}/$1`,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Prefer Webpack 5 updateAsset with RawSource when available
|
||||||
|
const RawSourceCtor =
|
||||||
|
wp?.sources?.RawSource || (sources as any)?.RawSource;
|
||||||
|
if (
|
||||||
|
typeof (compilation as any).updateAsset === 'function' &&
|
||||||
|
RawSourceCtor
|
||||||
|
) {
|
||||||
|
(compilation as any).updateAsset(filename, new RawSourceCtor(source));
|
||||||
|
} else {
|
||||||
|
(compilation as any).assets[filename] = {
|
||||||
|
source: () => source,
|
||||||
|
size: () => source.length,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (hasProcessAssets) {
|
||||||
|
compiler.hooks.thisCompilation.tap(
|
||||||
|
'FixSourceMapUrlPlugin',
|
||||||
|
(compilation: any) => {
|
||||||
|
const stage = wp.Compilation.PROCESS_ASSETS_STAGE_DEV_TOOLING;
|
||||||
|
compilation.hooks.processAssets.tap(
|
||||||
|
{ name: 'FixSourceMapUrlPlugin', stage },
|
||||||
|
(assets: Record<string, any>) => {
|
||||||
|
Object.keys(assets).forEach((filename) =>
|
||||||
|
processFile(filename, compilation),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Fallback for older setups: use emit (may log deprecation in newer webpack)
|
||||||
|
compiler.hooks.emit.tap('FixSourceMapUrlPlugin', (compilation: any) => {
|
||||||
|
Object.keys((compilation as any).assets).forEach((filename) =>
|
||||||
|
processFile(filename, compilation),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user