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
1 change: 1 addition & 0 deletions modules/testing/builder/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ ts_project(
# resolvable in the test project.
":node_modules/@angular/ssr",
":node_modules/browser-sync",
":node_modules/istanbul-lib-instrument",
":node_modules/jsdom",
":node_modules/ng-packagr",
":node_modules/vitest",
Expand Down
1 change: 1 addition & 0 deletions modules/testing/builder/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"@angular/ssr": "workspace:*",
"@vitest/coverage-v8": "4.1.2",
"browser-sync": "3.0.4",
"istanbul-lib-instrument": "6.0.3",
"jsdom": "29.0.1",
"ng-packagr": "22.0.0-next.1",
"rxjs": "7.8.2",
Expand Down
6 changes: 5 additions & 1 deletion packages/angular/build/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
"browserslist": "^4.26.0",
"esbuild": "0.27.3",
"https-proxy-agent": "8.0.0",
"istanbul-lib-instrument": "6.0.3",
"jsonc-parser": "3.3.1",
"listr2": "10.2.1",
"magic-string": "0.30.21",
Expand All @@ -51,6 +50,7 @@
"devDependencies": {
"@angular-devkit/core": "workspace:*",
"@angular/ssr": "workspace:*",
"istanbul-lib-instrument": "6.0.3",
"jsdom": "29.0.1",
"less": "4.6.4",
"ng-packagr": "22.0.0-next.1",
Expand All @@ -67,6 +67,7 @@
"@angular/platform-server": "0.0.0-ANGULAR-FW-PEER-DEP",
"@angular/service-worker": "0.0.0-ANGULAR-FW-PEER-DEP",
"@angular/ssr": "^0.0.0-PLACEHOLDER",
"istanbul-lib-instrument": "^6.0.0",
"karma": "^6.4.0",
"less": "^4.2.0",
"ng-packagr": "0.0.0-NG-PACKAGR-PEER-DEP",
Expand Down Expand Up @@ -95,6 +96,9 @@
"@angular/ssr": {
"optional": true
},
"istanbul-lib-instrument": {
"optional": true
},
"karma": {
"optional": true
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,17 @@
*/

import { NodePath, PluginObj, types } from '@babel/core';
import { Visitor, programVisitor } from 'istanbul-lib-instrument';
import type { Visitor } from 'istanbul-lib-instrument';
import assert from 'node:assert';

/**
* A babel plugin factory function for adding istanbul instrumentation.
*
* @returns A babel plugin object instance.
*/
export default function (): PluginObj {
export default function (
programVisitor: typeof import('istanbul-lib-instrument').programVisitor,
): PluginObj {
const visitors = new WeakMap<NodePath, Visitor>();

return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import { type PluginItem, transformAsync } from '@babel/core';
import fs from 'node:fs';
import { createRequire } from 'node:module';
import path from 'node:path';
import Piscina from 'piscina';

Expand Down Expand Up @@ -66,8 +67,31 @@ async function transformWithBabel(
const plugins: PluginItem[] = [];

if (options.instrumentForCoverage) {
const { default: coveragePlugin } = await import('../babel/plugins/add-code-coverage.js');
plugins.push(coveragePlugin);
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));
} 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 },
);
}
}

if (shouldLink) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/

import { Rule } from '@angular-devkit/schematics';
import { DependencyType, ExistingBehavior, addDependency } from '../../utility/dependency';
import { latestVersions } from '../../utility/latest-versions';
import { allTargetOptions, getWorkspace } from '../../utility/workspace';
import { Builders } from '../../utility/workspace-models';

