diff --git a/packages/angular/src/executors/utilities/ng-packagr/stylesheet-processor.di.ts b/packages/angular/src/executors/utilities/ng-packagr/stylesheet-processor.di.ts index c4320c6369f3b..274f0090a718d 100644 --- a/packages/angular/src/executors/utilities/ng-packagr/stylesheet-processor.di.ts +++ b/packages/angular/src/executors/utilities/ng-packagr/stylesheet-processor.di.ts @@ -1,9 +1,20 @@ -import { FactoryProvider } from 'injection-js'; +import type { FactoryProvider } from 'injection-js'; import { STYLESHEET_PROCESSOR_TOKEN } from 'ng-packagr/lib/styles/stylesheet-processor.di'; -import { StylesheetProcessor } from './stylesheet-processor'; +import { getInstalledPackageVersionInfo } from '../angular-version-utils'; +import { + AsyncStylesheetProcessor, + StylesheetProcessor, +} from './stylesheet-processor'; export const STYLESHEET_PROCESSOR: FactoryProvider = { provide: STYLESHEET_PROCESSOR_TOKEN, - useFactory: () => StylesheetProcessor, + useFactory: () => { + const { version: ngPackagrVersion } = + getInstalledPackageVersionInfo('ng-packagr'); + + return ngPackagrVersion !== '17.2.0' + ? StylesheetProcessor + : AsyncStylesheetProcessor; + }, deps: [], }; diff --git a/packages/angular/src/executors/utilities/ng-packagr/stylesheet-processor.ts b/packages/angular/src/executors/utilities/ng-packagr/stylesheet-processor.ts index 5b8e94ac44257..cc8de61dd7cfa 100644 --- a/packages/angular/src/executors/utilities/ng-packagr/stylesheet-processor.ts +++ b/packages/angular/src/executors/utilities/ng-packagr/stylesheet-processor.ts @@ -53,6 +53,110 @@ export class StylesheetProcessor { ]; } + async process({ + filePath, + content, + }: { + filePath: string; + content: string; + }): Promise { + this.createRenderWorker(); + + return this.renderWorker.run({ content, filePath }); + } + + /** Destory workers in pool. */ + destroy(): void { + void this.renderWorker?.destroy(); + } + + private createRenderWorker(): Promise { + if (this.renderWorker) { + return; + } + + const styleIncludePaths = [...this.includePaths]; + let prevDir = null; + let currentDir = this.basePath; + + while (currentDir !== prevDir) { + const p = join(currentDir, 'node_modules'); + if (existsSync(p)) { + styleIncludePaths.push(p); + } + + prevDir = currentDir; + currentDir = dirname(prevDir); + } + + const browserslistData = browserslist(undefined, { path: this.basePath }); + + const { version: ngPackagrVersion } = + getInstalledPackageVersionInfo('ng-packagr'); + let postcssConfiguration: PostcssConfiguration | undefined; + if (gte(ngPackagrVersion, '17.3.0')) { + const { + loadPostcssConfiguration, + } = require('ng-packagr/lib/styles/postcss-configuration'); + postcssConfiguration = loadPostcssConfiguration(this.projectBasePath); + } + + this.renderWorker = new Piscina({ + filename: require.resolve( + 'ng-packagr/lib/styles/stylesheet-processor-worker' + ), + maxThreads, + env: { + ...process.env, + FORCE_COLOR: '' + colors.enabled, + }, + workerData: { + postcssConfiguration, + tailwindConfigPath: getTailwindConfigPath( + this.projectBasePath, + workspaceRoot + ), + projectBasePath: this.projectBasePath, + browserslistData, + targets: transformSupportedBrowsersToTargets(browserslistData), + cacheDirectory: this.cacheDirectory, + cssUrl: this.cssUrl, + styleIncludePaths, + }, + }); + } +} + +/** + * This class is used when ng-packagr version is 17.2.0. The async `loadPostcssConfiguration` function + * introduced in ng-packagr 17.2.0 causes a memory leak due to multiple workers being created. We must + * keep this class to support any workspace that might be using ng-packagr 17.2.0 where that function + * need to be awaited. + */ +export class AsyncStylesheetProcessor { + private renderWorker: typeof Piscina | undefined; + + constructor( + private readonly projectBasePath: string, + private readonly basePath: string, + private readonly cssUrl?: CssUrl, + private readonly includePaths?: string[], + private readonly cacheDirectory?: string | false + ) { + // By default, browserslist defaults are too inclusive + // https://github.com/browserslist/browserslist/blob/83764ea81ffaa39111c204b02c371afa44a4ff07/index.js#L516-L522 + // We change the default query to browsers that Angular support. + // https://angular.io/guide/browser-support + (browserslist.defaults as string[]) = [ + 'last 2 Chrome versions', + 'last 1 Firefox version', + 'last 2 Edge major versions', + 'last 2 Safari major versions', + 'last 2 iOS major versions', + 'Firefox ESR', + ]; + } + async process({ filePath, content, @@ -94,7 +198,7 @@ export class StylesheetProcessor { const { version: ngPackagrVersion } = getInstalledPackageVersionInfo('ng-packagr'); let postcssConfiguration: PostcssConfiguration | undefined; - if (gte(ngPackagrVersion, '17.2.0')) { + if (ngPackagrVersion === '17.2.0') { const { loadPostcssConfiguration } = await import( 'ng-packagr/lib/styles/postcss-configuration' );