Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { readFile } from 'node:fs/promises';
import { createRequire } from 'node:module';
import { platform } from 'node:os';
import path from 'node:path';
import type { ExistingRawSourceMap } from 'rolldown';
import type {
BrowserConfigOptions,
InlineConfig,
Expand Down Expand Up @@ -178,9 +179,10 @@ export async function createVitestConfigPlugin(
};
}

const textDecoder = new TextDecoder('utf-8');
async function loadResultFile(file: ResultFile): Promise<string> {
if (file.origin === 'memory') {
return new TextDecoder('utf-8').decode(file.contents);
return textDecoder.decode(file.contents);
}

return readFile(file.inputPath, 'utf-8');
Expand Down Expand Up @@ -215,20 +217,6 @@ export function createVitestPlugins(pluginOptions: PluginOptions): VitestPlugins
}
}

if (importer && (id[0] === '.' || id[0] === '/')) {
let fullPath;
if (testFileToEntryPoint.has(importer)) {
fullPath = toPosixPath(path.join(workspaceRoot, id));
} else {
fullPath = toPosixPath(path.join(path.dirname(importer), id));
}

const relativePath = path.relative(workspaceRoot, fullPath);
if (buildResultFiles.has(toPosixPath(relativePath))) {
return fullPath;
}
}

// Determine the base directory for resolution.
let baseDir: string;
if (importer) {
Expand Down Expand Up @@ -265,7 +253,7 @@ export function createVitestPlugins(pluginOptions: PluginOptions): VitestPlugins
if (entryPoint) {
outputPath = entryPoint + '.js';

if (vitestConfig.coverage.enabled) {
if (vitestConfig?.coverage?.enabled) {
// To support coverage exclusion of the actual test file, the virtual
// test entry point only references the built and bundled intermediate file.
// If vitest supported an "excludeOnlyAfterRemap" option, this could be removed completely.
Expand All @@ -286,26 +274,9 @@ export function createVitestPlugins(pluginOptions: PluginOptions): VitestPlugins
const sourceMapFile = buildResultFiles.get(sourceMapPath);
const sourceMapText = sourceMapFile ? await loadResultFile(sourceMapFile) : undefined;

// Vitest will include files in the coverage report if the sourcemap contains no sources.
// For builder-internal generated code chunks, which are typically helper functions,
// a virtual source is added to the sourcemap to prevent them from being incorrectly
// included in the final coverage report.
const map = sourceMapText ? JSON.parse(sourceMapText) : undefined;
if (map) {
if (!map.sources?.length && !map.sourcesContent?.length && !map.mappings) {
map.sources = ['virtual:builder'];
} else if (!vitestConfig.coverage.enabled && Array.isArray(map.sources)) {
map.sources = (map.sources as string[]).map((source) => {
if (source.startsWith('angular:')) {
return source;
}

// source is relative to the workspace root because the output file is at the root of the output.
const absoluteSource = path.join(workspaceRoot, source);

return toPosixPath(path.relative(path.dirname(id), absoluteSource));
});
}
adjustSourcemapSources(map, !vitestConfig?.coverage?.enabled, workspaceRoot, id);
}

return {
Expand Down Expand Up @@ -338,6 +309,40 @@ export function createVitestPlugins(pluginOptions: PluginOptions): VitestPlugins
];
}

/**
* Adjusts the sources field in a sourcemap to ensure correct source mapping and coverage reporting.
*
* @param map The raw sourcemap to adjust.
* @param rebaseSources Whether to rebase the source paths relative to the test file.
* @param workspaceRoot The root directory of the workspace.
* @param id The ID (path) of the file being loaded.
*/
function adjustSourcemapSources(
map: ExistingRawSourceMap,
rebaseSources: boolean,
workspaceRoot: string,
id: string,
): void {
if (!map.sources?.length && !map.sourcesContent?.length && !map.mappings) {
// Vitest will include files in the coverage report if the sourcemap contains no sources.
// For builder-internal generated code chunks, which are typically helper functions,
// a virtual source is added to the sourcemap to prevent them from being incorrectly
// included in the final coverage report.
map.sources = ['virtual:builder'];
} else if (rebaseSources && map.sources) {
map.sources = map.sources.map((source) => {
if (!source || source.startsWith('angular:')) {
return source;
}

// source is relative to the workspace root because the output file is at the root of the output.
const absoluteSource = path.join(workspaceRoot, source);

return toPosixPath(path.relative(path.dirname(id), absoluteSource));
});
}
}

function createSourcemapSupportPlugin(): VitestPlugins[0] {
return {
name: 'angular:source-map-support',
Expand Down