Skip to content

Commit c5bad57

Browse files
committed
feat(@schematics/angular): add option to setup new workspace or application as zoneless mode
Introduces option `--experimental-zoneless` to setup workspace or application as zoneless mode.
1 parent c0b76e3 commit c5bad57

File tree

9 files changed

+84
-13
lines changed

9 files changed

+84
-13
lines changed
Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
1-
<% if(!!viewEncapsulation) { %>import { ViewEncapsulation } from '@angular/core';
2-
<% }%>import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
3-
1+
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
42
import { AppModule } from './app/app.module';
53

6-
platformBrowserDynamic().bootstrapModule(AppModule, {
7-
ngZoneEventCoalescing: true<% if(!!viewEncapsulation) { %>,
8-
defaultEncapsulation: ViewEncapsulation.<%= viewEncapsulation %><% } %>
9-
})
4+
const bootstrapOptions = {
5+
<% if(!experimentalZoneless) { %>ngZoneEventCoalescing: true<% } else { %>
6+
const { provideExperimentalZonelessChangeDetection } = await import('@angular/core');
7+
providers: [provideExperimentalZonelessChangeDetection()];
8+
<% } %>
9+
<% if(!!viewEncapsulation) { %>
10+
const { ViewEncapsulation } = await import('@angular/core');
11+
defaultEncapsulation: ViewEncapsulation.<%= viewEncapsulation %>;
12+
<% } %>
13+
};
14+
15+
platformBrowserDynamic().bootstrapModule(AppModule, bootstrapOptions)
1016
.catch(err => console.error(err));
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';<% if (routing) { %>
1+
import { ApplicationConfig, <% if(!experimentalZoneless) { %>provideZoneChangeDetection<% } else { %>provideExperimentalZonelessChangeDetection<% } %> } from '@angular/core';<% if (routing) { %>
22
import { provideRouter } from '@angular/router';
33

44
import { routes } from './app.routes';<% } %>
55

66
export const appConfig: ApplicationConfig = {
7-
providers: [provideZoneChangeDetection({ eventCoalescing: true })<% if (routing) { %>, provideRouter(routes)<% } %>]
7+
providers: [
8+
<% if(experimentalZoneless) { %>provideExperimentalZonelessChangeDetection()<% } else { %>provideZoneChangeDetection({ eventCoalescing: true })<% } %><% if (routing) {%>, provideRouter(routes)<% } %>
9+
]
810
};

