From 00753c71c7a60af9b7d2f990932d9c8bfea60f42 Mon Sep 17 00:00:00 2001 From: Alan Agius <17563226+alan-agius4@users.noreply.github.com> Date: Thu, 13 Nov 2025 09:06:43 +0000 Subject: [PATCH 1/2] refactor: address issue with bazel resolution of peer dependencies Key changes include: - **Removal of PNPM Hook:** The `.pnpmfile.cjs` file, which previously converted peer dependencies into direct dependencies, has been deleted. This workaround is no longer necessary with the adoption of dynamic imports. The `MODULE.bazel` file has been updated to remove references to it. - **Dynamic Imports** - In the `build_webpack` package, static `import` statements for `webpack` and `webpack-dev-server` are replaced with dynamic `import()` calls. This defers their loading until they are actively required during the process. - In the `ssr-dev-server`, `browser-sync` is now loaded using `createRequire` relative to the workspace root, improving resolution reliability. - **Build Configuration:** Associated `BUILD.bazel` files have been updated to remove dependency declarations that are now obsolete due to the new lazy-loading approach. --- .pnpmfile.cjs | 42 ------------ MODULE.bazel | 1 - modules/testing/builder/BUILD.bazel | 1 + modules/testing/builder/package.json | 1 + .../angular_devkit/build_angular/BUILD.bazel | 4 -- .../src/builders/ssr-dev-server/index.ts | 3 +- .../src/builders/webpack-dev-server/index.ts | 21 +++--- .../src/builders/webpack/index.ts | 4 +- pnpm-lock.yaml | 67 ++++++++++--------- 9 files changed, 53 insertions(+), 91 deletions(-) delete mode 100644 .pnpmfile.cjs diff --git a/.pnpmfile.cjs b/.pnpmfile.cjs deleted file mode 100644 index 111350ae8f9a..000000000000 --- a/.pnpmfile.cjs +++ /dev/null @@ -1,42 +0,0 @@ -const localPackages = new Set([ - '@angular-devkit/build-angular', - '@angular-devkit/build-webpack', - '@ngtools/webpack', -]); - -const peerDependenciesToTransform = ['webpack', 'webpack-dev-server', 'browser-sync']; - -function readPackage(pkg, context) { - // TODO(devversion): This allows us to make the peer dependencies of (e.g. webpack) a production dependency. - // because `rules_js` doesn't otherwise include the dependency in the `npm_package_store`. - // See: https://github.com/aspect-build/rules_js/issues/2226 - if (!pkg.peerDependencies || !localPackages.has(pkg.name)) { - return pkg; - } - - for (const key of peerDependenciesToTransform) { - // Any package that has a peerDependency on these deps, should instead treat the peerDependency as a - // regular dependency. - if (!pkg.peerDependencies[key]) { - continue; - } - - if (!pkg.devDependencies?.[key]) { - throw new Error( - `${key} is listed as a peerDependency in ${pkg.name}, but it is not listed in devDependencies. This is required.`, - ); - } - - pkg.dependencies ??= {}; - pkg.dependencies[key] = pkg.devDependencies[key]; - pkg.devDependencies[key] = undefined; - } - - return pkg; -} - -module.exports = { - hooks: { - readPackage, - }, -}; diff --git a/MODULE.bazel b/MODULE.bazel index c6d0c1005ef7..4e34d82ca83f 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -115,7 +115,6 @@ npm.npm_translate_lock( "webdriver-manager": "node ./bin/webdriver-manager update --standalone false --gecko false --versions.chrome 106.0.5249.21", }, data = [ - "//:.pnpmfile.cjs", "//:package.json", "//:pnpm-workspace.yaml", "//modules/testing/builder:package.json", diff --git a/modules/testing/builder/BUILD.bazel b/modules/testing/builder/BUILD.bazel index 9b88a714cd05..7f542efb0138 100644 --- a/modules/testing/builder/BUILD.bazel +++ b/modules/testing/builder/BUILD.bazel @@ -20,6 +20,7 @@ ts_project( # Needed at runtime by some builder tests relying on SSR being # resolvable in the test project. ":node_modules/@angular/ssr", + ":node_modules/browser-sync", ":node_modules/jsdom", ":node_modules/vitest", ":node_modules/@vitest/coverage-v8", diff --git a/modules/testing/builder/package.json b/modules/testing/builder/package.json index ddc029374d18..7d04585665af 100644 --- a/modules/testing/builder/package.json +++ b/modules/testing/builder/package.json @@ -4,6 +4,7 @@ "@angular-devkit/architect": "workspace:*", "@angular/ssr": "workspace:*", "@angular-devkit/build-angular": "workspace:*", + "browser-sync": "3.0.4", "@vitest/coverage-v8": "4.0.8", "jsdom": "27.1.0", "rxjs": "7.8.2", diff --git a/packages/angular_devkit/build_angular/BUILD.bazel b/packages/angular_devkit/build_angular/BUILD.bazel index 7654aeadf12a..b2f56aa5fa89 100644 --- a/packages/angular_devkit/build_angular/BUILD.bazel +++ b/packages/angular_devkit/build_angular/BUILD.bazel @@ -226,7 +226,6 @@ ts_project( ":build_angular", ":build_angular_test_utils", ":node_modules/tinyglobby", - ":node_modules/webpack", "//:node_modules/@types/node", "//:node_modules/prettier", "//:node_modules/typescript", @@ -333,7 +332,6 @@ LARGE_SPECS = { "//:node_modules/karma-jasmine", "//:node_modules/karma-jasmine-html-reporter", "//:node_modules/puppeteer", - ":node_modules/webpack", ], }, "protractor": { @@ -392,8 +390,6 @@ LARGE_SPECS = { ":node_modules/@angular/build", ":node_modules/@angular-devkit/architect", ":node_modules/@angular-devkit/core", - ":node_modules/@angular-devkit/build-webpack", - "//modules/testing/builder", # Base dependencies for the application in hello-world-app. # Some tests also require extra dependencies. diff --git a/packages/angular_devkit/build_angular/src/builders/ssr-dev-server/index.ts b/packages/angular_devkit/build_angular/src/builders/ssr-dev-server/index.ts index 6219ea6a46a2..8ec879993e4f 100644 --- a/packages/angular_devkit/build_angular/src/builders/ssr-dev-server/index.ts +++ b/packages/angular_devkit/build_angular/src/builders/ssr-dev-server/index.ts @@ -21,6 +21,7 @@ import type { MiddlewareHandler, ProxyOptions, } from 'browser-sync'; +import { createRequire } from 'node:module'; import { join, resolve as pathResolve } from 'node:path'; import * as url from 'node:url'; import { @@ -64,7 +65,7 @@ export function execute( ): Observable { let browserSync: typeof import('browser-sync'); try { - browserSync = require('browser-sync'); + browserSync = createRequire(context.workspaceRoot + '/')('browser-sync'); } catch { return of({ success: false, diff --git a/packages/angular_devkit/build_webpack/src/builders/webpack-dev-server/index.ts b/packages/angular_devkit/build_webpack/src/builders/webpack-dev-server/index.ts index f078b614796c..66a2f09a6422 100644 --- a/packages/angular_devkit/build_webpack/src/builders/webpack-dev-server/index.ts +++ b/packages/angular_devkit/build_webpack/src/builders/webpack-dev-server/index.ts @@ -10,8 +10,8 @@ import { Builder, BuilderContext, createBuilder } from '@angular-devkit/architec import assert from 'node:assert'; import { resolve as pathResolve } from 'node:path'; import { Observable, from, isObservable, of, switchMap } from 'rxjs'; -import webpack from 'webpack'; -import WebpackDevServer from 'webpack-dev-server'; +import type webpack from 'webpack'; +import type WebpackDevServer from 'webpack-dev-server'; import { getEmittedFiles, getWebpackConfig } from '../../utils'; import { BuildResult, WebpackFactory, WebpackLoggingCallback } from '../webpack'; import { Schema as WebpackDevServerBuilderSchema } from './schema'; @@ -44,7 +44,7 @@ export function runWebpackDevServer( return of(result); } } else { - return of(webpack(c)); + return from(import('webpack').then((mod) => mod.default(c))); } }; @@ -54,9 +54,9 @@ export function runWebpackDevServer( ) => { if (options.webpackDevServerFactory) { return new options.webpackDevServerFactory(config, webpack); + } else { + return from(import('webpack-dev-server').then((mod) => new mod.default(config, webpack))); } - - return new WebpackDevServer(config, webpack); }; const { @@ -70,8 +70,14 @@ export function runWebpackDevServer( } = options; return createWebpack({ ...config, watch: false }).pipe( + switchMap(async (webpackCompiler) => { + return [ + webpackCompiler, + options.webpackDevServerFactory ?? (await import('webpack-dev-server')).default, + ] as unknown as [webpack.Compiler | null, WebpackDevServerFactory]; + }), switchMap( - (webpackCompiler) => + ([webpackCompiler, webpackDevServerFactory]) => new Observable((obs) => { assert(webpackCompiler, 'Webpack compiler factory did not return a compiler instance.'); @@ -79,7 +85,6 @@ export function runWebpackDevServer( devServerConfig.host ??= 'localhost'; let result: Partial; - const statsOptions = typeof config.stats === 'boolean' ? undefined : config.stats; webpackCompiler.hooks.done.tap('build-webpack', (stats) => { @@ -94,7 +99,7 @@ export function runWebpackDevServer( } as unknown as DevServerBuildOutput); }); - const devServer = createWebpackDevServer(webpackCompiler, devServerConfig); + const devServer = new webpackDevServerFactory(devServerConfig, webpackCompiler); devServer.startCallback((err) => { if (err) { obs.error(err); diff --git a/packages/angular_devkit/build_webpack/src/builders/webpack/index.ts b/packages/angular_devkit/build_webpack/src/builders/webpack/index.ts index 166a6385a0e1..ce3f91fd69d4 100644 --- a/packages/angular_devkit/build_webpack/src/builders/webpack/index.ts +++ b/packages/angular_devkit/build_webpack/src/builders/webpack/index.ts @@ -10,7 +10,7 @@ import { Builder, BuilderContext, BuilderOutput, createBuilder } from '@angular- import assert from 'node:assert'; import { resolve as pathResolve } from 'node:path'; import { Observable, from, isObservable, of, switchMap } from 'rxjs'; -import webpack from 'webpack'; +import type webpack from 'webpack'; import { EmittedFiles, getEmittedFiles, getWebpackConfig } from '../../utils'; import { Schema as RealWebpackBuilderSchema } from './schema'; @@ -57,7 +57,7 @@ export function runWebpack( return of(result); } } else { - return of(webpack(c)); + return from(import('webpack').then((mod) => mod.default(c))); } }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e59501da4208..9ee2108110d3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,8 +10,6 @@ overrides: packageExtensionsChecksum: sha256-3L73Fw32UVtE6x5BJxJPBtQtH/mgsr31grNpdhHP1IY= -pnpmfileChecksum: sha256-uP8jbELX5QePWiOvv7dnhxXXQrLZ8/Qxn4a0akzOcCQ= - importers: .: @@ -336,6 +334,9 @@ importers: '@vitest/coverage-v8': specifier: 4.0.8 version: 4.0.8(vitest@4.0.8(@types/node@24.10.0)(jiti@2.6.1)(jsdom@27.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.4.2)(sass@1.94.0)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1)) + browser-sync: + specifier: 3.0.4 + version: 3.0.4(bufferutil@4.0.9)(utf-8-validate@6.0.5) jsdom: specifier: 27.1.0 version: 27.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5) @@ -655,9 +656,6 @@ importers: babel-loader: specifier: 10.0.0 version: 10.0.0(@babel/core@7.28.5)(webpack@5.102.1(esbuild@0.27.0)) - browser-sync: - specifier: 3.0.4 - version: 3.0.4(bufferutil@4.0.9) browserslist: specifier: ^4.26.0 version: 4.27.0 @@ -770,6 +768,9 @@ importers: '@web/test-runner': specifier: 0.20.2 version: 0.20.2(bufferutil@4.0.9) + browser-sync: + specifier: 3.0.4 + version: 3.0.4(bufferutil@4.0.9)(utf-8-validate@6.0.5) ng-packagr: specifier: 21.0.0-rc.1 version: 21.0.0-rc.1(@angular/compiler-cli@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(typescript@5.9.3))(tslib@2.8.1)(typescript@5.9.3) @@ -789,12 +790,6 @@ importers: rxjs: specifier: 7.8.2 version: 7.8.2 - webpack: - specifier: 5.102.1 - version: 5.102.1(esbuild@0.27.0) - webpack-dev-server: - specifier: 5.2.2 - version: 5.2.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)(webpack@5.102.1(esbuild@0.27.0)) devDependencies: '@angular-devkit/core': specifier: workspace:0.0.0-PLACEHOLDER @@ -802,6 +797,12 @@ importers: '@ngtools/webpack': specifier: workspace:0.0.0-PLACEHOLDER version: link:../../ngtools/webpack + webpack: + specifier: 5.102.1 + version: 5.102.1(esbuild@0.27.0) + webpack-dev-server: + specifier: 5.2.2 + version: 5.2.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)(webpack@5.102.1(esbuild@0.27.0)) packages/angular_devkit/core: dependencies: @@ -865,10 +866,6 @@ importers: version: 22.0.0 packages/ngtools/webpack: - dependencies: - webpack: - specifier: 5.102.1 - version: 5.102.1(esbuild@0.27.0) devDependencies: '@angular-devkit/core': specifier: workspace:0.0.0-PLACEHOLDER @@ -882,6 +879,9 @@ importers: typescript: specifier: 5.9.3 version: 5.9.3 + webpack: + specifier: 5.102.1 + version: 5.102.1(esbuild@0.27.0) packages/schematics/angular: dependencies: @@ -13455,24 +13455,24 @@ snapshots: fresh: 0.5.2 mitt: 1.2.0 - browser-sync-ui@3.0.4(bufferutil@4.0.9): + browser-sync-ui@3.0.4(bufferutil@4.0.9)(utf-8-validate@6.0.5): dependencies: async-each-series: 0.1.1 chalk: 4.1.2 connect-history-api-fallback: 1.6.0 immutable: 3.8.2 server-destroy: 1.0.1 - socket.io-client: 4.8.1(bufferutil@4.0.9) + socket.io-client: 4.8.1(bufferutil@4.0.9)(utf-8-validate@6.0.5) stream-throttle: 0.1.3 transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - browser-sync@3.0.4(bufferutil@4.0.9): + browser-sync@3.0.4(bufferutil@4.0.9)(utf-8-validate@6.0.5): dependencies: browser-sync-client: 3.0.4 - browser-sync-ui: 3.0.4(bufferutil@4.0.9) + browser-sync-ui: 3.0.4(bufferutil@4.0.9)(utf-8-validate@6.0.5) bs-recipes: 1.3.4 chalk: 4.1.2 chokidar: 3.6.0 @@ -13496,7 +13496,7 @@ snapshots: serve-index: 1.9.1 serve-static: 1.16.2 server-destroy: 1.0.1 - socket.io: 4.8.1(bufferutil@4.0.9) + socket.io: 4.8.1(bufferutil@4.0.9)(utf-8-validate@6.0.5) ua-parser-js: 1.0.41 yargs: 17.7.2 transitivePeerDependencies: @@ -14239,12 +14239,12 @@ snapshots: dependencies: once: 1.4.0 - engine.io-client@6.6.3(bufferutil@4.0.9): + engine.io-client@6.6.3(bufferutil@4.0.9)(utf-8-validate@6.0.5): dependencies: '@socket.io/component-emitter': 3.1.2 debug: 4.3.7 engine.io-parser: 5.2.3 - ws: 8.17.1(bufferutil@4.0.9) + ws: 8.17.1(bufferutil@4.0.9)(utf-8-validate@6.0.5) xmlhttprequest-ssl: 2.1.2 transitivePeerDependencies: - bufferutil @@ -14253,7 +14253,7 @@ snapshots: engine.io-parser@5.2.3: {} - engine.io@6.6.4(bufferutil@4.0.9): + engine.io@6.6.4(bufferutil@4.0.9)(utf-8-validate@6.0.5): dependencies: '@types/cors': 2.8.19 '@types/node': 22.19.0 @@ -14263,7 +14263,7 @@ snapshots: cors: 2.8.5 debug: 4.3.7 engine.io-parser: 5.2.3 - ws: 8.17.1(bufferutil@4.0.9) + ws: 8.17.1(bufferutil@4.0.9)(utf-8-validate@6.0.5) transitivePeerDependencies: - bufferutil - supports-color @@ -16000,7 +16000,7 @@ snapshots: qjobs: 1.2.0 range-parser: 1.2.1 rimraf: 3.0.2 - socket.io: 4.8.1(bufferutil@4.0.9) + socket.io: 4.8.1(bufferutil@4.0.9)(utf-8-validate@6.0.5) source-map: 0.6.1 tmp: 0.2.5 ua-parser-js: 0.7.41 @@ -17852,20 +17852,20 @@ snapshots: smart-buffer@4.2.0: {} - socket.io-adapter@2.5.5(bufferutil@4.0.9): + socket.io-adapter@2.5.5(bufferutil@4.0.9)(utf-8-validate@6.0.5): dependencies: debug: 4.3.7 - ws: 8.17.1(bufferutil@4.0.9) + ws: 8.17.1(bufferutil@4.0.9)(utf-8-validate@6.0.5) transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - socket.io-client@4.8.1(bufferutil@4.0.9): + socket.io-client@4.8.1(bufferutil@4.0.9)(utf-8-validate@6.0.5): dependencies: '@socket.io/component-emitter': 3.1.2 debug: 4.3.7 - engine.io-client: 6.6.3(bufferutil@4.0.9) + engine.io-client: 6.6.3(bufferutil@4.0.9)(utf-8-validate@6.0.5) socket.io-parser: 4.2.4 transitivePeerDependencies: - bufferutil @@ -17879,14 +17879,14 @@ snapshots: transitivePeerDependencies: - supports-color - socket.io@4.8.1(bufferutil@4.0.9): + socket.io@4.8.1(bufferutil@4.0.9)(utf-8-validate@6.0.5): dependencies: accepts: 1.3.8 base64id: 2.0.0 cors: 2.8.5 debug: 4.3.7 - engine.io: 6.6.4(bufferutil@4.0.9) - socket.io-adapter: 2.5.5(bufferutil@4.0.9) + engine.io: 6.6.4(bufferutil@4.0.9)(utf-8-validate@6.0.5) + socket.io-adapter: 2.5.5(bufferutil@4.0.9)(utf-8-validate@6.0.5) socket.io-parser: 4.2.4 transitivePeerDependencies: - bufferutil @@ -18973,9 +18973,10 @@ snapshots: optionalDependencies: bufferutil: 4.0.9 - ws@8.17.1(bufferutil@4.0.9): + ws@8.17.1(bufferutil@4.0.9)(utf-8-validate@6.0.5): optionalDependencies: bufferutil: 4.0.9 + utf-8-validate: 6.0.5 ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5): optionalDependencies: From aa598e8a96716e600360f904e26ff2cc2b86eb79 Mon Sep 17 00:00:00 2001 From: Alan Agius <17563226+alan-agius4@users.noreply.github.com> Date: Thu, 13 Nov 2025 09:33:30 +0000 Subject: [PATCH 2/2] fixup! refactor: address issue with bazel resolution of peer dependencies --- goldens/public-api/angular_devkit/build_webpack/index.api.md | 4 ++-- packages/angular_devkit/build_angular/BUILD.bazel | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/goldens/public-api/angular_devkit/build_webpack/index.api.md b/goldens/public-api/angular_devkit/build_webpack/index.api.md index db2b47c9ed74..0d60187627d5 100644 --- a/goldens/public-api/angular_devkit/build_webpack/index.api.md +++ b/goldens/public-api/angular_devkit/build_webpack/index.api.md @@ -7,8 +7,8 @@ import { BuilderContext } from '@angular-devkit/architect'; import { BuilderOutput } from '@angular-devkit/architect'; import { Observable } from 'rxjs'; -import webpack from 'webpack'; -import WebpackDevServer from 'webpack-dev-server'; +import type webpack from 'webpack'; +import type WebpackDevServer from 'webpack-dev-server'; // @public (undocumented) export type BuildResult = BuilderOutput & { diff --git a/packages/angular_devkit/build_angular/BUILD.bazel b/packages/angular_devkit/build_angular/BUILD.bazel index b2f56aa5fa89..2a505e1fd8ca 100644 --- a/packages/angular_devkit/build_angular/BUILD.bazel +++ b/packages/angular_devkit/build_angular/BUILD.bazel @@ -332,6 +332,7 @@ LARGE_SPECS = { "//:node_modules/karma-jasmine", "//:node_modules/karma-jasmine-html-reporter", "//:node_modules/puppeteer", + ":node_modules/webpack", ], }, "protractor": {