diff --git a/packages/angular_devkit/build_angular/src/builders/app-shell/index.ts b/packages/angular_devkit/build_angular/src/builders/app-shell/index.ts index 1cdcd213c055..abdaf31f3a98 100644 --- a/packages/angular_devkit/build_angular/src/builders/app-shell/index.ts +++ b/packages/angular_devkit/build_angular/src/builders/app-shell/index.ts @@ -43,7 +43,11 @@ async function _renderUniversal( // Locate zone.js to load in the render worker const root = context.workspaceRoot; - const zonePackage = require.resolve('zone.js', { paths: [root] }); + let zonePackage: string | undefined; + + try { + zonePackage = require.resolve('zone.js', { paths: [root] }); + } catch {} const projectName = context.target && context.target.project; if (!projectName) { diff --git a/packages/angular_devkit/build_angular/src/builders/app-shell/render-worker.ts b/packages/angular_devkit/build_angular/src/builders/app-shell/render-worker.ts index 21a39698b5a0..90af7e01b001 100644 --- a/packages/angular_devkit/build_angular/src/builders/app-shell/render-worker.ts +++ b/packages/angular_devkit/build_angular/src/builders/app-shell/render-worker.ts @@ -17,7 +17,7 @@ import { workerData } from 'node:worker_threads'; * This is passed as workerData when setting up the worker via the `piscina` package. */ const { zonePackage } = workerData as { - zonePackage: string; + zonePackage: string | undefined; }; interface ServerBundleExports { @@ -136,8 +136,9 @@ function isBootstrapFn( * @returns A promise resolving to the render function of the worker. */ async function initialize() { - // Setup Zone.js - await import(zonePackage); + if (zonePackage) { + await import(zonePackage); + } // Return the render function for use return render; diff --git a/packages/angular_devkit/build_angular/src/builders/jest/index.ts b/packages/angular_devkit/build_angular/src/builders/jest/index.ts index c972dfa41a46..00450a59b6d7 100644 --- a/packages/angular_devkit/build_angular/src/builders/jest/index.ts +++ b/packages/angular_devkit/build_angular/src/builders/jest/index.ts @@ -133,7 +133,7 @@ export default createBuilder( // the environment for fake async to work correctly. // Third, we initialize `TestBed`. This is dependent on fake async being set up correctly beforehand. `--setupFilesAfterEnv="/jest-global.mjs"`, - ...(options.polyfills ? [`--setupFilesAfterEnv="/polyfills.mjs"`] : []), + ...(options.polyfills?.length ? [`--setupFilesAfterEnv="/polyfills.mjs"`] : []), `--setupFilesAfterEnv="/init-test-bed.mjs"`, // Don't run any infrastructure files as tests, they are manually loaded where needed. diff --git a/packages/angular_devkit/build_angular/src/builders/jest/init-test-bed.mjs b/packages/angular_devkit/build_angular/src/builders/jest/init-test-bed.mjs index dc511242ee52..2a9913b70363 100644 --- a/packages/angular_devkit/build_angular/src/builders/jest/init-test-bed.mjs +++ b/packages/angular_devkit/build_angular/src/builders/jest/init-test-bed.mjs @@ -9,7 +9,7 @@ // TODO(dgp1130): These imports likely don't resolve in stricter package environments like `pnpm`, since they are resolved relative to // `@angular-devkit/build-angular` rather than the user's workspace. Should look into virtual modules to support those use cases. -import { provideZoneChangeDetection, NgModule } from '@angular/core'; +import { NgModule, provideZoneChangeDetection } from '@angular/core'; import { getTestBed } from '@angular/core/testing'; import { BrowserTestingModule, platformBrowserTesting } from '@angular/platform-browser/testing'; diff --git a/packages/angular_devkit/build_angular/src/builders/prerender/index.ts b/packages/angular_devkit/build_angular/src/builders/prerender/index.ts index b56aa9df4b26..9ebbd4402b94 100644 --- a/packages/angular_devkit/build_angular/src/builders/prerender/index.ts +++ b/packages/angular_devkit/build_angular/src/builders/prerender/index.ts @@ -57,6 +57,11 @@ async function getRoutes( } } + let zonePackage: string | undefined; + try { + zonePackage = require.resolve('zone.js/node', { paths: [workspaceRoot] }); + } catch {} + if (discoverRoutes) { const renderWorker = new Piscina({ filename: require.resolve('./routes-extractor-worker'), @@ -65,7 +70,7 @@ async function getRoutes( indexFile, outputPath, serverBundlePath, - zonePackage: require.resolve('zone.js', { paths: [workspaceRoot] }), + zonePackage, } as RoutesExtractorWorkerData, recordTiming: false, }); @@ -168,7 +173,10 @@ async function _renderUniversal( browserOptions.optimization, ); - const zonePackage = require.resolve('zone.js', { paths: [context.workspaceRoot] }); + let zonePackage: string | undefined; + try { + zonePackage = require.resolve('zone.js/node', { paths: [context.workspaceRoot] }); + } catch {} const { baseOutputPath = '' } = serverResult; const worker = new Piscina({ diff --git a/packages/angular_devkit/build_angular/src/builders/prerender/render-worker.ts b/packages/angular_devkit/build_angular/src/builders/prerender/render-worker.ts index e651b6f84344..da478318ebc7 100644 --- a/packages/angular_devkit/build_angular/src/builders/prerender/render-worker.ts +++ b/packages/angular_devkit/build_angular/src/builders/prerender/render-worker.ts @@ -51,7 +51,7 @@ interface ServerBundleExports { * This is passed as workerData when setting up the worker via the `piscina` package. */ const { zonePackage } = workerData as { - zonePackage: string; + zonePackage: string | undefined; }; /** @@ -163,8 +163,10 @@ function isBootstrapFn( * @returns A promise resolving to the render function of the worker. */ async function initialize() { - // Setup Zone.js - await import(zonePackage); + if (zonePackage) { + // Setup Zone.js + await import(zonePackage); + } // Return the render function for use return render; diff --git a/packages/angular_devkit/build_angular/src/builders/prerender/routes-extractor-worker.ts b/packages/angular_devkit/build_angular/src/builders/prerender/routes-extractor-worker.ts index ef324ba1dea6..0d5220d5f0e9 100644 --- a/packages/angular_devkit/build_angular/src/builders/prerender/routes-extractor-worker.ts +++ b/packages/angular_devkit/build_angular/src/builders/prerender/routes-extractor-worker.ts @@ -15,7 +15,7 @@ import * as path from 'node:path'; import { workerData } from 'node:worker_threads'; export interface RoutesExtractorWorkerData { - zonePackage: string; + zonePackage: string | undefined; indexFile: string; outputPath: string; serverBundlePath: string; @@ -74,8 +74,10 @@ async function extract(): Promise { * @returns A promise resolving to the extract function of the worker. */ async function initialize() { - // Setup Zone.js - await import(zonePackage); + if (zonePackage) { + // Setup Zone.js + await import(zonePackage); + } return extract; } diff --git a/packages/angular_devkit/build_angular/src/builders/server/index.ts b/packages/angular_devkit/build_angular/src/builders/server/index.ts index 3bdab8a55977..5a246178d10c 100644 --- a/packages/angular_devkit/build_angular/src/builders/server/index.ts +++ b/packages/angular_devkit/build_angular/src/builders/server/index.ts @@ -198,7 +198,6 @@ async function initialize( const originalOutputPath = options.outputPath; // Assets are processed directly by the builder except when watching const adjustedOptions = options.watch ? options : { ...options, assets: [] }; - const { config, projectRoot, projectSourceRoot, i18n } = await generateI18nBrowserWebpackConfigFromContext( { @@ -271,23 +270,18 @@ function getPlatformServerExportsConfig(wco: BrowserWebpackConfigOptions): Parti // Add `@angular/platform-server` exports. // This is needed so that DI tokens can be referenced and set at runtime outside of the bundle. - // Only add `@angular/platform-server` exports when it is installed. - // In some cases this builder is used when `@angular/platform-server` is not installed. - // Example: when using `@nguniversal/common/clover` which does not need `@angular/platform-server`. - - return isPackageInstalled(wco.root, '@angular/platform-server') - ? { - module: { - rules: [ - { - loader: require.resolve('./platform-server-exports-loader'), - include: [path.resolve(wco.root, wco.buildOptions.main)], - options: { - angularSSRInstalled: isPackageInstalled(wco.root, '@angular/ssr'), - }, - }, - ], + return { + module: { + rules: [ + { + loader: require.resolve('./platform-server-exports-loader'), + include: [path.resolve(wco.root, wco.buildOptions.main)], + options: { + angularSSRInstalled: isPackageInstalled(wco.root, '@angular/ssr'), + isZoneJsInstalled: isPackageInstalled(wco.root, 'zone.js'), + }, }, - } - : {}; + ], + }, + }; } diff --git a/packages/angular_devkit/build_angular/src/builders/server/platform-server-exports-loader.ts b/packages/angular_devkit/build_angular/src/builders/server/platform-server-exports-loader.ts index 41ec7feacf43..efaa2c87f533 100644 --- a/packages/angular_devkit/build_angular/src/builders/server/platform-server-exports-loader.ts +++ b/packages/angular_devkit/build_angular/src/builders/server/platform-server-exports-loader.ts @@ -12,11 +12,14 @@ * @see https://github.com/webpack/webpack/issues/15936. */ export default function ( - this: import('webpack').LoaderContext<{ angularSSRInstalled: boolean }>, + this: import('webpack').LoaderContext<{ + angularSSRInstalled: boolean; + isZoneJsInstalled: boolean; + }>, content: string, map: Parameters[1], ) { - const { angularSSRInstalled } = this.getOptions(); + const { angularSSRInstalled, isZoneJsInstalled } = this.getOptions(); let source = `${content} @@ -30,6 +33,11 @@ export default function ( `; } + if (isZoneJsInstalled) { + source = `import 'zone.js/node'; + ${source}`; + } + this.callback(null, source, map); return; diff --git a/packages/angular_devkit/build_angular/src/builders/ssr-dev-server/specs/proxy_spec.ts b/packages/angular_devkit/build_angular/src/builders/ssr-dev-server/specs/proxy_spec.ts index a4cafd66ee06..cbde961e59e4 100644 --- a/packages/angular_devkit/build_angular/src/builders/ssr-dev-server/specs/proxy_spec.ts +++ b/packages/angular_devkit/build_angular/src/builders/ssr-dev-server/specs/proxy_spec.ts @@ -32,8 +32,6 @@ describe('Serve SSR Builder', () => { host.writeMultipleFiles({ 'src/main.server.ts': ` - import 'zone.js/node'; - import { CommonEngine } from '@angular/ssr/node'; import * as express from 'express'; import { resolve, join } from 'node:path'; diff --git a/packages/angular_devkit/build_angular/src/builders/ssr-dev-server/specs/ssl_spec.ts b/packages/angular_devkit/build_angular/src/builders/ssr-dev-server/specs/ssl_spec.ts index 3792d87f839c..a67cf0b3c5b5 100644 --- a/packages/angular_devkit/build_angular/src/builders/ssr-dev-server/specs/ssl_spec.ts +++ b/packages/angular_devkit/build_angular/src/builders/ssr-dev-server/specs/ssl_spec.ts @@ -32,8 +32,6 @@ describe('Serve SSR Builder', () => { host.writeMultipleFiles({ 'src/main.server.ts': ` - import 'zone.js/node'; - import { CommonEngine } from '@angular/ssr/node'; import * as express from 'express'; import { resolve, join } from 'node:path'; diff --git a/packages/angular_devkit/build_angular/src/builders/ssr-dev-server/specs/works_spec.ts b/packages/angular_devkit/build_angular/src/builders/ssr-dev-server/specs/works_spec.ts index 8e92c4d666e3..64c56024f089 100644 --- a/packages/angular_devkit/build_angular/src/builders/ssr-dev-server/specs/works_spec.ts +++ b/packages/angular_devkit/build_angular/src/builders/ssr-dev-server/specs/works_spec.ts @@ -31,8 +31,6 @@ describe('Serve SSR Builder', () => { host.writeMultipleFiles({ 'src/main.server.ts': ` - import 'zone.js/node'; - import { CommonEngine } from '@angular/ssr/node'; import * as express from 'express'; import { resolve, join } from 'node:path'; diff --git a/packages/schematics/angular/application/index_spec.ts b/packages/schematics/angular/application/index_spec.ts index 45aa584c7d8e..ba0033f8f119 100644 --- a/packages/schematics/angular/application/index_spec.ts +++ b/packages/schematics/angular/application/index_spec.ts @@ -389,7 +389,7 @@ describe('Application Schematic', () => { expect(buildOpt.index).toBeUndefined(); expect(buildOpt.browser).toEqual('src/main.ts'); expect(buildOpt.assets).toEqual([{ 'glob': '**/*', 'input': 'public' }]); - expect(buildOpt.polyfills).toEqual(undefined); + expect(buildOpt.polyfills).toBeUndefined(); expect(buildOpt.tsConfig).toEqual('tsconfig.app.json'); const testOpt = prj.architect.test.options; @@ -478,7 +478,7 @@ describe('Application Schematic', () => { expect(project.root).toEqual('foo'); const buildOpt = project.architect.build.options; expect(buildOpt.browser).toEqual('foo/src/main.ts'); - expect(buildOpt.polyfills).toEqual(undefined); + expect(buildOpt.polyfills).toBeUndefined(); expect(buildOpt.tsConfig).toEqual('foo/tsconfig.app.json'); expect(buildOpt.assets).toEqual([{ 'glob': '**/*', 'input': 'foo/public' }]); diff --git a/packages/schematics/angular/ssr/files/server-builder/server.ts.template b/packages/schematics/angular/ssr/files/server-builder/server.ts.template index 7567fa65a81d..7327c26532ea 100644 --- a/packages/schematics/angular/ssr/files/server-builder/server.ts.template +++ b/packages/schematics/angular/ssr/files/server-builder/server.ts.template @@ -1,5 +1,3 @@ -import 'zone.js/node'; - import { APP_BASE_HREF } from '@angular/common'; import { CommonEngine } from '@angular/ssr/node'; import express from 'express'; diff --git a/tests/legacy-cli/e2e/initialize/500-create-project.ts b/tests/legacy-cli/e2e/initialize/500-create-project.ts index dfc48be927a7..837f54efbcde 100644 --- a/tests/legacy-cli/e2e/initialize/500-create-project.ts +++ b/tests/legacy-cli/e2e/initialize/500-create-project.ts @@ -20,7 +20,7 @@ export default async function () { // Ensure local test registry is used when outside a project await setNPMConfigRegistry(true); - await ng('new', 'test-project', '--skip-install', '--no-zoneless'); + await ng('new', 'test-project', '--skip-install'); await expectFileToExist(join(process.cwd(), 'test-project')); process.chdir('./test-project'); diff --git a/tests/legacy-cli/e2e/tests/basic/scripts-array.ts b/tests/legacy-cli/e2e/tests/basic/scripts-array.ts index 09bca1c04fb9..0721b120da2b 100644 --- a/tests/legacy-cli/e2e/tests/basic/scripts-array.ts +++ b/tests/legacy-cli/e2e/tests/basic/scripts-array.ts @@ -58,7 +58,6 @@ export default async function () { await expectFileToMatch( 'dist/test-project/browser/index.html', [ - '', '', '', '', @@ -69,7 +68,6 @@ export default async function () { 'dist/test-project/browser/index.html', [ '', - '', '', '', '', diff --git a/tests/legacy-cli/e2e/tests/build/app-shell/app-shell-ngmodule.ts b/tests/legacy-cli/e2e/tests/build/app-shell/app-shell-ngmodule.ts index 1175f083dcfe..b1b6cfab499d 100644 --- a/tests/legacy-cli/e2e/tests/build/app-shell/app-shell-ngmodule.ts +++ b/tests/legacy-cli/e2e/tests/build/app-shell/app-shell-ngmodule.ts @@ -7,15 +7,7 @@ import { updateJsonFile } from '../../../utils/project'; const snapshots = require('../../../ng-snapshot/package.json'); export default async function () { - await ng( - 'generate', - 'app', - 'test-project-two', - '--routing', - '--no-standalone', - '--skip-install', - '--no-zoneless', - ); + await ng('generate', 'app', 'test-project-two', '--routing', '--no-standalone', '--skip-install'); await ng('generate', 'app-shell', '--project', 'test-project-two'); const isSnapshotBuild = getGlobalVariable('argv')['ng-snapshots']; diff --git a/tests/legacy-cli/e2e/tests/build/jit-ngmodule.ts b/tests/legacy-cli/e2e/tests/build/jit-ngmodule.ts index ac784e5c341d..910f8993a16d 100644 --- a/tests/legacy-cli/e2e/tests/build/jit-ngmodule.ts +++ b/tests/legacy-cli/e2e/tests/build/jit-ngmodule.ts @@ -3,14 +3,7 @@ import { ng } from '../../utils/process'; import { updateJsonFile, useCIChrome, useCIDefaults } from '../../utils/project'; export default async function () { - await ng( - 'generate', - 'app', - 'test-project-two', - '--no-standalone', - '--skip-install', - '--no-zoneless', - ); + await ng('generate', 'app', 'test-project-two', '--no-standalone', '--skip-install'); await ng('generate', 'private-e2e', '--related-app-name=test-project-two'); // Setup testing to use CI Chrome. diff --git a/tests/legacy-cli/e2e/tests/build/prerender/http-requests-assets.ts b/tests/legacy-cli/e2e/tests/build/prerender/http-requests-assets.ts index bb57edcc88a0..dc238941dc81 100644 --- a/tests/legacy-cli/e2e/tests/build/prerender/http-requests-assets.ts +++ b/tests/legacy-cli/e2e/tests/build/prerender/http-requests-assets.ts @@ -1,7 +1,7 @@ import { ng } from '../../../utils/process'; import { getGlobalVariable } from '../../../utils/env'; -import { expectFileToMatch, rimraf, writeMultipleFiles } from '../../../utils/fs'; -import { installWorkspacePackages } from '../../../utils/packages'; +import { expectFileToMatch, writeMultipleFiles } from '../../../utils/fs'; +import { installWorkspacePackages, uninstallPackage } from '../../../utils/packages'; import { useSha } from '../../../utils/project'; export default async function () { @@ -11,16 +11,15 @@ export default async function () { return; } - // Forcibly remove in case another test doesn't clean itself up. - await rimraf('node_modules/@angular/ssr'); - await ng('add', '@angular/ssr', '--skip-confirmation'); + await uninstallPackage('@angular/ssr'); + await ng('add', '@angular/ssr', '--skip-confirmation', '--skip-install'); await useSha(); await installWorkspacePackages(); await writeMultipleFiles({ // Add http client and route 'src/app/app.config.ts': ` - import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; + import { ApplicationConfig } from '@angular/core'; import { provideRouter } from '@angular/router'; import {Home} from './home/home'; @@ -35,7 +34,6 @@ export default async function () { }]), provideClientHydration(), provideHttpClient(withFetch()), - provideZoneChangeDetection({ eventCoalescing: true }), ], }; `, @@ -46,7 +44,7 @@ export default async function () { // Update component to do an HTTP call to asset. 'src/app/app.ts': ` - import { Component, inject } from '@angular/core'; + import { ChangeDetectorRef, Component, inject } from '@angular/core'; import { CommonModule } from '@angular/common'; import { RouterOutlet } from '@angular/router'; import { HttpClient } from '@angular/common/http'; @@ -64,15 +62,18 @@ export default async function () { export class App { data: any; dataWithSpace: any; + private readonly cdr: ChangeDetectorRef = inject(ChangeDetectorRef); constructor() { const http = inject(HttpClient); http.get('/media.json').subscribe((d) => { this.data = d; + this.cdr.markForCheck(); }); http.get('/media%20with-space.json').subscribe((d) => { this.dataWithSpace = d; + this.cdr.markForCheck(); }); } } diff --git a/tests/legacy-cli/e2e/tests/build/server-rendering/express-engine-ngmodule.ts b/tests/legacy-cli/e2e/tests/build/server-rendering/express-engine-ngmodule.ts index f8b45482535e..f05d2182bbd2 100644 --- a/tests/legacy-cli/e2e/tests/build/server-rendering/express-engine-ngmodule.ts +++ b/tests/legacy-cli/e2e/tests/build/server-rendering/express-engine-ngmodule.ts @@ -15,14 +15,7 @@ export default async function () { // forcibly remove in case another test doesn't clean itself up await rimraf('node_modules/@angular/ssr'); - await ng( - 'generate', - 'app', - 'test-project-two', - '--no-standalone', - '--skip-install', - '--no-zoneless', - ); + await ng('generate', 'app', 'test-project-two', '--no-standalone', '--skip-install'); await ng('generate', 'private-e2e', '--related-app-name=test-project-two'); // Setup testing to use CI Chrome. diff --git a/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-static-http-calls.ts b/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-static-http-calls.ts index d1da216605bc..9b37d3218b3c 100644 --- a/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-static-http-calls.ts +++ b/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-static-http-calls.ts @@ -22,7 +22,7 @@ export default async function () { 'public/media.json': JSON.stringify({ dataFromAssets: true }), // Update component to do an HTTP call to asset and API. 'src/app/app.ts': ` - import { Component, inject } from '@angular/core'; + import { ChangeDetectorRef, Component, inject } from '@angular/core'; import { JsonPipe } from '@angular/common'; import { RouterOutlet } from '@angular/router'; import { HttpClient } from '@angular/common/http'; @@ -40,23 +40,26 @@ export default async function () { export class App { assetsData: any; apiData: any; + private readonly cdr: ChangeDetectorRef = inject(ChangeDetectorRef); constructor() { const http = inject(HttpClient); http.get('/media.json').toPromise().then((d) => { this.assetsData = d; + this.cdr.markForCheck(); }); http.get('/api').toPromise().then((d) => { this.apiData = d; + this.cdr.markForCheck(); }); } } `, // Add http client and route 'src/app/app.config.ts': ` - import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; + import { ApplicationConfig } from '@angular/core'; import { provideRouter } from '@angular/router'; import { Home } from './home/home'; @@ -71,7 +74,6 @@ export default async function () { }]), provideClientHydration(), provideHttpClient(withFetch()), - provideZoneChangeDetection({ eventCoalescing: true }), ], }; `, diff --git a/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-static-i18n_APP_BASE_HREF.ts b/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-static-i18n_APP_BASE_HREF.ts index 51f5e3990bae..245375101946 100644 --- a/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-static-i18n_APP_BASE_HREF.ts +++ b/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-static-i18n_APP_BASE_HREF.ts @@ -1,7 +1,7 @@ import { join } from 'node:path'; import { existsSync } from 'node:fs'; import assert from 'node:assert'; -import { expectFileNotToExist, expectFileToMatch, writeFile } from '../../../utils/fs'; +import { expectFileToMatch, writeFile } from '../../../utils/fs'; import { ng, noSilentNg, silentNg } from '../../../utils/process'; import { installWorkspacePackages, uninstallPackage } from '../../../utils/packages'; import { useSha } from '../../../utils/project'; @@ -66,7 +66,7 @@ export default async function () { await writeFile( 'src/app/app.config.ts', ` - import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; + import { ApplicationConfig } from '@angular/core'; import { provideRouter } from '@angular/router'; import { routes } from './app.routes'; @@ -75,7 +75,6 @@ export default async function () { export const appConfig: ApplicationConfig = { providers: [ - provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes), provideClientHydration(), { diff --git a/tests/legacy-cli/e2e/tests/build/sourcemap.ts b/tests/legacy-cli/e2e/tests/build/sourcemap.ts index cd3d6c32ab0c..2e153e637f30 100644 --- a/tests/legacy-cli/e2e/tests/build/sourcemap.ts +++ b/tests/legacy-cli/e2e/tests/build/sourcemap.ts @@ -12,10 +12,10 @@ export default async function () { await ng('build', '--output-hashing=bundles', '--source-map', '--configuration=development'); await ng('build', '--output-hashing=none', '--source-map'); - await testForSourceMaps(useWebpackBuilder ? 3 : 2); + await testForSourceMaps(useWebpackBuilder ? 2 : 1); await ng('build', '--output-hashing=none', '--source-map', '--configuration=development'); - await testForSourceMaps(useWebpackBuilder ? 4 : 2); + await testForSourceMaps(useWebpackBuilder ? 3 : 1); } async function testForSourceMaps(expectedNumberOfFiles: number): Promise { diff --git a/tests/legacy-cli/e2e/tests/build/wasm-esm.ts b/tests/legacy-cli/e2e/tests/build/wasm-esm.ts index 43d3708c2c36..8a9735172f1b 100644 --- a/tests/legacy-cli/e2e/tests/build/wasm-esm.ts +++ b/tests/legacy-cli/e2e/tests/build/wasm-esm.ts @@ -62,16 +62,6 @@ export default async function () { // of a JIT production build. json.projects['test-project'].architect.build.options.polyfills = []; }); - await replaceInFile( - 'src/app/app.config.ts', - 'provideZoneChangeDetection', - 'provideZonelessChangeDetection', - ); - await replaceInFile( - 'src/app/app.config.ts', - 'provideZoneChangeDetection({ eventCoalescing: true })', - 'provideZonelessChangeDetection()', - ); await ng('build'); @@ -94,7 +84,7 @@ export default async function () { await ng('e2e'); // Setup prerendering and build to test Node.js functionality - await ng('add', '@angular/ssr', '--skip-confirmation'); + await ng('add', '@angular/ssr', '--skip-confirmation', '--skip-install'); await useSha(); await installWorkspacePackages(); diff --git a/tests/legacy-cli/e2e/tests/commands/builder-project-by-cwd.ts b/tests/legacy-cli/e2e/tests/commands/builder-project-by-cwd.ts index 519274db5458..6033f4542391 100644 --- a/tests/legacy-cli/e2e/tests/commands/builder-project-by-cwd.ts +++ b/tests/legacy-cli/e2e/tests/commands/builder-project-by-cwd.ts @@ -3,8 +3,8 @@ import { expectFileToExist } from '../../utils/fs'; import { ng } from '../../utils/process'; export default async function () { - await ng('generate', 'app', 'second-app', '--skip-install', '--no-zoneless'); - await ng('generate', 'app', 'third-app', '--skip-install', '--no-zoneless'); + await ng('generate', 'app', 'second-app', '--skip-install'); + await ng('generate', 'app', 'third-app', '--skip-install'); const startCwd = process.cwd(); try { diff --git a/tests/legacy-cli/e2e/tests/commands/project-cannot-be-determined-by-cwd.ts b/tests/legacy-cli/e2e/tests/commands/project-cannot-be-determined-by-cwd.ts index 4d78ade44d85..b4b572bfa3ee 100644 --- a/tests/legacy-cli/e2e/tests/commands/project-cannot-be-determined-by-cwd.ts +++ b/tests/legacy-cli/e2e/tests/commands/project-cannot-be-determined-by-cwd.ts @@ -14,8 +14,8 @@ export default async function () { delete workspaceJson.projects['test-project']; }); - await ng('generate', 'app', 'second-app', '--skip-install', '--no-zoneless'); - await ng('generate', 'app', 'third-app', '--skip-install', '--no-zoneless'); + await ng('generate', 'app', 'second-app', '--skip-install'); + await ng('generate', 'app', 'third-app', '--skip-install'); const startCwd = process.cwd(); diff --git a/tests/legacy-cli/e2e/tests/commands/serve/ssr-http-requests-assets.ts b/tests/legacy-cli/e2e/tests/commands/serve/ssr-http-requests-assets.ts index c7cfc16a42fa..6ebb9c20022f 100644 --- a/tests/legacy-cli/e2e/tests/commands/serve/ssr-http-requests-assets.ts +++ b/tests/legacy-cli/e2e/tests/commands/serve/ssr-http-requests-assets.ts @@ -1,30 +1,20 @@ import assert from 'node:assert'; import { killAllProcesses, ng } from '../../../utils/process'; -import { rimraf, writeMultipleFiles } from '../../../utils/fs'; -import { installWorkspacePackages } from '../../../utils/packages'; +import { writeMultipleFiles } from '../../../utils/fs'; +import { installWorkspacePackages, uninstallPackage } from '../../../utils/packages'; import { ngServe, useSha } from '../../../utils/project'; -import { getGlobalVariable } from '../../../utils/env'; export default async function () { - const useWebpackBuilder = !getGlobalVariable('argv')['esbuild']; - - // Forcibly remove in case another test doesn't clean itself up. - await rimraf('node_modules/@angular/ssr'); - if (useWebpackBuilder) { - // `--server-routing` not supported in `browser` builder. - await ng('add', '@angular/ssr', '--skip-confirmation', '--skip-install'); - } else { - await ng('add', '@angular/ssr', '--skip-confirmation', '--skip-install'); - } - + await uninstallPackage('@angular/ssr'); + await ng('add', '@angular/ssr', '--skip-confirmation', '--skip-install'); await useSha(); await installWorkspacePackages(); await writeMultipleFiles({ // Add http client and route 'src/app/app.config.ts': ` - import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; + import { ApplicationConfig } from '@angular/core'; import { provideRouter } from '@angular/router'; import { Home } from './home/home'; @@ -39,7 +29,6 @@ export default async function () { }]), provideClientHydration(), provideHttpClient(withFetch()), - provideZoneChangeDetection({ eventCoalescing: true }), ], }; `, @@ -47,7 +36,7 @@ export default async function () { 'public/media.json': JSON.stringify({ dataFromAssets: true }), // Update component to do an HTTP call to asset. 'src/app/app.ts': ` - import { Component, inject } from '@angular/core'; + import { ChangeDetectorRef, Component, inject } from '@angular/core'; import { CommonModule } from '@angular/common'; import { RouterOutlet } from '@angular/router'; import { HttpClient } from '@angular/common/http'; @@ -63,10 +52,13 @@ export default async function () { }) export class App { data: any; + private readonly cdr: ChangeDetectorRef = inject(ChangeDetectorRef); + constructor() { const http = inject(HttpClient); http.get('/media.json').toPromise().then((d) => { this.data = d; + this.cdr.markForCheck(); }); } } diff --git a/tests/legacy-cli/e2e/tests/generate/application/application-basic.ts b/tests/legacy-cli/e2e/tests/generate/application/application-basic.ts deleted file mode 100644 index bff84ac1e37b..000000000000 --- a/tests/legacy-cli/e2e/tests/generate/application/application-basic.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { expectFileToMatch } from '../../../utils/fs'; -import { ng } from '../../../utils/process'; -import { useCIChrome } from '../../../utils/project'; - -export default function () { - return ng('generate', 'application', 'app2', '--no-zoneless') - .then(() => expectFileToMatch('angular.json', /\"app2\":/)) - .then(() => useCIChrome('app2', 'projects/app2')) - .then(() => ng('test', 'app2', '--watch=false')); -} diff --git a/tests/legacy-cli/e2e/tests/generate/application/application-no-zoneless-ng-module.ts b/tests/legacy-cli/e2e/tests/generate/application/application-no-zoneless-ng-module.ts new file mode 100644 index 000000000000..b475ed03b3a7 --- /dev/null +++ b/tests/legacy-cli/e2e/tests/generate/application/application-no-zoneless-ng-module.ts @@ -0,0 +1,16 @@ +import { installWorkspacePackages, uninstallPackage } from '../../../utils/packages'; +import { ng } from '../../../utils/process'; +import { useCIChrome, useSha } from '../../../utils/project'; + +export default async function () { + try { + await ng('generate', 'app', 'ngmodules', '--no-standalone', '--skip-install', '--no-zoneless'); + await useSha(); + await installWorkspacePackages(); + await useCIChrome('ngmodules', 'projects/ngmodules'); + await ng('test', 'ngmodules', '--watch=false'); + await ng('build', 'ngmodules'); + } finally { + await uninstallPackage('zone.js'); + } +} diff --git a/tests/legacy-cli/e2e/tests/generate/application/application-no-zoneless-standalone.ts b/tests/legacy-cli/e2e/tests/generate/application/application-no-zoneless-standalone.ts new file mode 100644 index 000000000000..065957f9fe11 --- /dev/null +++ b/tests/legacy-cli/e2e/tests/generate/application/application-no-zoneless-standalone.ts @@ -0,0 +1,16 @@ +import { installWorkspacePackages, uninstallPackage } from '../../../utils/packages'; +import { ng } from '../../../utils/process'; +import { useCIChrome, useSha } from '../../../utils/project'; + +export default async function () { + try { + await ng('generate', 'app', 'standalone', '--standalone', '--skip-install', '--no-zoneless'); + await useSha(); + await installWorkspacePackages(); + await useCIChrome('standalone', 'projects/standalone'); + await ng('test', 'standalone', '--watch=false'); + await ng('build', 'standalone'); + } finally { + await uninstallPackage('zone.js'); + } +} diff --git a/tests/legacy-cli/e2e/tests/generate/application/application-zoneless.ts b/tests/legacy-cli/e2e/tests/generate/application/application-zoneless-ng-module.ts similarity index 53% rename from tests/legacy-cli/e2e/tests/generate/application/application-zoneless.ts rename to tests/legacy-cli/e2e/tests/generate/application/application-zoneless-ng-module.ts index 721ce8fe3599..5ad207a2fa76 100644 --- a/tests/legacy-cli/e2e/tests/generate/application/application-zoneless.ts +++ b/tests/legacy-cli/e2e/tests/generate/application/application-zoneless-ng-module.ts @@ -1,13 +1,11 @@ +import { installWorkspacePackages } from '../../../utils/packages'; import { ng } from '../../../utils/process'; -import { useCIChrome } from '../../../utils/project'; +import { useCIChrome, useSha } from '../../../utils/project'; export default async function () { - await ng('generate', 'app', 'standalone', '--standalone'); - await useCIChrome('standalone', 'projects/standalone'); - await ng('test', 'standalone', '--watch=false'); - await ng('build', 'standalone'); - await ng('generate', 'app', 'ngmodules', '--no-standalone', '--skip-install'); + await useSha(); + await installWorkspacePackages(); await useCIChrome('ngmodules', 'projects/ngmodules'); await ng('test', 'ngmodules', '--watch=false'); await ng('build', 'ngmodules'); diff --git a/tests/legacy-cli/e2e/tests/generate/application/application-zoneless-standalone.ts b/tests/legacy-cli/e2e/tests/generate/application/application-zoneless-standalone.ts new file mode 100644 index 000000000000..b203ed4a28d0 --- /dev/null +++ b/tests/legacy-cli/e2e/tests/generate/application/application-zoneless-standalone.ts @@ -0,0 +1,12 @@ +import { installWorkspacePackages } from '../../../utils/packages'; +import { ng } from '../../../utils/process'; +import { useCIChrome, useSha } from '../../../utils/project'; + +export default async function () { + await ng('generate', 'app', 'standalone', '--standalone', '--skip-install'); + await useSha(); + await installWorkspacePackages(); + await useCIChrome('standalone', 'projects/standalone'); + await ng('test', 'standalone', '--watch=false'); + await ng('build', 'standalone'); +} diff --git a/tests/legacy-cli/e2e/tests/jest/no-zoneless.ts b/tests/legacy-cli/e2e/tests/jest/no-zoneless.ts new file mode 100644 index 000000000000..411378839b7f --- /dev/null +++ b/tests/legacy-cli/e2e/tests/jest/no-zoneless.ts @@ -0,0 +1,21 @@ +import { applyJestBuilder } from '../../utils/jest'; +import { installPackage, uninstallPackage } from '../../utils/packages'; +import { ng } from '../../utils/process'; + +export default async function (): Promise { + await applyJestBuilder({ + tsConfig: 'tsconfig.spec.json', + polyfills: ['zone.js', 'zone.js/testing'], + }); + + try { + await installPackage('zone.js'); + const { stderr } = await ng('test'); + + if (!stderr.includes('Jest builder is currently EXPERIMENTAL')) { + throw new Error(`No experimental notice in stderr.\nSTDERR:\n\n${stderr}`); + } + } finally { + await uninstallPackage('zone.js'); + } +} diff --git a/tests/legacy-cli/e2e/tests/misc/multiple-targets.ts b/tests/legacy-cli/e2e/tests/misc/multiple-targets.ts index 512ca748db50..a99a37d54b06 100644 --- a/tests/legacy-cli/e2e/tests/misc/multiple-targets.ts +++ b/tests/legacy-cli/e2e/tests/misc/multiple-targets.ts @@ -2,7 +2,7 @@ import { expectFileToExist } from '../../utils/fs'; import { ng } from '../../utils/process'; export default async function () { - await ng('generate', 'app', 'secondary-app', '--no-zoneless'); + await ng('generate', 'app', 'secondary-app'); await ng('build', 'secondary-app', '--configuration=development'); await expectFileToExist('dist/secondary-app/browser/index.html'); await expectFileToExist('dist/secondary-app/browser/main.js'); diff --git a/tests/legacy-cli/e2e/tests/vite/ssr-error-stack.ts b/tests/legacy-cli/e2e/tests/vite/ssr-error-stack.ts index a0d7c87bed13..8fce78b9e7ad 100644 --- a/tests/legacy-cli/e2e/tests/vite/ssr-error-stack.ts +++ b/tests/legacy-cli/e2e/tests/vite/ssr-error-stack.ts @@ -1,22 +1,13 @@ import { doesNotMatch, match } from 'node:assert'; import { ng } from '../../utils/process'; -import { appendToFile, rimraf } from '../../utils/fs'; +import { appendToFile } from '../../utils/fs'; import { ngServe, useSha } from '../../utils/project'; -import { installWorkspacePackages } from '../../utils/packages'; -import { getGlobalVariable } from '../../utils/env'; +import { installWorkspacePackages, uninstallPackage } from '../../utils/packages'; export default async function () { - const useWebpackBuilder = !getGlobalVariable('argv')['esbuild']; - // Forcibly remove in case another test doesn't clean itself up. - await rimraf('node_modules/@angular/ssr'); - if (useWebpackBuilder) { - // `--server-routing` not supported in `browser` builder. - await ng('add', '@angular/ssr', '--skip-confirmation', '--skip-install'); - } else { - await ng('add', '@angular/ssr', '--skip-confirmation', '--skip-install'); - } - + await uninstallPackage('@angular/ssr'); + await ng('add', '@angular/ssr', '--skip-confirmation', '--skip-install'); await useSha(); await installWorkspacePackages(); diff --git a/tests/legacy-cli/e2e/utils/jest.ts b/tests/legacy-cli/e2e/utils/jest.ts index 904cc6f903d6..ed7fe7c04aa1 100644 --- a/tests/legacy-cli/e2e/utils/jest.ts +++ b/tests/legacy-cli/e2e/utils/jest.ts @@ -4,8 +4,8 @@ import { updateJsonFile } from './project'; /** Updates the `test` builder in the current workspace to use Jest with the given options. */ export async function applyJestBuilder( options: {} = { + polyfills: [], tsConfig: 'tsconfig.spec.json', - polyfills: ['zone.js', 'zone.js/testing'], }, ): Promise { await silentNpm('install', 'jest@29.5.0', 'jest-environment-jsdom@29.5.0', '--save-dev');