packages/schematics/angular/application/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ function addAppToWorkspaceFile(
239239
outputPath: `dist/${folderName}`,
240240
index: `${sourceRoot}/index.html`,
241241
browser: `${sourceRoot}/main.ts`,
242-
polyfills: ['zone.js'],
242+
polyfills: options.experimentalZoneless ? [] : ['zone.js'],
243243
tsConfig: `${projectRoot}tsconfig.app.json`,
244244
inlineStyleLanguage,
245245
assets: [{ 'glob': '**/*', 'input': `${projectRoot}public` }],
@@ -279,7 +279,7 @@ function addAppToWorkspaceFile(
279279
: {
280280
builder: Builders.Karma,
281281
options: {
282-
polyfills: ['zone.js', 'zone.js/testing'],
282+
polyfills: options.experimentalZoneless ? [] : ['zone.js', 'zone.js/testing'],
283283
tsConfig: `${projectRoot}tsconfig.spec.json`,
284284
inlineStyleLanguage,
285285
assets: [{ 'glob': '**/*', 'input': `${projectRoot}public` }],

packages/schematics/angular/application/index_spec.ts

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -619,7 +619,7 @@ describe('Application Schematic', () => {
619619
const path = '/projects/foo/src/main.ts';
620620
const content = tree.readContent(path);
621621
expect(content).toContain('defaultEncapsulation: ViewEncapsulation.ShadowDom');
622-
expect(content).toContain(`import { ViewEncapsulation } from '@angular/core'`);
622+
expect(content).toContain(`const { ViewEncapsulation } = await import('@angular/core');`);
623623
});
624624

625625
it('should handle the routing flag', async () => {
@@ -698,5 +698,48 @@ describe('Application Schematic', () => {
698698
}),
699699
);
700700
});
701+
702+
it('should add provideExperimentalZonelessChangeDetection() when experimentalZoneless is true', async () => {
703+
const tree = await schematicRunner.runSchematic(
704+
'application',
705+
{
706+
...defaultOptions,
707+
experimentalZoneless: true,
708+
},
709+
workspaceTree,
710+
);
711+
const path = '/projects/foo/src/app/app.config.ts';
712+
console.log('tree => ', tree);
713+
const fileContent = tree.readContent(path);
714+
expect(fileContent).toContain('provideExperimentalZonelessChangeDetection()');
715+
});
716+
717+
it('should not add provideExperimentalZonelessChangeDetection() when experimentalZoneless is false', async () => {
718+
const tree = await schematicRunner.runSchematic(
719+
'application',
720+
{
721+
...defaultOptions,
722+
experimentalZoneless: false,
723+
},
724+
workspaceTree,
725+
);
726+
const path = '/projects/foo/src/app/app.config.ts';
727+
const fileContent = tree.readContent(path);
728+
expect(fileContent).not.toContain('provideExperimentalZonelessChangeDetection()');
729+
});
730+
731+
it('should not add provideZoneChangeDetection when experimentalZoneless is true', async () => {
732+
const tree = await schematicRunner.runSchematic(
733+
'application',
734+
{
735+
...defaultOptions,
736+
experimentalZoneless: true,
737+
},
738+
workspaceTree,
739+
);
740+
const path = '/projects/foo/src/app/app.config.ts';
741+
const fileContent = tree.readContent(path);
742+
expect(fileContent).not.toContain('provideZoneChangeDetection');
743+
});
701744
});
702745
});

packages/schematics/angular/application/schema.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,11 @@
117117
"type": "boolean",
118118
"default": false,
119119
"x-user-analytics": "ep.ng_ssr"
120+
},
121+
"experimentalZoneless": {
122+
"description": "Create an application that does not utilize zone.js.",
123+
"type": "boolean",
124+
"default": false
120125
}
121126
},
122127
"required": ["name"]

packages/schematics/angular/ng-new/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export default function (options: NgNewOptions): Rule {
5757
minimal: options.minimal,
5858
standalone: options.standalone,
5959
ssr: options.ssr,
60+
experimentalZoneless: options.experimentalZoneless,
6061
};
6162

6263
return chain([

packages/schematics/angular/ng-new/schema.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,11 @@
138138
"description": "Creates an application with Server-Side Rendering (SSR) and Static Site Generation (SSG/Prerendering) enabled.",
139139
"type": "boolean",
140140
"x-user-analytics": "ep.ng_ssr"
141+
},
142+
"experimentalZoneless": {
143+
"description": "Create an application that does not utilize zone.js.",
144+
"type": "boolean",
145+
"default": false
141146
}
142147
},
143148
"required": ["name", "version"]

packages/schematics/angular/service-worker/index_spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ describe('Service Worker Schematic', () => {
131131
provideServiceWorker('ngsw-worker.js', {
132132
enabled: !isDevMode(),
133133
registrationStrategy: 'registerWhenStable:30000'
134-
})
134+
})
135135
`);
136136
});
137137

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { ng } from '../../../utils/process';
2+
import { useCIChrome } from '../../../utils/project';
3+
4+
export default async function () {
5+
await ng('generate', 'application', 'app2', '--experimental-zoneless');
6+
await useCIChrome('app2', 'projects/app2');
7+
await ng('test', 'app2', '--watch=false');
8+
await ng('build', 'app2');
9+
}

0 commit comments

Comments
 (0)