export default function (): Rule {
return async (tree) => {
const workspace = await getWorkspace(tree);
let needInstrumenter = false;

for (const [, project] of workspace.projects) {
for (const [, target] of project.targets) {
if (target.builder === Builders.Karma || target.builder === Builders.BuildKarma) {
needInstrumenter = true;
break;
}

if (target.builder === Builders.BuildUnitTest) {
for (const [, options] of allTargetOptions(target)) {
if (options['runner'] === 'karma') {
needInstrumenter = true;
break;
}
}
}

if (needInstrumenter) {
break;
}
}
if (needInstrumenter) {
break;
}
}

if (needInstrumenter) {
return addDependency('istanbul-lib-instrument', latestVersions['istanbul-lib-instrument'], {
type: DependencyType.Dev,
existing: ExistingBehavior.Skip,
});
}
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/

/* eslint-disable @typescript-eslint/no-explicit-any */

import { EmptyTree } from '@angular-devkit/schematics';
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';

describe('Migration to add istanbul-lib-instrument', () => {
const schematicRunner = new SchematicTestRunner(
'migrations',
require.resolve('../migration-collection.json'),
);

let tree: UnitTestTree;
beforeEach(() => {
tree = new UnitTestTree(new EmptyTree());
tree.create(
'/package.json',
JSON.stringify({
devDependencies: {},
}),
);
});

function createWorkspace(builder: string, options?: any, configurations?: any) {
tree.create(
'/angular.json',
JSON.stringify({
version: 1,
projects: {
app: {
root: '',
targets: {
test: {
builder,
options,
configurations,
},
},
},
},
}),
);
}

async function expectDependency(defined: boolean) {
const newTree = await schematicRunner.runSchematic('add-istanbul-instrumenter', {}, tree);
const { devDependencies } = newTree.readJson('/package.json') as any;
if (defined) {
expect(devDependencies['istanbul-lib-instrument']).toBeDefined();
} else {
expect(devDependencies['istanbul-lib-instrument']).toBeUndefined();
}
}

it('should add istanbul-lib-instrument for @angular-devkit/build-angular:karma', async () => {
createWorkspace('@angular-devkit/build-angular:karma');

await expectDependency(true);
});

it('should add istanbul-lib-instrument for @angular/build:karma', async () => {
createWorkspace('@angular/build:karma');

await expectDependency(true);
});

it('should add istanbul-lib-instrument for @angular/build:unit-test with runner: karma', async () => {
createWorkspace('@angular/build:unit-test', { runner: 'karma' });

await expectDependency(true);
});

it('should add istanbul-lib-instrument if runner: karma is in configuration', async () => {
createWorkspace('@angular/build:unit-test', undefined, { ci: { runner: 'karma' } });

await expectDependency(true);
});

it('should NOT add istanbul-lib-instrument for @angular/build:unit-test with runner: vitest', async () => {
createWorkspace('@angular/build:unit-test', { runner: 'vitest' });

await expectDependency(false);
});

it('should NOT add istanbul-lib-instrument for @angular/build:unit-test with no runner specified (default vitest)', async () => {
createWorkspace('@angular/build:unit-test', {});

await expectDependency(false);
});

it('should NOT add istanbul-lib-instrument if no karma builder is used', async () => {
createWorkspace('@angular-devkit/build-angular:browser');

await expectDependency(false);
});
});
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
{
"encapsulation": false,
"schematics": {
"add-istanbul-instrumenter": {
"version": "22.0.0",
"factory": "./add-istanbul-instrumenter/migration",
"description": "Add istanbul-lib-instrument to devDependencies if Karma unit testing is used."
},
"use-application-builder": {
"version": "22.0.0",
"factory": "./use-application-builder/migration",
Expand Down
1 change: 1 addition & 0 deletions packages/schematics/angular/utility/dependencies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export function addTestRunnerDependencies(
'karma-jasmine-html-reporter',
'jasmine-core',
'@types/jasmine',
'istanbul-lib-instrument',
];

return dependencies.map((name) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"@types/node": "^20.17.19",
"browser-sync": "^3.0.0",
"express": "^5.1.0",
"istanbul-lib-instrument": "^6.0.3",
"jasmine-core": "~6.1.0",
"jasmine-spec-reporter": "~7.0.0",
"karma-chrome-launcher": "~3.2.0",
Expand Down
9 changes: 6 additions & 3 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading