Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 30 additions & 16 deletions packages/core/src/inner-plugins/plugins/sourcemapTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,15 @@ export async function collectSourceMaps(
}
}

function markAssetWithoutSourceMap(
assetsWithoutSourceMap: Set<string>,
assetName: unknown,
) {
if (typeof assetName === 'string' && /\.(?:js|bundle|css)$/.test(assetName)) {
assetsWithoutSourceMap.add(assetName);
}
}

/**
* Handles the afterEmit event for assets to collect source map information.
* @param compilation - The current compilation object.
Expand Down Expand Up @@ -280,25 +289,29 @@ export async function handleAfterEmitAssets(
});
}

if (sourceMapAsset?.source) {
map = JSON.parse(sourceMapAsset.source.source().toString());
const outputPath = compilation.options.output?.path;
if (outputPath && typeof outputPath === 'string') {
sourceMapPath = resolve(outputPath, sourceMapAsset.name);
try {
const sourceMapContent = sourceMapAsset?.source?.source();
if (sourceMapContent) {
map = JSON.parse(sourceMapContent.toString());
}
} catch (error) {
logger.debug(
`Error parsing source map asset ${sourceMapFile}:`,
error,
);
}
} else {
// Mark asset as having no sourcemap for AST parsing fallback
// Only mark js/css files that don't have sourcemap
if (
assetName &&
typeof assetName === 'string' &&
(assetName.endsWith('.js') ||
assetName.endsWith('.bundle') ||
assetName.endsWith('.css'))
) {
_this.assetsWithoutSourceMap.add(assetName);

if (!map) {
markAssetWithoutSourceMap(_this.assetsWithoutSourceMap, assetName);
continue;
Comment thread
yifancong marked this conversation as resolved.
}

const outputPath = compilation.options.output?.path;
if (outputPath && typeof outputPath === 'string' && sourceMapAsset) {
sourceMapPath = resolve(outputPath, sourceMapAsset.name);
}
} else {
markAssetWithoutSourceMap(_this.assetsWithoutSourceMap, assetName);
continue;
}
} else {
Expand All @@ -324,6 +337,7 @@ export async function handleAfterEmitAssets(
);
} catch (e) {
logger.debug(e);
markAssetWithoutSourceMap(_this.assetsWithoutSourceMap, assetName);
}
_this.sourceMapSets.forEach((_value: string, key: string) => {
if (!skipSources.has(key)) {
Expand Down
75 changes: 73 additions & 2 deletions packages/core/tests/plugins/sourcemapTool.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ describe('sourcemapTool', () => {
expect(sourceMap).toBe('console.log("test");');
});

it('should skip source map assets without a source', async () => {
it('should fall back to AST parsing when a related source map has no source', async () => {
const plugin = createMockPluginInstance();
const compilation = {
compiler: { rspack: {} },
Expand All @@ -331,6 +331,18 @@ describe('sourcemapTool', () => {
},
},
getAssets: () => [
{
name: 'main.js',
source: {
source: () => 'console.log("test");\n',
name: 'main.js',
sourceAndMap: () => ({
source: 'console.log("test");\n',
map: mockJsMap,
}),
},
info: {},
},
{
name: 'worker.abc123.js',
source: {
Expand All @@ -355,7 +367,66 @@ describe('sourcemapTool', () => {
} as any;

await handleAfterEmitAssets(compilation, plugin);
expect(plugin.sourceMapSets.size).toBe(0);
expect(plugin.sourceMapSets.size).toBe(1);
expect(plugin.assetsWithoutSourceMap).toEqual(
new Set(['worker.abc123.js']),
);
});

it('should fall back to AST parsing when a related source map is invalid', async () => {
const plugin = createMockPluginInstance();
const compilation = {
compiler: { rspack: {} },
options: {
output: {
filename: '[name].[contenthash].js',
},
},
getAssets: () => [
{
name: 'main.js',
source: {
source: () => 'console.log("test");\n',
name: 'main.js',
sourceAndMap: () => ({
source: 'console.log("test");\n',
map: mockJsMap,
}),
},
info: {},
},
{
name: 'worker.abc123.js',
source: {
source: () => 'console.log("worker");\n',
name: 'worker.abc123.js',
sourceAndMap: () => ({
source: 'console.log("worker");\n',
map: null,
}),
},
info: {
related: {
sourceMap: 'worker.abc123.js.map',
},
},
},
{
name: 'worker.abc123.js.map',
source: {
source: () => '{}',
name: 'worker.abc123.js.map',
},
info: {},
},
],
} as any;

await handleAfterEmitAssets(compilation, plugin);
expect(plugin.sourceMapSets.size).toBe(1);
expect(plugin.assetsWithoutSourceMap).toEqual(
new Set(['worker.abc123.js']),
);
});

it('should find source map by base name without hash when exact match fails', async () => {
Expand Down
Loading