diff --git a/docs/generated/manifests/nx-api.json b/docs/generated/manifests/nx-api.json index 75e17d7962523..06fd85d4bc487 100644 --- a/docs/generated/manifests/nx-api.json +++ b/docs/generated/manifests/nx-api.json @@ -108,7 +108,7 @@ "type": "executor" }, "/nx-api/angular/executors/application": { - "description": "Builds an Angular application using [esbuild](https://esbuild.github.io/) with integrated SSR and prerendering capabilities. _Note: this is only supported in Angular versions >= 17.0.0_.", + "description": "Builds an Angular application using [esbuild](https://esbuild.github.io/) with integrated SSR and prerendering capabilities.", "file": "generated/packages/angular/executors/application.json", "hidden": false, "name": "application", @@ -200,7 +200,7 @@ "type": "generator" }, "/nx-api/angular/generators/convert-to-application-executor": { - "description": "Converts projects to use the `@nx/angular:application` executor or the `@angular-devkit/build-angular:application` builder. _Note: this is only supported in Angular versions >= 17.0.0_.", + "description": "Converts projects to use the `@nx/angular:application` executor or the `@angular-devkit/build-angular:application` builder.", "file": "generated/packages/angular/generators/convert-to-application-executor.json", "hidden": false, "name": "convert-to-application-executor", diff --git a/docs/generated/packages-metadata.json b/docs/generated/packages-metadata.json index 04e75a49bb135..4e6c182b8db5f 100644 --- a/docs/generated/packages-metadata.json +++ b/docs/generated/packages-metadata.json @@ -103,7 +103,7 @@ "type": "executor" }, { - "description": "Builds an Angular application using [esbuild](https://esbuild.github.io/) with integrated SSR and prerendering capabilities. _Note: this is only supported in Angular versions >= 17.0.0_.", + "description": "Builds an Angular application using [esbuild](https://esbuild.github.io/) with integrated SSR and prerendering capabilities.", "file": "generated/packages/angular/executors/application.json", "hidden": false, "name": "application", @@ -195,7 +195,7 @@ "type": "generator" }, { - "description": "Converts projects to use the `@nx/angular:application` executor or the `@angular-devkit/build-angular:application` builder. _Note: this is only supported in Angular versions >= 17.0.0_.", + "description": "Converts projects to use the `@nx/angular:application` executor or the `@angular-devkit/build-angular:application` builder.", "file": "generated/packages/angular/generators/convert-to-application-executor.json", "hidden": false, "name": "convert-to-application-executor", diff --git a/docs/generated/packages/angular/documents/angular-nx-version-matrix.md b/docs/generated/packages/angular/documents/angular-nx-version-matrix.md index a725c3b6bd918..6628023534f11 100644 --- a/docs/generated/packages/angular/documents/angular-nx-version-matrix.md +++ b/docs/generated/packages/angular/documents/angular-nx-version-matrix.md @@ -14,6 +14,7 @@ We provide a recommended version, and it is usually the latest minor version of | Angular Version | **Nx Version _(recommended)_** | Nx Version _(range)_ | | --------------- | ------------------------------ | ---------------------------------------- | +| ~19.0.0 | **latest** | >=20.2.0 <=latest | | ~18.2.0 | **latest** | >=19.6.0 <=latest | | ~18.1.0 | **latest** | >=19.5.0 <=latest | | ~18.0.0 | **latest** | >=19.1.0 <=latest | @@ -21,9 +22,9 @@ We provide a recommended version, and it is usually the latest minor version of | ~17.2.0 | **latest** | >=18.1.1 <=latest | | ~17.1.0 | **latest** | >=17.3.0 <=latest | | ~17.0.0 | **latest** | >=17.1.0 <=latest | -| ~16.2.0 | **latest** | >=16.7.0 <=latest | -| ~16.1.0 | **latest** | >=16.4.0 <=latest | -| ~16.0.0 | **latest** | >=16.1.0 <=latest | +| ~16.2.0 | **~20.1.0** | >=16.7.0 <20.2.0 | +| ~16.1.0 | **~20.1.0** | >=16.4.0 <20.2.0 | +| ~16.0.0 | **~20.1.0** | >=16.1.0 <20.2.0 | | ~15.2.0 | **~19.0.0** | >=15.8.0 <19.1.0 | | ~15.1.0 | **~19.0.0** | >=15.5.0 <19.1.0 | | ~15.0.0 | **~19.0.0** | >=15.2.0 <=15.4.8 \|\| >=15.7.0 <19.1.0 | diff --git a/docs/generated/packages/angular/executors/application.json b/docs/generated/packages/angular/executors/application.json index 9509fe80b324f..96c3103273280 100644 --- a/docs/generated/packages/angular/executors/application.json +++ b/docs/generated/packages/angular/executors/application.json @@ -4,7 +4,7 @@ "schema": { "$schema": "http://json-schema.org/draft-07/schema", "title": "Schema for Nx Application Executor", - "description": "Builds an Angular application using [esbuild](https://esbuild.github.io/) with integrated SSR and prerendering capabilities. _Note: this is only supported in Angular versions >= 17.0.0_.", + "description": "Builds an Angular application using [esbuild](https://esbuild.github.io/) with integrated SSR and prerendering capabilities.", "examplesFile": "This executor is a drop-in replacement for the `@angular-devkit/build-angular:application` builder provided by the Angular CLI. It builds an Angular application using [esbuild](https://esbuild.github.io/) with integrated SSR and prerendering capabilities.\n\nIn addition to the features provided by the Angular CLI builder, the `@nx/angular:application` executor also supports the following:\n\n- Providing esbuild plugins\n- Providing a function to transform the application's `index.html` file\n- Incremental builds\n\n{% callout type=\"check\" title=\"Dev Server\" %}\nThe [`@nx/angular:dev-server` executor](/nx-api/angular/executors/dev-server) is required to serve your application when using the `@nx/angular:application` to build it. It is a drop-in replacement for the Angular CLI's `@angular-devkit/build-angular:dev-server` builder and ensures the application is correctly served with Vite when using the `@nx/angular:application` executor.\n{% /callout %}\n\n## Examples\n\n{% tabs %}\n{% tab label=\"Providing esbuild plugins\" %}\n\nThe executor accepts a `plugins` option that allows you to provide esbuild plugins that will be used when building your application. It allows providing a path to a plugin file or an object with a `path` and `options` property to provide options to the plugin.\n\n```json {% fileName=\"apps/my-app/project.json\" highlightLines=[\"8-16\"] %}\n{\n ...\n \"targets\": {\n \"build\": {\n \"executor\": \"@nx/angular:application\",\n \"options\": {\n ...\n \"plugins\": [\n \"apps/my-app/plugins/plugin1.js\",\n {\n \"path\": \"apps/my-app/plugins/plugin2.js\",\n \"options\": {\n \"someOption\": \"some value\"\n }\n }\n ]\n }\n }\n ...\n }\n}\n```\n\n```ts {% fileName=\"apps/my-app/plugins/plugin1.js\" %}\nconst plugin1 = {\n name: 'plugin1',\n setup(build) {\n const options = build.initialOptions;\n options.define.PLUGIN1_TEXT = '\"Value was provided at build time\"';\n },\n};\n\nmodule.exports = plugin1;\n```\n\n```ts {% fileName=\"apps/my-app/plugins/plugin2.js\" %}\nfunction plugin2({ someOption }) {\n return {\n name: 'plugin2',\n setup(build) {\n const options = build.initialOptions;\n options.define.PLUGIN2_TEXT = JSON.stringify(someOption);\n },\n };\n}\n\nmodule.exports = plugin2;\n```\n\nAdditionally, we need to inform TypeScript of the defined variables to prevent type-checking errors during the build. We can achieve this by creating or updating a type definition file included in the TypeScript build process (e.g. `src/types.d.ts`) with the following content:\n\n```ts {% fileName=\"apps/my-app/src/types.d.ts\" %}\ndeclare const PLUGIN1_TEXT: number;\ndeclare const PLUGIN2_TEXT: string;\n```\n\n{% /tab %}\n\n{% tab label=\"Transforming the 'index.html' file\" %}\n\nThe executor accepts an `indexHtmlTransformer` option to provide a path to a file with a default export for a function that receives the application's `index.html` file contents and outputs the updated contents.\n\n```json {% fileName=\"apps/my-app/project.json\" highlightLines=[8] %}\n{\n ...\n \"targets\": {\n \"build\": {\n \"executor\": \"@nx/angular:application\",\n \"options\": {\n ...\n \"indexHtmlTransformer\": \"apps/my-app/index-html.transformer.ts\"\n }\n }\n ...\n }\n}\n```\n\n```ts {% fileName=\"apps/my-app/index-html.transformer.ts\" %}\nexport default function (indexContent: string) {\n return indexContent.replace(\n 'my-app',\n 'my-app (transformed)'\n );\n}\n```\n\n{% /tab %}\n{% /tabs %}\n", "outputCapture": "direct-nodejs", "type": "object", @@ -55,7 +55,18 @@ }, "server": { "type": "string", - "description": "The full path for the server entry point to the application, relative to the current workspace." + "description": "The full path for the server entry point to the application, relative to the current workspace.", + "oneOf": [ + { + "type": "string", + "description": "The full path for the server entry point to the application, relative to the current workspace." + }, + { + "const": false, + "type": "boolean", + "description": "Indicates that a server entry point is not provided. _Note: this is only supported in Angular versions >= 19.0.0_." + } + ] }, "polyfills": { "description": "A list of polyfills to include in the build. Can be a full path for a file, relative to the current workspace or module specifier. Example: 'zone.js'.", @@ -71,6 +82,29 @@ "type": "string", "description": "Customize the base path for the URLs of resources in 'index.html' and component stylesheets. This option is only necessary for specific deployment scenarios, such as with Angular Elements or when utilizing different CDN locations. _Note: this is only supported in Angular versions >= 17.3.0_." }, + "security": { + "description": "Security features to protect against XSS and other common attacks. _Note: this is only supported in Angular versions >= 19.0.0_.", + "type": "object", + "additionalProperties": false, + "properties": { + "autoCsp": { + "description": "Enables automatic generation of a hash-based Strict Content Security Policy (https://web.dev/articles/strict-csp#choose-hash) based on scripts in index.html. Will default to true once we are out of experimental/preview phases. It defaults to `false`.", + "oneOf": [ + { + "type": "object", + "properties": { + "unsafeEval": { + "type": "boolean", + "description": "Include the `unsafe-eval` directive (https://web.dev/articles/strict-csp#remove-eval) in the auto-CSP. Please only enable this if you are absolutely sure that you need to, as allowing calls to eval will weaken the XSS defenses provided by the auto-CSP. It default to `false`." + } + }, + "additionalProperties": false + }, + { "type": "boolean" } + ] + } + } + }, "scripts": { "description": "Global scripts to be included in the build.", "type": "array", @@ -157,6 +191,28 @@ "type": "array", "items": { "type": "string" }, "default": [] + }, + "sass": { + "description": "Options to pass to the sass preprocessor. _Note: this is only supported in Angular versions >= 19.0.0_.", + "type": "object", + "properties": { + "fatalDeprecations": { + "description": "A set of deprecations to treat as fatal. If a deprecation warning of any provided type is encountered during compilation, the compiler will error instead. If a Version is provided, then all deprecations that were active in that compiler version will be treated as fatal.", + "type": "array", + "items": { "type": "string" } + }, + "silenceDeprecations": { + "description": " A set of active deprecations to ignore. If a deprecation warning of any provided type is encountered during compilation, the compiler will ignore it instead.", + "type": "array", + "items": { "type": "string" } + }, + "futureDeprecations": { + "description": "A set of future deprecations to opt into early. Future deprecations passed here will be treated as active by the compiler, emitting warnings as necessary.", + "type": "array", + "items": { "type": "string" } + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -546,8 +602,7 @@ "default": [] }, "prerender": { - "description": "Prerender (SSG) pages of your application during build time.", - "default": false, + "description": "Prerender (SSG) pages of your application during build time. It defaults to `false` in Angular versions < 19.0.0. Otherwise, the value will be `undefined`.", "oneOf": [ { "type": "boolean", @@ -584,6 +639,11 @@ "entry": { "type": "string", "description": "The server entry-point that when executed will spawn the web server." + }, + "experimentalPlatform": { + "description": "Specifies the platform for which the server bundle is generated. This affects the APIs and modules available in the server-side code. \n\n- `node`: (Default) Generates a bundle optimized for Node.js environments. \n- `neutral`: Generates a platform-neutral bundle suitable for environments like edge workers, and other serverless platforms. This option avoids using Node.js-specific APIs, making the bundle more portable. \n\nPlease note that this feature does not provide polyfills for Node.js modules. Additionally, it is experimental, and the feature may undergo changes in future versions. _Note: this is only supported in Angular versions >= 19.0.0_.", + "default": "node", + "enum": ["node", "neutral"] } }, "additionalProperties": false @@ -592,8 +652,12 @@ }, "appShell": { "type": "boolean", - "description": "Generates an application shell during build time.", - "default": false + "description": "Generates an application shell during build time. It defaults to `false` in Angular versions < 19.0.0. Otherwise, the value will be `undefined`." + }, + "outputMode": { + "type": "string", + "description": "Defines the build output target. 'static': Generates a static site for deployment on any static hosting service. 'server': Produces an application designed for deployment on a server that supports server-side rendering (SSR). _Note: this is only supported in Angular versions >= 19.0.0_.", + "enum": ["static", "server"] }, "buildLibsFromSource": { "type": "boolean", @@ -739,7 +803,7 @@ }, "presets": [] }, - "description": "Builds an Angular application using [esbuild](https://esbuild.github.io/) with integrated SSR and prerendering capabilities. _Note: this is only supported in Angular versions >= 17.0.0_.", + "description": "Builds an Angular application using [esbuild](https://esbuild.github.io/) with integrated SSR and prerendering capabilities.", "aliases": [], "hidden": false, "path": "/packages/angular/src/executors/application/schema.json", diff --git a/docs/generated/packages/angular/executors/browser-esbuild.json b/docs/generated/packages/angular/executors/browser-esbuild.json index 4617a8ba3bb5f..78316149d6d91 100644 --- a/docs/generated/packages/angular/executors/browser-esbuild.json +++ b/docs/generated/packages/angular/executors/browser-esbuild.json @@ -514,7 +514,7 @@ "default": true }, "plugins": { - "description": "A list of ESBuild plugins. _Note: this is only supported in Angular versions >= 17.0.0_.", + "description": "A list of ESBuild plugins.", "type": "array", "items": { "oneOf": [ diff --git a/docs/generated/packages/angular/executors/dev-server.json b/docs/generated/packages/angular/executors/dev-server.json index ec2c44ac3b23f..a91014df74843 100644 --- a/docs/generated/packages/angular/executors/dev-server.json +++ b/docs/generated/packages/angular/executors/dev-server.json @@ -93,8 +93,7 @@ }, "hmr": { "type": "boolean", - "description": "Enable hot module replacement.", - "default": false + "description": "Enable hot module replacement. It defaults to `false` in Angular versions < 19.0.0. Otherwise, the value will be `undefined`." }, "watch": { "type": "boolean", @@ -107,7 +106,7 @@ }, "forceEsbuild": { "type": "boolean", - "description": "Force the development server to use the 'browser-esbuild' builder when building. This is a developer preview option for the esbuild-based build system. _Note: this is only supported in Angular versions >= 16.1.0_.", + "description": "Force the development server to use the 'browser-esbuild' builder when building. This is a developer preview option for the esbuild-based build system.", "default": false }, "inspect": { @@ -145,7 +144,7 @@ "x-priority": "important" }, "esbuildMiddleware": { - "description": "A list of HTTP request middleware functions. _Note: this is only supported in Angular versions >= 17.0.0_.", + "description": "A list of HTTP request middleware functions.", "type": "array", "items": { "type": "string", diff --git a/docs/generated/packages/angular/executors/ng-packagr-lite.json b/docs/generated/packages/angular/executors/ng-packagr-lite.json index c90d21a0d901b..5afa9203fc2ba 100644 --- a/docs/generated/packages/angular/executors/ng-packagr-lite.json +++ b/docs/generated/packages/angular/executors/ng-packagr-lite.json @@ -10,10 +10,6 @@ "cli": "nx", "type": "object", "presets": [ - { - "name": "Buildable Library with Tailwind", - "keys": ["project", "tailwindConfig"] - }, { "name": "Updating Project Dependencies for Buildable Library", "keys": ["project"] @@ -39,10 +35,6 @@ "poll": { "type": "number", "description": "Enable and define the file watching poll time period in milliseconds. _Note: this is only supported in Angular versions >= 18.0.0_." - }, - "tailwindConfig": { - "type": "string", - "description": "The full path for the Tailwind configuration file, relative to the workspace root. If not provided and a `tailwind.config.js` file exists in the project or workspace root, it will be used. Otherwise, Tailwind will not be configured." } }, "additionalProperties": false, diff --git a/docs/generated/packages/angular/executors/package.json b/docs/generated/packages/angular/executors/package.json index 2df3b53f6d4ca..d8136cf2f53f0 100644 --- a/docs/generated/packages/angular/executors/package.json +++ b/docs/generated/packages/angular/executors/package.json @@ -10,13 +10,6 @@ "cli": "nx", "type": "object", "presets": [ - { - "name": "Publishable Library with Tailwind", - "keys": [ - "project", - "tailwindConfig" - ] - }, { "name": "Updating Project Dependencies for Publishable Library", "keys": [ @@ -44,12 +37,6 @@ "poll": { "type": "number", "description": "Enable and define the file watching poll time period in milliseconds. _Note: this is only supported in Angular versions >= 18.0.0_." - }, - "tailwindConfig": { - "type": "string", - "description": "The full path for the Tailwind configuration file, relative to the workspace root. If not provided and a `tailwind.config.js` file exists in the project or workspace root, it will be used. Otherwise, Tailwind will not be configured. _Note: starting with Angular v17, this option is no longer used and the configuration will be picked up if exists at the project or workspace root_.", - "x-completion-type": "file", - "x-completion-glob": "tailwind.config@(.js|.cjs|.mjs|.ts)" } }, "additionalProperties": false, diff --git a/docs/generated/packages/angular/generators/application.json b/docs/generated/packages/angular/generators/application.json index 27845a6855f38..9d8c205e128e7 100644 --- a/docs/generated/packages/angular/generators/application.json +++ b/docs/generated/packages/angular/generators/application.json @@ -166,9 +166,10 @@ "default": false }, "bundler": { - "description": "Bundler to use to build the application. It defaults to `esbuild` for Angular versions >= 17.0.0. Otherwise, it defaults to `webpack`. _Note: The `esbuild` bundler is only considered stable from Angular v17._", + "description": "Bundler to use to build the application.", "type": "string", - "enum": ["webpack", "esbuild"], + "enum": ["esbuild", "webpack"], + "default": "esbuild", "x-prompt": "Which bundler do you want to use to build the application?", "x-priority": "important" }, @@ -177,6 +178,10 @@ "type": "boolean", "x-prompt": "Do you want to enable Server-Side Rendering (SSR) and Static Site Generation (SSG/Prerendering)?", "default": false + }, + "serverRouting": { + "description": "Creates a server application using the Server Routing and App Engine APIs (Developer Preview). _Note: this is only supported in Angular versions >= 19.0.0_.", + "type": "boolean" } }, "additionalProperties": false, diff --git a/docs/generated/packages/angular/generators/component.json b/docs/generated/packages/angular/generators/component.json index bea59dcc8c6e3..0d404b9450851 100644 --- a/docs/generated/packages/angular/generators/component.json +++ b/docs/generated/packages/angular/generators/component.json @@ -104,6 +104,11 @@ "default": false, "x-priority": "important" }, + "exportDefault": { + "type": "boolean", + "default": false, + "description": "Use default export for the component instead of a named export." + }, "skipFormat": { "description": "Skip formatting files.", "type": "boolean", diff --git a/docs/generated/packages/angular/generators/convert-to-application-executor.json b/docs/generated/packages/angular/generators/convert-to-application-executor.json index f394734b7eb98..7561824b213ad 100644 --- a/docs/generated/packages/angular/generators/convert-to-application-executor.json +++ b/docs/generated/packages/angular/generators/convert-to-application-executor.json @@ -5,7 +5,7 @@ "$schema": "http://json-schema.org/schema", "$id": "NxAngularConvertToApplicationExecutorGenerator", "cli": "nx", - "title": "Converts projects to use the `@nx/angular:application` executor or the `@angular-devkit/build-angular:application` builder. _Note: this is only supported in Angular versions >= 17.0.0_.", + "title": "Converts projects to use the `@nx/angular:application` executor or the `@angular-devkit/build-angular:application` builder.", "description": "Converts a project or all projects using one of the `@angular-devkit/build-angular:browser`, `@angular-devkit/build-angular:browser-esbuild`, `@nx/angular:browser` and `@nx/angular:browser-esbuild` executors to use the `@nx/angular:application` executor or the `@angular-devkit/build-angular:application` builder. If the converted target is using one of the `@nx/angular` executors, the `@nx/angular:application` executor will be used. Otherwise, the `@angular-devkit/build-angular:application` builder will be used.", "type": "object", "properties": { @@ -25,7 +25,7 @@ "additionalProperties": false, "presets": [] }, - "description": "Converts projects to use the `@nx/angular:application` executor or the `@angular-devkit/build-angular:application` builder. _Note: this is only supported in Angular versions >= 17.0.0_.", + "description": "Converts projects to use the `@nx/angular:application` executor or the `@angular-devkit/build-angular:application` builder.", "implementation": "/packages/angular/src/generators/convert-to-application-executor/convert-to-application-executor.ts", "aliases": [], "hidden": false, diff --git a/docs/generated/packages/angular/generators/host.json b/docs/generated/packages/angular/generators/host.json index b5a8f60a83661..94872b74ae16b 100644 --- a/docs/generated/packages/angular/generators/host.json +++ b/docs/generated/packages/angular/generators/host.json @@ -169,6 +169,10 @@ "default": false, "x-priority": "important" }, + "serverRouting": { + "description": "Creates a server application using the Server Routing and App Engine APIs (Developer Preview). _Note: this is only supported in Angular versions >= 19.0.0_.", + "type": "boolean" + }, "typescriptConfiguration": { "type": "boolean", "description": "Whether the module federation configuration and webpack configuration files should use TS.", diff --git a/docs/generated/packages/angular/generators/remote.json b/docs/generated/packages/angular/generators/remote.json index 268d6b61bbf4a..f6aa75dfec956 100644 --- a/docs/generated/packages/angular/generators/remote.json +++ b/docs/generated/packages/angular/generators/remote.json @@ -162,6 +162,10 @@ "type": "boolean", "default": false }, + "serverRouting": { + "description": "Creates a server application using the Server Routing and App Engine APIs (Developer Preview). _Note: this is only supported in Angular versions >= 19.0.0_.", + "type": "boolean" + }, "typescriptConfiguration": { "type": "boolean", "description": "Whether the module federation configuration and webpack configuration files should use TS.", diff --git a/docs/generated/packages/angular/generators/setup-ssr.json b/docs/generated/packages/angular/generators/setup-ssr.json index a10318cebd78a..5c03b2aab9c6b 100644 --- a/docs/generated/packages/angular/generators/setup-ssr.json +++ b/docs/generated/packages/angular/generators/setup-ssr.json @@ -16,13 +16,6 @@ "x-prompt": "What app would you like to generate an Angular Universal configuration for?", "x-dropdown": "projects" }, - "appId": { - "type": "string", - "format": "html-selector", - "description": "The `appId` to use with `withServerTransition`.", - "default": "serverApp", - "x-deprecated": "This is deprecated and ignored since Angular 16 and not supported since Angular 17." - }, "main": { "type": "string", "format": "path", @@ -56,7 +49,12 @@ }, "hydration": { "type": "boolean", - "description": "Set up Hydration for the SSR application. It defaults to `true` for Angular versions >= 17.0.0. Otherwise, it defaults to `false`." + "description": "Set up Hydration for the SSR application.", + "default": true + }, + "serverRouting": { + "description": "Creates a server application using the Server Routing and App Engine APIs (Developer Preview). _Note: this is only supported in Angular versions >= 19.0.0_.", + "type": "boolean" }, "skipFormat": { "type": "boolean", diff --git a/docs/generated/packages/workspace/generators/new.json b/docs/generated/packages/workspace/generators/new.json index 8ea49dbecb671..1d1d63cb935a6 100644 --- a/docs/generated/packages/workspace/generators/new.json +++ b/docs/generated/packages/workspace/generators/new.json @@ -80,6 +80,10 @@ "type": "boolean", "default": false }, + "serverRouting": { + "description": "Use the Angular Server Routing and App Engine APIs (Developer Preview).", + "type": "boolean" + }, "prefix": { "description": "The prefix to use for Angular component and directive selectors.", "type": "string" diff --git a/docs/generated/packages/workspace/generators/preset.json b/docs/generated/packages/workspace/generators/preset.json index 3ebfe23c9410b..89a5aedc41643 100644 --- a/docs/generated/packages/workspace/generators/preset.json +++ b/docs/generated/packages/workspace/generators/preset.json @@ -97,6 +97,10 @@ "type": "boolean", "default": false }, + "serverRouting": { + "description": "Use the Angular Server Routing and App Engine APIs (Developer Preview).", + "type": "boolean" + }, "prefix": { "description": "The prefix to use for Angular component and directive selectors.", "type": "string" diff --git a/docs/shared/guides/using-tailwind-css-with-angular-projects.md b/docs/shared/guides/using-tailwind-css-with-angular-projects.md index db30735fa6386..7156b5321d202 100644 --- a/docs/shared/guides/using-tailwind-css-with-angular-projects.md +++ b/docs/shared/guides/using-tailwind-css-with-angular-projects.md @@ -423,8 +423,7 @@ to have the following targets: "executor": "@nx/angular:package", "outputs": ["{workspaceRoot}/dist/libs/lib1"], "options": { - "project": "libs/lib1/ng-package.json", - "tailwindConfig": "libs/lib1/tailwind.config.js" + "project": "libs/lib1/ng-package.json" }, "configurations": { "production": { diff --git a/docs/shared/packages/angular/angular-nx-version-matrix.md b/docs/shared/packages/angular/angular-nx-version-matrix.md index a725c3b6bd918..6628023534f11 100644 --- a/docs/shared/packages/angular/angular-nx-version-matrix.md +++ b/docs/shared/packages/angular/angular-nx-version-matrix.md @@ -14,6 +14,7 @@ We provide a recommended version, and it is usually the latest minor version of | Angular Version | **Nx Version _(recommended)_** | Nx Version _(range)_ | | --------------- | ------------------------------ | ---------------------------------------- | +| ~19.0.0 | **latest** | >=20.2.0 <=latest | | ~18.2.0 | **latest** | >=19.6.0 <=latest | | ~18.1.0 | **latest** | >=19.5.0 <=latest | | ~18.0.0 | **latest** | >=19.1.0 <=latest | @@ -21,9 +22,9 @@ We provide a recommended version, and it is usually the latest minor version of | ~17.2.0 | **latest** | >=18.1.1 <=latest | | ~17.1.0 | **latest** | >=17.3.0 <=latest | | ~17.0.0 | **latest** | >=17.1.0 <=latest | -| ~16.2.0 | **latest** | >=16.7.0 <=latest | -| ~16.1.0 | **latest** | >=16.4.0 <=latest | -| ~16.0.0 | **latest** | >=16.1.0 <=latest | +| ~16.2.0 | **~20.1.0** | >=16.7.0 <20.2.0 | +| ~16.1.0 | **~20.1.0** | >=16.4.0 <20.2.0 | +| ~16.0.0 | **~20.1.0** | >=16.1.0 <20.2.0 | | ~15.2.0 | **~19.0.0** | >=15.8.0 <19.1.0 | | ~15.1.0 | **~19.0.0** | >=15.5.0 <19.1.0 | | ~15.0.0 | **~19.0.0** | >=15.2.0 <=15.4.8 \|\| >=15.7.0 <19.1.0 | diff --git a/e2e/angular/src/config.test.ts b/e2e/angular/src/config.test.ts index f19ae00cab59e..95e0d4c099f5d 100644 --- a/e2e/angular/src/config.test.ts +++ b/e2e/angular/src/config.test.ts @@ -80,8 +80,8 @@ const angularV1Json = (appName: string) => `{ }, { "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" + "maximumWarning": "4kb", + "maximumError": "8kb" } ], "outputHashing": "all" diff --git a/e2e/angular/src/ng-add.test.ts b/e2e/angular/src/ng-add.test.ts index e9839f2b6e3ba..5a500ae1939cd 100644 --- a/e2e/angular/src/ng-add.test.ts +++ b/e2e/angular/src/ng-add.test.ts @@ -199,8 +199,8 @@ describe('convert Angular CLI workspace to an Nx workspace', () => { }, { type: 'anyComponentStyle', - maximumWarning: '2kB', - maximumError: '4kB', + maximumWarning: '4kB', + maximumError: '8kB', }, ], outputHashing: 'all', diff --git a/e2e/angular/src/projects.test.ts b/e2e/angular/src/projects.test.ts index 158ede3dcd4fc..9a630ef1d9a19 100644 --- a/e2e/angular/src/projects.test.ts +++ b/e2e/angular/src/projects.test.ts @@ -163,6 +163,15 @@ describe('Angular Projects', () => { }, 1000000); it('should lint correctly with eslint and handle external HTML files and inline templates', async () => { + // disable the prefer-standalone rule for app1 which is not standalone + let app1EslintConfig = readFile(`${app1}/eslint.config.js`); + app1EslintConfig = app1EslintConfig.replace( + `'@angular-eslint/directive-selector': [`, + `'@angular-eslint/prefer-standalone': 'off', + '@angular-eslint/directive-selector': [` + ); + updateFile(`${app1}/eslint.config.js`, app1EslintConfig); + // check apps and lib pass linting for initial generated code runCLI(`run-many --target lint --projects=${app1},${lib1} --parallel`); @@ -380,6 +389,7 @@ describe('Angular Projects', () => { @Component({ selector: 'app-root', + standalone: false, templateUrl: './app.component.html', }) export class AppComponent { diff --git a/e2e/angular/src/tailwind.test.ts b/e2e/angular/src/tailwind.test.ts index 4072723b2e258..4d14252b41f2b 100644 --- a/e2e/angular/src/tailwind.test.ts +++ b/e2e/angular/src/tailwind.test.ts @@ -137,6 +137,7 @@ describe('Tailwind support', () => { @Component({ selector: '${project}-foo', + standalone: false, template: '', styles: [\` .custom-btn { @@ -176,7 +177,7 @@ describe('Tailwind support', () => { libSpacing: (typeof spacing)['root'] ) => { const builtComponentContent = readFile( - `dist/${lib}/esm2022/lib/foo.component.mjs` + `dist/${lib}/fesm2022/${project}-${lib}.mjs` ); let expectedStylesRegex = new RegExp( `styles: \\[\\"\\.custom\\-btn(\\[_ngcontent\\-%COMP%\\])?{margin:${libSpacing.md};padding:${libSpacing.sm}}(\\\\n)?\\"\\]` diff --git a/e2e/eslint/src/linter.test.ts b/e2e/eslint/src/linter.test.ts index 31a4c9a14dfc6..67fe89ec353e5 100644 --- a/e2e/eslint/src/linter.test.ts +++ b/e2e/eslint/src/linter.test.ts @@ -701,6 +701,7 @@ describe('Linter', () => { runCLI( `generate @nx/react:app --name=${myapp} --unitTestRunner=jest --linter eslint --directory="."` ); + runCLI('reset', { env: { CI: 'false' } }); verifySuccessfulStandaloneSetup(myapp); let appEslint = readJson('.eslintrc.json'); @@ -716,6 +717,7 @@ describe('Linter', () => { runCLI( `generate @nx/js:lib libs/${mylib} --unitTestRunner=jest --linter eslint` ); + runCLI('reset', { env: { CI: 'false' } }); verifySuccessfulMigratedSetup(myapp, mylib); appEslint = readJson(`.eslintrc.json`); @@ -737,6 +739,7 @@ describe('Linter', () => { runCLI( `generate @nx/angular:app --name=${myapp} --directory="." --linter eslint --no-interactive` ); + runCLI('reset', { env: { CI: 'false' } }); verifySuccessfulStandaloneSetup(myapp); let appEslint = readJson('.eslintrc.json'); @@ -751,6 +754,7 @@ describe('Linter', () => { runCLI( `generate @nx/js:lib libs/${mylib} --linter eslint --no-interactive` ); + runCLI('reset', { env: { CI: 'false' } }); verifySuccessfulMigratedSetup(myapp, mylib); appEslint = readJson(`.eslintrc.json`); @@ -770,6 +774,7 @@ describe('Linter', () => { runCLI( `generate @nx/node:app --name=${myapp} --linter eslint --directory="." --no-interactive` ); + runCLI('reset', { env: { CI: 'false' } }); verifySuccessfulStandaloneSetup(myapp); let appEslint = readJson('.eslintrc.json'); @@ -786,6 +791,7 @@ describe('Linter', () => { runCLI( `generate @nx/js:lib libs/${mylib} --linter eslint --no-interactive` ); + runCLI('reset', { env: { CI: 'false' } }); verifySuccessfulMigratedSetup(myapp, mylib); appEslint = readJson(`.eslintrc.json`); diff --git a/nx-dev/nx-dev/project.json b/nx-dev/nx-dev/project.json index 437abd191ed90..7a82090294fdf 100644 --- a/nx-dev/nx-dev/project.json +++ b/nx-dev/nx-dev/project.json @@ -115,7 +115,7 @@ "description": "Copy generated docs to build output" }, { - "command": "npx ts-node scripts/documentation/plugin-quality-indicators.ts", + "command": "npx ts-node -P scripts/tsconfig.scripts.json scripts/documentation/plugin-quality-indicators.ts", "description": "Fetch plugin data" }, { diff --git a/package.json b/package.json index f77522a8cb222..3a4514959abf0 100644 --- a/package.json +++ b/package.json @@ -27,19 +27,19 @@ }, "devDependencies": { "@actions/core": "^1.10.0", - "@angular-devkit/architect": "~0.1802.0", - "@angular-devkit/build-angular": "~18.2.0", - "@angular-devkit/core": "~18.2.0", - "@angular-devkit/schematics": "~18.2.0", - "@angular-eslint/eslint-plugin": "^18.3.0", - "@angular-eslint/eslint-plugin-template": "^18.3.0", - "@angular-eslint/template-parser": "^18.3.0", - "@angular/cli": "~18.2.0", - "@angular/common": "~18.2.0", - "@angular/compiler": "~18.2.0", - "@angular/compiler-cli": "~18.2.0", - "@angular/core": "~18.2.0", - "@angular/router": "~18.2.0", + "@angular-devkit/architect": "~0.1900.0", + "@angular-devkit/build-angular": "~19.0.0", + "@angular-devkit/core": "~19.0.0", + "@angular-devkit/schematics": "~19.0.0", + "@angular-eslint/eslint-plugin": "^19.0.0", + "@angular-eslint/eslint-plugin-template": "^19.0.0", + "@angular-eslint/template-parser": "^19.0.0", + "@angular/cli": "~19.0.0", + "@angular/common": "~19.0.0", + "@angular/compiler": "~19.0.0", + "@angular/compiler-cli": "~19.0.0", + "@angular/core": "~19.0.0", + "@angular/router": "~19.0.0", "@babel/core": "^7.23.2", "@babel/helper-create-regexp-features-plugin": "^7.22.9", "@babel/plugin-transform-runtime": "^7.23.2", @@ -105,14 +105,14 @@ "@rspack/dev-server": "1.0.9", "@rspack/plugin-minify": "^0.7.5", "@rspack/plugin-react-refresh": "^1.0.0", - "@schematics/angular": "~18.2.0", - "@storybook/addon-essentials": "^8.2.8", - "@storybook/addon-interactions": "^8.2.8", - "@storybook/core-server": "^8.2.8", - "@storybook/react": "^8.2.8", - "@storybook/react-vite": "^8.2.8", - "@storybook/react-webpack5": "^8.2.8", - "@storybook/types": "^8.2.8", + "@schematics/angular": "~19.0.0", + "@storybook/addon-essentials": "^8.4.6", + "@storybook/addon-interactions": "^8.4.6", + "@storybook/core-server": "^8.4.6", + "@storybook/react": "^8.4.6", + "@storybook/react-vite": "^8.4.6", + "@storybook/react-webpack5": "^8.4.6", + "@storybook/types": "^8.4.6", "@supabase/supabase-js": "^2.26.0", "@svgr/rollup": "^8.1.0", "@svgr/webpack": "^8.0.1", @@ -145,16 +145,16 @@ "@types/tmp": "^0.2.0", "@types/yargs": "17.0.10", "@types/yarnpkg__lockfile": "^1.1.5", - "@typescript-eslint/rule-tester": "^8.0.0", - "@typescript-eslint/type-utils": "^8.0.0", - "@typescript-eslint/utils": "^8.0.0", + "@typescript-eslint/rule-tester": "^8.13.0", + "@typescript-eslint/type-utils": "^8.13.0", + "@typescript-eslint/utils": "^8.13.0", "@xstate/immer": "0.3.1", "@xstate/inspect": "0.7.0", "@xstate/react": "3.0.1", "@zkochan/js-yaml": "0.0.7", "ai": "^2.2.10", "ajv": "^8.12.0", - "angular-eslint": "^18.3.0", + "angular-eslint": "^19.0.0", "autoprefixer": "10.4.13", "babel-jest": "29.7.0", "babel-loader": "^9.1.2", @@ -244,7 +244,7 @@ "mini-css-extract-plugin": "~2.4.7", "minimatch": "9.0.3", "next-sitemap": "^3.1.10", - "ng-packagr": "~18.2.0", + "ng-packagr": "~19.0.0", "node-fetch": "^2.6.7", "npm-package-arg": "11.0.1", "nuxt": "^3.10.0", @@ -283,7 +283,7 @@ "source-map": "0.7.3", "source-map-loader": "^5.0.0", "source-map-support": "0.5.19", - "storybook": "^8.2.8", + "storybook": "^8.4.6", "storybook-dark-mode": "^4.0.2", "style-loader": "^3.3.0", "tar-stream": "~2.2.0", @@ -298,8 +298,8 @@ "tsconfig-paths-webpack-plugin": "4.0.0", "typedoc": "0.25.12", "typedoc-plugin-markdown": "3.17.1", - "typescript": "~5.5.2", - "typescript-eslint": "^8.0.0", + "typescript": "~5.6.2", + "typescript-eslint": "^8.13.0", "unist-builder": "^4.0.0", "unzipper": "^0.10.11", "url-loader": "^4.1.1", diff --git a/packages/angular/executors.json b/packages/angular/executors.json index a567140e80d0d..74b31c86c0e88 100644 --- a/packages/angular/executors.json +++ b/packages/angular/executors.json @@ -33,7 +33,7 @@ "application": { "implementation": "./src/executors/application/application.impl", "schema": "./src/executors/application/schema.json", - "description": "Builds an Angular application using [esbuild](https://esbuild.github.io/) with integrated SSR and prerendering capabilities. _Note: this is only supported in Angular versions >= 17.0.0_." + "description": "Builds an Angular application using [esbuild](https://esbuild.github.io/) with integrated SSR and prerendering capabilities." }, "extract-i18n": { "implementation": "./src/executors/extract-i18n/extract-i18n.impl", diff --git a/packages/angular/generators.json b/packages/angular/generators.json index 6e348790ef9cc..608fa8bd70226 100644 --- a/packages/angular/generators.json +++ b/packages/angular/generators.json @@ -36,7 +36,7 @@ "convert-to-application-executor": { "factory": "./src/generators/convert-to-application-executor/convert-to-application-executor", "schema": "./src/generators/convert-to-application-executor/schema.json", - "description": "Converts projects to use the `@nx/angular:application` executor or the `@angular-devkit/build-angular:application` builder. _Note: this is only supported in Angular versions >= 17.0.0_." + "description": "Converts projects to use the `@nx/angular:application` executor or the `@angular-devkit/build-angular:application` builder." }, "directive": { "factory": "./src/generators/directive/directive", diff --git a/packages/angular/migrations.json b/packages/angular/migrations.json index a55fd0f10c013..97a800742ad04 100644 --- a/packages/angular/migrations.json +++ b/packages/angular/migrations.json @@ -278,6 +278,33 @@ "version": "20.2.0-beta.2", "description": "Update the withModuleFederation import use @nx/module-federation/angular.", "factory": "./src/migrations/update-20-2-0/migrate-with-mf-import-to-new-package" + }, + "update-angular-cli-version-19-0-0": { + "cli": "nx", + "version": "20.2.0-beta.5", + "requires": { + "@angular/core": ">=19.0.0" + }, + "description": "Update the @angular/cli package version to ~19.0.0.", + "factory": "./src/migrations/update-20-2-0/update-angular-cli" + }, + "add-localize-polyfill-to-targets": { + "cli": "nx", + "version": "20.2.0-beta.5", + "requires": { + "@angular/core": ">=19.0.0-rc.1" + }, + "description": "Add the '@angular/localize/init' polyfill to the 'polyfills' option of targets using esbuild-based executors.", + "factory": "./src/migrations/update-20-2-0/add-localize-polyfill-to-targets" + }, + "update-angular-ssr-imports-to-use-node-entry-point": { + "cli": "nx", + "version": "20.2.0-beta.5", + "requires": { + "@angular/core": ">=19.0.0-rc.1" + }, + "description": "Update '@angular/ssr' import paths to use the new '/node' entry point when 'CommonEngine' is detected.", + "factory": "./src/migrations/update-20-2-0/update-angular-ssr-imports-to-use-node-entry-point" } }, "packageJsonUpdates": { @@ -1171,7 +1198,7 @@ } } }, - "20.2.0": { + "20.2.0-module-federation": { "version": "20.2.0-beta.3", "packages": { "@module-federation/enhanced": { @@ -1191,6 +1218,129 @@ "alwaysAddToPackageJson": false } } + }, + "20.2.0": { + "version": "20.2.0-beta.5", + "x-prompt": "Do you want to update the Angular version to v19?", + "requires": { + "@angular/core": ">=18.2.0 <19.0.0" + }, + "packages": { + "@angular-devkit/build-angular": { + "version": "~19.0.0", + "alwaysAddToPackageJson": false + }, + "@angular-devkit/core": { + "version": "~19.0.0", + "alwaysAddToPackageJson": false + }, + "@angular-devkit/schematics": { + "version": "~19.0.0", + "alwaysAddToPackageJson": false + }, + "@angular/build": { + "version": "~19.0.0", + "alwaysAddToPackageJson": false + }, + "@angular/pwa": { + "version": "~19.0.0", + "alwaysAddToPackageJson": false + }, + "@angular/ssr": { + "version": "~19.0.0", + "alwaysAddToPackageJson": false + }, + "@schematics/angular": { + "version": "~19.0.0", + "alwaysAddToPackageJson": false + }, + "@angular-devkit/architect": { + "version": "~0.1900.0", + "alwaysAddToPackageJson": false + }, + "@angular-devkit/build-webpack": { + "version": "~0.1900.0", + "alwaysAddToPackageJson": false + }, + "@angular/core": { + "version": "~19.0.0", + "alwaysAddToPackageJson": true + }, + "@angular/material": { + "version": "~19.0.0", + "alwaysAddToPackageJson": false + }, + "@angular/cdk": { + "version": "~19.0.0", + "alwaysAddToPackageJson": false + }, + "ng-packagr": { + "version": "~19.0.0", + "alwaysAddToPackageJson": false + }, + "zone.js": { + "version": "~0.15.0", + "alwaysAddToPackageJson": false + } + } + }, + "20.2.0-analog": { + "version": "20.2.0-beta.5", + "packages": { + "@analogjs/vitest-angular": { + "version": "~1.10.0-beta.6", + "alwaysAddToPackageJson": false + }, + "@analogjs/vite-plugin-angular": { + "version": "~1.10.0-beta.6", + "alwaysAddToPackageJson": false + } + } + }, + "20.2.0-jest": { + "version": "20.2.0-beta.5", + "requires": { + "@angular-devkit/build-angular": ">=15.0.0 <20.0.0", + "@angular/compiler-cli": ">=15.0.0 <20.0.0", + "@angular/core": ">=15.0.0 <20.0.0", + "@angular/platform-browser-dynamic": ">=15.0.0 <20.0.0", + "jest": "^29.0.0" + }, + "packages": { + "jest-preset-angular": { + "version": "~14.4.0", + "alwaysAddToPackageJson": false + } + } + }, + "20.2.0-angular-eslint": { + "version": "20.2.0-beta.5", + "requires": { + "eslint": "^8.57.0 || ^9.0.0", + "@angular/core": ">= 19.0.0 < 20.0.0" + }, + "packages": { + "angular-eslint": { + "version": "^19.0.0", + "alwaysAddToPackageJson": false + }, + "@angular-eslint/eslint-plugin": { + "version": "^19.0.0", + "alwaysAddToPackageJson": false + }, + "@angular-eslint/eslint-plugin-template": { + "version": "^19.0.0", + "alwaysAddToPackageJson": false + }, + "@angular-eslint/template-parser": { + "version": "^19.0.0", + "alwaysAddToPackageJson": false + }, + "@angular-eslint/utils": { + "version": "^19.0.0", + "alwaysAddToPackageJson": false + } + } } } } diff --git a/packages/angular/package.json b/packages/angular/package.json index 10f1b1c0723c1..54b76661b4752 100644 --- a/packages/angular/package.json +++ b/packages/angular/package.json @@ -51,7 +51,6 @@ "@phenomnomnominal/tsquery": "~5.0.1", "@typescript-eslint/type-utils": "^8.0.0", "chalk": "^4.1.0", - "find-cache-dir": "^3.3.2", "magic-string": "~0.30.2", "minimatch": "9.0.3", "semver": "^7.5.3", diff --git a/packages/angular/src/builders/dev-server/lib/normalize-options.ts b/packages/angular/src/builders/dev-server/lib/normalize-options.ts index 7cc9cb1f1ecd7..6df81466fd712 100644 --- a/packages/angular/src/builders/dev-server/lib/normalize-options.ts +++ b/packages/angular/src/builders/dev-server/lib/normalize-options.ts @@ -1,3 +1,4 @@ +import { getInstalledAngularVersionInfo } from '../../../executors/utilities/angular-version-utils'; import type { NormalizedSchema, Schema, @@ -12,12 +13,15 @@ export function normalizeOptions(schema: Schema): NormalizedSchema { delete (schema as SchemaWithBrowserTarget).browserTarget; } + const { major: angularMajorVersion } = getInstalledAngularVersionInfo(); + return { ...schema, buildTarget, host: schema.host ?? 'localhost', port: schema.port ?? 4200, liveReload: schema.liveReload ?? true, + hmr: angularMajorVersion < 19 ? schema.hmr ?? false : undefined, open: schema.open ?? false, ssl: schema.ssl ?? false, }; diff --git a/packages/angular/src/builders/dev-server/lib/validate-options.ts b/packages/angular/src/builders/dev-server/lib/validate-options.ts index 8042f83414103..f9beebfffa594 100644 --- a/packages/angular/src/builders/dev-server/lib/validate-options.ts +++ b/packages/angular/src/builders/dev-server/lib/validate-options.ts @@ -4,18 +4,7 @@ import { getInstalledAngularVersionInfo } from '../../../executors/utilities/ang import type { Schema } from '../schema'; export function validateOptions(options: Schema): void { - const { major: angularMajorVersion, version: angularVersion } = - getInstalledAngularVersionInfo(); - - if (lt(angularVersion, '16.1.0') && options.forceEsbuild) { - throw new Error(stripIndents`The "forceEsbuild" option is only supported in Angular >= 16.1.0. You are currently using "${angularVersion}". - You can resolve this error by removing the "forceEsbuild" option or by migrating to Angular 16.1.0.`); - } - - if (angularMajorVersion < 17 && options.esbuildMiddleware?.length > 0) { - throw new Error(stripIndents`The "esbuildMiddleware" option is only supported in Angular >= 17.0.0. You are currently using "${angularVersion}". - You can resolve this error by removing the "esbuildMiddleware" option or by migrating to Angular 17.0.0.`); - } + const { version: angularVersion } = getInstalledAngularVersionInfo(); if (lt(angularVersion, '17.2.0') && options.prebundle) { throw new Error(stripIndents`The "prebundle" option is only supported in Angular >= 17.2.0. You are currently using "${angularVersion}". diff --git a/packages/angular/src/builders/dev-server/schema.json b/packages/angular/src/builders/dev-server/schema.json index 0ec002f57d541..f6e00605962c0 100644 --- a/packages/angular/src/builders/dev-server/schema.json +++ b/packages/angular/src/builders/dev-server/schema.json @@ -99,8 +99,7 @@ }, "hmr": { "type": "boolean", - "description": "Enable hot module replacement.", - "default": false + "description": "Enable hot module replacement. It defaults to `false` in Angular versions < 19.0.0. Otherwise, the value will be `undefined`." }, "watch": { "type": "boolean", @@ -113,7 +112,7 @@ }, "forceEsbuild": { "type": "boolean", - "description": "Force the development server to use the 'browser-esbuild' builder when building. This is a developer preview option for the esbuild-based build system. _Note: this is only supported in Angular versions >= 16.1.0_.", + "description": "Force the development server to use the 'browser-esbuild' builder when building. This is a developer preview option for the esbuild-based build system.", "default": false }, "inspect": { @@ -151,7 +150,7 @@ "x-priority": "important" }, "esbuildMiddleware": { - "description": "A list of HTTP request middleware functions. _Note: this is only supported in Angular versions >= 17.0.0_.", + "description": "A list of HTTP request middleware functions.", "type": "array", "items": { "type": "string", diff --git a/packages/angular/src/executors/application/application.impl.ts b/packages/angular/src/executors/application/application.impl.ts index a6e4ba4b9493b..b0a570ab92448 100644 --- a/packages/angular/src/executors/application/application.impl.ts +++ b/packages/angular/src/executors/application/application.impl.ts @@ -1,3 +1,4 @@ +import type { buildApplication as buildApplicationFn } from '@angular-devkit/build-angular'; import type { ExecutorContext } from '@nx/devkit'; import type { DependentBuildableProjectNode } from '@nx/js/src/utils/buildable-libs-utils'; import { createBuilderContext } from 'nx/src/adapter/ngcli-adapter'; @@ -9,14 +10,15 @@ import { loadPlugins, } from '../utilities/esbuild-extensions'; import type { ApplicationExecutorOptions } from './schema'; +import { normalizeOptions } from './utils/normalize-options'; import { validateOptions } from './utils/validate-options'; -import type { buildApplication as buildApplicationFn } from '@angular-devkit/build-angular'; export default async function* applicationExecutor( options: ApplicationExecutorOptions, context: ExecutorContext ): ReturnType { validateOptions(options); + options = normalizeOptions(options); const { buildLibsFromSource = true, @@ -42,7 +44,6 @@ export default async function* applicationExecutor( ? await loadIndexHtmlTransformer(indexHtmlTransformerPath, options.tsConfig) : undefined; - const { buildApplication } = await import('@angular-devkit/build-angular'); const builderContext = await createBuilderContext( { builderName: 'application', @@ -54,12 +55,14 @@ export default async function* applicationExecutor( const { version: angularVersion } = getInstalledAngularVersionInfo(); if (gte(angularVersion, '17.1.0')) { + const { buildApplication } = await import('@angular-devkit/build-angular'); return yield* buildApplication(delegateExecutorOptions, builderContext, { codePlugins: plugins, indexHtmlTransformer, }); } + const { buildApplication } = require('@angular-devkit/build-angular'); return yield* buildApplication( delegateExecutorOptions, builderContext, diff --git a/packages/angular/src/executors/application/schema.json b/packages/angular/src/executors/application/schema.json index 23be6da59d4a3..8939f2d3a82b8 100644 --- a/packages/angular/src/executors/application/schema.json +++ b/packages/angular/src/executors/application/schema.json @@ -1,7 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema", "title": "Schema for Nx Application Executor", - "description": "Builds an Angular application using [esbuild](https://esbuild.github.io/) with integrated SSR and prerendering capabilities. _Note: this is only supported in Angular versions >= 17.0.0_.", + "description": "Builds an Angular application using [esbuild](https://esbuild.github.io/) with integrated SSR and prerendering capabilities.", "examplesFile": "../../../docs/application-executor-examples.md", "outputCapture": "direct-nodejs", "type": "object", @@ -20,7 +20,18 @@ }, "server": { "type": "string", - "description": "The full path for the server entry point to the application, relative to the current workspace." + "description": "The full path for the server entry point to the application, relative to the current workspace.", + "oneOf": [ + { + "type": "string", + "description": "The full path for the server entry point to the application, relative to the current workspace." + }, + { + "const": false, + "type": "boolean", + "description": "Indicates that a server entry point is not provided. _Note: this is only supported in Angular versions >= 19.0.0_." + } + ] }, "polyfills": { "description": "A list of polyfills to include in the build. Can be a full path for a file, relative to the current workspace or module specifier. Example: 'zone.js'.", @@ -39,6 +50,31 @@ "type": "string", "description": "Customize the base path for the URLs of resources in 'index.html' and component stylesheets. This option is only necessary for specific deployment scenarios, such as with Angular Elements or when utilizing different CDN locations. _Note: this is only supported in Angular versions >= 17.3.0_." }, + "security": { + "description": "Security features to protect against XSS and other common attacks. _Note: this is only supported in Angular versions >= 19.0.0_.", + "type": "object", + "additionalProperties": false, + "properties": { + "autoCsp": { + "description": "Enables automatic generation of a hash-based Strict Content Security Policy (https://web.dev/articles/strict-csp#choose-hash) based on scripts in index.html. Will default to true once we are out of experimental/preview phases. It defaults to `false`.", + "oneOf": [ + { + "type": "object", + "properties": { + "unsafeEval": { + "type": "boolean", + "description": "Include the `unsafe-eval` directive (https://web.dev/articles/strict-csp#remove-eval) in the auto-CSP. Please only enable this if you are absolutely sure that you need to, as allowing calls to eval will weaken the XSS defenses provided by the auto-CSP. It default to `false`." + } + }, + "additionalProperties": false + }, + { + "type": "boolean" + } + ] + } + } + }, "scripts": { "description": "Global scripts to be included in the build.", "type": "array", @@ -127,6 +163,34 @@ "type": "string" }, "default": [] + }, + "sass": { + "description": "Options to pass to the sass preprocessor. _Note: this is only supported in Angular versions >= 19.0.0_.", + "type": "object", + "properties": { + "fatalDeprecations": { + "description": "A set of deprecations to treat as fatal. If a deprecation warning of any provided type is encountered during compilation, the compiler will error instead. If a Version is provided, then all deprecations that were active in that compiler version will be treated as fatal.", + "type": "array", + "items": { + "type": "string" + } + }, + "silenceDeprecations": { + "description": " A set of active deprecations to ignore. If a deprecation warning of any provided type is encountered during compilation, the compiler will ignore it instead.", + "type": "array", + "items": { + "type": "string" + } + }, + "futureDeprecations": { + "description": "A set of future deprecations to opt into early. Future deprecations passed here will be treated as active by the compiler, emitting warnings as necessary.", + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -480,8 +544,7 @@ "default": [] }, "prerender": { - "description": "Prerender (SSG) pages of your application during build time.", - "default": false, + "description": "Prerender (SSG) pages of your application during build time. It defaults to `false` in Angular versions < 19.0.0. Otherwise, the value will be `undefined`.", "oneOf": [ { "type": "boolean", @@ -518,6 +581,11 @@ "entry": { "type": "string", "description": "The server entry-point that when executed will spawn the web server." + }, + "experimentalPlatform": { + "description": "Specifies the platform for which the server bundle is generated. This affects the APIs and modules available in the server-side code. \n\n- `node`: (Default) Generates a bundle optimized for Node.js environments. \n- `neutral`: Generates a platform-neutral bundle suitable for environments like edge workers, and other serverless platforms. This option avoids using Node.js-specific APIs, making the bundle more portable. \n\nPlease note that this feature does not provide polyfills for Node.js modules. Additionally, it is experimental, and the feature may undergo changes in future versions. _Note: this is only supported in Angular versions >= 19.0.0_.", + "default": "node", + "enum": ["node", "neutral"] } }, "additionalProperties": false @@ -526,8 +594,12 @@ }, "appShell": { "type": "boolean", - "description": "Generates an application shell during build time.", - "default": false + "description": "Generates an application shell during build time. It defaults to `false` in Angular versions < 19.0.0. Otherwise, the value will be `undefined`." + }, + "outputMode": { + "type": "string", + "description": "Defines the build output target. 'static': Generates a static site for deployment on any static hosting service. 'server': Produces an application designed for deployment on a server that supports server-side rendering (SSR). _Note: this is only supported in Angular versions >= 19.0.0_.", + "enum": ["static", "server"] }, "buildLibsFromSource": { "type": "boolean", diff --git a/packages/angular/src/executors/application/utils/normalize-options.ts b/packages/angular/src/executors/application/utils/normalize-options.ts new file mode 100644 index 0000000000000..b6e4cb5bc6138 --- /dev/null +++ b/packages/angular/src/executors/application/utils/normalize-options.ts @@ -0,0 +1,38 @@ +import { getInstalledAngularVersionInfo } from '../../utilities/angular-version-utils'; +import type { ApplicationExecutorOptions } from '../schema'; + +export function normalizeOptions( + options: ApplicationExecutorOptions +): ApplicationExecutorOptions { + const { major: angularMajorVersion } = getInstalledAngularVersionInfo(); + + /** + * We can't set the default values for `security.autoCsp` and + * `security.autoCsp.unsafeEval` in the schema because our current schema + * parsing would (incorrectly?) default `security` to an object with the + * `autoCsp` property set to `false`. This would be problematic because the + * option is not supported in Angular versions < 19. So, we don't set those + * defaults in the schema and we normalize them here correctly. + */ + let security: ApplicationExecutorOptions['security'] = options.security; + if (angularMajorVersion >= 19) { + if (typeof security === 'object') { + if (security.autoCsp === undefined) { + security.autoCsp = false; + } else if ( + typeof security.autoCsp === 'object' && + security.autoCsp.unsafeEval === undefined + ) { + security.autoCsp.unsafeEval = false; + } + } + } + + return { + ...options, + appShell: angularMajorVersion < 19 ? options.appShell ?? false : undefined, + prerender: + angularMajorVersion < 19 ? options.prerender ?? false : undefined, + security, + }; +} diff --git a/packages/angular/src/executors/application/utils/validate-options.ts b/packages/angular/src/executors/application/utils/validate-options.ts index fa73e6f41811e..832399dfdcef7 100644 --- a/packages/angular/src/executors/application/utils/validate-options.ts +++ b/packages/angular/src/executors/application/utils/validate-options.ts @@ -3,14 +3,7 @@ import { getInstalledAngularVersionInfo } from '../../utilities/angular-version- import type { ApplicationExecutorOptions } from '../schema'; export function validateOptions(options: ApplicationExecutorOptions): void { - const { major: angularMajorVersion, version: angularVersion } = - getInstalledAngularVersionInfo(); - - if (angularMajorVersion < 17) { - throw new Error( - `The "application" executor requires Angular version 17 or greater. You are currently using version ${angularVersion}.` - ); - } + const { version: angularVersion } = getInstalledAngularVersionInfo(); if (lt(angularVersion, '17.1.0')) { if (options.loader) { @@ -78,4 +71,36 @@ export function validateOptions(options: ApplicationExecutorOptions): void { ); } } + + if (lt(angularVersion, '19.0.0')) { + if (options.outputMode) { + throw new Error( + `The "outputMode" option requires Angular version 19.0.0 or greater. You are currently using version ${angularVersion}.` + ); + } + + if (options.stylePreprocessorOptions?.sass) { + throw new Error( + `The "stylePreprocessorOptions.sass" option requires Angular version 19.0.0 or greater. You are currently using version ${angularVersion}.` + ); + } + + if (typeof options.ssr === 'object' && options.ssr?.experimentalPlatform) { + throw new Error( + `The "ssr.experimentalPlatform" option requires Angular version 19.0.0 or greater. You are currently using version ${angularVersion}.` + ); + } + + if (options.security !== undefined) { + throw new Error( + `The "security" option requires Angular version 19.0.0 or greater. You are currently using version ${angularVersion}.` + ); + } + + if (typeof options.server === 'boolean' && options.server === false) { + throw new Error( + `The "false" value for the "server" option requires Angular version 19.0.0 or greater. You are currently using version ${angularVersion}.` + ); + } + } } diff --git a/packages/angular/src/executors/browser-esbuild/browser-esbuild.impl.ts b/packages/angular/src/executors/browser-esbuild/browser-esbuild.impl.ts index e9f0f1bc6cd70..c0e9b016443a3 100644 --- a/packages/angular/src/executors/browser-esbuild/browser-esbuild.impl.ts +++ b/packages/angular/src/executors/browser-esbuild/browser-esbuild.impl.ts @@ -1,8 +1,7 @@ import type { buildEsbuildBrowser as buildEsbuildBrowserFn } from '@angular-devkit/build-angular/src/builders/browser-esbuild'; -import { stripIndents, type ExecutorContext } from '@nx/devkit'; +import type { ExecutorContext } from '@nx/devkit'; import type { DependentBuildableProjectNode } from '@nx/js/src/utils/buildable-libs-utils'; import { createBuilderContext } from 'nx/src/adapter/ngcli-adapter'; -import { getInstalledAngularVersionInfo } from '../utilities/angular-version-utils'; import { createTmpTsConfigForBuildableLibs } from '../utilities/buildable-libs'; import { loadPlugins } from '../utilities/esbuild-extensions'; import type { EsBuildSchema } from './schema'; @@ -11,15 +10,6 @@ export default async function* esbuildExecutor( options: EsBuildSchema, context: ExecutorContext ): ReturnType { - if (options.plugins) { - const { major: angularMajorVersion, version: angularVersion } = - getInstalledAngularVersionInfo(); - if (angularMajorVersion < 17) { - throw new Error(stripIndents`The "plugins" option is only supported in Angular >= 17.0.0. You are currently using "${angularVersion}". - You can resolve this error by removing the "plugins" option or by migrating to Angular 17.0.0.`); - } - } - options.buildLibsFromSource ??= true; const { diff --git a/packages/angular/src/executors/browser-esbuild/schema.json b/packages/angular/src/executors/browser-esbuild/schema.json index fd0f82487a647..59ceaff81b844 100644 --- a/packages/angular/src/executors/browser-esbuild/schema.json +++ b/packages/angular/src/executors/browser-esbuild/schema.json @@ -441,7 +441,7 @@ "default": true }, "plugins": { - "description": "A list of ESBuild plugins. _Note: this is only supported in Angular versions >= 17.0.0_.", + "description": "A list of ESBuild plugins.", "type": "array", "items": { "oneOf": [ diff --git a/packages/angular/src/executors/extract-i18n/extract-i18n.impl.ts b/packages/angular/src/executors/extract-i18n/extract-i18n.impl.ts index 2c506951e895d..05842fcf85760 100644 --- a/packages/angular/src/executors/extract-i18n/extract-i18n.impl.ts +++ b/packages/angular/src/executors/extract-i18n/extract-i18n.impl.ts @@ -56,7 +56,9 @@ export default async function* extractI18nExecutor( function getDelegateBuilderOptions( options: ExtractI18nExecutorOptions ): ExtractI18nBuilderOptions { - const delegateBuilderOptions: ExtractI18nBuilderOptions = { ...options }; + const delegateBuilderOptions: ExtractI18nBuilderOptions & { + browserTarget?: string; + } = { ...options }; const { major: angularMajorVersion } = getInstalledAngularVersionInfo(); if (angularMajorVersion <= 17) { diff --git a/packages/angular/src/executors/module-federation-ssr-dev-server/module-federation-ssr-dev-server.impl.ts b/packages/angular/src/executors/module-federation-ssr-dev-server/module-federation-ssr-dev-server.impl.ts index 61d68fa73916b..9810eba64d438 100644 --- a/packages/angular/src/executors/module-federation-ssr-dev-server/module-federation-ssr-dev-server.impl.ts +++ b/packages/angular/src/executors/module-federation-ssr-dev-server/module-federation-ssr-dev-server.impl.ts @@ -1,31 +1,31 @@ +import { executeSSRDevServerBuilder } from '@angular-devkit/build-angular'; import { type ExecutorContext, logger } from '@nx/devkit'; -import { existsSync } from 'fs'; -import { readProjectsConfigurationFromProjectGraph } from 'nx/src/project-graph/project-graph'; -import { extname, join } from 'path'; import { - getDynamicMfManifestFile, - validateDevRemotes, -} from '../../builders/utilities/module-federation'; -import type { Schema } from './schema'; + combineAsyncIterables, + createAsyncIterable, + mapAsyncIterable, +} from '@nx/devkit/src/utils/async-iterable'; +import { eachValueFrom } from '@nx/devkit/src/utils/rxjs-for-await'; import { getModuleFederationConfig, getRemotes, parseStaticSsrRemotesConfig, startSsrRemoteProxies, } from '@nx/module-federation/src/utils'; +import { waitForPortOpen } from '@nx/web/src/utils/wait-for-port-open'; +import { existsSync } from 'fs'; +import { createBuilderContext } from 'nx/src/adapter/ngcli-adapter'; +import { readProjectsConfigurationFromProjectGraph } from 'nx/src/project-graph/project-graph'; +import { extname, join } from 'path'; +import { + getDynamicMfManifestFile, + validateDevRemotes, +} from '../../builders/utilities/module-federation'; import { buildStaticRemotes } from './lib/build-static-remotes'; +import { normalizeOptions } from './lib/normalize-options'; import { startRemotes } from './lib/start-dev-remotes'; import { startStaticRemotes } from './lib/start-static-remotes'; -import { - combineAsyncIterables, - createAsyncIterable, - mapAsyncIterable, -} from '@nx/devkit/src/utils/async-iterable'; -import { eachValueFrom } from '@nx/devkit/src/utils/rxjs-for-await'; -import { createBuilderContext } from 'nx/src/adapter/ngcli-adapter'; -import { normalizeOptions } from './lib/normalize-options'; -import { waitForPortOpen } from '@nx/web/src/utils/wait-for-port-open'; -import { getInstalledAngularVersionInfo } from '../utilities/angular-version-utils'; +import type { Schema } from './schema'; export async function* moduleFederationSsrDevServerExecutor( schema: Schema, @@ -34,12 +34,6 @@ export async function* moduleFederationSsrDevServerExecutor( const nxBin = require.resolve('nx/bin/nx'); const options = normalizeOptions(schema); - const { major: angularMajorVersion } = getInstalledAngularVersionInfo(); - const { executeSSRDevServerBuilder } = - angularMajorVersion >= 17 - ? require('@angular-devkit/build-angular') - : require('@nguniversal/builders'); - const currIter = eachValueFrom( executeSSRDevServerBuilder( options, diff --git a/packages/angular/src/executors/module-federation-ssr-dev-server/schema.d.ts b/packages/angular/src/executors/module-federation-ssr-dev-server/schema.d.ts index 843777f3c6a03..4c43a78789aae 100644 --- a/packages/angular/src/executors/module-federation-ssr-dev-server/schema.d.ts +++ b/packages/angular/src/executors/module-federation-ssr-dev-server/schema.d.ts @@ -1,23 +1,11 @@ import { type DevRemoteDefinition } from '../../builders/utilities/module-federation'; +import type { SSRDevServerBuilderOptions } from '@angular-devkit/build-angular'; -export interface Schema { - browserTarget: string; - serverTarget: string; - host?: string; - port?: number; - progress: boolean; - open?: boolean; - publicHost?: string; - ssl?: boolean; - sslKey?: string; - sslCert?: string; - proxyConfig?: string; +export interface Schema extends SSRDevServerBuilderOptions { devRemotes?: DevRemoteDefinition[]; skipRemotes?: string[]; - verbose: boolean; pathToManifestFile?: string; parallel?: number; staticRemotesPort?: number; - parallel?: number; isInitialHost?: boolean; } diff --git a/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/ng-packagr.ts b/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/ng-packagr.ts index b5a0c099c834e..5b2e216f61227 100644 --- a/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/ng-packagr.ts +++ b/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/ng-packagr.ts @@ -1,51 +1,35 @@ import { NgPackagr, ngPackagr } from 'ng-packagr'; -import type { BuildAngularLibraryExecutorOptions } from '../../package/schema'; import { getInstalledAngularVersionInfo } from '../../utilities/angular-version-utils'; -export async function getNgPackagrInstance( - options: BuildAngularLibraryExecutorOptions -): Promise { +export async function getNgPackagrInstance(): Promise { const { major: angularMajorVersion } = getInstalledAngularVersionInfo(); - if (angularMajorVersion >= 17) { - const { WRITE_BUNDLES_TRANSFORM } = await import( - './v17+/ng-package/entry-point/write-bundles.di.js' - ); - const { WRITE_PACKAGE_TRANSFORM } = await import( - './v17+/ng-package/entry-point/write-package.di.js' - ); + if (angularMajorVersion >= 19) { const { STYLESHEET_PROCESSOR } = await import( '../../utilities/ng-packagr/stylesheet-processor.di.js' ); const packagr = ngPackagr(); - packagr.withProviders([ - WRITE_BUNDLES_TRANSFORM, - WRITE_PACKAGE_TRANSFORM, - STYLESHEET_PROCESSOR, - ]); + packagr.withProviders([STYLESHEET_PROCESSOR]); return packagr; } - const { NX_ENTRY_POINT_PROVIDERS } = await import( - './pre-v17/ng-package/entry-point/entry-point.di.js' + const { WRITE_BUNDLES_TRANSFORM } = await import( + './pre-v19/ng-package/entry-point/write-bundles.di.js' ); - const { nxProvideOptions } = await import( - './pre-v17/ng-package/options.di.js' + const { WRITE_PACKAGE_TRANSFORM } = await import( + './pre-v19/ng-package/entry-point/write-package.di.js' ); - const { NX_PACKAGE_PROVIDERS, NX_PACKAGE_TRANSFORM } = await import( - './pre-v17/ng-package/package.di.js' + const { STYLESHEET_PROCESSOR } = await import( + '../../utilities/ng-packagr/stylesheet-processor.di.js' ); - const packagr = new NgPackagr([ - ...NX_PACKAGE_PROVIDERS, - ...NX_ENTRY_POINT_PROVIDERS, - nxProvideOptions({ - tailwindConfig: options.tailwindConfig, - watch: options.watch, - }), + const packagr = ngPackagr(); + packagr.withProviders([ + WRITE_BUNDLES_TRANSFORM, + WRITE_PACKAGE_TRANSFORM, + STYLESHEET_PROCESSOR, ]); - packagr.withBuildTransform(NX_PACKAGE_TRANSFORM.provide); return packagr; } diff --git a/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/pre-v17/ng-package/entry-point/compile-ngc.di.ts b/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/pre-v17/ng-package/entry-point/compile-ngc.di.ts deleted file mode 100644 index 075f7ddf83cff..0000000000000 --- a/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/pre-v17/ng-package/entry-point/compile-ngc.di.ts +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Adapted from the original ng-packagr source. - * - * Changes made: - * - Use our own compileNgcTransformFactory instead of the one provided by ng-packagr. - * - Use NX_STYLESHEET_PROCESSOR instead of STYLESHEET_PROCESSOR. - * - Use NX_STYLESHEET_PROCESSOR_TOKEN instead of STYLESHEET_PROCESSOR_TOKEN. - * - USE NX_OPTIONS_TOKEN instead of OPTIONS_TOKEN. - */ - -import { InjectionToken, Provider } from 'injection-js'; -import type { Transform } from 'ng-packagr/lib/graph/transform'; -import { provideTransform } from 'ng-packagr/lib/graph/transform.di'; -import { - NX_STYLESHEET_PROCESSOR, - NX_STYLESHEET_PROCESSOR_TOKEN, -} from '../../styles/stylesheet-processor.di'; -import { NX_OPTIONS_TOKEN } from '../options.di'; -import { nxCompileNgcTransformFactory } from './compile-ngc.transform'; - -export const NX_COMPILE_NGC_TOKEN = new InjectionToken( - `nx.v1.compileNgc` -); -export const NX_COMPILE_NGC_TRANSFORM = provideTransform({ - provide: NX_COMPILE_NGC_TOKEN, - useFactory: nxCompileNgcTransformFactory, - deps: [NX_STYLESHEET_PROCESSOR_TOKEN, NX_OPTIONS_TOKEN], -}); -export const NX_COMPILE_NGC_PROVIDERS: Provider[] = [ - NX_STYLESHEET_PROCESSOR, - NX_COMPILE_NGC_TRANSFORM, -]; diff --git a/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/pre-v17/ng-package/entry-point/compile-ngc.transform.ts b/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/pre-v17/ng-package/entry-point/compile-ngc.transform.ts deleted file mode 100644 index eb711bf06403a..0000000000000 --- a/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/pre-v17/ng-package/entry-point/compile-ngc.transform.ts +++ /dev/null @@ -1,87 +0,0 @@ -/** - * Adapted from the original ngPackagr source. - * - * Changes made: - * - Use our own StylesheetProcessor files instead of the ones provide by ng-packagr. - * - Excludes the ngcc compilation for faster builds (angular < v16) - * - Support ESM2020 for Angular < 16. - */ - -import type { Transform } from 'ng-packagr/lib/graph/transform'; -import { transformFromPromise } from 'ng-packagr/lib/graph/transform'; -import { - EntryPointNode, - isEntryPoint, - isEntryPointInProgress, - isPackage, - PackageNode, -} from 'ng-packagr/lib/ng-package/nodes'; -import { setDependenciesTsConfigPaths } from 'ng-packagr/lib/ts/tsconfig'; -import * as path from 'path'; -import * as ts from 'typescript'; -import { getInstalledAngularVersionInfo } from '../../../../../utilities/angular-version-utils'; -import { compileSourceFiles } from '../../ngc/compile-source-files'; -import { StylesheetProcessor as StylesheetProcessorClass } from '../../styles/stylesheet-processor'; -import { NgPackagrOptions } from '../options.di'; - -export const nxCompileNgcTransformFactory = ( - StylesheetProcessor: typeof StylesheetProcessorClass, - options: NgPackagrOptions -): Transform => { - return transformFromPromise(async (graph) => { - const entryPoints: EntryPointNode[] = graph.filter(isEntryPoint); - const entryPoint: EntryPointNode = graph.find(isEntryPointInProgress()); - const ngPackageNode: PackageNode = graph.find(isPackage); - const projectBasePath = ngPackageNode.data.primary.basePath; - - try { - // Add paths mappings for dependencies - const tsConfig = setDependenciesTsConfigPaths( - entryPoint.data.tsConfig, - entryPoints - ); - - const angularVersion = getInstalledAngularVersionInfo(); - - // Compile TypeScript sources - const { declarations } = entryPoint.data.destinationFiles; - const esmModulePath = - angularVersion.major < 16 - ? (entryPoint.data.destinationFiles as any).esm2020 - : entryPoint.data.destinationFiles.esm2022; - const { basePath, cssUrl, styleIncludePaths } = - entryPoint.data.entryPoint; - const { moduleResolutionCache } = entryPoint.cache; - - entryPoint.cache.stylesheetProcessor ??= new StylesheetProcessor( - projectBasePath, - basePath, - cssUrl, - styleIncludePaths, - options.cacheEnabled && options.cacheDirectory, - options.tailwindConfig - ) as any; - - await compileSourceFiles( - graph, - tsConfig, - moduleResolutionCache, - options, - { - outDir: path.dirname(esmModulePath), - declarationDir: path.dirname(declarations), - declaration: true, - target: - angularVersion.major >= 16 - ? ts.ScriptTarget.ES2022 - : ts.ScriptTarget.ES2020, - }, - entryPoint.cache.stylesheetProcessor as any - ); - } catch (error) { - throw error; - } - - return graph; - }); -}; diff --git a/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/pre-v17/ng-package/entry-point/entry-point.di.ts b/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/pre-v17/ng-package/entry-point/entry-point.di.ts deleted file mode 100644 index 35595cf3f0e24..0000000000000 --- a/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/pre-v17/ng-package/entry-point/entry-point.di.ts +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Adapted from the original ng-packagr source. - * - * Changes made: - * - Provide our own entryPointTransformFactory function. - * - Use NX_COMPILE_NGC_TOKEN instead of COMPILE_NGC_TOKEN. - * - Use NX_COMPILE_NGC_PROVIDERS instead of COMPILE_NGC_PROVIDERS. - * - Removed usage of WRITE_BUNDLES_TRANSFORM_TOKEN and WRITE_BUNDLES_TRANSFORM. - */ - -import type { Provider } from 'injection-js'; -import { InjectionToken } from 'injection-js'; -import type { Transform } from 'ng-packagr/lib/graph/transform'; -import { provideTransform } from 'ng-packagr/lib/graph/transform.di'; -import { - NX_COMPILE_NGC_PROVIDERS, - NX_COMPILE_NGC_TOKEN, -} from './compile-ngc.di'; -import { nxEntryPointTransformFactory } from './entry-point.transform'; -import { - NX_WRITE_PACKAGE_TRANSFORM, - NX_WRITE_PACKAGE_TRANSFORM_TOKEN, -} from './write-package.di'; - -export const NX_ENTRY_POINT_TRANSFORM_TOKEN = new InjectionToken( - `nx.v1.entryPointTransform` -); - -export const NX_ENTRY_POINT_TRANSFORM = provideTransform({ - provide: NX_ENTRY_POINT_TRANSFORM_TOKEN, - useFactory: nxEntryPointTransformFactory, - deps: [NX_COMPILE_NGC_TOKEN, NX_WRITE_PACKAGE_TRANSFORM_TOKEN], -}); - -export const NX_ENTRY_POINT_PROVIDERS: Provider[] = [ - NX_ENTRY_POINT_TRANSFORM, - ...NX_COMPILE_NGC_PROVIDERS, - NX_WRITE_PACKAGE_TRANSFORM, -]; diff --git a/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/pre-v17/ng-package/entry-point/entry-point.transform.ts b/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/pre-v17/ng-package/entry-point/entry-point.transform.ts deleted file mode 100644 index 36f8a92da170a..0000000000000 --- a/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/pre-v17/ng-package/entry-point/entry-point.transform.ts +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Adapted from the original ng-packagr source. - * - * Changes made: - * - Removed writing bundles as we don't generate them for incremental builds. - */ - -import { logger } from '@nx/devkit'; -import { STATE_DONE } from 'ng-packagr/lib/graph/node'; -import { isInProgress } from 'ng-packagr/lib/graph/select'; -import type { Transform } from 'ng-packagr/lib/graph/transform'; -import { transformFromPromise } from 'ng-packagr/lib/graph/transform'; -import { byEntryPoint } from 'ng-packagr/lib/ng-package/nodes'; -import { pipe } from 'rxjs'; - -/** - * A re-write of the `transformSources()` script that transforms an entry point from sources to distributable format. - * - * Sources are TypeScript source files accompanied by HTML templates and xCSS stylesheets. - * See the Angular Package Format for a detailed description of what the distributables include. - * - * The current transformation pipeline can be thought of as: - * - * - clean - * - compileTs - * - downlevelTs - * - relocateSourceMaps - * - writePackage - * - copyStagedFiles (esm, dts, sourcemaps) - * - writePackageJson - * - * The transformation pipeline is pluggable through the dependency injection system. - * Sub-transformations are passed to this factory function as arguments. - * - * @param compileTs Transformation compiling typescript sources to ES2022 modules. - * @param writeBundles Transformation flattening ES2022 modules to ESM2022, UMD, and minified UMD. - */ -export const nxEntryPointTransformFactory = ( - compileTs: Transform, - writePackage: Transform -): Transform => - pipe( - transformFromPromise(async (graph) => { - // Peek the first entry point from the graph - const entryPoint = graph.find(byEntryPoint().and(isInProgress)); - logger.info( - '\n------------------------------------------------------------------------------' - ); - logger.info( - `Building entry point '${entryPoint.data.entryPoint.moduleId}'` - ); - logger.info( - '------------------------------------------------------------------------------' - ); - }), - // TypeScript sources compilation - compileTs, - // After TypeScript: write package - writePackage, - transformFromPromise(async (graph) => { - const entryPoint = graph.find(byEntryPoint().and(isInProgress)); - entryPoint.state = STATE_DONE; - }) - ); diff --git a/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/pre-v17/ng-package/entry-point/write-package.di.ts b/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/pre-v17/ng-package/entry-point/write-package.di.ts deleted file mode 100644 index b1038c6931613..0000000000000 --- a/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/pre-v17/ng-package/entry-point/write-package.di.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Adapted from the original ng-packagr source. - * - * Changes made: - * - Provide our own writePackageTransform function. - * - USE NX_OPTIONS_TOKEN instead of OPTIONS_TOKEN. - */ - -import { InjectionToken } from 'injection-js'; -import type { Transform } from 'ng-packagr/lib/graph/transform'; -import { - provideTransform, - TransformProvider, -} from 'ng-packagr/lib/graph/transform.di'; -import { NX_OPTIONS_TOKEN } from '../options.di'; -import { nxWritePackageTransform } from './write-package.transform'; - -export const NX_WRITE_PACKAGE_TRANSFORM_TOKEN = new InjectionToken( - `nx.v1.writePackageTransform` -); -export const NX_WRITE_PACKAGE_TRANSFORM: TransformProvider = provideTransform({ - provide: NX_WRITE_PACKAGE_TRANSFORM_TOKEN, - useFactory: nxWritePackageTransform, - deps: [NX_OPTIONS_TOKEN], -}); diff --git a/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/pre-v17/ng-package/entry-point/write-package.transform.ts b/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/pre-v17/ng-package/entry-point/write-package.transform.ts deleted file mode 100644 index d8e72b072e967..0000000000000 --- a/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/pre-v17/ng-package/entry-point/write-package.transform.ts +++ /dev/null @@ -1,489 +0,0 @@ -/** - * Adapted from the original ng-packagr. - * - * Changes made: - * - Change the package.json metadata to only use the ESM2022 output. - * - Change the package.json metadata to only use the ESM2020 output (Angular < 16). - */ - -import { logger } from '@nx/devkit'; -import { BuildGraph } from 'ng-packagr/lib/graph/build-graph'; -import { Node } from 'ng-packagr/lib/graph/node'; -import { transformFromPromise } from 'ng-packagr/lib/graph/transform'; -import { NgEntryPoint } from 'ng-packagr/lib/ng-package/entry-point/entry-point'; -import { - EntryPointNode, - fileUrl, - isEntryPointInProgress, - isEntryPoint, - isPackage, - PackageNode, -} from 'ng-packagr/lib/ng-package/nodes'; -import { NgPackagrOptions } from 'ng-packagr/lib/ng-package/options.di'; -import { NgPackage } from 'ng-packagr/lib/ng-package/package'; -import { - copyFile, - exists, - readFile, - rmdir, - stat, - writeFile, -} from 'ng-packagr/lib/utils/fs'; -import { globFiles } from 'ng-packagr/lib/utils/glob'; -import { ensureUnixPath } from 'ng-packagr/lib/utils/path'; -import { AssetPattern } from 'ng-packagr/ng-package.schema'; -import * as path from 'path'; -import { - getInstalledAngularVersionInfo, - VersionInfo, -} from '../../../../../utilities/angular-version-utils'; - -export const nxWritePackageTransform = (options: NgPackagrOptions) => - transformFromPromise(async (graph) => { - const entryPoint: EntryPointNode = graph.find(isEntryPointInProgress()); - const ngEntryPoint: NgEntryPoint = entryPoint.data.entryPoint; - const ngPackageNode: PackageNode = graph.find(isPackage); - const ngPackage = ngPackageNode.data; - const { destinationFiles } = entryPoint.data; - - const angularVersion = getInstalledAngularVersionInfo(); - - if (!ngEntryPoint.isSecondaryEntryPoint) { - logger.log('Copying assets'); - - try { - await copyAssets(graph, entryPoint, ngPackageNode, angularVersion); - } catch (error) { - throw error; - } - } - - // 6. WRITE PACKAGE.JSON - const relativeUnixFromDestPath = (filePath: string) => - ensureUnixPath(path.relative(ngEntryPoint.destinationPath, filePath)); - - if (!ngEntryPoint.isSecondaryEntryPoint) { - try { - logger.info('Writing package manifest'); - if (!options.watch) { - const primary = ngPackageNode.data.primary; - await writeFile( - path.join(primary.destinationPath, '.npmignore'), - `# Nested package.json's are only needed for development.\n**/package.json` - ); - } - - await writePackageJson( - ngEntryPoint, - ngPackage, - { - // backward compat for Angular < 16 - ...(angularVersion.major < 16 - ? { - module: relativeUnixFromDestPath( - (destinationFiles as any).esm2020 - ), - es2020: relativeUnixFromDestPath( - (destinationFiles as any).esm2020 - ), - esm2020: relativeUnixFromDestPath( - (destinationFiles as any).esm2020 - ), - } - : { - module: relativeUnixFromDestPath(destinationFiles.esm2022), - }), - typings: relativeUnixFromDestPath(destinationFiles.declarations), - exports: generatePackageExports( - ngEntryPoint, - graph, - angularVersion - ), - // webpack v4+ specific flag to enable advanced optimizations and code splitting - sideEffects: ngEntryPoint.packageJson.sideEffects ?? false, - }, - !!options.watch - ); - } catch (error) { - throw error; - } - } else if (ngEntryPoint.isSecondaryEntryPoint) { - if (options.watch) { - // Update the watch version of the primary entry point `package.json` file. - // this is needed because of Webpack's 5 `cachemanagedpaths` - // https://github.com/ng-packagr/ng-packagr/issues/2069 - const primary = ngPackageNode.data.primary; - const packageJsonPath = path.join( - primary.destinationPath, - 'package.json' - ); - - if (await exists(packageJsonPath)) { - const packageJson = JSON.parse( - await readFile(packageJsonPath, { encoding: 'utf8' }) - ); - packageJson.version = generateWatchVersion(); - await writeFile( - path.join(primary.destinationPath, 'package.json'), - JSON.stringify(packageJson, undefined, 2) - ); - } - } - - // Write a package.json in each secondary entry-point - // This is need for esbuild to secondary entry-points in dist correctly. - await writeFile( - path.join(ngEntryPoint.destinationPath, 'package.json'), - JSON.stringify( - { - module: relativeUnixFromDestPath( - angularVersion.major < 16 - ? (destinationFiles as any).esm2020 - : destinationFiles.esm2022 - ), - }, - undefined, - 2 - ) - ); - } - - logger.info(`Built ${ngEntryPoint.moduleId}`); - - return graph; - }); - -type AssetEntry = Exclude; - -async function copyAssets( - graph: BuildGraph, - entryPointNode: EntryPointNode, - ngPackageNode: PackageNode, - angularVersion: VersionInfo -): Promise { - const ngPackage = ngPackageNode.data; - - const globsForceIgnored: string[] = [ - '.gitkeep', - '**/.DS_Store', - '**/Thumbs.db', - `${ngPackage.dest}/**`, - ]; - - const assets: AssetEntry[] = []; - - for (const assetPath of ngPackage.assets) { - let asset: AssetEntry; - if (typeof assetPath === 'object') { - asset = { ...assetPath }; - } else { - const [isDir, isFile] = await stat(path.join(ngPackage.src, assetPath)) - .then((stats) => [stats.isDirectory(), stats.isFile()]) - .catch(() => [false, false]); - if (isDir) { - asset = { glob: '**/*', input: assetPath, output: assetPath }; - } else if (isFile) { - // filenames are their own glob - asset = { - glob: path.basename(assetPath), - input: path.dirname(assetPath), - output: path.dirname(assetPath), - }; - } else { - asset = { glob: assetPath, input: '/', output: '/' }; - } - } - - asset.input = path.join(ngPackage.src, asset.input); - asset.output = path.join(ngPackage.dest, asset.output); - - const isAncestorPath = (target: string, datum: string) => - path.relative(datum, target).startsWith('..'); - if (isAncestorPath(asset.input, ngPackage.src)) { - throw new Error( - 'Cannot read assets from a location outside of the project root.' - ); - } - if (isAncestorPath(asset.output, ngPackage.dest)) { - throw new Error( - 'Cannot write assets to a location outside of the output path.' - ); - } - - assets.push(asset); - } - - for (const asset of assets) { - const globOptions: Parameters[1] = { - cwd: asset.input, - ignore: [...(asset.ignore ?? []), ...globsForceIgnored], - dot: true, - }; - - if (angularVersion.major < 16) { - // versions lower than v16 support these properties - (globOptions as any).cache = (ngPackageNode.cache as any).globCache; - (globOptions as any).nodir = true; - (globOptions as any).follow = asset.followSymlinks; - } else { - // starting in v16 these properties are supported - globOptions.onlyFiles = true; - globOptions.followSymbolicLinks = asset.followSymlinks; - } - - const filePaths = await globFiles(asset.glob, globOptions); - for (const filePath of filePaths) { - const fileSrcFullPath = path.join(asset.input, filePath); - const fileDestFullPath = path.join(asset.output, filePath); - const nodeUri = fileUrl(ensureUnixPath(fileSrcFullPath)); - let node = graph.get(nodeUri); - if (!node) { - node = new Node(nodeUri); - graph.put(node); - } - entryPointNode.dependsOn(node); - await copyFile(fileSrcFullPath, fileDestFullPath); - } - } -} - -/** - * Creates and writes a `package.json` file of the entry point used by the `node_module` - * resolution strategies. - * - * #### Example - * - * A consumer of the entry point depends on it by `import {..} from '@my/module/id';`. - * The module id `@my/module/id` will be resolved to the `package.json` file that is written by - * this build step. - * The properties `main`, `module`, `typings` (and so on) in the `package.json` point to the - * flattened JavaScript bundles, type definitions, (...). - * - * @param entryPoint An entry point of an Angular package / library - * @param additionalProperties Additional properties, e.g. binary artefacts (bundle files), to merge into `package.json` - */ -async function writePackageJson( - entryPoint: NgEntryPoint, - pkg: NgPackage, - additionalProperties: { - [key: string]: string | boolean | string[] | ConditionalExport; - }, - isWatchMode: boolean -): Promise { - // set additional properties - const packageJson = { ...entryPoint.packageJson, ...additionalProperties }; - - // read tslib version from `@angular/compiler` so that our tslib - // version at least matches that of angular if we use require('tslib').version - // it will get what installed and not the minimum version nor if it is a `~` or `^` - // this is only required for primary - if (isWatchMode) { - // Needed because of Webpack's 5 `cachemanagedpaths` - // https://github.com/angular/angular-cli/issues/20962 - packageJson.version = generateWatchVersion(); - } - - if ( - !packageJson.peerDependencies?.tslib && - !packageJson.dependencies?.tslib - ) { - const { - peerDependencies: angularPeerDependencies = {}, - dependencies: angularDependencies = {}, - } = require('@angular/compiler/package.json'); - const tsLibVersion = - angularPeerDependencies.tslib || angularDependencies.tslib; - - if (tsLibVersion) { - packageJson.dependencies = { - ...packageJson.dependencies, - tslib: tsLibVersion, - }; - } - } else if (packageJson.peerDependencies?.tslib) { - logger.warn( - `'tslib' is no longer recommended to be used as a 'peerDependencies'. Moving it to 'dependencies'.` - ); - packageJson.dependencies = { - ...(packageJson.dependencies || {}), - tslib: packageJson.peerDependencies.tslib, - }; - - delete packageJson.peerDependencies.tslib; - } - - // Verify non-peerDependencies as they can easily lead to duplicate installs or version conflicts - // in the node_modules folder of an application - const allowedList = pkg.allowedNonPeerDependencies.map( - (value) => new RegExp(value) - ); - try { - checkNonPeerDependencies(packageJson, 'dependencies', allowedList); - } catch (e) { - await rmdir(entryPoint.destinationPath, { recursive: true }); - throw e; - } - - // Removes scripts from package.json after build - if (packageJson.scripts) { - if (pkg.keepLifecycleScripts !== true) { - logger.info( - `Removing scripts section in package.json as it's considered a potential security vulnerability.` - ); - delete packageJson.scripts; - } else { - logger.warn( - `You enabled keepLifecycleScripts explicitly. The scripts section in package.json will be published to npm.` - ); - } - } - - // keep the dist package.json clean - // this will not throw if ngPackage field does not exist - delete packageJson.ngPackage; - - const packageJsonPropertiesToDelete = [ - 'stylelint', - 'prettier', - 'browserslist', - 'devDependencies', - 'jest', - 'workspaces', - 'husky', - ]; - - for (const prop of packageJsonPropertiesToDelete) { - if (prop in packageJson) { - delete packageJson[prop]; - logger.info(`Removing ${prop} section in package.json.`); - } - } - - packageJson.name = entryPoint.moduleId; - await writeFile( - path.join(entryPoint.destinationPath, 'package.json'), - JSON.stringify(packageJson, undefined, 2) - ); -} - -function checkNonPeerDependencies( - packageJson: Record, - property: string, - allowed: RegExp[] -) { - if (!packageJson[property]) { - return; - } - - for (const dep of Object.keys(packageJson[property])) { - if (!allowed.some((regex) => regex.test(dep))) { - logger.warn( - `Distributing npm packages with '${property}' is not recommended. Please consider adding ${dep} to 'peerDependencies' or remove it from '${property}'.` - ); - throw new Error( - `Dependency ${dep} must be explicitly allowed using the "allowedNonPeerDependencies" option.` - ); - } - } -} - -type PackageExports = Record; - -/** - * Type describing the conditional exports descriptor for an entry-point. - * https://nodejs.org/api/packages.html#packages_conditional_exports - */ -type ConditionalExport = { - types?: string; - esm2022?: string; - esm?: string; - default?: string; - - // backward compat for Angular < 16 - node?: string; - esm2020?: string; - es2020?: string; - es2015?: string; -}; - -/** - * Generates the `package.json` package exports following APF v13. - * This is supposed to match with: https://github.com/angular/angular/blob/e0667efa6eada64d1fb8b143840689090fc82e52/packages/bazel/src/ng_package/packager.ts#L415. - */ -function generatePackageExports( - { destinationPath, packageJson }: NgEntryPoint, - graph: BuildGraph, - angularVersion: VersionInfo -): PackageExports { - const exports: PackageExports = packageJson.exports - ? JSON.parse(JSON.stringify(packageJson.exports)) - : {}; - - const insertMappingOrError = ( - subpath: string, - mapping: ConditionalExport - ) => { - exports[subpath] ??= {}; - const subpathExport = exports[subpath]; - - // Go through all conditions that should be inserted. If the condition is already - // manually set of the subpath export, we throw an error. In general, we allow for - // additional conditions to be set. These will always precede the generated ones. - for (const conditionName of Object.keys(mapping)) { - if (subpathExport[conditionName] !== undefined) { - logger.warn( - `Found a conflicting export condition for "${subpath}". The "${conditionName}" ` + - `condition would be overridden by ng-packagr. Please unset it.` - ); - } - - // **Note**: The order of the conditions is preserved even though we are setting - // the conditions once at a time (the latest assignment will be at the end). - subpathExport[conditionName] = mapping[conditionName]; - } - }; - - const relativeUnixFromDestPath = (filePath: string) => - './' + ensureUnixPath(path.relative(destinationPath, filePath)); - - insertMappingOrError('./package.json', { default: './package.json' }); - - const entryPoints = graph.filter(isEntryPoint); - for (const entryPoint of entryPoints) { - const { destinationFiles, isSecondaryEntryPoint } = - entryPoint.data.entryPoint; - const subpath = isSecondaryEntryPoint - ? `./${destinationFiles.directory}` - : '.'; - - // backward compat for Angular < 16 - const mapping = - angularVersion.major < 16 - ? { - types: relativeUnixFromDestPath(destinationFiles.declarations), - es2020: relativeUnixFromDestPath((destinationFiles as any).esm2020), - esm2020: relativeUnixFromDestPath( - (destinationFiles as any).esm2020 - ), - default: relativeUnixFromDestPath( - (destinationFiles as any).esm2020 - ), - } - : { - esm2022: relativeUnixFromDestPath(destinationFiles.esm2022), - esm: relativeUnixFromDestPath(destinationFiles.esm2022), - default: relativeUnixFromDestPath(destinationFiles.esm2022), - }; - - insertMappingOrError(subpath, mapping); - } - - return exports; -} - -/** - * Generates a new version for the package `package.json` when runing in watch mode. - */ -function generateWatchVersion() { - return `0.0.0-watch+${Date.now()}`; -} diff --git a/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/pre-v17/ng-package/options.di.ts b/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/pre-v17/ng-package/options.di.ts deleted file mode 100644 index 517ee478affa7..0000000000000 --- a/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/pre-v17/ng-package/options.di.ts +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Adapted from the original ng-packagr. - * - * Changes made: - * - Use our own options interface to add support for tailwindConfig. - */ - -import findCacheDirectory from 'find-cache-dir'; -import { InjectionToken, Provider, ValueProvider } from 'injection-js'; -import { NgPackagrOptions as NgPackagrOptionsBase } from 'ng-packagr/lib/ng-package/options.di'; -import { tmpdir } from 'os'; -import { resolve } from 'path'; - -export interface NgPackagrOptions extends NgPackagrOptionsBase { - tailwindConfig?: string; -} - -export const NX_OPTIONS_TOKEN = new InjectionToken( - `nx.v1.options` -); - -export const nxProvideOptions = ( - options: NgPackagrOptions = {} -): ValueProvider => ({ - provide: NX_OPTIONS_TOKEN, - useValue: normalizeOptions(options), -}); - -export const NX_DEFAULT_OPTIONS_PROVIDER: Provider = nxProvideOptions(); - -function normalizeOptions(options: NgPackagrOptions = {}) { - const ciEnv = process.env['CI']; - const isCI = ciEnv?.toLowerCase() === 'true' || ciEnv === '1'; - const { cacheEnabled = !isCI, cacheDirectory = findCachePath() } = options; - - return { - ...options, - cacheEnabled, - cacheDirectory, - }; -} - -function findCachePath(): string { - const name = 'ng-packagr'; - - return findCacheDirectory({ name }) || resolve(tmpdir(), name); -} diff --git a/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/pre-v17/ng-package/package.di.ts b/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/pre-v17/ng-package/package.di.ts deleted file mode 100644 index 53c385333eb24..0000000000000 --- a/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/pre-v17/ng-package/package.di.ts +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Adapted from the original ng-packagr. - * - * Changes made: - * - Use NX_ENTRY_POINT_TRANSFORM_TOKEN instead of ENTRY_POINT_TRANSFORM_TOKEN. - * - USE NX_OPTIONS_TOKEN instead of OPTIONS_TOKEN. - * - USE NX_DEFAULT_OPTIONS_PROVIDER instead of DEFAULT_OPTIONS_PROVIDER. - */ - -import type { Provider } from 'injection-js'; -import { InjectionToken } from 'injection-js'; -import type { Transform } from 'ng-packagr/lib/graph/transform'; -import { provideTransform } from 'ng-packagr/lib/graph/transform.di'; -import { - ANALYSE_SOURCES_TOKEN, - ANALYSE_SOURCES_TRANSFORM, -} from 'ng-packagr/lib/ng-package/entry-point/analyse-sources.di'; -import { - INIT_TS_CONFIG_TOKEN, - INIT_TS_CONFIG_TRANSFORM, -} from 'ng-packagr/lib/ng-package/entry-point/init-tsconfig.di'; -import { packageTransformFactory } from 'ng-packagr/lib/ng-package/package.transform'; -import { PROJECT_TOKEN } from 'ng-packagr/lib/project.di'; -import { NX_ENTRY_POINT_TRANSFORM_TOKEN } from './entry-point/entry-point.di'; -import { NX_DEFAULT_OPTIONS_PROVIDER, NX_OPTIONS_TOKEN } from './options.di'; - -export const NX_PACKAGE_TRANSFORM_TOKEN = new InjectionToken( - `nx.v1.packageTransform` -); - -export const NX_PACKAGE_TRANSFORM = provideTransform({ - provide: NX_PACKAGE_TRANSFORM_TOKEN, - useFactory: packageTransformFactory, - deps: [ - PROJECT_TOKEN, - NX_OPTIONS_TOKEN, - INIT_TS_CONFIG_TOKEN, - ANALYSE_SOURCES_TOKEN, - NX_ENTRY_POINT_TRANSFORM_TOKEN, - ], -}); - -export const NX_PACKAGE_PROVIDERS: Provider[] = [ - NX_PACKAGE_TRANSFORM, - NX_DEFAULT_OPTIONS_PROVIDER, - INIT_TS_CONFIG_TRANSFORM, - ANALYSE_SOURCES_TRANSFORM, -]; diff --git a/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/pre-v17/ngc/compile-source-files.ts b/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/pre-v17/ngc/compile-source-files.ts deleted file mode 100644 index 6c3465b6e93b5..0000000000000 --- a/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/pre-v17/ngc/compile-source-files.ts +++ /dev/null @@ -1,272 +0,0 @@ -/** - * Adapted from the original ng-packagr source. - * - * Changes made: - * - Use custom StylesheetProcessor instead of the one provided by ng-packagr. - * - Support Angular Compiler `incrementalDriver` for Angular < 16. - */ - -import { BuildGraph } from 'ng-packagr/lib/graph/build-graph'; -import { - EntryPointNode, - PackageNode, - isEntryPointInProgress, - isPackage, -} from 'ng-packagr/lib/ng-package/nodes'; -import { - augmentProgramWithVersioning, - cacheCompilerHost, -} from 'ng-packagr/lib/ts/cache-compiler-host'; -import * as log from 'ng-packagr/lib/utils/log'; -import { join } from 'node:path'; -import * as ts from 'typescript'; -import { getInstalledAngularVersionInfo } from '../../../../utilities/angular-version-utils'; -import { loadEsmModule } from '../../../../utilities/module-loader'; -import { NgPackagrOptions } from '../ng-package/options.di'; -import { StylesheetProcessor } from '../styles/stylesheet-processor'; - -export async function compileSourceFiles( - graph: BuildGraph, - tsConfig: any, - moduleResolutionCache: ts.ModuleResolutionCache, - options: NgPackagrOptions, - extraOptions?: Partial, - stylesheetProcessor?: StylesheetProcessor -) { - const { NgtscProgram, formatDiagnostics } = await loadEsmModule( - '@angular/compiler-cli' - ); - const { cacheDirectory, watch, cacheEnabled } = options; - const tsConfigOptions: ts.CompilerOptions = { - ...tsConfig.options, - ...extraOptions, - }; - const entryPoint: EntryPointNode = graph.find(isEntryPointInProgress()); - const ngPackageNode: PackageNode = graph.find(isPackage); - const inlineStyleLanguage = ngPackageNode.data.inlineStyleLanguage; - - const cacheDir = cacheEnabled && cacheDirectory; - if (cacheDir) { - tsConfigOptions.incremental ??= true; - tsConfigOptions.tsBuildInfoFile ??= join( - cacheDir, - `tsbuildinfo/${entryPoint.data.entryPoint.flatModuleFile}.tsbuildinfo` - ); - } - - const tsCompilerHost = cacheCompilerHost( - graph, - entryPoint, - tsConfigOptions, - moduleResolutionCache, - stylesheetProcessor as any, - inlineStyleLanguage - ); - - const cache = entryPoint.cache; - const sourceFileCache = cache.sourcesFileCache; - let usingBuildInfo = false; - - let oldBuilder = cache.oldBuilder; - if (!oldBuilder && cacheDir) { - oldBuilder = ts.readBuilderProgram(tsConfigOptions, tsCompilerHost); - usingBuildInfo = true; - } - - // Create the Angular specific program that contains the Angular compiler - const angularProgram = new NgtscProgram( - tsConfig.rootNames, - tsConfigOptions, - tsCompilerHost, - cache.oldNgtscProgram - ); - - const angularCompiler = angularProgram.compiler; - const { ignoreForDiagnostics, ignoreForEmit } = angularCompiler; - - // SourceFile versions are required for builder programs. - // The wrapped host inside NgtscProgram adds additional files that will not have versions. - const typeScriptProgram = angularProgram.getTsProgram(); - augmentProgramWithVersioning(typeScriptProgram); - - let builder: ts.BuilderProgram | ts.EmitAndSemanticDiagnosticsBuilderProgram; - if (watch || cacheDir) { - builder = cache.oldBuilder = - ts.createEmitAndSemanticDiagnosticsBuilderProgram( - typeScriptProgram, - tsCompilerHost, - oldBuilder - ); - cache.oldNgtscProgram = angularProgram; - } else { - builder = ts.createEmitAndSemanticDiagnosticsBuilderProgram( - typeScriptProgram, - tsCompilerHost - ); - } - - // Update semantic diagnostics cache - const affectedFiles = new Set(); - - // Analyze affected files when in watch mode for incremental type checking - if ('getSemanticDiagnosticsOfNextAffectedFile' in builder) { - // eslint-disable-next-line no-constant-condition - while (true) { - const result = builder.getSemanticDiagnosticsOfNextAffectedFile( - undefined, - (sourceFile) => { - // If the affected file is a TTC shim, add the shim's original source file. - // This ensures that changes that affect TTC are typechecked even when the changes - // are otherwise unrelated from a TS perspective and do not result in Ivy codegen changes. - // For example, changing @Input property types of a directive used in another component's - // template. - if ( - ignoreForDiagnostics.has(sourceFile) && - sourceFile.fileName.endsWith('.ngtypecheck.ts') - ) { - // This file name conversion relies on internal compiler logic and should be converted - // to an official method when available. 15 is length of `.ngtypecheck.ts` - const originalFilename = sourceFile.fileName.slice(0, -15) + '.ts'; - const originalSourceFile = builder.getSourceFile(originalFilename); - if (originalSourceFile) { - affectedFiles.add(originalSourceFile); - } - - return true; - } - - return false; - } - ); - - if (!result) { - break; - } - - affectedFiles.add(result.affected as ts.SourceFile); - } - - // Add all files with associated template type checking files. - // Stored TS build info does not have knowledge of the AOT compiler or the typechecking state of the templates. - // To ensure that errors are reported correctly, all AOT component diagnostics need to be analyzed even if build - // info is present. - if (usingBuildInfo) { - for (const sourceFile of builder.getSourceFiles()) { - if ( - ignoreForDiagnostics.has(sourceFile) && - sourceFile.fileName.endsWith('.ngtypecheck.ts') - ) { - // This file name conversion relies on internal compiler logic and should be converted - // to an official method when available. 15 is length of `.ngtypecheck.ts` - const originalFilename = sourceFile.fileName.slice(0, -15) + '.ts'; - const originalSourceFile = builder.getSourceFile(originalFilename); - if (originalSourceFile) { - affectedFiles.add(originalSourceFile); - } - } - } - } - } - - // Collect program level diagnostics - const allDiagnostics: ts.Diagnostic[] = [ - ...angularCompiler.getOptionDiagnostics(), - ...builder.getOptionsDiagnostics(), - ...builder.getGlobalDiagnostics(), - ]; - - // Required to support asynchronous resource loading - // Must be done before creating transformers or getting template diagnostics - await angularCompiler.analyzeAsync(); - - // Collect source file specific diagnostics - for (const sourceFile of builder.getSourceFiles()) { - if (ignoreForDiagnostics.has(sourceFile)) { - continue; - } - - allDiagnostics.push( - ...builder.getDeclarationDiagnostics(sourceFile), - ...builder.getSyntacticDiagnostics(sourceFile), - ...builder.getSemanticDiagnostics(sourceFile) - ); - - // Declaration files cannot have template diagnostics - if (sourceFile.isDeclarationFile) { - continue; - } - - // Only request Angular template diagnostics for affected files to avoid - // overhead of template diagnostics for unchanged files. - if (affectedFiles.has(sourceFile)) { - const angularDiagnostics = angularCompiler.getDiagnosticsForFile( - sourceFile, - affectedFiles.size === 1 - ? /** OptimizeFor.SingleFile **/ 0 - : /** OptimizeFor.WholeProgram */ 1 - ); - - allDiagnostics.push(...angularDiagnostics); - sourceFileCache.updateAngularDiagnostics(sourceFile, angularDiagnostics); - } - } - - const otherDiagnostics = []; - const errorDiagnostics = []; - for (const diagnostic of allDiagnostics) { - if (diagnostic.category === ts.DiagnosticCategory.Error) { - errorDiagnostics.push(diagnostic); - } else { - otherDiagnostics.push(diagnostic); - } - } - - if (otherDiagnostics.length) { - log.msg(formatDiagnostics(errorDiagnostics)); - } - - if (errorDiagnostics.length) { - throw new Error(formatDiagnostics(errorDiagnostics)); - } - - const transformers = angularCompiler.prepareEmit().transformers; - - if ('getSemanticDiagnosticsOfNextAffectedFile' in builder) { - while ( - builder.emitNextAffectedFile( - (fileName, data, writeByteOrderMark, onError, sourceFiles) => { - if (fileName.endsWith('.tsbuildinfo')) { - tsCompilerHost.writeFile( - fileName, - data, - writeByteOrderMark, - onError, - sourceFiles - ); - } - } - ) - ) { - // empty - } - } - - const angularVersion = getInstalledAngularVersionInfo(); - const incrementalCompilation: typeof angularCompiler.incrementalCompilation = - angularVersion.major < 16 - ? (angularCompiler as any).incrementalDriver - : angularCompiler.incrementalCompilation; - - for (const sourceFile of builder.getSourceFiles()) { - if (ignoreForEmit.has(sourceFile)) { - continue; - } - - if (incrementalCompilation.safeToSkipEmit(sourceFile)) { - continue; - } - - builder.emit(sourceFile, undefined, undefined, undefined, transformers); - incrementalCompilation.recordSuccessfulEmit(sourceFile); - } -} diff --git a/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/pre-v17/styles/stylesheet-processor.di.ts b/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/pre-v17/styles/stylesheet-processor.di.ts deleted file mode 100644 index 868e90db24c6d..0000000000000 --- a/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/pre-v17/styles/stylesheet-processor.di.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Adapted from the original ng-packagr source. - * - * Changes made: - * - Use our own StylesheetProcessor instead of the one provided by ng-packagr. - */ - -import { FactoryProvider, InjectionToken } from 'injection-js'; -import { StylesheetProcessor } from './stylesheet-processor'; - -export const NX_STYLESHEET_PROCESSOR_TOKEN = - new InjectionToken(`nx.v1.stylesheetProcessor`); - -export const NX_STYLESHEET_PROCESSOR: FactoryProvider = { - provide: NX_STYLESHEET_PROCESSOR_TOKEN, - useFactory: () => StylesheetProcessor, - deps: [], -}; diff --git a/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/pre-v17/styles/stylesheet-processor.ts b/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/pre-v17/styles/stylesheet-processor.ts deleted file mode 100644 index e5718b26483f2..0000000000000 --- a/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/pre-v17/styles/stylesheet-processor.ts +++ /dev/null @@ -1,279 +0,0 @@ -/** - * Adapted from the original ng-packagr source. - * - * Changes made: - * - Refactored caching to take into account TailwindCSS processing. - * - Added PostCSS plugin needed to support TailwindCSS. - */ - -import browserslist from 'browserslist'; -import { existsSync } from 'fs'; -import { - generateKey, - readCacheEntry, - saveCacheEntry, -} from 'ng-packagr/lib/utils/cache'; -import * as log from 'ng-packagr/lib/utils/log'; -import { dirname, extname, join } from 'path'; -import autoprefixer from 'autoprefixer'; -import postcssUrl from 'postcss-url'; -import { pathToFileURL } from 'node:url'; -import { - getTailwindPostCssPlugin, - getTailwindSetup, - TailwindSetup, -} from '../../../../utilities/ng-packagr/tailwindcss'; - -const postcss = require('postcss'); - -export enum CssUrl { - inline = 'inline', - none = 'none', -} - -export enum InlineStyleLanguage { - sass = 'sass', - scss = 'scss', - css = 'css', - less = 'less', -} - -export interface Result { - css: string; - warnings: string[]; - error?: string; -} - -export class StylesheetProcessor { - private browserslistData: string[]; - private targets: string[]; - private postCssProcessor: ReturnType; - private esbuild = - new (require('ng-packagr/lib/esbuild/esbuild-executor').EsbuildExecutor)(); - private styleIncludePaths: string[]; - - constructor( - private readonly projectBasePath: string, - private readonly basePath: string, - private readonly cssUrl?: CssUrl, - private readonly includePaths?: string[], - private cacheDirectory?: string | false, - private readonly tailwindConfig?: string - ) { - // 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', - ]; - - this.styleIncludePaths = [...this.includePaths]; - let prevDir = null; - let currentDir = this.basePath; - - while (currentDir !== prevDir) { - const p = join(currentDir, 'node_modules'); - if (existsSync(p)) { - this.styleIncludePaths.push(p); - } - - prevDir = currentDir; - currentDir = dirname(prevDir); - } - - this.browserslistData = browserslist(undefined, { path: this.basePath }); - this.targets = transformSupportedBrowsersToTargets(this.browserslistData); - const tailwindSetup = getTailwindSetup( - this.projectBasePath, - this.tailwindConfig - ); - if (tailwindSetup) { - this.cacheDirectory = undefined; - } - this.postCssProcessor = this.createPostCssProcessor(tailwindSetup); - } - - async process({ - filePath, - content, - }: { - filePath: string; - content: string; - }): Promise { - const CACHE_KEY_VALUES = [ - ...this.browserslistData, - ...this.styleIncludePaths, - this.cssUrl, - ].join(':'); - - let key: string | undefined; - if ( - this.cacheDirectory && - !content.includes('@import') && - !content.includes('@use') - ) { - // No transitive deps and no Tailwind directives, we can cache more aggressively. - key = await generateKey(content, CACHE_KEY_VALUES); - const result = await readCacheEntry(this.cacheDirectory, key); - if (result) { - result.warnings.forEach((msg) => log.warn(msg)); - - return result.css; - } - } - - // Render pre-processor language (sass, styl, less) - const renderedCss = await this.renderCss(filePath, content); - - // We cannot cache CSS re-rendering phase, because a transitive dependency via (@import) can case different CSS output. - // Example a change in a mixin or SCSS variable. - if (!key) { - key = await generateKey(renderedCss, CACHE_KEY_VALUES); - } - - if (this.cacheDirectory) { - const cachedResult = await readCacheEntry(this.cacheDirectory, key); - if (cachedResult) { - cachedResult.warnings.forEach((msg) => log.warn(msg)); - - return cachedResult.css; - } - } - - // Render postcss (autoprefixing and friends) - const result = await this.postCssProcessor.process(renderedCss, { - from: filePath, - to: filePath.replace(extname(filePath), '.css'), - }); - - const warnings = result.warnings().map((w) => w.toString()); - const { code, warnings: esBuildWarnings } = await this.esbuild.transform( - result.css, - { - loader: 'css', - minify: true, - target: this.targets, - sourcefile: filePath, - } - ); - - if (esBuildWarnings.length > 0) { - warnings.push( - ...(await this.esbuild.formatMessages(esBuildWarnings, { - kind: 'warning', - })) - ); - } - - if (this.cacheDirectory) { - await saveCacheEntry( - this.cacheDirectory, - key, - JSON.stringify({ - css: code, - warnings, - }) - ); - } - warnings.forEach((msg) => log.warn(msg)); - - return code; - } - - private createPostCssProcessor( - tailwindSetup: TailwindSetup - ): ReturnType { - const postCssPlugins = []; - if (tailwindSetup) { - postCssPlugins.push(getTailwindPostCssPlugin(tailwindSetup)); - } - - if (this.cssUrl !== CssUrl.none) { - postCssPlugins.push(postcssUrl({ url: this.cssUrl })); - } - - postCssPlugins.push( - autoprefixer({ - ignoreUnknownVersions: true, - overrideBrowserslist: this.browserslistData, - }) - ); - - return postcss(postCssPlugins); - } - - private async renderCss(filePath: string, css: string): Promise { - const ext = extname(filePath); - - switch (ext) { - case '.sass': - case '.scss': { - return (await import('sass')).compileString(css, { - url: pathToFileURL(filePath), - syntax: '.sass' === ext ? 'indented' : 'scss', - loadPaths: this.styleIncludePaths, - }).css; - } - case '.less': { - const { css: content } = await ( - await import('less') - ).render(css, { - filename: filePath, - javascriptEnabled: true, - paths: this.styleIncludePaths, - }); - - return content; - } - case '.css': - default: - return css; - } - } -} - -function transformSupportedBrowsersToTargets( - supportedBrowsers: string[] -): string[] { - const transformed: string[] = []; - - // https://esbuild.github.io/api/#target - const esBuildSupportedBrowsers = new Set([ - 'safari', - 'firefox', - 'edge', - 'chrome', - 'ios', - ]); - - for (const browser of supportedBrowsers) { - let [browserName, version] = browser.split(' '); - - // browserslist uses the name `ios_saf` for iOS Safari whereas esbuild uses `ios` - if (browserName === 'ios_saf') { - browserName = 'ios'; - } - - // browserslist uses ranges `15.2-15.3` versions but only the lowest is required - // to perform minimum supported feature checks. esbuild also expects a single version. - [version] = version.split('-'); - - if (esBuildSupportedBrowsers.has(browserName)) { - if (browserName === 'safari' && version === 'tp') { - // esbuild only supports numeric versions so `TP` is converted to a high number (999) since - // a Technology Preview (TP) of Safari is assumed to support all currently known features. - version = '999'; - } - - transformed.push(browserName + version); - } - } - - return transformed.length ? transformed : undefined; -} diff --git a/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/v17+/ng-package/entry-point/write-bundles.di.ts b/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/pre-v19/ng-package/entry-point/write-bundles.di.ts similarity index 100% rename from packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/v17+/ng-package/entry-point/write-bundles.di.ts rename to packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/pre-v19/ng-package/entry-point/write-bundles.di.ts diff --git a/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/v17+/ng-package/entry-point/write-bundles.transform.ts b/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/pre-v19/ng-package/entry-point/write-bundles.transform.ts similarity index 100% rename from packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/v17+/ng-package/entry-point/write-bundles.transform.ts rename to packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/pre-v19/ng-package/entry-point/write-bundles.transform.ts diff --git a/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/v17+/ng-package/entry-point/write-package.di.ts b/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/pre-v19/ng-package/entry-point/write-package.di.ts similarity index 100% rename from packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/v17+/ng-package/entry-point/write-package.di.ts rename to packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/pre-v19/ng-package/entry-point/write-package.di.ts diff --git a/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/v17+/ng-package/entry-point/write-package.transform.ts b/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/pre-v19/ng-package/entry-point/write-package.transform.ts similarity index 100% rename from packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/v17+/ng-package/entry-point/write-package.transform.ts rename to packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/pre-v19/ng-package/entry-point/write-package.transform.ts diff --git a/packages/angular/src/executors/ng-packagr-lite/ng-packagr-lite.impl.ts b/packages/angular/src/executors/ng-packagr-lite/ng-packagr-lite.impl.ts index 4b051f523074b..ea9c65bb3e7ff 100644 --- a/packages/angular/src/executors/ng-packagr-lite/ng-packagr-lite.impl.ts +++ b/packages/angular/src/executors/ng-packagr-lite/ng-packagr-lite.impl.ts @@ -15,7 +15,7 @@ async function initializeNgPackgrLite( context: ExecutorContext, projectDependencies: DependentBuildableProjectNode[] ): Promise { - const ngPackagr = await getNgPackagrInstance(options); + const ngPackagr = await getNgPackagrInstance(); ngPackagr.forProject(resolve(context.root, options.project)); if (options.tsConfig) { diff --git a/packages/angular/src/executors/ng-packagr-lite/schema.json b/packages/angular/src/executors/ng-packagr-lite/schema.json index 65036cd3dca05..da37fab49faa6 100644 --- a/packages/angular/src/executors/ng-packagr-lite/schema.json +++ b/packages/angular/src/executors/ng-packagr-lite/schema.json @@ -7,10 +7,6 @@ "cli": "nx", "type": "object", "presets": [ - { - "name": "Buildable Library with Tailwind", - "keys": ["project", "tailwindConfig"] - }, { "name": "Updating Project Dependencies for Buildable Library", "keys": ["project"] @@ -36,10 +32,6 @@ "poll": { "type": "number", "description": "Enable and define the file watching poll time period in milliseconds. _Note: this is only supported in Angular versions >= 18.0.0_." - }, - "tailwindConfig": { - "type": "string", - "description": "The full path for the Tailwind configuration file, relative to the workspace root. If not provided and a `tailwind.config.js` file exists in the project or workspace root, it will be used. Otherwise, Tailwind will not be configured." } }, "additionalProperties": false, diff --git a/packages/angular/src/executors/package/ng-packagr-adjustments/ng-package/entry-point/compile-ngc.di.ts b/packages/angular/src/executors/package/ng-packagr-adjustments/ng-package/entry-point/compile-ngc.di.ts deleted file mode 100644 index a0ea7cf921bba..0000000000000 --- a/packages/angular/src/executors/package/ng-packagr-adjustments/ng-package/entry-point/compile-ngc.di.ts +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Adapted from the original ng-packagr source. - * - * Changes made: - * - Use our own compileNgcTransformFactory instead of the one provided by ng-packagr. - * - Use NX_STYLESHEET_PROCESSOR instead of STYLESHEET_PROCESSOR. - * - Use NX_STYLESHEET_PROCESSOR_TOKEN instead of STYLESHEET_PROCESSOR_TOKEN. - */ - -import { InjectionToken, Provider } from 'injection-js'; -import { Transform } from 'ng-packagr/lib/graph/transform'; -import { - provideTransform, - TransformProvider, -} from 'ng-packagr/lib/graph/transform.di'; -import { - NX_STYLESHEET_PROCESSOR, - NX_STYLESHEET_PROCESSOR_TOKEN, -} from '../../styles/stylesheet-processor.di'; -import { NX_OPTIONS_TOKEN } from '../options.di'; -import { compileNgcTransformFactory } from './compile-ngc.transform'; - -export const NX_COMPILE_NGC_TOKEN = new InjectionToken( - `nx.v1.compileNgcTransform` -); - -export const NX_COMPILE_NGC_TRANSFORM: TransformProvider = provideTransform({ - provide: NX_COMPILE_NGC_TOKEN, - useFactory: compileNgcTransformFactory, - deps: [NX_STYLESHEET_PROCESSOR_TOKEN, NX_OPTIONS_TOKEN], -}); - -export const NX_COMPILE_NGC_PROVIDERS: Provider[] = [ - NX_STYLESHEET_PROCESSOR, - NX_COMPILE_NGC_TRANSFORM, -]; diff --git a/packages/angular/src/executors/package/ng-packagr-adjustments/ng-package/entry-point/compile-ngc.transform.ts b/packages/angular/src/executors/package/ng-packagr-adjustments/ng-package/entry-point/compile-ngc.transform.ts deleted file mode 100644 index c3d5885e37456..0000000000000 --- a/packages/angular/src/executors/package/ng-packagr-adjustments/ng-package/entry-point/compile-ngc.transform.ts +++ /dev/null @@ -1,120 +0,0 @@ -/** - * Adapted from the original ng-packagr source. - * - * Changes made: - * - Use our own StylesheetProcessor files instead of the ones provide by ng-packagr. - * - Support ngcc for Angular < 16. - * - Support ESM2020 for Angular < 16. - */ - -import { - Transform, - transformFromPromise, -} from 'ng-packagr/lib/graph/transform'; -import { - EntryPointNode, - isEntryPoint, - isEntryPointInProgress, - isPackage, - PackageNode, -} from 'ng-packagr/lib/ng-package/nodes'; -import { setDependenciesTsConfigPaths } from 'ng-packagr/lib/ts/tsconfig'; -import ora from 'ora'; -import * as path from 'path'; -import * as ts from 'typescript'; -import { getInstalledAngularVersionInfo } from '../../../../utilities/angular-version-utils'; -import { compileSourceFiles } from '../../ngc/compile-source-files'; -import { StylesheetProcessor as StylesheetProcessorClass } from '../../styles/stylesheet-processor'; -import { ngccCompilerCli } from '../../utils/ng-compiler-cli'; -import { NgPackagrOptions } from '../options.di'; - -export const compileNgcTransformFactory = ( - StylesheetProcessor: typeof StylesheetProcessorClass, - options: NgPackagrOptions -): Transform => { - return transformFromPromise(async (graph) => { - const spinner = ora({ - hideCursor: false, - discardStdin: false, - }); - - const entryPoints: EntryPointNode[] = graph.filter(isEntryPoint); - const entryPoint: EntryPointNode = graph.find(isEntryPointInProgress()); - const ngPackageNode: PackageNode = graph.find(isPackage); - const projectBasePath = ngPackageNode.data.primary.basePath; - - try { - // Add paths mappings for dependencies - const tsConfig = setDependenciesTsConfigPaths( - entryPoint.data.tsConfig, - entryPoints - ); - - const angularVersion = getInstalledAngularVersionInfo(); - - // Compile TypeScript sources - const { declarations } = entryPoint.data.destinationFiles; - const esmModulePath = - angularVersion.major < 16 - ? (entryPoint.data.destinationFiles as any).esm2020 - : entryPoint.data.destinationFiles.esm2022; - const { basePath, cssUrl, styleIncludePaths } = - entryPoint.data.entryPoint; - const { moduleResolutionCache } = entryPoint.cache; - - spinner.start( - `Compiling with Angular sources in Ivy ${ - tsConfig.options.compilationMode || 'full' - } compilation mode.` - ); - let ngccProcessor: any; - if (angularVersion && angularVersion.major < 16) { - ngccProcessor = - new (require('ng-packagr/lib/ngc/ngcc-processor').NgccProcessor)( - await ngccCompilerCli(), - (entryPoint.cache as any).ngccProcessingCache, - tsConfig.project, - tsConfig.options, - entryPoints - ); - if (!entryPoint.data.entryPoint.isSecondaryEntryPoint) { - // Only run the async version of NGCC during the primary entrypoint processing. - await ngccProcessor.process(); - } - } - - entryPoint.cache.stylesheetProcessor ??= new StylesheetProcessor( - projectBasePath, - basePath, - cssUrl, - styleIncludePaths, - options.cacheEnabled && options.cacheDirectory, - options.tailwindConfig - ) as any; - - await compileSourceFiles( - graph, - tsConfig, - moduleResolutionCache, - options, - { - outDir: path.dirname(esmModulePath), - declarationDir: path.dirname(declarations), - declaration: true, - target: - angularVersion.major >= 16 - ? ts.ScriptTarget.ES2022 - : ts.ScriptTarget.ES2020, - }, - entryPoint.cache.stylesheetProcessor as any, - ngccProcessor - ); - } catch (error) { - spinner.fail(); - throw error; - } - - spinner.succeed(); - return graph; - }); -}; diff --git a/packages/angular/src/executors/package/ng-packagr-adjustments/ng-package/entry-point/entry-point.di.ts b/packages/angular/src/executors/package/ng-packagr-adjustments/ng-package/entry-point/entry-point.di.ts deleted file mode 100644 index d8070cb478bcf..0000000000000 --- a/packages/angular/src/executors/package/ng-packagr-adjustments/ng-package/entry-point/entry-point.di.ts +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Adapted from the original ng-packagr source. - * - * Changes made: - * - Use NX_COMPILE_NGC_TOKEN instead of COMPILE_NGC_TOKEN. - * - Use NX_COMPILE_NGC_PROVIDERS instead of COMPILE_NGC_PROVIDERS. - */ - -import { InjectionToken, Provider } from 'injection-js'; -import { Transform } from 'ng-packagr/lib/graph/transform'; -import { - provideTransform, - TransformProvider, -} from 'ng-packagr/lib/graph/transform.di'; -import { entryPointTransformFactory } from 'ng-packagr/lib/ng-package/entry-point/entry-point.transform'; -import { - WRITE_BUNDLES_TRANSFORM, - WRITE_BUNDLES_TRANSFORM_TOKEN, -} from 'ng-packagr/lib/ng-package/entry-point/write-bundles.di'; -import { - WRITE_PACKAGE_TRANSFORM, - WRITE_PACKAGE_TRANSFORM_TOKEN, -} from 'ng-packagr/lib/ng-package/entry-point/write-package.di'; -import { - NX_COMPILE_NGC_PROVIDERS, - NX_COMPILE_NGC_TOKEN, -} from './compile-ngc.di'; - -export const NX_ENTRY_POINT_TRANSFORM_TOKEN = new InjectionToken( - `nx.v1.entryPointTransform` -); - -export const NX_ENTRY_POINT_TRANSFORM: TransformProvider = provideTransform({ - provide: NX_ENTRY_POINT_TRANSFORM_TOKEN, - useFactory: entryPointTransformFactory, - deps: [ - NX_COMPILE_NGC_TOKEN, - WRITE_BUNDLES_TRANSFORM_TOKEN, - WRITE_PACKAGE_TRANSFORM_TOKEN, - ], -}); - -export const NX_ENTRY_POINT_PROVIDERS: Provider[] = [ - NX_ENTRY_POINT_TRANSFORM, - ...NX_COMPILE_NGC_PROVIDERS, - WRITE_BUNDLES_TRANSFORM, - WRITE_PACKAGE_TRANSFORM, -]; diff --git a/packages/angular/src/executors/package/ng-packagr-adjustments/ng-package/options.di.ts b/packages/angular/src/executors/package/ng-packagr-adjustments/ng-package/options.di.ts deleted file mode 100644 index 517ee478affa7..0000000000000 --- a/packages/angular/src/executors/package/ng-packagr-adjustments/ng-package/options.di.ts +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Adapted from the original ng-packagr. - * - * Changes made: - * - Use our own options interface to add support for tailwindConfig. - */ - -import findCacheDirectory from 'find-cache-dir'; -import { InjectionToken, Provider, ValueProvider } from 'injection-js'; -import { NgPackagrOptions as NgPackagrOptionsBase } from 'ng-packagr/lib/ng-package/options.di'; -import { tmpdir } from 'os'; -import { resolve } from 'path'; - -export interface NgPackagrOptions extends NgPackagrOptionsBase { - tailwindConfig?: string; -} - -export const NX_OPTIONS_TOKEN = new InjectionToken( - `nx.v1.options` -); - -export const nxProvideOptions = ( - options: NgPackagrOptions = {} -): ValueProvider => ({ - provide: NX_OPTIONS_TOKEN, - useValue: normalizeOptions(options), -}); - -export const NX_DEFAULT_OPTIONS_PROVIDER: Provider = nxProvideOptions(); - -function normalizeOptions(options: NgPackagrOptions = {}) { - const ciEnv = process.env['CI']; - const isCI = ciEnv?.toLowerCase() === 'true' || ciEnv === '1'; - const { cacheEnabled = !isCI, cacheDirectory = findCachePath() } = options; - - return { - ...options, - cacheEnabled, - cacheDirectory, - }; -} - -function findCachePath(): string { - const name = 'ng-packagr'; - - return findCacheDirectory({ name }) || resolve(tmpdir(), name); -} diff --git a/packages/angular/src/executors/package/ng-packagr-adjustments/ng-package/package.di.ts b/packages/angular/src/executors/package/ng-packagr-adjustments/ng-package/package.di.ts deleted file mode 100644 index 084c8076726fe..0000000000000 --- a/packages/angular/src/executors/package/ng-packagr-adjustments/ng-package/package.di.ts +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Adapted from the original ng-packagr source. - * - * Changes made: - * - Use NX_ENTRY_POINT_TRANSFORM_TOKEN instead of ENTRY_POINT_TRANSFORM_TOKEN. - * - USE NX_OPTIONS_TOKEN instead of OPTIONS_TOKEN. - * - USE NX_DEFAULT_OPTIONS_PROVIDER instead of DEFAULT_OPTIONS_PROVIDER. - */ - -import { InjectionToken, Provider } from 'injection-js'; -import { Transform } from 'ng-packagr/lib/graph/transform'; -import { - provideTransform, - TransformProvider, -} from 'ng-packagr/lib/graph/transform.di'; -import { - ANALYSE_SOURCES_TOKEN, - ANALYSE_SOURCES_TRANSFORM, -} from 'ng-packagr/lib/ng-package/entry-point/analyse-sources.di'; -import { - INIT_TS_CONFIG_TOKEN, - INIT_TS_CONFIG_TRANSFORM, -} from 'ng-packagr/lib/ng-package/entry-point/init-tsconfig.di'; -import { packageTransformFactory } from 'ng-packagr/lib/ng-package/package.transform'; -import { PROJECT_TOKEN } from 'ng-packagr/lib/project.di'; -import { NX_ENTRY_POINT_TRANSFORM_TOKEN } from './entry-point/entry-point.di'; -import { NX_DEFAULT_OPTIONS_PROVIDER, NX_OPTIONS_TOKEN } from './options.di'; - -export const NX_PACKAGE_TRANSFORM_TOKEN = new InjectionToken( - `nx.v1.packageTransform` -); - -export const NX_PACKAGE_TRANSFORM: TransformProvider = provideTransform({ - provide: NX_PACKAGE_TRANSFORM_TOKEN, - useFactory: packageTransformFactory, - deps: [ - PROJECT_TOKEN, - NX_OPTIONS_TOKEN, - INIT_TS_CONFIG_TOKEN, - ANALYSE_SOURCES_TOKEN, - NX_ENTRY_POINT_TRANSFORM_TOKEN, - ], -}); - -export const NX_PACKAGE_PROVIDERS: Provider[] = [ - NX_PACKAGE_TRANSFORM, - NX_DEFAULT_OPTIONS_PROVIDER, - INIT_TS_CONFIG_TRANSFORM, - ANALYSE_SOURCES_TRANSFORM, -]; diff --git a/packages/angular/src/executors/package/ng-packagr-adjustments/ng-packagr.ts b/packages/angular/src/executors/package/ng-packagr-adjustments/ng-packagr.ts index b7c6d051cda75..839eac59581be 100644 --- a/packages/angular/src/executors/package/ng-packagr-adjustments/ng-packagr.ts +++ b/packages/angular/src/executors/package/ng-packagr-adjustments/ng-packagr.ts @@ -1,38 +1,11 @@ import { NgPackagr, ngPackagr } from 'ng-packagr'; -import type { BuildAngularLibraryExecutorOptions } from '../../package/schema'; -import { getInstalledAngularVersionInfo } from '../../utilities/angular-version-utils'; -export async function getNgPackagrInstance( - options: BuildAngularLibraryExecutorOptions -): Promise { - const { major: angularMajorVersion } = getInstalledAngularVersionInfo(); - if (angularMajorVersion >= 17) { - const { STYLESHEET_PROCESSOR } = await import( - '../../utilities/ng-packagr/stylesheet-processor.di.js' - ); - - const packagr = ngPackagr(); - packagr.withProviders([STYLESHEET_PROCESSOR]); - return packagr; - } - - const { NX_ENTRY_POINT_PROVIDERS } = await import( - './ng-package/entry-point/entry-point.di.js' +export async function getNgPackagrInstance(): Promise { + const { STYLESHEET_PROCESSOR } = await import( + '../../utilities/ng-packagr/stylesheet-processor.di.js' ); - const { nxProvideOptions } = await import('./ng-package/options.di.js'); - const { NX_PACKAGE_PROVIDERS, NX_PACKAGE_TRANSFORM } = await import( - './ng-package/package.di.js' - ); - - const packagr = new NgPackagr([ - ...NX_PACKAGE_PROVIDERS, - ...NX_ENTRY_POINT_PROVIDERS, - nxProvideOptions({ - tailwindConfig: options.tailwindConfig, - watch: options.watch, - }), - ]); - packagr.withBuildTransform(NX_PACKAGE_TRANSFORM.provide); + const packagr = ngPackagr(); + packagr.withProviders([STYLESHEET_PROCESSOR]); return packagr; } diff --git a/packages/angular/src/executors/package/ng-packagr-adjustments/ngc/compile-source-files.ts b/packages/angular/src/executors/package/ng-packagr-adjustments/ngc/compile-source-files.ts deleted file mode 100644 index 096ec517e2f55..0000000000000 --- a/packages/angular/src/executors/package/ng-packagr-adjustments/ngc/compile-source-files.ts +++ /dev/null @@ -1,284 +0,0 @@ -/** - * Adapted from the original ng-packagr source. - * - * Changes made: - * - Use custom StylesheetProcessor instead of the one provided by ng-packagr. - * - Support ngcc for Angular < 16. - * - Support Angular Compiler `incrementalDriver` for Angular < 16. - */ - -import { BuildGraph } from 'ng-packagr/lib/graph/build-graph'; -import { - EntryPointNode, - PackageNode, - isEntryPointInProgress, - isPackage, -} from 'ng-packagr/lib/ng-package/nodes'; -import * as log from 'ng-packagr/lib/utils/log'; -import { - augmentProgramWithVersioning, - cacheCompilerHost, -} from 'ng-packagr/lib/ts/cache-compiler-host'; -import { join } from 'node:path'; -import * as ts from 'typescript'; -import { getInstalledAngularVersionInfo } from '../../../utilities/angular-version-utils'; -import { loadEsmModule } from '../../../utilities/module-loader'; -import { NgPackagrOptions } from '../ng-package/options.di'; -import { StylesheetProcessor } from '../styles/stylesheet-processor'; - -export async function compileSourceFiles( - graph: BuildGraph, - tsConfig: any, - moduleResolutionCache: ts.ModuleResolutionCache, - options: NgPackagrOptions, - extraOptions?: Partial, - stylesheetProcessor?: StylesheetProcessor, - ngccProcessor?: any -) { - const { NgtscProgram, formatDiagnostics } = await loadEsmModule( - '@angular/compiler-cli' - ); - const { cacheDirectory, watch, cacheEnabled } = options; - const tsConfigOptions: ts.CompilerOptions = { - ...tsConfig.options, - ...extraOptions, - }; - const entryPoint: EntryPointNode = graph.find(isEntryPointInProgress()); - const ngPackageNode: PackageNode = graph.find(isPackage); - const inlineStyleLanguage = ngPackageNode.data.inlineStyleLanguage; - - const cacheDir = cacheEnabled && cacheDirectory; - if (cacheDir) { - tsConfigOptions.incremental ??= true; - tsConfigOptions.tsBuildInfoFile ??= join( - cacheDir, - `tsbuildinfo/${entryPoint.data.entryPoint.flatModuleFile}.tsbuildinfo` - ); - } - - let tsCompilerHost = cacheCompilerHost( - graph, - entryPoint, - tsConfigOptions, - moduleResolutionCache, - stylesheetProcessor as any, - inlineStyleLanguage - ); - - if (ngccProcessor) { - tsCompilerHost = - require('ng-packagr/lib/ts/ngcc-transform-compiler-host').ngccTransformCompilerHost( - tsCompilerHost, - tsConfigOptions, - ngccProcessor, - moduleResolutionCache - ); - } - - const cache = entryPoint.cache; - const sourceFileCache = cache.sourcesFileCache; - let usingBuildInfo = false; - - let oldBuilder = cache.oldBuilder; - if (!oldBuilder && cacheDir) { - oldBuilder = ts.readBuilderProgram(tsConfigOptions, tsCompilerHost); - usingBuildInfo = true; - } - - // Create the Angular specific program that contains the Angular compiler - const angularProgram = new NgtscProgram( - tsConfig.rootNames, - tsConfigOptions, - tsCompilerHost, - cache.oldNgtscProgram - ); - - const angularCompiler = angularProgram.compiler; - const { ignoreForDiagnostics, ignoreForEmit } = angularCompiler; - - // SourceFile versions are required for builder programs. - // The wrapped host inside NgtscProgram adds additional files that will not have versions. - const typeScriptProgram = angularProgram.getTsProgram(); - augmentProgramWithVersioning(typeScriptProgram); - - let builder: ts.BuilderProgram | ts.EmitAndSemanticDiagnosticsBuilderProgram; - if (watch || cacheDir) { - builder = cache.oldBuilder = - ts.createEmitAndSemanticDiagnosticsBuilderProgram( - typeScriptProgram, - tsCompilerHost, - oldBuilder - ); - cache.oldNgtscProgram = angularProgram; - } else { - builder = ts.createEmitAndSemanticDiagnosticsBuilderProgram( - typeScriptProgram, - tsCompilerHost - ); - } - - // Update semantic diagnostics cache - const affectedFiles = new Set(); - - // Analyze affected files when in watch mode for incremental type checking - if ('getSemanticDiagnosticsOfNextAffectedFile' in builder) { - // eslint-disable-next-line no-constant-condition - while (true) { - const result = builder.getSemanticDiagnosticsOfNextAffectedFile( - undefined, - (sourceFile) => { - // If the affected file is a TTC shim, add the shim's original source file. - // This ensures that changes that affect TTC are typechecked even when the changes - // are otherwise unrelated from a TS perspective and do not result in Ivy codegen changes. - // For example, changing @Input property types of a directive used in another component's - // template. - if ( - ignoreForDiagnostics.has(sourceFile) && - sourceFile.fileName.endsWith('.ngtypecheck.ts') - ) { - // This file name conversion relies on internal compiler logic and should be converted - // to an official method when available. 15 is length of `.ngtypecheck.ts` - const originalFilename = sourceFile.fileName.slice(0, -15) + '.ts'; - const originalSourceFile = builder.getSourceFile(originalFilename); - if (originalSourceFile) { - affectedFiles.add(originalSourceFile); - } - - return true; - } - - return false; - } - ); - - if (!result) { - break; - } - - affectedFiles.add(result.affected as ts.SourceFile); - } - - // Add all files with associated template type checking files. - // Stored TS build info does not have knowledge of the AOT compiler or the typechecking state of the templates. - // To ensure that errors are reported correctly, all AOT component diagnostics need to be analyzed even if build - // info is present. - if (usingBuildInfo) { - for (const sourceFile of builder.getSourceFiles()) { - if ( - ignoreForDiagnostics.has(sourceFile) && - sourceFile.fileName.endsWith('.ngtypecheck.ts') - ) { - // This file name conversion relies on internal compiler logic and should be converted - // to an official method when available. 15 is length of `.ngtypecheck.ts` - const originalFilename = sourceFile.fileName.slice(0, -15) + '.ts'; - const originalSourceFile = builder.getSourceFile(originalFilename); - if (originalSourceFile) { - affectedFiles.add(originalSourceFile); - } - } - } - } - } - - // Collect program level diagnostics - const allDiagnostics: ts.Diagnostic[] = [ - ...angularCompiler.getOptionDiagnostics(), - ...builder.getOptionsDiagnostics(), - ...builder.getGlobalDiagnostics(), - ]; - - // Required to support asynchronous resource loading - // Must be done before creating transformers or getting template diagnostics - await angularCompiler.analyzeAsync(); - - // Collect source file specific diagnostics - for (const sourceFile of builder.getSourceFiles()) { - if (ignoreForDiagnostics.has(sourceFile)) { - continue; - } - - allDiagnostics.push( - ...builder.getDeclarationDiagnostics(sourceFile), - ...builder.getSyntacticDiagnostics(sourceFile), - ...builder.getSemanticDiagnostics(sourceFile) - ); - - // Declaration files cannot have template diagnostics - if (sourceFile.isDeclarationFile) { - continue; - } - - // Only request Angular template diagnostics for affected files to avoid - // overhead of template diagnostics for unchanged files. - if (affectedFiles.has(sourceFile)) { - const angularDiagnostics = angularCompiler.getDiagnosticsForFile( - sourceFile, - affectedFiles.size === 1 - ? /** OptimizeFor.SingleFile **/ 0 - : /** OptimizeFor.WholeProgram */ 1 - ); - - allDiagnostics.push(...angularDiagnostics); - sourceFileCache.updateAngularDiagnostics(sourceFile, angularDiagnostics); - } - } - - const otherDiagnostics = []; - const errorDiagnostics = []; - for (const diagnostic of allDiagnostics) { - if (diagnostic.category === ts.DiagnosticCategory.Error) { - errorDiagnostics.push(diagnostic); - } else { - otherDiagnostics.push(diagnostic); - } - } - - if (otherDiagnostics.length) { - log.msg(formatDiagnostics(errorDiagnostics)); - } - - if (errorDiagnostics.length) { - throw new Error(formatDiagnostics(errorDiagnostics)); - } - - const transformers = angularCompiler.prepareEmit().transformers; - - if ('getSemanticDiagnosticsOfNextAffectedFile' in builder) { - while ( - builder.emitNextAffectedFile( - (fileName, data, writeByteOrderMark, onError, sourceFiles) => { - if (fileName.endsWith('.tsbuildinfo')) { - tsCompilerHost.writeFile( - fileName, - data, - writeByteOrderMark, - onError, - sourceFiles - ); - } - } - ) - ) { - // empty - } - } - - const angularVersion = getInstalledAngularVersionInfo(); - const incrementalCompilation: typeof angularCompiler.incrementalCompilation = - angularVersion.major < 16 - ? (angularCompiler as any).incrementalDriver - : angularCompiler.incrementalCompilation; - - for (const sourceFile of builder.getSourceFiles()) { - if (ignoreForEmit.has(sourceFile)) { - continue; - } - - if (incrementalCompilation.safeToSkipEmit(sourceFile)) { - continue; - } - - builder.emit(sourceFile, undefined, undefined, undefined, transformers); - incrementalCompilation.recordSuccessfulEmit(sourceFile); - } -} diff --git a/packages/angular/src/executors/package/ng-packagr-adjustments/styles/stylesheet-processor.di.ts b/packages/angular/src/executors/package/ng-packagr-adjustments/styles/stylesheet-processor.di.ts deleted file mode 100644 index 868e90db24c6d..0000000000000 --- a/packages/angular/src/executors/package/ng-packagr-adjustments/styles/stylesheet-processor.di.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Adapted from the original ng-packagr source. - * - * Changes made: - * - Use our own StylesheetProcessor instead of the one provided by ng-packagr. - */ - -import { FactoryProvider, InjectionToken } from 'injection-js'; -import { StylesheetProcessor } from './stylesheet-processor'; - -export const NX_STYLESHEET_PROCESSOR_TOKEN = - new InjectionToken(`nx.v1.stylesheetProcessor`); - -export const NX_STYLESHEET_PROCESSOR: FactoryProvider = { - provide: NX_STYLESHEET_PROCESSOR_TOKEN, - useFactory: () => StylesheetProcessor, - deps: [], -}; diff --git a/packages/angular/src/executors/package/ng-packagr-adjustments/styles/stylesheet-processor.ts b/packages/angular/src/executors/package/ng-packagr-adjustments/styles/stylesheet-processor.ts deleted file mode 100644 index badfab34274e7..0000000000000 --- a/packages/angular/src/executors/package/ng-packagr-adjustments/styles/stylesheet-processor.ts +++ /dev/null @@ -1,272 +0,0 @@ -/** - * Adapted from the original ng-packagr source. - * - * Changes made: - * - Refactored caching to take into account TailwindCSS processing. - * - Added PostCSS plugin needed to support TailwindCSS. - */ - -import browserslist from 'browserslist'; -import { existsSync } from 'fs'; -import { - generateKey, - readCacheEntry, - saveCacheEntry, -} from 'ng-packagr/lib/utils/cache'; -import * as log from 'ng-packagr/lib/utils/log'; -import { dirname, extname, join } from 'path'; -import autoprefixer from 'autoprefixer'; -import postcssUrl from 'postcss-url'; -import { pathToFileURL } from 'node:url'; -import { - getTailwindPostCssPlugin, - getTailwindSetup, - TailwindSetup, -} from '../../../utilities/ng-packagr/tailwindcss'; - -const postcss = require('postcss'); - -export enum CssUrl { - inline = 'inline', - none = 'none', -} - -export interface Result { - css: string; - warnings: string[]; - error?: string; -} - -export class StylesheetProcessor { - private browserslistData: string[]; - private targets: string[]; - private postCssProcessor: ReturnType; - private esbuild = - new (require('ng-packagr/lib/esbuild/esbuild-executor').EsbuildExecutor)(); - private styleIncludePaths: string[]; - - constructor( - private readonly projectBasePath: string, - private readonly basePath: string, - private readonly cssUrl?: CssUrl, - private readonly includePaths?: string[], - private cacheDirectory?: string | false, - private readonly tailwindConfig?: string - ) { - // 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', - ]; - - this.styleIncludePaths = [...this.includePaths]; - let prevDir = null; - let currentDir = this.basePath; - - while (currentDir !== prevDir) { - const p = join(currentDir, 'node_modules'); - if (existsSync(p)) { - this.styleIncludePaths.push(p); - } - - prevDir = currentDir; - currentDir = dirname(prevDir); - } - - this.browserslistData = browserslist(undefined, { path: this.basePath }); - this.targets = transformSupportedBrowsersToTargets(this.browserslistData); - const tailwindSetup = getTailwindSetup( - this.projectBasePath, - this.tailwindConfig - ); - if (tailwindSetup) { - this.cacheDirectory = undefined; - } - this.postCssProcessor = this.createPostCssProcessor(tailwindSetup); - } - - async process({ - filePath, - content, - }: { - filePath: string; - content: string; - }): Promise { - const CACHE_KEY_VALUES = [ - ...this.browserslistData, - ...this.styleIncludePaths, - this.cssUrl, - ].join(':'); - - let key: string | undefined; - if ( - this.cacheDirectory && - !content.includes('@import') && - !content.includes('@use') - ) { - // No transitive deps and no Tailwind directives, we can cache more aggressively. - key = await generateKey(content, CACHE_KEY_VALUES); - const result = await readCacheEntry(this.cacheDirectory, key); - if (result) { - result.warnings.forEach((msg) => log.warn(msg)); - - return result.css; - } - } - - // Render pre-processor language (sass, styl, less) - const renderedCss = await this.renderCss(filePath, content); - - // We cannot cache CSS re-rendering phase, because a transitive dependency via (@import) can case different CSS output. - // Example a change in a mixin or SCSS variable. - if (!key) { - key = await generateKey(renderedCss, CACHE_KEY_VALUES); - } - - if (this.cacheDirectory) { - const cachedResult = await readCacheEntry(this.cacheDirectory, key); - if (cachedResult) { - cachedResult.warnings.forEach((msg) => log.warn(msg)); - - return cachedResult.css; - } - } - - // Render postcss (autoprefixing and friends) - const result = await this.postCssProcessor.process(renderedCss, { - from: filePath, - to: filePath.replace(extname(filePath), '.css'), - }); - - const warnings = result.warnings().map((w) => w.toString()); - const { code, warnings: esBuildWarnings } = await this.esbuild.transform( - result.css, - { - loader: 'css', - minify: true, - target: this.targets, - sourcefile: filePath, - } - ); - - if (esBuildWarnings.length > 0) { - warnings.push( - ...(await this.esbuild.formatMessages(esBuildWarnings, { - kind: 'warning', - })) - ); - } - - if (this.cacheDirectory) { - await saveCacheEntry( - this.cacheDirectory, - key, - JSON.stringify({ - css: code, - warnings, - }) - ); - } - warnings.forEach((msg) => log.warn(msg)); - - return code; - } - - private createPostCssProcessor( - tailwindSetup: TailwindSetup - ): ReturnType { - const postCssPlugins = []; - if (tailwindSetup) { - postCssPlugins.push(getTailwindPostCssPlugin(tailwindSetup)); - } - - if (this.cssUrl !== CssUrl.none) { - postCssPlugins.push(postcssUrl({ url: this.cssUrl })); - } - - postCssPlugins.push( - autoprefixer({ - ignoreUnknownVersions: true, - overrideBrowserslist: this.browserslistData, - }) - ); - - return postcss(postCssPlugins); - } - - private async renderCss(filePath: string, css: string): Promise { - const ext = extname(filePath); - - switch (ext) { - case '.sass': - case '.scss': { - return (await import('sass')).compileString(css, { - url: pathToFileURL(filePath), - syntax: '.sass' === ext ? 'indented' : 'scss', - loadPaths: this.styleIncludePaths, - }).css; - } - case '.less': { - const { css: content } = await ( - await import('less') - ).render(css, { - filename: filePath, - javascriptEnabled: true, - paths: this.styleIncludePaths, - }); - - return content; - } - case '.css': - default: - return css; - } - } -} - -function transformSupportedBrowsersToTargets( - supportedBrowsers: string[] -): string[] { - const transformed: string[] = []; - - // https://esbuild.github.io/api/#target - const esBuildSupportedBrowsers = new Set([ - 'safari', - 'firefox', - 'edge', - 'chrome', - 'ios', - ]); - - for (const browser of supportedBrowsers) { - let [browserName, version] = browser.split(' '); - - // browserslist uses the name `ios_saf` for iOS Safari whereas esbuild uses `ios` - if (browserName === 'ios_saf') { - browserName = 'ios'; - } - - // browserslist uses ranges `15.2-15.3` versions but only the lowest is required - // to perform minimum supported feature checks. esbuild also expects a single version. - [version] = version.split('-'); - - if (esBuildSupportedBrowsers.has(browserName)) { - if (browserName === 'safari' && version === 'tp') { - // esbuild only supports numeric versions so `TP` is converted to a high number (999) since - // a Technology Preview (TP) of Safari is assumed to support all currently known features. - version = '999'; - } - - transformed.push(browserName + version); - } - } - - return transformed.length ? transformed : undefined; -} diff --git a/packages/angular/src/executors/package/ng-packagr-adjustments/utils/ng-compiler-cli.ts b/packages/angular/src/executors/package/ng-packagr-adjustments/utils/ng-compiler-cli.ts deleted file mode 100644 index 3ee6811271ad5..0000000000000 --- a/packages/angular/src/executors/package/ng-packagr-adjustments/utils/ng-compiler-cli.ts +++ /dev/null @@ -1,9 +0,0 @@ -export async function ngccCompilerCli(): Promise { - const compilerCliModule = await new Function( - `return import('@angular/compiler-cli/ngcc');` - )(); - - return compilerCliModule.process - ? compilerCliModule - : compilerCliModule.default; -} diff --git a/packages/angular/src/executors/package/package.impl.ts b/packages/angular/src/executors/package/package.impl.ts index 65b9386912549..e4424764d9748 100644 --- a/packages/angular/src/executors/package/package.impl.ts +++ b/packages/angular/src/executors/package/package.impl.ts @@ -20,7 +20,7 @@ async function initializeNgPackagr( context: ExecutorContext, projectDependencies: DependentBuildableProjectNode[] ): Promise { - const ngPackagr = await getNgPackagrInstance(options); + const ngPackagr = await getNgPackagrInstance(); ngPackagr.forProject(resolve(context.root, options.project)); if (options.tsConfig) { diff --git a/packages/angular/src/executors/package/schema.d.ts b/packages/angular/src/executors/package/schema.d.ts index d738f50028596..eb0248ee02a40 100644 --- a/packages/angular/src/executors/package/schema.d.ts +++ b/packages/angular/src/executors/package/schema.d.ts @@ -1,6 +1,4 @@ import type { NgPackagrBuilderOptions } from '@angular-devkit/build-angular'; export interface BuildAngularLibraryExecutorOptions - extends NgPackagrBuilderOptions { - tailwindConfig?: string; -} + extends NgPackagrBuilderOptions {} diff --git a/packages/angular/src/executors/package/schema.json b/packages/angular/src/executors/package/schema.json index da7b5e4f13de9..552318e3d115d 100644 --- a/packages/angular/src/executors/package/schema.json +++ b/packages/angular/src/executors/package/schema.json @@ -7,10 +7,6 @@ "cli": "nx", "type": "object", "presets": [ - { - "name": "Publishable Library with Tailwind", - "keys": ["project", "tailwindConfig"] - }, { "name": "Updating Project Dependencies for Publishable Library", "keys": ["project"] @@ -36,12 +32,6 @@ "poll": { "type": "number", "description": "Enable and define the file watching poll time period in milliseconds. _Note: this is only supported in Angular versions >= 18.0.0_." - }, - "tailwindConfig": { - "type": "string", - "description": "The full path for the Tailwind configuration file, relative to the workspace root. If not provided and a `tailwind.config.js` file exists in the project or workspace root, it will be used. Otherwise, Tailwind will not be configured. _Note: starting with Angular v17, this option is no longer used and the configuration will be picked up if exists at the project or workspace root_.", - "x-completion-type": "file", - "x-completion-glob": "tailwind.config@(.js|.cjs|.mjs|.ts)" } }, "additionalProperties": false, diff --git a/packages/angular/src/executors/utilities/ng-packagr/stylesheet-processor.ts b/packages/angular/src/executors/utilities/ng-packagr/pre-v19/stylesheet-processor.ts similarity index 98% rename from packages/angular/src/executors/utilities/ng-packagr/stylesheet-processor.ts rename to packages/angular/src/executors/utilities/ng-packagr/pre-v19/stylesheet-processor.ts index d59605b7ca442..723235118ce29 100644 --- a/packages/angular/src/executors/utilities/ng-packagr/stylesheet-processor.ts +++ b/packages/angular/src/executors/utilities/ng-packagr/pre-v19/stylesheet-processor.ts @@ -12,11 +12,11 @@ import { dirname, join } from 'path'; const Piscina = require('piscina'); import { colors } from 'ng-packagr/lib/utils/color'; // using this instead of the one from ng-packagr -import { getTailwindConfigPath } from './tailwindcss'; +import { getTailwindConfigPath } from '../tailwindcss'; import { workspaceRoot } from '@nx/devkit'; import type { PostcssConfiguration } from 'ng-packagr/lib/styles/postcss-configuration'; import { gt, gte } from 'semver'; -import { getInstalledPackageVersionInfo } from '../angular-version-utils'; +import { getInstalledPackageVersionInfo } from '../../angular-version-utils'; const maxWorkersVariable = process.env['NG_BUILD_MAX_WORKERS']; const maxThreads = 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 274f0090a718d..36e37f5405487 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,20 +1,27 @@ import type { FactoryProvider } from 'injection-js'; import { STYLESHEET_PROCESSOR_TOKEN } from 'ng-packagr/lib/styles/stylesheet-processor.di'; import { getInstalledPackageVersionInfo } from '../angular-version-utils'; -import { - AsyncStylesheetProcessor, - StylesheetProcessor, -} from './stylesheet-processor'; export const STYLESHEET_PROCESSOR: FactoryProvider = { provide: STYLESHEET_PROCESSOR_TOKEN, useFactory: () => { - const { version: ngPackagrVersion } = + const { major: ngPackagrMajorVersion, version: ngPackagrVersion } = getInstalledPackageVersionInfo('ng-packagr'); - return ngPackagrVersion !== '17.2.0' - ? StylesheetProcessor - : AsyncStylesheetProcessor; + if (ngPackagrMajorVersion >= 19) { + const { StylesheetProcessor } = require('./v19+/stylesheet-processor'); + return StylesheetProcessor; + } + + if (ngPackagrVersion !== '17.2.0') { + const { StylesheetProcessor } = require('./pre-v19/stylesheet-processor'); + return StylesheetProcessor; + } + + const { + AsyncStylesheetProcessor, + } = require('./pre-v19/stylesheet-processor'); + return AsyncStylesheetProcessor; }, deps: [], }; diff --git a/packages/angular/src/executors/utilities/ng-packagr/tailwindcss.ts b/packages/angular/src/executors/utilities/ng-packagr/tailwindcss.ts index 3e76adb170481..39a43d518d3f7 100644 --- a/packages/angular/src/executors/utilities/ng-packagr/tailwindcss.ts +++ b/packages/angular/src/executors/utilities/ng-packagr/tailwindcss.ts @@ -1,51 +1,5 @@ -import { logger } from '@nx/devkit'; -import { workspaceRoot } from '@nx/devkit'; import { existsSync } from 'fs'; -import { join, relative } from 'path'; - -export interface TailwindSetup { - tailwindConfigPath: string; - tailwindPackagePath: string; -} - -export function getTailwindSetup( - basePath: string, - tailwindConfig?: string -): TailwindSetup | undefined { - let tailwindConfigPath = tailwindConfig; - - if (!tailwindConfigPath) { - tailwindConfigPath = getTailwindConfigPath(basePath, workspaceRoot); - } - - // Only load Tailwind CSS plugin if configuration file was found. - if (!tailwindConfigPath) { - return undefined; - } - - let tailwindPackagePath: string | undefined; - try { - tailwindPackagePath = require.resolve('tailwindcss'); - } catch { - const relativeTailwindConfigPath = relative( - workspaceRoot, - tailwindConfigPath - ); - logger.warn( - `Tailwind CSS configuration file found (${relativeTailwindConfigPath})` + - ` but the 'tailwindcss' package is not installed.` + - ` To enable Tailwind CSS, please install the 'tailwindcss' package.` - ); - - return undefined; - } - - if (!tailwindPackagePath) { - return undefined; - } - - return { tailwindConfigPath, tailwindPackagePath }; -} +import { join } from 'path'; export function getTailwindConfigPath( projectRoot: string, @@ -70,10 +24,3 @@ export function getTailwindConfigPath( return undefined; } - -export function getTailwindPostCssPlugin({ - tailwindConfigPath, - tailwindPackagePath, -}: TailwindSetup) { - return require(tailwindPackagePath)({ config: tailwindConfigPath }); -} diff --git a/packages/angular/src/executors/utilities/ng-packagr/v19+/stylesheet-processor.ts b/packages/angular/src/executors/utilities/ng-packagr/v19+/stylesheet-processor.ts new file mode 100644 index 0000000000000..585e35622a9a7 --- /dev/null +++ b/packages/angular/src/executors/utilities/ng-packagr/v19+/stylesheet-processor.ts @@ -0,0 +1,113 @@ +/** + * Adapted from the original ng-packagr source. + * + * Changes made: + * - Add the project root to the search directories. + */ + +import browserslist from 'browserslist'; +import { NgPackageEntryConfig } from 'ng-packagr/ng-entrypoint.schema'; +import { ComponentStylesheetBundler } from 'ng-packagr/lib/styles/component-stylesheets'; +import { + generateSearchDirectories, + getTailwindConfig, + loadPostcssConfiguration, +} from 'ng-packagr/lib/styles/postcss-configuration'; +import { workspaceRoot } from '@nx/devkit'; + +export enum CssUrl { + inline = 'inline', + none = 'none', +} + +export class StylesheetProcessor extends ComponentStylesheetBundler { + constructor( + protected readonly projectBasePath: string, + protected readonly basePath: string, + protected readonly cssUrl?: CssUrl, + protected readonly includePaths?: string[], + protected readonly sass?: NgPackageEntryConfig['lib']['sass'], + protected readonly cacheDirectory?: string | false, + protected readonly watch?: boolean + ) { + // 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', + ]; + + const browserslistData = browserslist(undefined, { path: basePath }); + let searchDirs = generateSearchDirectories([projectBasePath]); + const postcssConfiguration = loadPostcssConfiguration(searchDirs); + // (nx-specific): we support loading the TailwindCSS config from the root of the workspace + searchDirs = generateSearchDirectories([projectBasePath, workspaceRoot]); + + super( + { + cacheDirectory: cacheDirectory, + postcssConfiguration: postcssConfiguration, + tailwindConfiguration: postcssConfiguration + ? undefined + : getTailwindConfig(searchDirs, projectBasePath), + sass: sass as any, + workspaceRoot: projectBasePath, + cssUrl: cssUrl, + target: transformSupportedBrowsersToTargets(browserslistData), + includePaths: includePaths, + }, + 'css', + watch + ); + } + + destroy(): void { + void super.dispose(); + } +} + +function transformSupportedBrowsersToTargets( + supportedBrowsers: string[] +): string[] { + const transformed: string[] = []; + + // https://esbuild.github.io/api/#target + const esBuildSupportedBrowsers = new Set([ + 'safari', + 'firefox', + 'edge', + 'chrome', + 'ios', + ]); + + for (const browser of supportedBrowsers) { + let [browserName, version] = browser.split(' '); + + // browserslist uses the name `ios_saf` for iOS Safari whereas esbuild uses `ios` + if (browserName === 'ios_saf') { + browserName = 'ios'; + } + + // browserslist uses ranges `15.2-15.3` versions but only the lowest is required + // to perform minimum supported feature checks. esbuild also expects a single version. + [version] = version.split('-'); + + if (esBuildSupportedBrowsers.has(browserName)) { + if (browserName === 'safari' && version === 'tp') { + // esbuild only supports numeric versions so `TP` is converted to a high number (999) since + // a Technology Preview (TP) of Safari is assumed to support all currently known features. + version = '999'; + } + + transformed.push(browserName + version); + } + } + + return transformed.length ? transformed : undefined; +} diff --git a/packages/angular/src/generators/add-linting/add-linting.spec.ts b/packages/angular/src/generators/add-linting/add-linting.spec.ts index 8d26d45c2313a..927bede169141 100644 --- a/packages/angular/src/generators/add-linting/add-linting.spec.ts +++ b/packages/angular/src/generators/add-linting/add-linting.spec.ts @@ -66,7 +66,7 @@ describe('addLinting generator', () => { const { devDependencies } = readJson(tree, 'package.json'); expect(devDependencies['@typescript-eslint/utils']).toMatchInlineSnapshot( - `"^8.0.0"` + `"^8.13.0"` ); delete process.env.ESLINT_USE_FLAT_CONFIG; }); diff --git a/packages/angular/src/generators/application/__snapshots__/application.spec.ts.snap b/packages/angular/src/generators/application/__snapshots__/application.spec.ts.snap index a53f05ac8dc61..a9bbfcec9e35e 100644 --- a/packages/angular/src/generators/application/__snapshots__/application.spec.ts.snap +++ b/packages/angular/src/generators/application/__snapshots__/application.spec.ts.snap @@ -25,6 +25,7 @@ exports[`app --minimal should skip "nx-welcome.component.ts" file and references @Component({ selector: 'app-root', + standalone: false, templateUrl: './app.component.html', styleUrl: './app.component.css', }) @@ -85,6 +86,7 @@ exports[`app --minimal should skip "nx-welcome.component.ts" file and references @Component({ selector: 'app-root', + standalone: false, templateUrl: './app.component.html', styleUrl: './app.component.css', }) @@ -126,7 +128,6 @@ exports[`app --minimal should skip "nx-welcome.component.ts" file and references import { RouterModule } from '@angular/router'; @Component({ - standalone: true, imports: [RouterModule], selector: 'app-root', templateUrl: './app.component.html', @@ -170,7 +171,6 @@ exports[`app --minimal should skip "nx-welcome.component.ts" file and references "import { Component } from '@angular/core'; @Component({ - standalone: true, imports: [], selector: 'app-root', templateUrl: './app.component.html', @@ -243,7 +243,6 @@ import { RouterModule } from '@angular/router'; import { NxWelcomeComponent } from './nx-welcome.component'; @Component({ - standalone: true, imports: [NxWelcomeComponent, RouterModule], selector: 'app-root', templateUrl: './app.component.html', @@ -311,7 +310,6 @@ exports[`app --standalone should generate a standalone app correctly without rou import { NxWelcomeComponent } from './nx-welcome.component'; @Component({ - standalone: true, imports: [NxWelcomeComponent, ], selector: 'app-root', templateUrl: './app.component.html', @@ -545,6 +543,7 @@ exports[`app format files should format files 2`] = ` @Component({ selector: 'app-root', + standalone: false, templateUrl: './app.component.html', styleUrl: './app.component.css', }) @@ -618,8 +617,8 @@ exports[`app nested should create project configs 1`] = ` "type": "initial", }, { - "maximumError": "4kb", - "maximumWarning": "2kb", + "maximumError": "8kb", + "maximumWarning": "4kb", "type": "anyComponentStyle", }, ], @@ -734,8 +733,8 @@ exports[`app not nested should create project configs 1`] = ` "type": "initial", }, { - "maximumError": "4kb", - "maximumWarning": "2kb", + "maximumError": "8kb", + "maximumWarning": "4kb", "type": "anyComponentStyle", }, ], diff --git a/packages/angular/src/generators/application/application.spec.ts b/packages/angular/src/generators/application/application.spec.ts index 21c4a31622064..fb9d6b02aac34 100644 --- a/packages/angular/src/generators/application/application.spec.ts +++ b/packages/angular/src/generators/application/application.spec.ts @@ -1,5 +1,5 @@ import { installedCypressVersion } from '@nx/cypress/src/utils/cypress-version'; -import { Tree, writeJson } from '@nx/devkit'; +import { Tree, updateProjectConfiguration, writeJson } from '@nx/devkit'; import * as devkit from '@nx/devkit'; import { NxJsonConfiguration, @@ -253,6 +253,19 @@ describe('app', () => { ).toBe(true); }); + it('should not set esModuleInterop when using the browser-esbuild builder', async () => { + await generateApp(appTree, 'my-app', { bundler: 'webpack' }); + const project = readProjectConfiguration(appTree, 'my-app'); + project.targets.build.executor = + '@angular-devkit/build-angular:browser-esbuild'; + updateProjectConfiguration(appTree, 'my-app', project); + + expect( + readJson(appTree, 'my-app/tsconfig.json').compilerOptions + .esModuleInterop + ).toBeUndefined(); + }); + it('should not set esModuleInterop when using the browser builder', async () => { await generateApp(appTree, 'my-app', { bundler: 'webpack' }); @@ -487,9 +500,14 @@ describe('app', () => { it('should create Nx specific `nx-welcome.component.ts` file', async () => { await generateApp(appTree, 'my-dir/my-app'); - expect( - appTree.read('my-dir/my-app/src/app/nx-welcome.component.ts', 'utf-8') - ).toContain('Hello there'); + + const nxWelcomeComponentText = appTree.read( + 'my-dir/my-app/src/app/nx-welcome.component.ts', + 'utf-8' + ); + expect(nxWelcomeComponentText).not.toContain('standalone: true'); + expect(nxWelcomeComponentText).toContain('standalone: false'); + expect(nxWelcomeComponentText).toContain('Hello there'); }); it('should update the AppComponent spec to target Nx content', async () => { @@ -963,9 +981,12 @@ describe('app', () => { appTree.read('standalone/src/app/app.component.spec.ts', 'utf-8') ).toMatchSnapshot(); expect(appTree.exists('standalone/src/app/app.module.ts')).toBeFalsy(); - expect( - appTree.read('standalone/src/app/nx-welcome.component.ts', 'utf-8') - ).toContain('standalone: true'); + const nxWelcomeComponentText = appTree.read( + 'standalone/src/app/nx-welcome.component.ts', + 'utf-8' + ); + expect(nxWelcomeComponentText).not.toContain('standalone: true'); + expect(nxWelcomeComponentText).not.toContain('standalone: false'); }); it('should generate a standalone app correctly without routing', async () => { @@ -987,9 +1008,12 @@ describe('app', () => { appTree.read('standalone/src/app/app.component.spec.ts', 'utf-8') ).toMatchSnapshot(); expect(appTree.exists('standalone/src/app/app.module.ts')).toBeFalsy(); - expect( - appTree.read('standalone/src/app/nx-welcome.component.ts', 'utf-8') - ).toContain('standalone: true'); + const nxWelcomeComponentText = appTree.read( + 'standalone/src/app/nx-welcome.component.ts', + 'utf-8' + ); + expect(nxWelcomeComponentText).not.toContain('standalone: true'); + expect(nxWelcomeComponentText).not.toContain('standalone: false'); }); it('should should not use event coalescing in versions lower than v18', async () => { @@ -1177,8 +1201,8 @@ describe('app', () => { "type": "initial", }, { - "maximumError": "4kb", - "maximumWarning": "2kb", + "maximumError": "8kb", + "maximumWarning": "4kb", "type": "anyComponentStyle", }, ] @@ -1212,8 +1236,8 @@ describe('app', () => { "type": "initial", }, { - "maximumError": "4kb", - "maximumWarning": "2kb", + "maximumError": "8kb", + "maximumWarning": "4kb", "type": "anyComponentStyle", }, ] @@ -1236,7 +1260,7 @@ describe('app', () => { await generateApp(appTree, 'app1', { ssr: true }); expect(appTree.exists('app1/src/main.server.ts')).toBe(true); - expect(appTree.exists('app1/server.ts')).toBe(true); + expect(appTree.exists('app1/src/server.ts')).toBe(true); }); }); @@ -1247,7 +1271,7 @@ describe('app', () => { ...json, dependencies: { ...json.dependencies, - '@angular/core': '~16.2.0', + '@angular/core': '~17.2.0', }, })); }); @@ -1257,13 +1281,13 @@ describe('app', () => { const { devDependencies } = readJson(appTree, 'package.json'); expect(devDependencies['@angular-devkit/build-angular']).toEqual( - backwardCompatibleVersions.angularV16.angularDevkitVersion + backwardCompatibleVersions.angularV17.angularDevkitVersion ); expect(devDependencies['@angular-devkit/schematics']).toEqual( - backwardCompatibleVersions.angularV16.angularDevkitVersion + backwardCompatibleVersions.angularV17.angularDevkitVersion ); expect(devDependencies['@schematics/angular']).toEqual( - backwardCompatibleVersions.angularV16.angularDevkitVersion + backwardCompatibleVersions.angularV17.angularDevkitVersion ); }); @@ -1311,46 +1335,6 @@ describe('app', () => { `); }); - it('should use "@angular-devkit/build-angular:browser-esbuild" for --bundler=esbuild', async () => { - await generateApp(appTree, 'my-app', { - standalone: true, - bundler: 'esbuild', - }); - - const project = readProjectConfiguration(appTree, 'my-app'); - expect(project.targets.build.executor).toEqual( - '@angular-devkit/build-angular:browser-esbuild' - ); - }); - - it('should generate target options "main" and "browserTarget"', async () => { - await generateApp(appTree, 'my-app', { standalone: true }); - - const project = readProjectConfiguration(appTree, 'my-app'); - expect(project.targets.build.options.main).toBeDefined(); - expect( - project.targets.serve.configurations.development.browserTarget - ).toBeDefined(); - }); - - it('should not set esModuleInterop when using the browser-esbuild builder', async () => { - await generateApp(appTree, 'my-app', { bundler: 'esbuild' }); - - expect( - readJson(appTree, 'my-app/tsconfig.json').compilerOptions - .esModuleInterop - ).toBeUndefined(); - }); - - it('should not set esModuleInterop when using the browser builder', async () => { - await generateApp(appTree, 'my-app', { bundler: 'webpack' }); - - expect( - readJson(appTree, 'my-app/tsconfig.json').compilerOptions - .esModuleInterop - ).toBeUndefined(); - }); - it('should disable modern class fields behavior', async () => { await generateApp(appTree, 'my-app'); diff --git a/packages/angular/src/generators/application/application.ts b/packages/angular/src/generators/application/application.ts index 8a4390e628c96..230b5c5fce3bc 100644 --- a/packages/angular/src/generators/application/application.ts +++ b/packages/angular/src/generators/application/application.ts @@ -98,6 +98,7 @@ export async function applicationGenerator( project: options.name, standalone: options.standalone, skipPackageJson: options.skipPackageJson, + serverRouting: options.serverRouting, }); } diff --git a/packages/angular/src/generators/application/files/ng-module/src/app/app.component.ts__tpl__ b/packages/angular/src/generators/application/files/ng-module/src/app/app.component.ts__tpl__ index e8b42f0f4f32f..7005b31d614e3 100644 --- a/packages/angular/src/generators/application/files/ng-module/src/app/app.component.ts__tpl__ +++ b/packages/angular/src/generators/application/files/ng-module/src/app/app.component.ts__tpl__ @@ -1,13 +1,12 @@ import { Component<% if(viewEncapsulation) { %>, ViewEncapsulation<% } %> } from '@angular/core'; @Component({ - selector: '<%= rootSelector %>',<% if(!inlineTemplate) { %> + selector: '<%= rootSelector %>',<% if (setStandaloneFalse) { %> + standalone: false,<% } if(!inlineTemplate) { %> templateUrl: './app.component.html',<% } else { %> - template: `<% if(minimal) { %>

Welcome <%= appName %>

<% } else { %><<%= nxWelcomeSelector %>>><% } %> <% if(routing) { %><% } %>`,<% } %><% if (angularMajorVersion < 17) { if(!inlineStyle) { %> - styleUrls: ['./app.component.<%= style %>'],<% } else { %> - styles: [``],<% } %><% } else { if(!inlineStyle) { %> + template: `<% if(minimal) { %>

Welcome <%= appName %>

<% } else { %><<%= nxWelcomeSelector %>>><% } %> <% if(routing) { %><% } %>`,<% } %><% if(!inlineStyle) { %> styleUrl: './app.component.<%= style %>',<% } else { %> - styles: ``,<% } %><% } %><% if(viewEncapsulation) { %> + styles: ``,<% } %><% if(viewEncapsulation) { %> encapsulation: ViewEncapsulation.<%= viewEncapsulation %>,<% } %> }) export class AppComponent {<% if(!minimal) { %> diff --git a/packages/angular/src/generators/application/files/nx-welcome/claimed/ng-module/src/app/nx-welcome.component.ts__tpl__ b/packages/angular/src/generators/application/files/nx-welcome/claimed/ng-module/src/app/nx-welcome.component.ts__tpl__ index f721e36d8e4ba..fe42de40b2cdd 100644 --- a/packages/angular/src/generators/application/files/nx-welcome/claimed/ng-module/src/app/nx-welcome.component.ts__tpl__ +++ b/packages/angular/src/generators/application/files/nx-welcome/claimed/ng-module/src/app/nx-welcome.component.ts__tpl__ @@ -1,7 +1,8 @@ import { Component, ViewEncapsulation } from '@angular/core'; @Component({ - selector: '<%= nxWelcomeSelector %>', + selector: '<%= nxWelcomeSelector %>',<% if (setStandaloneFalse) { %> + standalone: false,<% } %> template: `