Skip to content
Open
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

This file was deleted.

16 changes: 8 additions & 8 deletions packages/angular/build/src/tools/babel/plugins/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@
*/

declare module 'istanbul-lib-instrument' {
export interface Visitor {
enter(path: import('@babel/core').NodePath<types.Program>): void;
exit(path: import('@babel/core').NodePath<types.Program>): void;
export interface Instrumenter {
instrumentSync(code: string, filename: string, inputSourceMap?: object): string;
lastSourceMap(): object | undefined;
}

export function programVisitor(
types: typeof import('@babel/core').types,
filePath?: string,
options?: { inputSourceMap?: object | null },
): Visitor;
export function createInstrumenter(options?: {
produceSourceMap?: boolean;
esModules?: boolean;
coverageVariable?: string;
}): Instrumenter;
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,61 @@ const textEncoder = new TextEncoder();
*/
const LINKER_DECLARATION_PREFIX = 'ɵɵngDeclare';

function extractInlineSourceMap(code: string): object | undefined {
const match = code.match(
/\/\/# sourceMappingURL=data:application\/json;(?:charset=utf-8;)?base64,(.*)$/m,
);
if (!match) {
return undefined;
}
try {
return JSON.parse(Buffer.from(match[1], 'base64').toString()) as object;
} catch {
return undefined;
}
}

async function instrumentCoverage(
filename: string,
data: string,
useInputSourcemap: boolean,
): Promise<string> {
try {
let resolvedPath = 'istanbul-lib-instrument';
try {
const requireFn = createRequire(filename);
resolvedPath = requireFn.resolve('istanbul-lib-instrument');
} catch {
// Fallback to pool worker import traversal
}

const { createInstrumenter } = (await import(
resolvedPath
)) as typeof import('istanbul-lib-instrument');
const instrumenter = createInstrumenter({
produceSourceMap: useInputSourcemap,
esModules: true,
});

const inputSourceMap = useInputSourcemap ? extractInlineSourceMap(data) : undefined;
const instrumentedCode = instrumenter.instrumentSync(data, filename, inputSourceMap);
const lastMap = instrumenter.lastSourceMap();

if (useInputSourcemap && lastMap) {
const inlineMap = Buffer.from(JSON.stringify(lastMap)).toString('base64');

return instrumentedCode + `\n//# sourceMappingURL=data:application/json;base64,${inlineMap}`;
}

return removeSourceMappingURL(instrumentedCode);
} catch (error) {
throw new Error(
`The 'istanbul-lib-instrument' package is required for code coverage but was not found. Please install the package.`,
{ cause: error },
);
}
}

export default async function transformJavaScript(
request: JavaScriptTransformRequest,
): Promise<unknown> {
Expand All @@ -59,41 +114,18 @@ async function transformWithBabel(
data: string,
options: Omit<JavaScriptTransformRequest, 'filename' | 'data'>,
): Promise<string> {
const shouldLink = !options.skipLinker && (await requiresLinking(filename, data));
const useInputSourcemap =
options.sourcemap &&
(!!options.thirdPartySourcemaps || !/[\\/]node_modules[\\/]/.test(filename));

const plugins: PluginItem[] = [];

let code = data;
if (options.instrumentForCoverage) {
try {
let resolvedPath = 'istanbul-lib-instrument';
try {
const requireFn = createRequire(filename);
resolvedPath = requireFn.resolve('istanbul-lib-instrument');
} catch {
// Fallback to pool worker import traversal
}

const istanbul = await import(resolvedPath);
const programVisitor = istanbul.programVisitor ?? istanbul.default?.programVisitor;

if (!programVisitor) {
throw new Error('programVisitor is not available in istanbul-lib-instrument.');
}

const { default: coveragePluginFactory } =
await import('../babel/plugins/add-code-coverage.js');
plugins.push(coveragePluginFactory(programVisitor) as unknown as PluginItem);
} catch (error) {
throw new Error(
`The 'istanbul-lib-instrument' package is required for code coverage but was not found. Please install the package.`,
{ cause: error },
);
}
code = await instrumentCoverage(filename, code, useInputSourcemap);
}

const shouldLink = !options.skipLinker && (await requiresLinking(filename, code));
const plugins: PluginItem[] = [];

if (shouldLink) {
// Lazy load the linker plugin only when linking is required
const linkerPlugin = await createLinkerPlugin(options);
Expand All @@ -116,13 +148,13 @@ async function transformWithBabel(
);
}

// If no additional transformations are needed, return the data directly
// If no additional transformations are needed, return the code directly
if (plugins.length === 0) {
// Strip sourcemaps if they should not be used
return useInputSourcemap ? data : removeSourceMappingURL(data);
return useInputSourcemap ? code : removeSourceMappingURL(code);
}

const result = await transformAsync(data, {
const result = await transformAsync(code, {
filename,
inputSourceMap: (useInputSourcemap ? undefined : false) as undefined,
sourceMaps: useInputSourcemap ? 'inline' : false,
Expand All @@ -133,7 +165,7 @@ async function transformWithBabel(
plugins,
});

const outputCode = result?.code ?? data;
const outputCode = result?.code ?? code;

// Strip sourcemaps if they should not be used.
// Babel will keep the original comments even if sourcemaps are disabled.
Expand Down
Loading