Skip to content

fix(@schematics/angular): skip zone.js dependency for zoneless applications #30421

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 3, 2025
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
8 changes: 8 additions & 0 deletions packages/schematics/angular/application/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,14 @@ function addDependenciesToPackageJson(options: ApplicationOptions) {
},
].forEach((dependency) => addPackageJsonDependency(host, dependency));

if (!options.zoneless) {
addPackageJsonDependency(host, {
type: NodeDependencyType.Default,
name: 'zone.js',
version: latestVersions['zone.js'],
});
}

if (!options.skipInstall) {
context.addTask(new NodePackageInstallTask());
}
Expand Down
42 changes: 42 additions & 0 deletions packages/schematics/angular/application/index_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,48 @@ describe('Application Schematic', () => {
expect(pkg.devDependencies['typescript']).toEqual(latestVersions['typescript']);
});

it('should include zone.js if "zoneless" option is false', async () => {
const tree = await schematicRunner.runSchematic(
'application',
{
...defaultOptions,
zoneless: false,
},
workspaceTree,
);

const pkg = JSON.parse(tree.readContent('/package.json'));
expect(pkg.dependencies['zone.js']).toEqual(latestVersions['zone.js']);
});

it('should include zone.js if "zoneless" option is not present', async () => {
const tree = await schematicRunner.runSchematic(
'application',
{
...defaultOptions,
zoneless: undefined,
},
workspaceTree,
);

const pkg = JSON.parse(tree.readContent('/package.json'));
expect(pkg.dependencies['zone.js']).toEqual(latestVersions['zone.js']);
});

it('should not include zone.js if "zoneless" option is true', async () => {
const tree = await schematicRunner.runSchematic(
'application',
{
...defaultOptions,
zoneless: true,
},
workspaceTree,
);

const pkg = JSON.parse(tree.readContent('/package.json'));
expect(pkg.dependencies['zone.js']).toBeUndefined();
});

it(`should not override existing users dependencies`, async () => {
const oldPackageJson = workspaceTree.readContent('package.json');
workspaceTree.overwrite(
Expand Down
13 changes: 10 additions & 3 deletions packages/schematics/angular/library/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ import {
} from '@angular-devkit/schematics';
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
import { join } from 'node:path/posix';
import { NodeDependencyType, addPackageJsonDependency } from '../utility/dependencies';
import {
NodeDependencyType,
addPackageJsonDependency,
getPackageJsonDependency,
} from '../utility/dependencies';
import { JSONFile } from '../utility/json-file';
import { latestVersions } from '../utility/latest-versions';
import { relativePathToWorkspaceRoot } from '../utility/paths';
Expand Down Expand Up @@ -96,6 +100,7 @@ function addLibToWorkspaceFile(
options: LibraryOptions,
projectRoot: string,
projectName: string,
hasZoneDependency: boolean,
): Rule {
return updateWorkspace((workspace) => {
workspace.projects.add({
Expand All @@ -121,7 +126,7 @@ function addLibToWorkspaceFile(
builder: Builders.BuildKarma,
options: {
tsConfig: `${projectRoot}/tsconfig.spec.json`,
polyfills: ['zone.js', 'zone.js/testing'],
polyfills: hasZoneDependency ? ['zone.js', 'zone.js/testing'] : undefined,
},
},
},
Expand Down Expand Up @@ -172,9 +177,11 @@ export default function (options: LibraryOptions): Rule {
move(libDir),
]);

const hasZoneDependency = getPackageJsonDependency(host, 'zone.js') !== null;

return chain([
mergeWith(templateSource),
addLibToWorkspaceFile(options, libDir, packageName),
addLibToWorkspaceFile(options, libDir, packageName, hasZoneDependency),
options.skipPackageJson ? noop() : addDependenciesToPackageJson(),
options.skipTsConfig ? noop() : updateTsConfig(packageName, './' + distRoot),
options.skipTsConfig
Expand Down
7 changes: 7 additions & 0 deletions packages/schematics/angular/library/index_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,13 @@ describe('Library Schematic', () => {
expect(workspace.projects.foo.prefix).toEqual('pre');
});

it(`should not add zone.js to test polyfills when no zone.js dependency`, async () => {
const tree = await schematicRunner.runSchematic('library', defaultOptions, workspaceTree);

const workspace = getJsonFileContent(tree, '/angular.json');
expect(workspace.projects.foo.architect.test.options.polyfills).toBeUndefined();
});

it('should handle a pascalCasedName', async () => {
const options = { ...defaultOptions, name: 'pascalCasedName' };
const tree = await schematicRunner.runSchematic('library', options, workspaceTree);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@
"@angular/platform-browser": "<%= latestVersions.Angular %>",
"@angular/router": "<%= latestVersions.Angular %>",
"rxjs": "<%= latestVersions['rxjs'] %>",
"tslib": "<%= latestVersions['tslib'] %>",
"zone.js": "<%= latestVersions['zone.js'] %>"
"tslib": "<%= latestVersions['tslib'] %>"
},
"devDependencies": {
"@angular/cli": "<%= '^' + version %>",
Expand Down
1 change: 0 additions & 1 deletion packages/schematics/angular/workspace/index_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ describe('Workspace Schematic', () => {
const pkg = JSON.parse(tree.readContent('/package.json'));
expect(pkg.dependencies['@angular/core']).toEqual(latestVersions.Angular);
expect(pkg.dependencies['rxjs']).toEqual(latestVersions['rxjs']);
expect(pkg.dependencies['zone.js']).toEqual(latestVersions['zone.js']);
expect(pkg.devDependencies['typescript']).toEqual(latestVersions['typescript']);
});

Expand Down