From f235087223b62903157841786d37dd1547563165 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 | 68 +++++++++---------- 9 files changed, 53 insertions(+), 92 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 0482b57bdcd7..5cd379c923f5 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.93.2)(terser@5.44.0)(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.4)(webpack@5.102.1(esbuild@0.26.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.26.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.26.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.26.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.26.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.26.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.26.0) packages/schematics/angular: dependencies: @@ -7817,7 +7817,6 @@ packages: engines: {node: '>=0.6.0', teleport: '>=0.2.0'} deprecated: |- You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other. - (For a CapTP with native promises, see @endo/eventual-send and @endo/captp) qjobs@1.2.0: @@ -13507,24 +13506,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 @@ -13548,7 +13547,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: @@ -14289,12 +14288,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 @@ -14303,7 +14302,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 @@ -14313,7 +14312,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 @@ -16079,7 +16078,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 @@ -17905,20 +17904,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 @@ -17932,14 +17931,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 @@ -19026,9 +19025,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 89b1c414bf4a515ff316687ddc34fc60e5c1d89b Mon Sep 17 00:00:00 2001 From: Alan Agius <17563226+alan-agius4@users.noreply.github.com> Date: Thu, 13 Nov 2025 09:32:46 +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": {