feat(angular): support angular cli v20.0.0-rc.3 (#30715)

Add support for the Angular CLI **20.0.0-rc.3** version.
This commit is contained in:
Leosvel Pérez Espinosa 2025-05-26 16:00:47 +02:00 committed by GitHub
parent 07baaafb43
commit 752d418f78
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
366 changed files with 13928 additions and 6652 deletions

View File

@ -51,3 +51,6 @@ CODEOWNERS
/.nx/workflows/dynamic-changesets.yaml
_files
_solution
# this file uses TS import attributes which the current prettier version does not support
tools/documentation/create-embeddings/src/main.mts

View File

@ -444,6 +444,66 @@
}
},
"migrations": {
"/nx-api/angular/migrations/update-angular-cli-version-20-0-0-rc-3": {
"description": "Update the @angular/cli package version to 20.0.0-rc.3.",
"file": "generated/packages/angular/migrations/update-angular-cli-version-20-0-0-rc-3.json",
"hidden": false,
"name": "update-angular-cli-version-20-0-0-rc-3",
"version": "21.2.0-beta.0",
"originalFilePath": "/packages/angular",
"path": "/nx-api/angular/migrations/update-angular-cli-version-20-0-0-rc-3",
"type": "migration"
},
"/nx-api/angular/migrations/migrate-provide-server-rendering-import": {
"description": "Migrate imports of `provideServerRendering` from `@angular/platform-server` to `@angular/ssr`.",
"file": "generated/packages/angular/migrations/migrate-provide-server-rendering-import.json",
"hidden": false,
"name": "migrate-provide-server-rendering-import",
"version": "21.2.0-beta.0",
"originalFilePath": "/packages/angular",
"path": "/nx-api/angular/migrations/migrate-provide-server-rendering-import",
"type": "migration"
},
"/nx-api/angular/migrations/replace-provide-server-routing": {
"description": "Replace `provideServerRouting` with `provideServerRendering` using `withRoutes`.",
"file": "generated/packages/angular/migrations/replace-provide-server-routing.json",
"hidden": false,
"name": "replace-provide-server-routing",
"version": "21.2.0-beta.0",
"originalFilePath": "/packages/angular",
"path": "/nx-api/angular/migrations/replace-provide-server-routing",
"type": "migration"
},
"/nx-api/angular/migrations/set-generator-defaults-for-previous-style-guide": {
"description": "Update the generator defaults to maintain the previous style guide behavior.",
"file": "generated/packages/angular/migrations/set-generator-defaults-for-previous-style-guide.json",
"hidden": false,
"name": "set-generator-defaults-for-previous-style-guide",
"version": "21.2.0-beta.0",
"originalFilePath": "/packages/angular",
"path": "/nx-api/angular/migrations/set-generator-defaults-for-previous-style-guide",
"type": "migration"
},
"/nx-api/angular/migrations/update-module-resolution": {
"description": "Update 'moduleResolution' to 'bundler' in TypeScript configurations. You can read more about this here: https://www.typescriptlang.org/tsconfig/#moduleResolution.",
"file": "generated/packages/angular/migrations/update-module-resolution.json",
"hidden": false,
"name": "update-module-resolution",
"version": "21.2.0-beta.0",
"originalFilePath": "/packages/angular",
"path": "/nx-api/angular/migrations/update-module-resolution",
"type": "migration"
},
"/nx-api/angular/migrations/21.2.0-package-updates": {
"description": "",
"file": "generated/packages/angular/migrations/21.2.0-package-updates.json",
"hidden": false,
"name": "21.2.0-package-updates",
"version": "21.2.0-beta.0",
"originalFilePath": "/packages/angular",
"path": "/nx-api/angular/migrations/21.2.0-package-updates",
"type": "migration"
},
"/nx-api/angular/migrations/21.1.0-package-updates": {
"description": "",
"file": "generated/packages/angular/migrations/21.1.0-package-updates.json",
@ -1687,6 +1747,26 @@
}
},
"migrations": {
"/nx-api/eslint/migrations/21.2.0-typescript-eslint-package-updates": {
"description": "",
"file": "generated/packages/eslint/migrations/21.2.0-typescript-eslint-package-updates.json",
"hidden": false,
"name": "21.2.0-typescript-eslint-package-updates",
"version": "21.2.0-beta.0",
"originalFilePath": "/packages/eslint",
"path": "/nx-api/eslint/migrations/21.2.0-typescript-eslint-package-updates",
"type": "migration"
},
"/nx-api/eslint/migrations/21.2.0-@typescript-eslint-package-updates": {
"description": "",
"file": "generated/packages/eslint/migrations/21.2.0-@typescript-eslint-package-updates.json",
"hidden": false,
"name": "21.2.0-@typescript-eslint-package-updates",
"version": "21.2.0-beta.0",
"originalFilePath": "/packages/eslint",
"path": "/nx-api/eslint/migrations/21.2.0-@typescript-eslint-package-updates",
"type": "migration"
},
"/nx-api/eslint/migrations/20.7.0-package-updates": {
"description": "",
"file": "generated/packages/eslint/migrations/20.7.0-package-updates.json",
@ -2454,6 +2534,16 @@
}
},
"migrations": {
"/nx-api/js/migrations/21.2.0-package-updates": {
"description": "",
"file": "generated/packages/js/migrations/21.2.0-package-updates.json",
"hidden": false,
"name": "21.2.0-package-updates",
"version": "21.2.0-beta.0",
"originalFilePath": "/packages/js",
"path": "/nx-api/js/migrations/21.2.0-package-updates",
"type": "migration"
},
"/nx-api/js/migrations/20.7.1-beta.0-package-updates": {
"description": "",
"file": "generated/packages/js/migrations/20.7.1-beta.0-package-updates.json",
@ -5724,6 +5814,16 @@
}
},
"migrations": {
"/nx-api/workspace/migrations/21.2.0-package-updates": {
"description": "",
"file": "generated/packages/workspace/migrations/21.2.0-package-updates.json",
"hidden": false,
"name": "21.2.0-package-updates",
"version": "21.2.0-beta.0",
"originalFilePath": "/packages/workspace",
"path": "/nx-api/workspace/migrations/21.2.0-package-updates",
"type": "migration"
},
"/nx-api/workspace/migrations/20.4.0-package-updates": {
"description": "",
"file": "generated/packages/workspace/migrations/20.4.0-package-updates.json",

View File

@ -439,6 +439,66 @@
}
],
"migrations": [
{
"description": "Update the @angular/cli package version to 20.0.0-rc.3.",
"file": "generated/packages/angular/migrations/update-angular-cli-version-20-0-0-rc-3.json",
"hidden": false,
"name": "update-angular-cli-version-20-0-0-rc-3",
"version": "21.2.0-beta.0",
"originalFilePath": "/packages/angular",
"path": "angular/migrations/update-angular-cli-version-20-0-0-rc-3",
"type": "migration"
},
{
"description": "Migrate imports of `provideServerRendering` from `@angular/platform-server` to `@angular/ssr`.",
"file": "generated/packages/angular/migrations/migrate-provide-server-rendering-import.json",
"hidden": false,
"name": "migrate-provide-server-rendering-import",
"version": "21.2.0-beta.0",
"originalFilePath": "/packages/angular",
"path": "angular/migrations/migrate-provide-server-rendering-import",
"type": "migration"
},
{
"description": "Replace `provideServerRouting` with `provideServerRendering` using `withRoutes`.",
"file": "generated/packages/angular/migrations/replace-provide-server-routing.json",
"hidden": false,
"name": "replace-provide-server-routing",
"version": "21.2.0-beta.0",
"originalFilePath": "/packages/angular",
"path": "angular/migrations/replace-provide-server-routing",
"type": "migration"
},
{
"description": "Update the generator defaults to maintain the previous style guide behavior.",
"file": "generated/packages/angular/migrations/set-generator-defaults-for-previous-style-guide.json",
"hidden": false,
"name": "set-generator-defaults-for-previous-style-guide",
"version": "21.2.0-beta.0",
"originalFilePath": "/packages/angular",
"path": "angular/migrations/set-generator-defaults-for-previous-style-guide",
"type": "migration"
},
{
"description": "Update 'moduleResolution' to 'bundler' in TypeScript configurations. You can read more about this here: https://www.typescriptlang.org/tsconfig/#moduleResolution.",
"file": "generated/packages/angular/migrations/update-module-resolution.json",
"hidden": false,
"name": "update-module-resolution",
"version": "21.2.0-beta.0",
"originalFilePath": "/packages/angular",
"path": "angular/migrations/update-module-resolution",
"type": "migration"
},
{
"description": "",
"file": "generated/packages/angular/migrations/21.2.0-package-updates.json",
"hidden": false,
"name": "21.2.0-package-updates",
"version": "21.2.0-beta.0",
"originalFilePath": "/packages/angular",
"path": "angular/migrations/21.2.0-package-updates",
"type": "migration"
},
{
"description": "",
"file": "generated/packages/angular/migrations/21.1.0-package-updates.json",
@ -1675,6 +1735,26 @@
}
],
"migrations": [
{
"description": "",
"file": "generated/packages/eslint/migrations/21.2.0-typescript-eslint-package-updates.json",
"hidden": false,
"name": "21.2.0-typescript-eslint-package-updates",
"version": "21.2.0-beta.0",
"originalFilePath": "/packages/eslint",
"path": "eslint/migrations/21.2.0-typescript-eslint-package-updates",
"type": "migration"
},
{
"description": "",
"file": "generated/packages/eslint/migrations/21.2.0-@typescript-eslint-package-updates.json",
"hidden": false,
"name": "21.2.0-@typescript-eslint-package-updates",
"version": "21.2.0-beta.0",
"originalFilePath": "/packages/eslint",
"path": "eslint/migrations/21.2.0-@typescript-eslint-package-updates",
"type": "migration"
},
{
"description": "",
"file": "generated/packages/eslint/migrations/20.7.0-package-updates.json",
@ -2436,6 +2516,16 @@
}
],
"migrations": [
{
"description": "",
"file": "generated/packages/js/migrations/21.2.0-package-updates.json",
"hidden": false,
"name": "21.2.0-package-updates",
"version": "21.2.0-beta.0",
"originalFilePath": "/packages/js",
"path": "js/migrations/21.2.0-package-updates",
"type": "migration"
},
{
"description": "",
"file": "generated/packages/js/migrations/20.7.1-beta.0-package-updates.json",
@ -5686,6 +5776,16 @@
}
],
"migrations": [
{
"description": "",
"file": "generated/packages/workspace/migrations/21.2.0-package-updates.json",
"hidden": false,
"name": "21.2.0-package-updates",
"version": "21.2.0-beta.0",
"originalFilePath": "/packages/workspace",
"path": "workspace/migrations/21.2.0-package-updates",
"type": "migration"
},
{
"description": "",
"file": "generated/packages/workspace/migrations/20.4.0-package-updates.json",

View File

@ -54,7 +54,6 @@
"description": "The full path for the browser entry point to the application, relative to the current workspace."
},
"server": {
"type": "string",
"description": "The full path for the server entry point to the application, relative to the current workspace.",
"oneOf": [
{
@ -80,7 +79,7 @@
},
"deployUrl": {
"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_."
"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."
},
"security": {
"description": "Security features to protect against XSS and other common attacks. _Note: this is only supported in Angular versions >= 19.0.0_.",
@ -226,7 +225,7 @@
"clearScreen": {
"type": "boolean",
"default": false,
"description": "Automatically clear the terminal screen during rebuilds. _Note: this is only supported in Angular versions >= 17.2.0_."
"description": "Automatically clear the terminal screen during rebuilds."
},
"optimization": {
"description": "Enables optimization of the build output. Including minification of scripts and styles, tree-shaking, dead-code elimination, inlining of critical CSS and fonts inlining. For more information, see https://angular.dev/reference/configs/workspace-config#optimization-configuration.",
@ -259,7 +258,7 @@
},
"removeSpecialComments": {
"type": "boolean",
"description": "Remove comments in global CSS that contains '@license' or '@preserve' or that starts with '//!' or '/*!'. _Note: this is only supported in Angular versions >= 17.1.0_.",
"description": "Remove comments in global CSS that contains '@license' or '@preserve' or that starts with '//!' or '/*!'.",
"default": true
}
},
@ -293,17 +292,22 @@
]
},
"loader": {
"description": "Defines the type of loader to use with a specified file extension when used with a JavaScript `import`. `text` inlines the content as a string; `binary` inlines the content as a Uint8Array; `file` emits the file and provides the runtime location of the file; `empty` considers the content to be empty and not include it in bundles. _Note: this is only supported in Angular versions >= 17.1.0_.",
"description": "Defines the type of loader to use with a specified file extension when used with a JavaScript `import`. `text` inlines the content as a string; `binary` inlines the content as a Uint8Array; `file` emits the file and provides the runtime location of the file; `empty` considers the content to be empty and not include it in bundles.",
"type": "object",
"patternProperties": {
"^\\.\\S+$": { "enum": ["text", "binary", "file", "empty"] }
}
},
"define": {
"description": "Defines global identifiers that will be replaced with a specified constant value when found in any JavaScript or TypeScript code including libraries. The value will be used directly. String values must be put in quotes. Identifiers within Angular metadata such as Component Decorators will not be replaced. _Note: this is only supported in Angular versions >= 17.2.0_.",
"description": "Defines global identifiers that will be replaced with a specified constant value when found in any JavaScript or TypeScript code including libraries. The value will be used directly. String values must be put in quotes. Identifiers within Angular metadata such as Component Decorators will not be replaced.",
"type": "object",
"additionalProperties": { "type": "string" }
},
"conditions": {
"description": "Custom package resolution conditions used to resolve conditional exports/imports. Defaults to ['module', 'development'/'production']. The following special conditions are always present if the requirements are satisfied: 'default', 'import', 'require', 'browser', 'node'. _Note: this is only supported in Angular versions >= 20.0.0_.",
"type": "array",
"items": { "type": "string" }
},
"fileReplacements": {
"description": "Replace compilation source files with other compilation source files in the build.",
"type": "array",
@ -322,7 +326,7 @@
"default": []
},
"outputPath": {
"description": "Specify the output path relative to workspace root. _Note: the object notation is only supported in Angular versions >= 17.1.0_.",
"description": "Specify the output path relative to workspace root.",
"oneOf": [
{
"type": "object",
@ -387,6 +391,11 @@
"type": "boolean",
"description": "Resolve vendor packages source maps.",
"default": false
},
"sourcesContent": {
"type": "boolean",
"description": "Output original source content for files within the source map. _Note: this is only supported in Angular versions >= 20.0.0_.",
"default": true
}
},
"additionalProperties": false
@ -511,7 +520,7 @@
"preloadInitial": {
"type": "boolean",
"default": true,
"description": "Generates 'preload', 'modulepreload', and 'preconnect' link elements for initial application files and resources. _Note: this is only supported in Angular versions >= 17.1.0_."
"description": "Generates 'preload', 'modulepreload', and 'preconnect' link elements for initial application files and resources."
}
},
"required": ["input"]
@ -694,12 +703,12 @@
}
},
"indexHtmlTransformer": {
"description": "Path to a file exposing a default function to transform the `index.html` file. _Note: this is only supported in Angular versions >= 17.1.0_.",
"description": "Path to a file exposing a default function to transform the `index.html` file.",
"type": "string"
}
},
"additionalProperties": false,
"required": ["outputPath", "index", "browser", "tsConfig"],
"required": ["outputPath", "tsConfig"],
"definitions": {
"assetPattern": {
"oneOf": [

View File

@ -8,18 +8,12 @@
"$schema": "http://json-schema.org/draft-07/schema",
"title": "Schema for Webpack Dev Server",
"description": "Serves an Angular application using [webpack](https://webpack.js.org/) when the build target is using a webpack-based executor, or [Vite](https://vitejs.dev/) when the build target uses an [esbuild](https://esbuild.github.io/)-based executor.",
"examplesFile": "This executor is a drop-in replacement for the `@angular-devkit/build-angular:dev-server` builder provided by the Angular CLI. In addition to the features provided by the Angular CLI builder, the `@nx/angular:dev-server` executor also supports the following:\n\n- Serving applications with Vite when using the `@nx/angular:application` or `@nx/angular:browser-esbuild` executors to build them\n- Serving applications with webpack when using the `@nx/angular:webpack-browser` executor\n- Providing HTTP request middleware functions when the build target is using an esbuild-based executor\n- Incremental builds\n\n## Examples\n\n{% tabs %}\n{% tab label=\"Using a custom webpack configuration\" %}\n\nThis executor should be used along with `@nx/angular:webpack-browser` to serve an application using a custom webpack configuration.\n\nAdd the `serve` target using the `@nx/angular:dev-server` executor, set the `build` target executor as `@nx/angular:webpack-browser` and set the `customWebpackConfig` option as shown below:\n\n```json {% fileName=\"apps/my-app/project.json\" highlightLines=[2,\"5-7\",\"10-20\"] %}\n\"build\": {\n \"executor\": \"@nx/angular:webpack-browser\",\n \"options\": {\n ...\n \"customWebpackConfig\": {\n \"path\": \"apps/my-app/webpack.config.js\"\n }\n }\n},\n\"serve\": {\n \"executor\": \"@nx/angular:dev-server\",\n \"configurations\": {\n \"production\": {\n \"buildTarget\": \"my-app:build:production\"\n },\n \"development\": {\n \"buildTarget\": \"my-app:build:development\"\n }\n },\n \"defaultConfiguration\": \"development\",\n}\n```\n\n```js {% fileName=\"apps/my-app/webpack.config.js\" %}\nmodule.exports = (config) => {\n // update the config with your custom configuration\n\n return config;\n};\n```\n\n{% /tab %}\n\n{% tab label=\"Providing HTTP request middleware function\" %}\n\n{% callout type=\"warning\" title=\"Overrides\" }\n\nAvailable for workspaces using Angular version 17.0.0 or greater and with `build` targets using an esbuild-based executor.\n\n{% /callout %}\n\nThe executor accepts an `esbuildMiddleware` option that allows you to provide HTTP require middleware functions that will be used by the Vite development server.\n\n```json {% fileName=\"apps/my-app/project.json\" highlightLines=[8] %}\n{\n ...\n \"targets\": {\n \"serve\": {\n \"executor\": \"@nx/angular:dev-server\",\n \"options\": {\n ...\n \"esbuildMiddleware\": [\"apps/my-app/hello-world.middleware.ts\"]\n }\n }\n ...\n }\n}\n```\n\n```ts {% fileName=\"apps/my-app/hello-world.middleware.ts\" %}\nimport type { IncomingMessage, ServerResponse } from 'node:http';\n\nconst helloWorldMiddleware = (\n req: IncomingMessage,\n res: ServerResponse,\n next: (err?: unknown) => void\n) => {\n if (req.url === '/hello-world') {\n res.end('<h1>Hello World!</h1>');\n } else {\n next();\n }\n};\n\nexport default helloWorldMiddleware;\n```\n\n{% /tab %}\n",
"examplesFile": "This executor is a drop-in replacement for the `@angular-devkit/build-angular:dev-server` builder provided by the Angular CLI. In addition to the features provided by the Angular CLI builder, the `@nx/angular:dev-server` executor also supports the following:\n\n- Serving applications with Vite when using the `@nx/angular:application` or `@nx/angular:browser-esbuild` executors to build them\n- Serving applications with webpack when using the `@nx/angular:webpack-browser` executor\n- Providing HTTP request middleware functions when the build target is using an esbuild-based executor\n- Incremental builds\n\n## Examples\n\n{% tabs %}\n{% tab label=\"Using a custom webpack configuration\" %}\n\nThis executor should be used along with `@nx/angular:webpack-browser` to serve an application using a custom webpack configuration.\n\nAdd the `serve` target using the `@nx/angular:dev-server` executor, set the `build` target executor as `@nx/angular:webpack-browser` and set the `customWebpackConfig` option as shown below:\n\n```json {% fileName=\"apps/my-app/project.json\" highlightLines=[2,\"5-7\",\"10-20\"] %}\n\"build\": {\n \"executor\": \"@nx/angular:webpack-browser\",\n \"options\": {\n ...\n \"customWebpackConfig\": {\n \"path\": \"apps/my-app/webpack.config.js\"\n }\n }\n},\n\"serve\": {\n \"executor\": \"@nx/angular:dev-server\",\n \"configurations\": {\n \"production\": {\n \"buildTarget\": \"my-app:build:production\"\n },\n \"development\": {\n \"buildTarget\": \"my-app:build:development\"\n }\n },\n \"defaultConfiguration\": \"development\",\n}\n```\n\n```js {% fileName=\"apps/my-app/webpack.config.js\" %}\nmodule.exports = (config) => {\n // update the config with your custom configuration\n\n return config;\n};\n```\n\n{% /tab %}\n\n{% tab label=\"Providing HTTP request middleware function\" %}\n\nThe executor accepts an `esbuildMiddleware` option that allows you to provide HTTP require middleware functions that will be used by the Vite development server.\n\n```json {% fileName=\"apps/my-app/project.json\" highlightLines=[8] %}\n{\n ...\n \"targets\": {\n \"serve\": {\n \"executor\": \"@nx/angular:dev-server\",\n \"options\": {\n ...\n \"esbuildMiddleware\": [\"apps/my-app/hello-world.middleware.ts\"]\n }\n }\n ...\n }\n}\n```\n\n```ts {% fileName=\"apps/my-app/hello-world.middleware.ts\" %}\nimport type { IncomingMessage, ServerResponse } from 'node:http';\n\nconst helloWorldMiddleware = (\n req: IncomingMessage,\n res: ServerResponse,\n next: (err?: unknown) => void\n) => {\n if (req.url === '/hello-world') {\n res.end('<h1>Hello World!</h1>');\n } else {\n next();\n }\n};\n\nexport default helloWorldMiddleware;\n```\n\n{% /tab %}\n",
"type": "object",
"presets": [
{ "name": "Using a Different Port", "keys": ["buildTarget", "port"] }
],
"properties": {
"browserTarget": {
"type": "string",
"description": "A browser builder target to serve in the format of `project:target[:configuration]`. Ignored if `buildTarget` is set.",
"pattern": "^[^:\\s]+:[^:\\s]+(:[^\\s]+)?$",
"x-deprecated": "Use 'buildTarget' instead. It will be removed when Angular v20 is released."
},
"buildTarget": {
"type": "string",
"description": "A build builder target to serve in the format of `project:target[:configuration]`.",
@ -122,7 +116,7 @@
]
},
"prebundle": {
"description": "Enable and control the Vite-based development server's prebundling capabilities. To enable prebundling, the Angular CLI cache must also be enabled. This option has no effect when using the 'browser' or other Webpack-based builders. _Note: this is only supported in Angular versions >= 17.2.0_.",
"description": "Enable and control the Vite-based development server's prebundling capabilities. To enable prebundling, the Angular CLI cache must also be enabled. This option has no effect when using the 'browser' or other Webpack-based builders.",
"oneOf": [
{ "type": "boolean" },
{
@ -141,7 +135,7 @@
},
"buildLibsFromSource": {
"type": "boolean",
"description": "Read buildable libraries from source instead of building them separately. If not set, it will take the value specified in the `browserTarget` options, or it will default to `true` if it's also not set in the `browserTarget` options.",
"description": "Read buildable libraries from source instead of building them separately. If not set, it will take the value specified in the `buildTarget` options, or it will default to `true` if it's also not set in the `buildTarget` options.",
"x-priority": "important"
},
"esbuildMiddleware": {
@ -159,10 +153,7 @@
}
},
"additionalProperties": false,
"anyOf": [
{ "required": ["buildTarget"] },
{ "required": ["browserTarget"] }
]
"required": ["buildTarget"]
},
"description": "Serves an Angular application using [webpack](https://webpack.js.org/) when the build target is using a webpack-based executor, or [Vite](https://vitejs.dev/) when the build target uses an [esbuild](https://esbuild.github.io/)-based executor.",
"aliases": [],

View File

@ -41,6 +41,11 @@
"outFile": {
"type": "string",
"description": "Name of the file to output."
},
"i18nDuplicateTranslation": {
"type": "string",
"description": "How to handle duplicate translations. _Note: this is only available in Angular 20.0.0 and above._",
"enum": ["error", "warning", "ignore"]
}
},
"additionalProperties": false,

View File

@ -12,12 +12,6 @@
{ "name": "Using a Different Port", "keys": ["buildTarget", "port"] }
],
"properties": {
"browserTarget": {
"type": "string",
"description": "A browser builder target to serve in the format of `project:target[:configuration]`.",
"pattern": "^[^:\\s]+:[^:\\s]+(:[^\\s]+)?$",
"x-deprecated": "Use 'buildTarget' instead. It will be removed when Angular v20 is released."
},
"buildTarget": {
"type": "string",
"description": "A build builder target to serve in the format of `project:target[:configuration]`.",
@ -152,15 +146,12 @@
},
"buildLibsFromSource": {
"type": "boolean",
"description": "Read buildable libraries from source instead of building them separately. If not set, it will take the value specified in the `browserTarget` options, or it will default to `true` if it's also not set in the `browserTarget` options.",
"description": "Read buildable libraries from source instead of building them separately. If not set, it will take the value specified in the `buildTarget` options, or it will default to `true` if it's also not set in the `buildTarget` options.",
"x-priority": "important"
}
},
"additionalProperties": false,
"anyOf": [
{ "required": ["buildTarget"] },
{ "required": ["browserTarget"] }
],
"required": ["buildTarget"],
"examplesFile": "## Examples\n\n{% tabs %}\n\n{% tab label=\"Basic Usage\" %}\nThe Module Federation Dev Server will serve a host application and find the remote applications associated with the host and serve them statically also. \nSee an example set up of it below:\n\n```json\n{\n \"serve\": {\n \"executor\": \"@nx/angular:module-federation-dev-server\",\n \"configurations\": {\n \"production\": {\n \"buildTarget\": \"host:build:production\"\n },\n \"development\": {\n \"buildTarget\": \"host:build:development\"\n }\n },\n \"defaultConfiguration\": \"development\",\n \"options\": {\n \"port\": 4200,\n \"publicHost\": \"http://localhost:4200\"\n }\n }\n}\n```\n\n{% /tab %}\n\n{% tab label=\"Serve host with remotes that can be live reloaded\" %}\nThe Module Federation Dev Server will serve a host application and find the remote applications associated with the host and serve a set selection with live reloading enabled also. \nSee an example set up of it below:\n\n```json\n{\n \"serve-with-hmr-remotes\": {\n \"executor\": \"@nx/angular:module-federation-dev-server\",\n \"configurations\": {\n \"production\": {\n \"buildTarget\": \"host:build:production\"\n },\n \"development\": {\n \"buildTarget\": \"host:build:development\"\n }\n },\n \"defaultConfiguration\": \"development\",\n \"options\": {\n \"port\": 4200,\n \"publicHost\": \"http://localhost:4200\",\n \"devRemotes\": [\n \"remote1\",\n {\n \"remoteName\": \"remote2\",\n \"configuration\": \"development\"\n }\n ]\n }\n }\n}\n```\n\n{% /tab %}\n\n{% /tabs %}\n"
},
"description": "Serves host [Module Federation](https://module-federation.io/) applications ([webpack](https://webpack.js.org/)-based) allowing to specify which remote applications should be served with the host.",

View File

@ -34,11 +34,10 @@
},
"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_."
"description": "Enable and define the file watching poll time period in milliseconds."
}
},
"additionalProperties": false,
"required": ["project"]
"additionalProperties": false
},
"description": "Builds an Angular library with support for incremental builds.\n\nThis executor is meant to be used with buildable libraries in an incremental build scenario. It is similar to the `@nx/angular:package` executor but it only produces ESM2022 bundles.",
"aliases": [],

View File

@ -36,13 +36,10 @@
},
"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_."
"description": "Enable and define the file watching poll time period in milliseconds."
}
},
"additionalProperties": false,
"required": [
"project"
]
"additionalProperties": false
},
"description": "Builds and packages an Angular library producing an output following the Angular Package Format (APF) to be distributed as an NPM package.\n\nThis executor is a drop-in replacement for the `@angular-devkit/build-angular:ng-packagr` and `@angular/build:ng-packagr` builders, with additional support for incremental builds.",
"aliases": [],

View File

@ -174,7 +174,7 @@
"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_.",
"description": "Creates a server application using the Server Routing and App Engine APIs for application using the `application` builder (Developer Preview). _Note: this is only supported in Angular versions 19.x.x_. From Angular 20 onwards, SSR will always enable server routing when using the `application` builder.",
"type": "boolean"
}
},

View File

@ -95,8 +95,7 @@
},
"type": {
"type": "string",
"description": "Adds a developer-defined type to the filename, in the format `name.type.ts`.",
"default": "component"
"description": "Append a custom type to the component's filename. It defaults to 'component' for Angular versions below v20. For Angular v20 and above, no type is appended unless specified."
},
"export": {
"type": "boolean",
@ -109,6 +108,11 @@
"default": false,
"description": "Use default export for the component instead of a named export."
},
"ngHtml": {
"type": "boolean",
"default": false,
"description": "Generate component template files with an '.ng.html' file extension instead of '.html'."
},
"skipFormat": {
"description": "Skip formatting files.",
"type": "boolean",
@ -117,7 +121,7 @@
}
},
"required": ["path"],
"examplesFile": "## Examples\n\n{% tabs %}\n{% tab label=\"Simple Component\" %}\n\nGenerate a component named `MyComponent` at `apps/my-app/src/lib/my-component/my-component.component.ts`:\n\n```bash\nnx g @nx/angular:component apps/my-app/src/lib/my-component/my-component.ts\n```\n\n{% /tab %}\n\n{% tab label=\"Without Providing the File Extension\" %}\n\nGenerate a component named `MyComponent` at `apps/my-app/src/lib/my-component/my-component.component.ts`:\n\n```bash\nnx g @nx/angular:component apps/my-app/src/lib/my-component/my-component\n```\n\n{% /tab %}\n\n{% tab label=\"With Different Symbol Name\" %}\n\nGenerate a component named `CustomComponent` at `apps/my-app/src/lib/my-component/my-component.component.ts`:\n\n```bash\nnx g @nx/angular:component apps/my-app/src/lib/my-component/my-component --name=custom\n```\n\n{% /tab %}\n\n{% tab label=\"Single File Component\" %}\n\nCreate a component named `my-component` with inline styles and inline template:\n\n```bash\nnx g @nx/angular:component apps/my-app/src/lib/my-component/my-component --inlineStyle --inlineTemplate\n```\n\n{% /tab %}\n\n{% tab label=\"Component with OnPush Change Detection Strategy\" %}\n\nCreate a component named `my-component` with OnPush Change Detection Strategy:\n\n```bash\nnx g @nx/angular:component apps/my-app/src/lib/my-component/my-component --changeDetection=OnPush\n```\n\n{% /tab %}\n",
"examplesFile": "## Examples\n\n{% tabs %}\n{% tab label=\"Simple Component\" %}\n\nGenerate a component named `Card` at `apps/my-app/src/lib/card/card.ts`:\n\n```bash\nnx g @nx/angular:component apps/my-app/src/lib/card/card.ts\n```\n\n{% /tab %}\n\n{% tab label=\"Without Providing the File Extension\" %}\n\nGenerate a component named `Card` at `apps/my-app/src/lib/card/card.ts`:\n\n```bash\nnx g @nx/angular:component apps/my-app/src/lib/card/card\n```\n\n{% /tab %}\n\n{% tab label=\"With Different Symbol Name\" %}\n\nGenerate a component named `Custom` at `apps/my-app/src/lib/card/card.ts`:\n\n```bash\nnx g @nx/angular:component apps/my-app/src/lib/card/card --name=custom\n```\n\n{% /tab %}\n\n{% tab label=\"With a Component Type\" %}\n\nGenerate a component named `CardComponent` at `apps/my-app/src/lib/card/card.component.ts`:\n\n```bash\nnx g @nx/angular:component apps/my-app/src/lib/card/card --type=component\n```\n\n{% /tab %}\n\n{% tab label=\"Single File Component\" %}\n\nCreate a component named `Card` with inline styles and inline template:\n\n```bash\nnx g @nx/angular:component apps/my-app/src/lib/card/card --inlineStyle --inlineTemplate\n```\n\n{% /tab %}\n\n{% tab label=\"Component with OnPush Change Detection Strategy\" %}\n\nCreate a component named `Card` with `OnPush` Change Detection Strategy:\n\n```bash\nnx g @nx/angular:component apps/my-app/src/lib/card/card --changeDetection=OnPush\n```\n\n{% /tab %}\n",
"presets": []
},
"aliases": ["c"],

View File

@ -15,11 +15,15 @@
"command": "nx g @nx/angular:directive mylib/src/lib/foo.directive.ts"
},
{
"description": "Generate a directive without providing the file extension. It results in the directive `FooDirective` at `mylib/src/lib/foo.directive.ts`",
"description": "Generate a directive without providing the file extension. It results in the directive `Foo` at `mylib/src/lib/foo.ts`",
"command": "nx g @nx/angular:directive mylib/src/lib/foo"
},
{
"description": "Generate a directive with the exported symbol different from the file name. It results in the directive `CustomDirective` at `mylib/src/lib/foo.directive.ts`",
"description": "Generate a directive with a given type/suffix. It results in the directive `FooDirective` at `mylib/src/lib/foo.directive.ts`",
"command": "nx g @nx/angular:directive mylib/src/lib/foo --type=directive"
},
{
"description": "Generate a directive with the exported symbol different from the file name. It results in the directive `Custom` at `mylib/src/lib/foo.ts`",
"command": "nx g @nx/angular:directive mylib/src/lib/foo --name=custom"
}
],
@ -73,6 +77,10 @@
"default": false,
"description": "The declaring NgModule exports this directive."
},
"type": {
"type": "string",
"description": "Append a custom type to the directive's filename. It defaults to 'directive' for Angular versions below v20. For Angular v20 and above, no type is appended unless specified."
},
"skipFormat": {
"type": "boolean",
"default": false,

View File

@ -175,7 +175,7 @@
"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_.",
"description": "Creates a server application using the Server Routing and App Engine APIs for application using the `application` builder (Developer Preview). _Note: this is only supported in Angular versions 19.x.x_. From Angular 20 onwards, SSR will always enable server routing when using the `application` builder.",
"type": "boolean"
},
"typescriptConfiguration": {

View File

@ -11,11 +11,11 @@
"type": "object",
"examples": [
{
"command": "nx g @nx/angular:ngrx --root --parent=apps/my-app/src/app/app.module.ts --facade=false placeholder",
"command": "nx g @nx/angular:ngrx --root --parent=apps/my-app/src/app/app-module.ts --facade=false placeholder",
"description": "Add root ngrx configration to the `my-app` application"
},
{
"command": "nx g @nx/angular:ngrx --parent=libs/my-lib/src/lib/my-lib.module.ts --facade=true --root=false users",
"command": "nx g @nx/angular:ngrx --parent=libs/my-lib/src/lib/my-lib-module.ts --facade=true --root=false users",
"description": "Add a `users` state with a facade to the `my-lib` library. It will be tracked under the default `+state` folder in the lib"
},
{
@ -42,7 +42,7 @@
},
"parent": {
"type": "string",
"description": "The path to the file where the state will be registered. For NgModule usage, this will be your `app.module.ts` for your root state, or your Feature Module for feature state. For Standalone API usage, this will be your `app.config.ts` file for your root state, or the Routes definition file for your feature state. The host directory will create/use the new state directory.",
"description": "The path to the file where the state will be registered. For NgModule usage, this will be your `app-module.ts` for your root state, or your Feature Module for feature state. For Standalone API usage, this will be your `app.config.ts` file for your root state, or the Routes definition file for your feature state. The host directory will create/use the new state directory.",
"x-prompt": "What is the path to the module or Routes definition where this NgRx state should be registered?",
"x-priority": "important"
},

View File

@ -15,11 +15,15 @@
"command": "nx g @nx/angular:pipe mylib/src/lib/foo.pipe.ts"
},
{
"description": "Generate a pipe without providing the file extension. It results in the pipe `FooPipe` at `mylib/src/lib/foo.pipe.ts`",
"description": "Generate a pipe without providing the file extension. It results in the pipe `FooPipe` at `mylib/src/lib/foo-pipe.ts`",
"command": "nx g @nx/angular:pipe mylib/src/lib/foo"
},
{
"description": "Generate a pipe with the exported symbol different from the file name. It results in the pipe `CustomPipe` at `mylib/src/lib/foo.pipe.ts`",
"description": "Generate a pipe with a different type separator. It results in the pipe `FooPipe` at `mylib/src/lib/foo.pipe.ts`",
"command": "nx g @nx/angular:pipe mylib/src/lib/foo --typeSeparator=."
},
{
"description": "Generate a pipe with the exported symbol different from the file name. It results in the pipe `CustomPipe` at `mylib/src/lib/foo-pipe.ts`",
"command": "nx g @nx/angular:pipe mylib/src/lib/foo --name=custom"
}
],
@ -59,6 +63,11 @@
"default": false,
"description": "The declaring NgModule exports this pipe."
},
"typeSeparator": {
"type": "string",
"enum": ["-", "."],
"description": "The separator character to use before the type within the generated file's name. For example, if you set the option to `.`, the file will be named `example.pipe.ts`. It defaults to '-' for Angular v20+. For versions below v20, it defaults to '.'."
},
"skipFormat": {
"type": "boolean",
"default": false,

View File

@ -168,7 +168,7 @@
"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_.",
"description": "Creates a server application using the Server Routing and App Engine APIs for application using the `application` builder (Developer Preview). _Note: this is only supported in Angular versions 19.x.x_. From Angular 20 onwards, SSR will always enable server routing when using the `application` builder.",
"type": "boolean"
},
"typescriptConfiguration": {

View File

@ -13,11 +13,15 @@
"command": "nx g @nx/angular:scam-directive mylib/src/lib/foo.directive.ts"
},
{
"description": "Generate a directive without providing the file extension. It results in the directive `FooDirective` at `mylib/src/lib/foo.directive.ts`",
"description": "Generate a directive without providing the file extension. It results in the directive `Foo` at `mylib/src/lib/foo.ts`",
"command": "nx g @nx/angular:scam-directive mylib/src/lib/foo"
},
{
"description": "Generate a directive with the exported symbol different from the file name. It results in the directive `CustomDirective` at `mylib/src/lib/foo.directive.ts`",
"description": "Generate a directive with a given type/suffix. It results in the directive `FooDirective` at `mylib/src/lib/foo.directive.ts`",
"command": "nx g @nx/angular:scam-directive mylib/src/lib/foo --type=directive"
},
{
"description": "Generate a directive with the exported symbol different from the file name. It results in the directive `Custom` at `mylib/src/lib/foo.ts`",
"command": "nx g @nx/angular:scam-directive mylib/src/lib/foo --name=custom"
}
],
@ -65,6 +69,10 @@
"default": true,
"x-priority": "important"
},
"type": {
"type": "string",
"description": "Append a custom type to the directive's filename. It defaults to 'directive' for Angular versions below v20. For Angular v20 and above, no type is appended unless specified."
},
"skipFormat": {
"description": "Skip formatting files.",
"type": "boolean",

View File

@ -51,6 +51,11 @@
"default": true,
"x-priority": "important"
},
"typeSeparator": {
"type": "string",
"enum": ["-", "."],
"description": "The separator character to use before the type within the generated file's name. For example, if you set the option to `.`, the file will be named `example.pipe.ts`. It defaults to '-' for Angular v20+. For versions below v20, it defaults to '.'."
},
"skipFormat": {
"description": "Skip formatting files.",
"type": "boolean",

View File

@ -94,8 +94,7 @@
},
"type": {
"type": "string",
"description": "Adds a developer-defined type to the filename, in the format `name.type.ts`.",
"default": "component"
"description": "Append a custom type to the component's filename. It defaults to 'component' for Angular versions below v20. For Angular v20 and above, no type is appended unless specified."
},
"prefix": {
"type": "string",

View File

@ -53,7 +53,7 @@
"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_.",
"description": "Creates a server application using the Server Routing and App Engine APIs for application using the `application` builder (Developer Preview). _Note: this is only supported in Angular versions 19.x.x_. From Angular 20 onwards, SSR will always enable server routing when using the `application` builder.",
"type": "boolean"
},
"skipFormat": {

View File

@ -0,0 +1,68 @@
{
"name": "21.2.0-package-updates",
"version": "21.2.0-beta.0",
"x-prompt": "Do you want to update the Angular version to v20?",
"requires": { "@angular/core": ">=19.2.0 <20.0.0-rc.2" },
"packages": {
"@angular-devkit/build-angular": {
"version": "20.0.0-rc.3",
"alwaysAddToPackageJson": false
},
"@angular-devkit/core": {
"version": "20.0.0-rc.3",
"alwaysAddToPackageJson": false
},
"@angular-devkit/schematics": {
"version": "20.0.0-rc.3",
"alwaysAddToPackageJson": false
},
"@angular/build": {
"version": "20.0.0-rc.3",
"alwaysAddToPackageJson": false
},
"@angular/pwa": {
"version": "20.0.0-rc.3",
"alwaysAddToPackageJson": false
},
"@angular/ssr": {
"version": "20.0.0-rc.3",
"alwaysAddToPackageJson": false
},
"@schematics/angular": {
"version": "20.0.0-rc.3",
"alwaysAddToPackageJson": false
},
"@angular-devkit/architect": {
"version": "0.2000.0-rc.3",
"alwaysAddToPackageJson": false
},
"@angular-devkit/build-webpack": {
"version": "0.2000.0-rc.3",
"alwaysAddToPackageJson": false
},
"@angular/core": {
"version": "20.0.0-rc.2",
"alwaysAddToPackageJson": true
},
"@angular/material": {
"version": "20.0.0-rc.2",
"alwaysAddToPackageJson": false
},
"@angular/cdk": {
"version": "20.0.0-rc.2",
"alwaysAddToPackageJson": false
},
"@angular/google-maps": {
"version": "20.0.0-rc.2",
"alwaysAddToPackageJson": false
},
"ng-packagr": { "version": "20.0.0-rc.1", "alwaysAddToPackageJson": false }
},
"aliases": [],
"description": "",
"hidden": false,
"implementation": "",
"path": "/packages/angular",
"schema": null,
"type": "migration"
}

View File

@ -0,0 +1,14 @@
{
"name": "migrate-provide-server-rendering-import",
"version": "21.2.0-beta.0",
"requires": { "@angular/core": ">=20.0.0-rc.2" },
"description": "Migrate imports of `provideServerRendering` from `@angular/platform-server` to `@angular/ssr`.",
"factory": "./src/migrations/update-21-2-0/migrate-provide-server-rendering-import",
"implementation": "/packages/angular/src/migrations/update-21-2-0/migrate-provide-server-rendering-import.ts",
"aliases": [],
"hidden": false,
"path": "/packages/angular",
"schema": null,
"type": "migration",
"examplesFile": "#### Migrate Imports of `provideServerRendering` from `@angular/platform-server` to `@angular/ssr`\n\nMigrate the imports of `provideServerRendering` from `@angular/platform-server` to `@angular/ssr`. This migration will also add the `@angular/ssr` package to your dependencies if needed.\n\n#### Examples\n\nChange the import of `provideServerRendering` from `@angular/platform-server` to `@angular/ssr`:\n\n{% tabs %}\n{% tab label=\"Before\" %}\n\n```ts {% fileName=\"app/app.config.server.ts\" highlightLines=[2] %}\nimport { ApplicationConfig } from '@angular/core';\nimport { provideServerRendering } from '@angular/platform-server';\n\nconst serverConfig: ApplicationConfig = {\n providers: [provideServerRendering()],\n};\n```\n\n{% /tab %}\n\n{% tab label=\"After\" %}\n\n```ts {% fileName=\"app/app.config.server.ts\" highlightLines=[2] %}\nimport { ApplicationConfig } from '@angular/core';\nimport { provideServerRendering } from '@angular/ssr';\n\nconst serverConfig: ApplicationConfig = {\n providers: [provideServerRendering()],\n};\n```\n\n{% /tab %}\n{% /tabs %}\n\nIf you already have imports from `@angular/ssr`, the migration will add `provideServerRendering` to the existing import:\n\n{% tabs %}\n{% tab label=\"Before\" %}\n\n```ts {% fileName=\"app/app.config.server.ts\" highlightLines=[2,3] %}\nimport { ApplicationConfig } from '@angular/core';\nimport { provideServerRendering } from '@angular/platform-server';\nimport { provideServerRouting } from '@angular/ssr';\nimport { serverRoutes } from './app.routes.server';\n\nconst serverConfig: ApplicationConfig = {\n providers: [provideServerRendering(), provideServerRouting(serverRoutes)],\n};\n```\n\n{% /tab %}\n\n{% tab label=\"After\" %}\n\n```ts {% fileName=\"app/app.config.server.ts\" highlightLines=[2] %}\nimport { ApplicationConfig } from '@angular/core';\nimport { provideServerRouting, provideServerRendering } from '@angular/ssr';\nimport { serverRoutes } from './app.routes.server';\n\nconst serverConfig: ApplicationConfig = {\n providers: [provideServerRendering(), provideServerRouting(serverRoutes)],\n};\n```\n\n{% /tab %}\n{% /tabs %}\n"
}

View File

@ -0,0 +1,14 @@
{
"name": "replace-provide-server-routing",
"version": "21.2.0-beta.0",
"requires": { "@angular/core": ">=20.0.0-rc.2" },
"description": "Replace `provideServerRouting` with `provideServerRendering` using `withRoutes`.",
"factory": "./src/migrations/update-21-2-0/replace-provide-server-routing",
"implementation": "/packages/angular/src/migrations/update-21-2-0/replace-provide-server-routing.ts",
"aliases": [],
"hidden": false,
"path": "/packages/angular",
"schema": null,
"type": "migration",
"examplesFile": "#### Replace `provideServerRouting` with `provideServerRendering`\n\nReplace `provideServerRouting` calls with `provideServerRendering` using `withRoutes`.\n\n#### Examples\n\nRemove `provideServerRouting` from your providers array and update the `provideServerRendering` call to use `withRoutes`:\n\n{% tabs %}\n{% tab label=\"Before\" %}\n\n```ts {% fileName=\"app/app.config.server.ts\" highlightLines=[2,6] %}\nimport { ApplicationConfig } from '@angular/core';\nimport { provideServerRendering, provideServerRouting } from '@angular/ssr';\nimport { serverRoutes } from './app.routes.server';\n\nconst serverConfig: ApplicationConfig = {\n providers: [provideServerRendering(), provideServerRouting(serverRoutes)],\n};\n```\n\n{% /tab %}\n\n{% tab label=\"After\" %}\n\n```ts {% fileName=\"app/app.config.server.ts\" highlightLines=[2,6] %}\nimport { ApplicationConfig } from '@angular/core';\nimport { provideServerRendering, withRoutes } from '@angular/ssr';\nimport { serverRoutes } from './app.routes.server';\n\nconst serverConfig: ApplicationConfig = {\n providers: [provideServerRendering(withRoutes(serverRoutes))],\n};\n```\n\n{% /tab %}\n{% /tabs %}\n\nIf you have `provideServerRouting` with additional arguments, the migration will preserve them:\n\n{% tabs %}\n{% tab label=\"Before\" %}\n\n```ts {% fileName=\"app/app.config.server.ts\" highlightLines=[4,11,12] %}\nimport { ApplicationConfig } from '@angular/core';\nimport {\n provideServerRendering,\n provideServerRouting,\n withAppShell,\n} from '@angular/ssr';\nimport { serverRoutes } from './app.routes.server';\n\nconst serverConfig: ApplicationConfig = {\n providers: [\n provideServerRendering(),\n provideServerRouting(serverRoutes, withAppShell(AppShellComponent)),\n ],\n};\n```\n\n{% /tab %}\n\n{% tab label=\"After\" %}\n\n```ts {% fileName=\"app/app.config.server.ts\" highlightLines=[2,\"7-10\"] %}\nimport { ApplicationConfig } from '@angular/core';\nimport { provideServerRendering, withAppShell, withRoutes } from '@angular/ssr';\nimport { serverRoutes } from './app.routes.server';\n\nconst serverConfig: ApplicationConfig = {\n providers: [\n provideServerRendering(\n withRoutes(serverRoutes),\n withAppShell(AppShellComponent)\n ),\n ],\n};\n```\n\n{% /tab %}\n{% /tabs %}\n"
}

View File

@ -0,0 +1,14 @@
{
"name": "set-generator-defaults-for-previous-style-guide",
"version": "21.2.0-beta.0",
"requires": { "@angular/core": ">=20.0.0-rc.2" },
"description": "Update the generator defaults to maintain the previous style guide behavior.",
"factory": "./src/migrations/update-21-2-0/set-generator-defaults-for-previous-style-guide",
"implementation": "/packages/angular/src/migrations/update-21-2-0/set-generator-defaults-for-previous-style-guide.ts",
"aliases": [],
"hidden": false,
"path": "/packages/angular",
"schema": null,
"type": "migration",
"examplesFile": "#### Set Generator Defaults for Previous Style Guide\n\nUpdates the generator defaults in the `nx.json` file to maintain the previous Angular Style Guide behavior. This ensures that newly generated code in existing workspaces follows the same conventions as the existing codebase.\n\n#### Examples\n\nThe migration will add default configurations for the relevant Angular generators in the workspace's `nx.json` file:\n\n{% tabs %}\n{% tab label=\"Before\" %}\n\n```json {% fileName=\"nx.json\" %}\n{\n \"generators\": {}\n}\n```\n\n{% /tab %}\n\n{% tab label=\"After\" %}\n\n```json {% fileName=\"nx.json\" %}\n{\n \"generators\": {\n \"@nx/angular:component\": {\n \"type\": \"component\"\n },\n \"@nx/angular:directive\": {\n \"type\": \"directive\"\n },\n \"@nx/angular:service\": {\n \"type\": \"service\"\n },\n \"@nx/angular:scam\": {\n \"type\": \"component\"\n },\n \"@nx/angular:scam-directive\": {\n \"type\": \"directive\"\n },\n \"@nx/angular:guard\": {\n \"typeSeparator\": \".\"\n },\n \"@nx/angular:interceptor\": {\n \"typeSeparator\": \".\"\n },\n \"@nx/angular:module\": {\n \"typeSeparator\": \".\"\n },\n \"@nx/angular:pipe\": {\n \"typeSeparator\": \".\"\n },\n \"@nx/angular:resolver\": {\n \"typeSeparator\": \".\"\n },\n \"@schematics/angular:component\": {\n \"type\": \"component\"\n },\n \"@schematics/angular:directive\": {\n \"type\": \"directive\"\n },\n \"@schematics/angular:service\": {\n \"type\": \"service\"\n },\n \"@schematics/angular:guard\": {\n \"typeSeparator\": \".\"\n },\n \"@schematics/angular:interceptor\": {\n \"typeSeparator\": \".\"\n },\n \"@schematics/angular:module\": {\n \"typeSeparator\": \".\"\n },\n \"@schematics/angular:pipe\": {\n \"typeSeparator\": \".\"\n },\n \"@schematics/angular:resolver\": {\n \"typeSeparator\": \".\"\n }\n }\n}\n```\n\n{% /tab %}\n{% /tabs %}\n\nIf some of the generator defaults are already set, the migration will not override them:\n\n{% tabs %}\n{% tab label=\"Before\" %}\n\n```json {% fileName=\"nx.json\" highlightLines=[\"3-14\"] %}\n{\n \"generators\": {\n \"@nx/angular:component\": {\n \"type\": \"cmp\"\n },\n \"@schematics/angular:component\": {\n \"type\": \"cmp\"\n },\n \"@nx/angular:interceptor\": {\n \"typeSeparator\": \"-\"\n },\n \"@schematics/angular:interceptor\": {\n \"typeSeparator\": \"-\"\n }\n }\n}\n```\n\n{% /tab %}\n\n{% tab label=\"After\" %}\n\n```json {% fileName=\"nx.json\" highlightLines=[\"3-14\"] %}\n{\n \"generators\": {\n \"@nx/angular:component\": {\n \"type\": \"cmp\"\n },\n \"@schematics/angular:component\": {\n \"type\": \"cmp\"\n },\n \"@nx/angular:interceptor\": {\n \"typeSeparator\": \"-\"\n },\n \"@schematics/angular:interceptor\": {\n \"typeSeparator\": \"-\"\n },\n \"@nx/angular:directive\": {\n \"type\": \"directive\"\n },\n \"@nx/angular:service\": {\n \"type\": \"service\"\n },\n \"@nx/angular:scam\": {\n \"type\": \"component\"\n },\n \"@nx/angular:scam-directive\": {\n \"type\": \"directive\"\n },\n \"@nx/angular:guard\": {\n \"typeSeparator\": \".\"\n },\n \"@nx/angular:module\": {\n \"typeSeparator\": \".\"\n },\n \"@nx/angular:pipe\": {\n \"typeSeparator\": \".\"\n },\n \"@nx/angular:resolver\": {\n \"typeSeparator\": \".\"\n },\n \"@schematics/angular:directive\": {\n \"type\": \"directive\"\n },\n \"@schematics/angular:service\": {\n \"type\": \"service\"\n },\n \"@schematics/angular:guard\": {\n \"typeSeparator\": \".\"\n },\n \"@schematics/angular:module\": {\n \"typeSeparator\": \".\"\n },\n \"@schematics/angular:pipe\": {\n \"typeSeparator\": \".\"\n },\n \"@schematics/angular:resolver\": {\n \"typeSeparator\": \".\"\n }\n }\n}\n```\n\n{% /tab %}\n{% /tabs %}\n"
}

View File

@ -0,0 +1,15 @@
{
"name": "update-angular-cli-version-20-0-0-rc-3",
"cli": "nx",
"version": "21.2.0-beta.0",
"requires": { "@angular/core": ">=20.0.0-rc.2" },
"description": "Update the @angular/cli package version to 20.0.0-rc.3.",
"factory": "./src/migrations/update-21-2-0/update-angular-cli",
"implementation": "/packages/angular/src/migrations/update-21-2-0/update-angular-cli.ts",
"aliases": [],
"hidden": false,
"path": "/packages/angular",
"schema": null,
"type": "migration",
"examplesFile": "#### Sample Code Changes\n\nUpdate the `@angular/cli` package version in the `package.json` file at the workspace root to **~20.0.0**.\n\n{% tabs %}\n{% tab label=\"Before\" %}\n\n```json {% fileName=\"package.json\" %}\n{\n \"devDependencies\": {\n \"@angular/cli\": \"~19.2.0\"\n }\n}\n```\n\n{% /tab %}\n{% tab label=\"After\" %}\n\n```json {% highlightLines=[3] fileName=\"package.json\" %}\n{\n \"devDependencies\": {\n \"@angular/cli\": \"~20.0.0\"\n }\n}\n```\n\n{% /tab %}\n\n{% /tabs %}\n"
}

View File

@ -0,0 +1,14 @@
{
"name": "update-module-resolution",
"version": "21.2.0-beta.0",
"requires": { "@angular/core": ">=20.0.0-rc.2" },
"description": "Update 'moduleResolution' to 'bundler' in TypeScript configurations. You can read more about this here: https://www.typescriptlang.org/tsconfig/#moduleResolution.",
"factory": "./src/migrations/update-21-2-0/update-module-resolution",
"implementation": "/packages/angular/src/migrations/update-21-2-0/update-module-resolution.ts",
"aliases": [],
"hidden": false,
"path": "/packages/angular",
"schema": null,
"type": "migration",
"examplesFile": "#### Update `moduleResolution` to `bundler` in TypeScript configurations\n\nUpdates the TypeScript `moduleResolution` option to `'bundler'` for improved compatibility with modern package resolution algorithms used by bundlers like Webpack, Vite, and esbuild.\n\n#### Examples\n\nThe migration will update TypeScript configuration files in your workspace to use the `'bundler'` module resolution strategy:\n\n{% tabs %}\n{% tab label=\"Before\" %}\n\n```json {% fileName=\"apps/app1/tsconfig.app.json\" highlightLines=[\"4\"] %}\n{\n \"compilerOptions\": {\n \"module\": \"es2020\",\n \"moduleResolution\": \"node\"\n }\n}\n```\n\n{% /tab %}\n\n{% tab label=\"After\" %}\n\n```json {% fileName=\"apps/app1/tsconfig.app.json\" highlightLines=[\"4\"] %}\n{\n \"compilerOptions\": {\n \"module\": \"es2020\",\n \"moduleResolution\": \"bundler\"\n }\n}\n```\n\n{% /tab %}\n{% /tabs %}\n\nIf the `moduleResolution` is already set to `'bundler'` or the `module` is set to `'preserve'`, the migration will not modify the configuration:\n\n{% tabs %}\n{% tab label=\"Before\" %}\n\n```json {% fileName=\"apps/app1/tsconfig.app.json\" highlightLines=[3,4] %}\n{\n \"compilerOptions\": {\n \"module\": \"preserve\",\n \"moduleResolution\": \"node\"\n }\n}\n```\n\n{% /tab %}\n\n{% tab label=\"After\" %}\n\n```json {% fileName=\"apps/app1/tsconfig.app.json\" highlightLines=[3,4] %}\n{\n \"compilerOptions\": {\n \"module\": \"preserve\",\n \"moduleResolution\": \"node\"\n }\n}\n```\n\n{% /tab %}\n{% /tabs %}\n"
}

View File

@ -0,0 +1,30 @@
{
"name": "21.2.0-@typescript-eslint-package-updates",
"version": "21.2.0-beta.0",
"requires": { "@typescript-eslint/eslint-plugin": ">8.0.0 <8.29.0" },
"packages": {
"typescript-eslint": { "version": "^8.29.0" },
"@typescript-eslint/eslint-plugin": { "version": "^8.29.0" },
"@typescript-eslint/parser": { "version": "^8.29.0" },
"@typescript-eslint/utils": { "version": "^8.29.0" },
"@typescript-eslint/rule-tester": {
"version": "^8.29.0",
"alwaysAddToPackageJson": false
},
"@typescript-eslint/scope-manager": {
"version": "^8.29.0",
"alwaysAddToPackageJson": false
},
"@typescript-eslint/typescript-estree": {
"version": "^8.29.0",
"alwaysAddToPackageJson": false
}
},
"aliases": [],
"description": "",
"hidden": false,
"implementation": "",
"path": "/packages/eslint",
"schema": null,
"type": "migration"
}

View File

@ -0,0 +1,30 @@
{
"name": "21.2.0-typescript-eslint-package-updates",
"version": "21.2.0-beta.0",
"requires": { "typescript-eslint": ">8.0.0 <8.29.0" },
"packages": {
"typescript-eslint": { "version": "^8.29.0" },
"@typescript-eslint/eslint-plugin": { "version": "^8.29.0" },
"@typescript-eslint/parser": { "version": "^8.29.0" },
"@typescript-eslint/utils": { "version": "^8.29.0" },
"@typescript-eslint/rule-tester": {
"version": "^8.29.0",
"alwaysAddToPackageJson": false
},
"@typescript-eslint/scope-manager": {
"version": "^8.29.0",
"alwaysAddToPackageJson": false
},
"@typescript-eslint/typescript-estree": {
"version": "^8.29.0",
"alwaysAddToPackageJson": false
}
},
"aliases": [],
"description": "",
"hidden": false,
"implementation": "",
"path": "/packages/eslint",
"schema": null,
"type": "migration"
}

View File

@ -0,0 +1,16 @@
{
"name": "21.2.0-package-updates",
"version": "21.2.0-beta.0",
"x-prompt": "Do you want to update to TypeScript v5.8?",
"requires": { "typescript": ">=5.7.0 <5.8.0" },
"packages": {
"typescript": { "version": "~5.8.2", "alwaysAddToPackageJson": false }
},
"aliases": [],
"description": "",
"hidden": false,
"implementation": "",
"path": "/packages/js",
"schema": null,
"type": "migration"
}

View File

@ -90,10 +90,6 @@
"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"

View File

@ -107,10 +107,6 @@
"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"

View File

@ -0,0 +1,16 @@
{
"name": "21.2.0-package-updates",
"version": "21.2.0-beta.0",
"x-prompt": "Do you want to update to TypeScript v5.8?",
"requires": { "typescript": ">=5.7.0 <5.8.0" },
"packages": {
"typescript": { "version": "~5.8.2", "alwaysAddToPackageJson": false }
},
"aliases": [],
"description": "",
"hidden": false,
"implementation": "",
"path": "/packages/workspace",
"schema": null,
"type": "migration"
}

View File

@ -13,7 +13,9 @@ describe('angular.json v1 config', () => {
beforeAll(() => {
newProject({ packages: ['@nx/angular'] });
runCLI(`generate @nx/angular:app ${app1} --no-interactive`);
runCLI(
`generate @nx/angular:app ${app1} --bundler=webpack --no-interactive`
);
// reset workspace to use v1 config
updateFile(`angular.json`, angularV1Json(app1));
removeFile(`${app1}/project.json`);

View File

@ -88,7 +88,7 @@ describe('Angular Cypress Component Tests', () => {
}
);
updateFile(
`${buildableLibName}/src/lib/input-standalone/input-standalone.component.cy.ts`,
`${buildableLibName}/src/lib/input-standalone/input-standalone.cy.ts`,
(content) => {
// text-green-500 should now apply
return content.replace('rgb(0, 0, 0)', 'rgb(34, 197, 94)');
@ -149,7 +149,7 @@ function createLib(projectName: string, appName: string, libName: string) {
`generate @nx/angular:component ${libName}/src/lib/btn-standalone/btn-standalone --inlineTemplate --inlineStyle --export --standalone --no-interactive`
);
updateFile(
`${libName}/src/lib/btn/btn.component.ts`,
`${libName}/src/lib/btn/btn.ts`,
`
import { Component, Input } from '@angular/core';
@ -158,13 +158,13 @@ import { Component, Input } from '@angular/core';
template: '<button class="text-green-500">{{text}}</button>',
styles: []
})
export class BtnComponent {
export class Btn {
@Input() text = 'something';
}
`
);
updateFile(
`${libName}/src/lib/btn-standalone/btn-standalone.component.ts`,
`${libName}/src/lib/btn-standalone/btn-standalone.ts`,
`
import { Component, Input } from '@angular/core';
import { CommonModule } from '@angular/common';
@ -175,7 +175,7 @@ import { CommonModule } from '@angular/common';
template: '<button class="text-green-500">standlone-{{text}}</button>',
styles: [],
})
export class BtnStandaloneComponent {
export class BtnStandalone {
@Input() text = 'something';
}
`
@ -187,7 +187,7 @@ function createBuildableLib(projectName: string, libName: string) {
runCLI(`generate @nx/angular:lib ${libName} --buildable --no-interactive`);
// create cmp for lib
runCLI(
`generate @nx/angular:component ${libName}/src/lib/input/input --inlineTemplate --inlineStyle --export --no-interactive`
`generate @nx/angular:component ${libName}/src/lib/input/input.component --inlineTemplate --inlineStyle --export --no-interactive`
);
// create standlone cmp for lib
runCLI(
@ -210,7 +210,7 @@ import {Component, Input} from '@angular/core';
`
);
updateFile(
`${libName}/src/lib/input-standalone/input-standalone.component.ts`,
`${libName}/src/lib/input-standalone/input-standalone.ts`,
`
import {Component, Input} from '@angular/core';
import {CommonModule} from '@angular/common';
@ -221,7 +221,7 @@ import {CommonModule} from '@angular/common';
template: \`<label class="text-green-500">Email: <input class="border-blue-500" type="email" [readOnly]="readOnly"></label>\`,
styles : []
})
export class InputStandaloneComponent{
export class InputStandalone{
@Input() readOnly = false;
}
`
@ -230,7 +230,7 @@ import {CommonModule} from '@angular/common';
function useLibInApp(projectName: string, appName: string, libName: string) {
createFile(
`${appName}/src/app/app.component.html`,
`${appName}/src/app/app.html`,
`
<${projectName}-btn></${projectName}-btn>
<${projectName}-btn-standalone></${projectName}-btn-standalone>
@ -239,7 +239,7 @@ function useLibInApp(projectName: string, appName: string, libName: string) {
);
const btnModuleName = names(libName).className;
updateFile(
`${appName}/src/app/app.component.scss`,
`${appName}/src/app/app.scss`,
`
@use 'styleguide' as *;
@ -248,20 +248,20 @@ h1 {
}`
);
updateFile(
`${appName}/src/app/app.module.ts`,
`${appName}/src/app/app-module.ts`,
`
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import {${btnModuleName}Module} from "@${projectName}/${libName}";
import { AppComponent } from './app.component';
import { NxWelcomeComponent } from './nx-welcome.component';
import { App } from './app';
import { NxWelcome } from './nx-welcome';
@NgModule({
declarations: [AppComponent, NxWelcomeComponent],
declarations: [App, NxWelcome],
imports: [BrowserModule, ${btnModuleName}Module],
providers: [],
bootstrap: [AppComponent],
bootstrap: [App],
})
export class AppModule {}
`
@ -328,25 +328,25 @@ describe(InputComponent.name, () => {
);
createFile(
`${libName}/src/lib/input-standalone/input-standalone.component.cy.ts`,
`${libName}/src/lib/input-standalone/input-standalone.cy.ts`,
`
import { MountConfig } from 'cypress/angular';
import { InputStandaloneComponent } from './input-standalone.component';
import { InputStandalone } from './input-standalone';
describe(InputStandaloneComponent.name, () => {
const config: MountConfig<InputStandaloneComponent> = {
describe(InputStandalone.name, () => {
const config: MountConfig<InputStandalone> = {
declarations: [],
imports: [],
providers: [],
};
it('renders', () => {
cy.mount(InputStandaloneComponent, config);
cy.mount(InputStandalone, config);
// make sure tailwind isn't getting applied
cy.get('label').should('have.css', 'color', 'rgb(0, 0, 0)');
});
it('should be readonly', () => {
cy.mount(InputStandaloneComponent, {
cy.mount(InputStandalone, {
...config,
componentProperties: {
readOnly: true,
@ -367,19 +367,19 @@ function useBuildableLibInLib(
const buildLibNames = names(buildableLibName);
// use the buildable lib in lib so now buildableLib has an indirect dep on app
updateFile(
`${libName}/src/lib/btn-standalone/btn-standalone.component.ts`,
`${libName}/src/lib/btn-standalone/btn-standalone.ts`,
`
import { Component, Input } from '@angular/core';
import { CommonModule } from '@angular/common';
import { InputStandaloneComponent } from '@${projectName}/${buildLibNames.fileName}';
import { InputStandalone } from '@${projectName}/${buildLibNames.fileName}';
@Component({
selector: '${projectName}-btn-standalone',
standalone: true,
imports: [CommonModule, InputStandaloneComponent],
imports: [CommonModule, InputStandalone],
template: '<button class="text-green-500">standlone-{{text}}</button>${projectName} <${projectName}-input-standalone></${projectName}-input-standalone>',
styles: [],
})
export class BtnStandaloneComponent {
export class BtnStandalone {
@Input() text = 'something';
}
`
@ -394,7 +394,7 @@ function updateBuilableLibTestsToAssertAppStyles(
removeFile(`${buildableLibName}/src/lib/input/input.component.cy.ts`);
updateFile(
`${buildableLibName}/src/lib/input-standalone/input-standalone.component.cy.ts`,
`${buildableLibName}/src/lib/input-standalone/input-standalone.cy.ts`,
(content) => {
// app styles should now apply
return content.replace('rgb(34, 197, 94)', 'rgb(255, 192, 203)');

View File

@ -46,10 +46,8 @@ describe('Move Angular Project', () => {
expect(moveOutput).toContain(`CREATE ${newPath}/src/main.ts`);
expect(moveOutput).toContain(`CREATE ${newPath}/src/styles.css`);
expect(moveOutput).toContain(`CREATE ${newPath}/src/test-setup.ts`);
expect(moveOutput).toContain(
`CREATE ${newPath}/src/app/app.component.html`
);
expect(moveOutput).toContain(`CREATE ${newPath}/src/app/app.component.ts`);
expect(moveOutput).toContain(`CREATE ${newPath}/src/app/app.html`);
expect(moveOutput).toContain(`CREATE ${newPath}/src/app/app.ts`);
expect(moveOutput).toContain(`CREATE ${newPath}/src/app/app.config.ts`);
});
@ -105,7 +103,7 @@ describe('Move Angular Project', () => {
runCLI(`generate @nx/angular:lib ${lib2} --no-standalone --no-interactive`);
updateFile(
`${lib2}/src/lib/${lib2}.module.ts`,
`${lib2}/src/lib/${lib2}-module.ts`,
`import { ${classify(lib1)}Module } from '@${proj}/${lib1}';
export class ExtendedModule extends ${classify(lib1)}Module { }`
@ -122,7 +120,7 @@ describe('Move Angular Project', () => {
expect(moveOutput).toContain(`CREATE ${testSetupPath}`);
checkFilesExist(testSetupPath);
const modulePath = `${newPath}/src/lib/shared-${lib1}.module.ts`;
const modulePath = `${newPath}/src/lib/shared-${lib1}-module.ts`;
expect(moveOutput).toContain(`CREATE ${modulePath}`);
checkFilesExist(modulePath);
const moduleFile = readFile(modulePath);
@ -132,12 +130,12 @@ describe('Move Angular Project', () => {
expect(moveOutput).toContain(`CREATE ${indexPath}`);
checkFilesExist(indexPath);
const index = readFile(indexPath);
expect(index).toContain(`export * from './lib/shared-${lib1}.module'`);
expect(index).toContain(`export * from './lib/shared-${lib1}-module'`);
/**
* Check that the import in lib2 has been updated
*/
const lib2FilePath = `${lib2}/src/lib/${lib2}.module.ts`;
const lib2FilePath = `${lib2}/src/lib/${lib2}-module.ts`;
const lib2File = readFile(lib2FilePath);
expect(lib2File).toContain(
`import { ${newModule} } from '@${proj}/shared-${lib1}';`

View File

@ -2,14 +2,11 @@ import { names } from '@nx/devkit';
import {
checkFilesExist,
cleanupProject,
killPorts,
killProcessAndPorts,
newProject,
readFile,
readJson,
runCLI,
runCommandUntil,
runE2ETests,
uniq,
updateFile,
updateJson,
@ -63,8 +60,8 @@ describe('Angular Module Federation', () => {
// check files are generated without the layout directory ("apps/")
checkFilesExist(
`${hostApp}/src/app/app.module.ts`,
`${remoteApp1}/src/app/app.module.ts`
`${hostApp}/src/app/app-module.ts`,
`${remoteApp1}/src/app/app-module.ts`
);
// check default generated host is built successfully
@ -96,26 +93,26 @@ describe('Angular Module Federation', () => {
// update host & remote files to use shared library
updateFile(
`${hostApp}/src/app/app.module.ts`,
`${hostApp}/src/app/app-module.ts`,
`import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { ${
names(wildcardLib).className
}Module } from '@${proj}/${wildcardLib}/${
names(secondaryEntry).fileName
}.module';
}-module';
import { ${
names(sharedLib).className
}Module } from '@${proj}/${sharedLib}';
import { ${
names(secondaryEntry).className
}Module } from '@${proj}/${sharedLib}/${secondaryEntry}';
import { AppComponent } from './app.component';
import { NxWelcomeComponent } from './nx-welcome.component';
import { App } from './app';
import { NxWelcome } from './nx-welcome';
import { RouterModule } from '@angular/router';
@NgModule({
declarations: [AppComponent, NxWelcomeComponent],
declarations: [App, NxWelcome],
imports: [
BrowserModule,
${names(sharedLib).className}Module,
@ -134,13 +131,13 @@ describe('Angular Module Federation', () => {
),
],
providers: [],
bootstrap: [AppComponent],
bootstrap: [App],
})
export class AppModule {}
`
);
updateFile(
`${remoteApp1}/src/app/remote-entry/entry.module.ts`,
`${remoteApp1}/src/app/remote-entry/entry-module.ts`,
`import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
@ -148,18 +145,18 @@ describe('Angular Module Federation', () => {
import { ${
names(secondaryEntry).className
}Module } from '@${proj}/${sharedLib}/${secondaryEntry}';
import { RemoteEntryComponent } from './entry.component';
import { NxWelcomeComponent } from './nx-welcome.component';
import { RemoteEntry } from './entry';
import { NxWelcome } from './nx-welcome';
@NgModule({
declarations: [RemoteEntryComponent, NxWelcomeComponent],
declarations: [RemoteEntry, NxWelcome],
imports: [
CommonModule,
${names(sharedLib).className}Module,
RouterModule.forChild([
{
path: '',
component: RemoteEntryComponent,
component: RemoteEntry,
},
]),
],

View File

@ -49,8 +49,8 @@ describe('Angular Module Federation', () => {
// check files are generated without the layout directory ("apps/")
checkFilesExist(
`${hostApp}/src/app/app.module.ts`,
`${remoteApp1}/src/app/app.module.ts`
`${hostApp}/src/app/app-module.ts`,
`${remoteApp1}/src/app/app-module.ts`
);
// check default generated host is built successfully
@ -80,26 +80,26 @@ describe('Angular Module Federation', () => {
// update host & remote files to use shared library
updateFile(
`${hostApp}/src/app/app.module.ts`,
`${hostApp}/src/app/app-module.ts`,
`import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { ${
names(wildcardLib).className
}Module } from '@${proj}/${wildcardLib}/${
names(secondaryEntry).fileName
}.module';
names(wildcardLib).fileName
}-module';
import { ${
names(sharedLib).className
}Module } from '@${proj}/${sharedLib}';
import { ${
names(secondaryEntry).className
}Module } from '@${proj}/${sharedLib}/${secondaryEntry}';
import { AppComponent } from './app.component';
import { NxWelcomeComponent } from './nx-welcome.component';
import { App } from './app';
import { NxWelcome } from './nx-welcome';
import { RouterModule } from '@angular/router';
@NgModule({
declarations: [AppComponent, NxWelcomeComponent],
declarations: [App, NxWelcome],
imports: [
BrowserModule,
${names(sharedLib).className}Module,
@ -118,13 +118,13 @@ describe('Angular Module Federation', () => {
),
],
providers: [],
bootstrap: [AppComponent],
bootstrap: [App],
})
export class AppModule {}
`
);
updateFile(
`${remoteApp1}/src/app/remote-entry/entry.module.ts`,
`${remoteApp1}/src/app/remote-entry/entry-module.ts`,
`import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
@ -132,18 +132,18 @@ describe('Angular Module Federation', () => {
import { ${
names(secondaryEntry).className
}Module } from '@${proj}/${sharedLib}/${secondaryEntry}';
import { RemoteEntryComponent } from './entry.component';
import { NxWelcomeComponent } from './nx-welcome.component';
import { RemoteEntry } from './entry';
import { NxWelcome } from './nx-welcome';
@NgModule({
declarations: [RemoteEntryComponent, NxWelcomeComponent],
declarations: [RemoteEntry, NxWelcome],
imports: [
CommonModule,
${names(sharedLib).className}Module,
RouterModule.forChild([
{
path: '',
component: RemoteEntryComponent,
component: RemoteEntry,
},
]),
],
@ -301,7 +301,7 @@ test('renders remotes', async ({ page }) => {
// Update Host to use the module
updateFile(
`${host}/src/app/app.component.ts`,
`${host}/src/app/app.ts`,
`
import { Component } from '@angular/core';
import { isEven } from '${remote}/${module}';
@ -311,7 +311,7 @@ test('renders remotes', async ({ page }) => {
template: \`<div class="host">{{title}}</div>\`,
standalone: true
})
export class AppComponent {
export class App {
title = \`shell is \${isEven(2) ? 'even' : 'odd'}\`;
}`
);
@ -373,7 +373,7 @@ test('renders remotes', async ({ page }) => {
// Update Host to use the module
updateFile(
`${remote}/src/app/remote-entry/entry.component.ts`,
`${remote}/src/app/remote-entry/entry.ts`,
`
import { Component } from '@angular/core';
import { isEven } from '${childRemote}/${module}';
@ -383,7 +383,7 @@ test('renders remotes', async ({ page }) => {
template: \`<div class="childremote">{{title}}</div>\`,
standalone: true
})
export class RemoteEntryComponent {
export class RemoteEntry {
title = \`shell is \${isEven(2) ? 'even' : 'odd'}\`;
}`
);
@ -398,7 +398,7 @@ test('renders remotes', async ({ page }) => {
remotes: ['${childRemote}'],
exposes: {
'./Routes': '${remote}/src/app/remote-entry/entry.routes.ts',
'./Module': '${remote}/src/app/remote-entry/entry.component.ts',
'./Module': '${remote}/src/app/remote-entry/entry.ts',
},
};

View File

@ -115,7 +115,7 @@ describe('convert Angular CLI workspace to an Nx workspace', () => {
'.prettierrc',
`apps/${project}/src/main.ts`,
`apps/${project}/src/app/app.config.ts`,
`apps/${project}/src/app/app.component.ts`,
`apps/${project}/src/app/app.ts`,
`apps/${project}/src/app/app.routes.ts`
);
@ -178,10 +178,10 @@ describe('convert Angular CLI workspace to an Nx workspace', () => {
const projectConfig = readJson(`apps/${project}/project.json`);
expect(projectConfig.sourceRoot).toEqual(`apps/${project}/src`);
expect(projectConfig.targets.build).toStrictEqual({
executor: '@angular-devkit/build-angular:application',
executor: '@angular/build:application',
outputs: ['{options.outputPath}'],
options: {
outputPath: `dist/apps/${project}`,
index: `apps/${project}/src/index.html`,
outputPath: `dist/${project}`,
browser: `apps/${project}/src/main.ts`,
polyfills: [`zone.js`],
tsConfig: `apps/${project}/tsconfig.app.json`,
@ -214,7 +214,7 @@ describe('convert Angular CLI workspace to an Nx workspace', () => {
defaultConfiguration: 'production',
});
expect(projectConfig.targets.serve).toEqual({
executor: '@angular-devkit/build-angular:dev-server',
executor: '@angular/build:dev-server',
configurations: {
production: { buildTarget: `${project}:build:production` },
development: { buildTarget: `${project}:build:development` },
@ -222,7 +222,7 @@ describe('convert Angular CLI workspace to an Nx workspace', () => {
defaultConfiguration: 'development',
});
expect(projectConfig.targets.test).toStrictEqual({
executor: '@angular-devkit/build-angular:karma',
executor: '@angular/build:karma',
options: {
polyfills: [`zone.js`, `zone.js/testing`],
tsConfig: `apps/${project}/tsconfig.spec.json`,
@ -249,7 +249,7 @@ describe('convert Angular CLI workspace to an Nx workspace', () => {
});
runCLI(`build ${project} --configuration production --outputHashing none`);
checkFilesExist(`dist/apps/${project}/browser/main.js`);
checkFilesExist(`dist/${project}/browser/main.js`);
});
it('should handle a workspace with cypress v9', () => {
@ -436,7 +436,7 @@ describe('convert Angular CLI workspace to an Nx workspace', () => {
expect(output).toContain(
`Successfully ran target build for project ${project}`
);
checkFilesExist(`dist/apps/${project}/browser/main.js`);
checkFilesExist(`dist/${project}/browser/main.js`);
output = runCLI(`build ${project} --outputHashing none`);
expect(output).toContain(
@ -454,7 +454,7 @@ describe('convert Angular CLI workspace to an Nx workspace', () => {
expect(output).toContain(
`Successfully ran target build for project ${app1}`
);
checkFilesExist(`dist/apps/${app1}/browser/main.js`);
checkFilesExist(`dist/${app1}/browser/main.js`);
output = runCLI(`build ${app1} --outputHashing none`);
expect(output).toContain(

View File

@ -26,7 +26,7 @@ describe('Angular Package', () => {
// Generate root ngrx state management
runCLI(
`generate @nx/angular:ngrx users --parent=${myapp}/src/app/app.module.ts --root --minimal=false`
`generate @nx/angular:ngrx users --parent=${myapp}/src/app/app-module.ts --root --minimal=false`
);
const packageJson = readJson('package.json');
expect(packageJson.dependencies['@ngrx/store']).toBeDefined();
@ -38,7 +38,7 @@ describe('Angular Package', () => {
// Generate feature library and ngrx state within that library
runCLI(`g @nx/angular:lib ${mylib} --prefix=fl --no-standalone`);
runCLI(
`generate @nx/angular:ngrx flights --parent=${mylib}/src/lib/${mylib}.module.ts --facade`
`generate @nx/angular:ngrx flights --parent=${mylib}/src/lib/${mylib}-module.ts --facade`
);
expect(runCLI(`build ${myapp}`)).toMatch(/main-[a-zA-Z0-9]+\.js/);
@ -57,7 +57,7 @@ describe('Angular Package', () => {
// Generate root ngrx state management
runCLI(
`generate @nx/angular:ngrx users --parent=${myapp}/src/app/app.module.ts --root`
`generate @nx/angular:ngrx users --parent=${myapp}/src/app/app-module.ts --root`
);
const packageJson = readJson('package.json');
expect(packageJson.dependencies['@ngrx/entity']).toBeDefined();
@ -73,7 +73,7 @@ describe('Angular Package', () => {
const flags = `--facade --barrels`;
runCLI(
`generate @nx/angular:ngrx flights --parent=${mylib}/src/lib/${mylib}.module.ts ${flags}`
`generate @nx/angular:ngrx flights --parent=${mylib}/src/lib/${mylib}-module.ts ${flags}`
);
expect(runCLI(`build ${myapp}`)).toMatch(/main-[a-zA-Z0-9]+\.js/);
@ -92,7 +92,7 @@ describe('Angular Package', () => {
// Generate root ngrx state management
runCLI(
`generate @nx/angular:ngrx users --parent=${myapp}/src/app/app.module.ts --root`
`generate @nx/angular:ngrx users --parent=${myapp}/src/app/app-module.ts --root`
);
const packageJson = readJson('package.json');
expect(packageJson.dependencies['@ngrx/entity']).toBeDefined();
@ -108,7 +108,7 @@ describe('Angular Package', () => {
const flags = `--facade --barrels`;
runCLI(
`generate @nx/angular:ngrx flights --module=${mylib}/src/lib/${mylib}.module.ts ${flags}`
`generate @nx/angular:ngrx flights --module=${mylib}/src/lib/${mylib}-module.ts ${flags}`
);
expect(runCLI(`build ${myapp}`)).toMatch(/main-[a-zA-Z0-9]+\.js/);

View File

@ -40,26 +40,21 @@ describe('Angular Projects', () => {
`generate @nx/angular:app ${esbuildApp} --bundler=esbuild --no-standalone --no-interactive`
);
runCLI(`generate @nx/angular:lib ${lib1} --no-interactive`);
app1DefaultModule = readFile(`${app1}/src/app/app.module.ts`);
app1DefaultComponentTemplate = readFile(
`${app1}/src/app/app.component.html`
);
esbuildAppDefaultModule = readFile(`${esbuildApp}/src/app/app.module.ts`);
app1DefaultModule = readFile(`${app1}/src/app/app-module.ts`);
app1DefaultComponentTemplate = readFile(`${app1}/src/app/app.html`);
esbuildAppDefaultModule = readFile(`${esbuildApp}/src/app/app-module.ts`);
esbuildAppDefaultComponentTemplate = readFile(
`${esbuildApp}/src/app/app.component.html`
`${esbuildApp}/src/app/app.html`
);
esbuildAppDefaultProjectConfig = readFile(`${esbuildApp}/project.json`);
});
afterEach(() => {
updateFile(`${app1}/src/app/app.module.ts`, app1DefaultModule);
updateFile(`${app1}/src/app/app-module.ts`, app1DefaultModule);
updateFile(`${app1}/src/app/app.html`, app1DefaultComponentTemplate);
updateFile(`${esbuildApp}/src/app/app-module.ts`, esbuildAppDefaultModule);
updateFile(
`${app1}/src/app/app.component.html`,
app1DefaultComponentTemplate
);
updateFile(`${esbuildApp}/src/app/app.module.ts`, esbuildAppDefaultModule);
updateFile(
`${esbuildApp}/src/app/app.component.html`,
`${esbuildApp}/src/app/app.html`,
esbuildAppDefaultComponentTemplate
);
updateFile(`${esbuildApp}/project.json`, esbuildAppDefaultProjectConfig);
@ -79,24 +74,24 @@ describe('Angular Projects', () => {
);
updateFile(
`${app1}/src/app/app.module.ts`,
`${app1}/src/app/app-module.ts`,
`
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouterModule } from '@angular/router';
import { AppComponent } from './app.component';
import { App } from './app';
import { appRoutes } from './app.routes';
import { NxWelcomeComponent } from './nx-welcome.component';
import { ${names(lib1).className}Component } from '@${proj}/${lib1}';
import { NxWelcome } from './nx-welcome';
import { ${names(lib1).className} } from '@${proj}/${lib1}';
@NgModule({
imports: [
BrowserModule,
RouterModule.forRoot(appRoutes, { initialNavigation: 'enabledBlocking' }),
${names(lib1).className}Component
${names(lib1).className}
],
declarations: [AppComponent, NxWelcomeComponent],
bootstrap: [AppComponent]
declarations: [App, NxWelcome],
bootstrap: [App]
})
export class AppModule {}
`
@ -193,7 +188,7 @@ describe('Angular Projects', () => {
// External HTML template file
const templateWhichFailsBananaInBoxLintCheck = `<div ([foo])="bar"></div>`;
updateFile(
`${app1}/src/app/app.component.html`,
`${app1}/src/app/app.html`,
templateWhichFailsBananaInBoxLintCheck
);
// Inline template within component.ts file
@ -216,9 +211,7 @@ describe('Angular Projects', () => {
const appLintStdOut = runCLI(`lint ${app1}`, {
silenceError: true,
});
expect(appLintStdOut).toContain(
normalize(`${app1}/src/app/app.component.html`)
);
expect(appLintStdOut).toContain(normalize(`${app1}/src/app/app.html`));
expect(appLintStdOut).toContain(`1:6`);
expect(appLintStdOut).toContain(`Invalid binding syntax`);
expect(appLintStdOut).toContain(
@ -249,53 +242,53 @@ describe('Angular Projects', () => {
// update the app module to include a ref to the buildable lib
updateFile(
`${app1}/src/app/app.module.ts`,
`${app1}/src/app/app-module.ts`,
`
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouterModule } from '@angular/router';
import { AppComponent } from './app.component';
import { App } from './app';
import { appRoutes } from './app.routes';
import { NxWelcomeComponent } from './nx-welcome.component';
import { NxWelcome } from './nx-welcome';
import {${
names(buildableLib).className
}Module} from '@${proj}/${buildableLib}';
@NgModule({
declarations: [AppComponent, NxWelcomeComponent],
declarations: [App, NxWelcome],
imports: [
BrowserModule,
RouterModule.forRoot(appRoutes, { initialNavigation: 'enabledBlocking' }),
${names(buildableLib).className}Module
],
providers: [],
bootstrap: [AppComponent],
bootstrap: [App],
})
export class AppModule {}
`
);
updateFile(
`${esbuildApp}/src/app/app.module.ts`,
`${esbuildApp}/src/app/app-module.ts`,
`
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouterModule } from '@angular/router';
import { AppComponent } from './app.component';
import { App } from './app';
import { appRoutes } from './app.routes';
import { NxWelcomeComponent } from './nx-welcome.component';
import { NxWelcome } from './nx-welcome';
import {${
names(buildableLib).className
}Module} from '@${proj}/${buildableLib}';
@NgModule({
declarations: [AppComponent, NxWelcomeComponent],
declarations: [App, NxWelcome],
imports: [
BrowserModule,
RouterModule.forRoot(appRoutes, { initialNavigation: 'enabledBlocking' }),
${names(buildableLib).className}Module
],
providers: [],
bootstrap: [AppComponent],
bootstrap: [App],
})
export class AppModule {}
`
@ -303,7 +296,7 @@ describe('Angular Projects', () => {
// update the buildable lib module to include a ref to the buildable child lib
updateFile(
`${buildableLib}/src/lib/${names(buildableLib).fileName}.module.ts`,
`${buildableLib}/src/lib/${names(buildableLib).fileName}-module.ts`,
`
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
@ -333,6 +326,7 @@ describe('Angular Projects', () => {
config.targets.build.options = {
...config.targets.build.options,
outputPath: `dist/${esbuildApp}`,
index: `${esbuildApp}/src/index.html`,
main: config.targets.build.options.browser,
browser: undefined,
buildLibsFromSource: false,
@ -397,7 +391,7 @@ describe('Angular Projects', () => {
export default replaceTextPlugin;`
);
updateFile(
`${esbuildApp}/src/app/app.component.ts`,
`${esbuildApp}/src/app/app.ts`,
`import { Component } from '@angular/core';
declare const BUILD_DEFINED: string;
@ -405,9 +399,9 @@ describe('Angular Projects', () => {
@Component({
selector: 'app-root',
standalone: false,
templateUrl: './app.component.html',
templateUrl: './app.html',
})
export class AppComponent {
export class App {
title = 'esbuild-app';
buildDefined = BUILD_DEFINED;
}`
@ -437,6 +431,7 @@ describe('Angular Projects', () => {
...config.targets.build.options,
main: config.targets.build.options.browser,
browser: undefined,
index: `${esbuildApp}/src/index.html`,
};
return config;
});
@ -511,7 +506,7 @@ describe('Angular Projects', () => {
})
export class ${names(lib).className}Module {}`;
updateFile(`${lib}/src/lib/${lib}.module.ts`, moduleContent);
updateFile(`${lib}/src/lib/${lib}-module.ts`, moduleContent);
// ACT
const buildOutput = runCLI(`build ${lib}`, { env: { CI: 'false' } });
@ -536,9 +531,7 @@ describe('Angular Projects', () => {
// using the project name as the directory when no directory is provided
checkFilesExist(
`${libName}/src/index.ts`,
`${libName}/src/lib/${libName.split('/')[1]}/${
libName.split('/')[1]
}.component.ts`
`${libName}/src/lib/${libName.split('/')[1]}/${libName.split('/')[1]}.ts`
);
// check build works
expect(() => runCLI(`build ${libName}`)).not.toThrow();

View File

@ -132,7 +132,7 @@ describe('Tailwind support', () => {
buttonBgColor: string = defaultButtonBgColor
) => {
updateFile(
`${lib}/src/lib/foo.component.ts`,
`${lib}/src/lib/foo.ts`,
`import { Component } from '@angular/core';
@Component({
@ -145,20 +145,20 @@ describe('Tailwind support', () => {
}
\`]
})
export class FooComponent {}
export class Foo {}
`
);
updateFile(
`${lib}/src/lib/${lib}.module.ts`,
`${lib}/src/lib/${lib}-module.ts`,
`import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FooComponent } from './foo.component';
import { Foo } from './foo';
@NgModule({
imports: [CommonModule],
declarations: [FooComponent],
exports: [FooComponent],
declarations: [Foo],
exports: [Foo],
})
export class LibModule {}
`
@ -166,8 +166,8 @@ describe('Tailwind support', () => {
updateFile(
`${lib}/src/index.ts`,
`export * from './lib/foo.component';
export * from './lib/${lib}.module';
`export * from './lib/foo';
export * from './lib/${lib}-module';
`
);
};
@ -180,7 +180,7 @@ describe('Tailwind support', () => {
const builtComponentContent = readFile(
isPublishable
? `dist/${lib}/fesm2022/${project}-${lib}.mjs`
: `dist/${lib}/esm2022/lib/foo.component.mjs`
: `dist/${lib}/esm2022/lib/foo.js`
);
let expectedStylesRegex = new RegExp(
`styles: \\[\\"\\.custom\\-btn(\\[_ngcontent\\-%COMP%\\])?{margin:${libSpacing.md};padding:${libSpacing.sm}}(\\\\n)?\\"\\]`
@ -356,30 +356,30 @@ describe('Tailwind support', () => {
spacing.projectVariant1
);
updateFile(
`${appName}/src/app/app.module.ts`,
`${appName}/src/app/app-module.ts`,
`import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { LibModule as LibModule1 } from '@${project}/${buildLibWithTailwind.name}';
import { LibModule as LibModule2 } from '@${project}/${pubLibWithTailwind.name}';
import { AppComponent } from './app.component';
import { App } from './app';
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, LibModule1, LibModule2],
declarations: [],
imports: [BrowserModule, App, LibModule1, LibModule2],
providers: [],
bootstrap: [AppComponent],
bootstrap: [App],
})
export class AppModule {}
`
);
updateFile(
`${appName}/src/app/app.component.html`,
`${appName}/src/app/app.html`,
`<button class="custom-btn text-white">Click me!</button>`
);
updateFile(
`${appName}/src/app/app.component.css`,
`${appName}/src/app/app.css`,
`.custom-btn {
@apply m-md p-sm;
}`

View File

@ -85,7 +85,7 @@ describe('nx init (Angular CLI - legacy)', () => {
expect(coldBuildOutput).toContain(
`Successfully ran target build for project ${project}`
);
checkFilesExist(`dist/apps/${project}/browser/main.js`);
checkFilesExist(`dist/${project}/browser/main.js`);
// run build again to check is coming from cache
const cachedBuildOutput = runCLI(`build ${project} --outputHashing none`);

View File

@ -230,7 +230,6 @@ describe('release publishable libraries', () => {
XXX.XXX kb fesm2022/proj-{project-name}.mjs
XXX.XXX kb fesm2022/proj-{project-name}.mjs.map
XXB index.d.ts
XXB lib/{project-name}/{project-name}.component.d.ts
XXXB package.json
=== Tarball Details ===
name: @proj/{project-name}

View File

@ -38,7 +38,7 @@ describe('create-nx-workspace', () => {
checkFilesExist('package.json');
checkFilesExist('project.json');
checkFilesExist('src/app/app.module.ts');
checkFilesExist('src/app/app-module.ts');
checkFilesDoNotExist('src/app/app.routes.ts');
expectCodeIsFormatted();
});
@ -61,7 +61,7 @@ describe('create-nx-workspace', () => {
checkFilesExist('package.json');
checkFilesExist('project.json');
checkFilesExist('src/app/app.routes.ts');
checkFilesDoNotExist('src/app/app.module.ts');
checkFilesDoNotExist('src/app/app-module.ts');
expectCodeIsFormatted();
});

View File

@ -28,19 +28,21 @@
},
"devDependencies": {
"@actions/core": "^1.10.0",
"@angular-devkit/architect": "0.1902.0",
"@angular-devkit/build-angular": "19.2.0",
"@angular-devkit/core": "19.2.0",
"@angular-devkit/schematics": "19.2.0",
"@angular-devkit/architect": "0.2000.0-rc.3",
"@angular-devkit/build-angular": "20.0.0-rc.3",
"@angular-devkit/core": "20.0.0-rc.3",
"@angular-devkit/schematics": "20.0.0-rc.3",
"@angular-eslint/eslint-plugin": "19.2.0",
"@angular-eslint/eslint-plugin-template": "19.2.0",
"@angular-eslint/template-parser": "19.2.0",
"@angular/cli": "~19.2.0",
"@angular/common": "19.2.0",
"@angular/compiler": "19.2.0",
"@angular/compiler-cli": "19.2.0",
"@angular/core": "19.2.0",
"@angular/router": "19.2.0",
"@angular/build": "20.0.0-rc.3",
"@angular/cli": "20.0.0-rc.3",
"@angular/common": "20.0.0-rc.2",
"@angular/compiler": "20.0.0-rc.2",
"@angular/compiler-cli": "20.0.0-rc.2",
"@angular/core": "20.0.0-rc.2",
"@angular/platform-browser": "20.0.0-rc.2",
"@angular/router": "20.0.0-rc.2",
"@astrojs/check": "^0.7.0",
"@astrojs/react": "^3.6.2",
"@babel/core": "^7.23.2",
@ -114,7 +116,7 @@
"@rspack/dev-server": "1.1.1",
"@rspack/plugin-minify": "^0.7.5",
"@rspack/plugin-react-refresh": "^1.0.0",
"@schematics/angular": "~19.2.0",
"@schematics/angular": "20.0.0-rc.3",
"@storybook/addon-essentials": "8.4.6",
"@storybook/addon-interactions": "8.4.6",
"@storybook/core-server": "8.4.6",
@ -160,10 +162,10 @@
"@types/tmp": "^0.2.0",
"@types/yargs": "17.0.10",
"@types/yarnpkg__lockfile": "^1.1.5",
"@typescript-eslint/eslint-plugin": "^8.19.0",
"@typescript-eslint/rule-tester": "^8.19.0",
"@typescript-eslint/type-utils": "^8.19.0",
"@typescript-eslint/utils": "^8.19.0",
"@typescript-eslint/eslint-plugin": "^8.29.0",
"@typescript-eslint/rule-tester": "^8.29.0",
"@typescript-eslint/type-utils": "^8.29.0",
"@typescript-eslint/utils": "^8.29.0",
"@vitejs/plugin-react": "^4.2.0",
"@webcontainer/api": "1.5.1",
"@xstate/immer": "0.3.1",
@ -264,7 +266,7 @@
"mini-css-extract-plugin": "~2.4.7",
"minimatch": "9.0.3",
"next-sitemap": "^3.1.10",
"ng-packagr": "19.2.0",
"ng-packagr": "20.0.0-rc.1",
"npm-package-arg": "11.0.1",
"nuxt": "^3.10.0",
"nx": "21.1.0-beta.2",
@ -276,7 +278,6 @@
"parse5": "4.0.0",
"picocolors": "^1.1.0",
"picomatch": "4.0.2",
"piscina": "^4.4.0",
"postcss": "8.4.38",
"postcss-import": "~14.1.0",
"postcss-preset-env": "~7.5.0",
@ -296,7 +297,7 @@
"rollup-plugin-copy": "^3.5.0",
"rollup-plugin-postcss": "^4.0.2",
"rollup-plugin-typescript2": "^0.36.0",
"rxjs": "^7.8.0",
"rxjs": "^7.8.2",
"sass": "1.55.0",
"sass-embedded": "1.85.1",
"sass-loader": "16.0.5",
@ -319,14 +320,14 @@
"tsconfig-paths-webpack-plugin": "4.0.0",
"typedoc": "0.25.12",
"typedoc-plugin-markdown": "3.17.1",
"typescript": "~5.7.2",
"typescript-eslint": "^8.19.0",
"typescript": "~5.8.2",
"typescript-eslint": "^8.29.0",
"unist-builder": "^4.0.0",
"use-sync-external-store": "^1.2.0",
"verdaccio": "6.0.5",
"vite": "6.2.0",
"vitest": "3.0.5",
"webpack": "5.98.0",
"webpack": "5.99.8",
"webpack-dev-server": "5.2.1",
"webpack-merge": "^5.8.0",
"webpack-node-externals": "^3.0.0",

View File

@ -80,7 +80,6 @@
"@storybook/angular",
"@module-federation/node",
"@nguniversal/builders",
"ng-packagr",
"injection-js",
"browserslist",
"cacache",
@ -96,7 +95,8 @@
"stylus",
"tailwindcss",
"cypress",
"esbuild"
"esbuild",
"piscina"
]
}
]

View File

@ -3,50 +3,60 @@
{% tabs %}
{% tab label="Simple Component" %}
Generate a component named `MyComponent` at `apps/my-app/src/lib/my-component/my-component.component.ts`:
Generate a component named `Card` at `apps/my-app/src/lib/card/card.ts`:
```bash
nx g @nx/angular:component apps/my-app/src/lib/my-component/my-component.ts
nx g @nx/angular:component apps/my-app/src/lib/card/card.ts
```
{% /tab %}
{% tab label="Without Providing the File Extension" %}
Generate a component named `MyComponent` at `apps/my-app/src/lib/my-component/my-component.component.ts`:
Generate a component named `Card` at `apps/my-app/src/lib/card/card.ts`:
```bash
nx g @nx/angular:component apps/my-app/src/lib/my-component/my-component
nx g @nx/angular:component apps/my-app/src/lib/card/card
```
{% /tab %}
{% tab label="With Different Symbol Name" %}
Generate a component named `CustomComponent` at `apps/my-app/src/lib/my-component/my-component.component.ts`:
Generate a component named `Custom` at `apps/my-app/src/lib/card/card.ts`:
```bash
nx g @nx/angular:component apps/my-app/src/lib/my-component/my-component --name=custom
nx g @nx/angular:component apps/my-app/src/lib/card/card --name=custom
```
{% /tab %}
{% tab label="With a Component Type" %}
Generate a component named `CardComponent` at `apps/my-app/src/lib/card/card.component.ts`:
```bash
nx g @nx/angular:component apps/my-app/src/lib/card/card --type=component
```
{% /tab %}
{% tab label="Single File Component" %}
Create a component named `my-component` with inline styles and inline template:
Create a component named `Card` with inline styles and inline template:
```bash
nx g @nx/angular:component apps/my-app/src/lib/my-component/my-component --inlineStyle --inlineTemplate
nx g @nx/angular:component apps/my-app/src/lib/card/card --inlineStyle --inlineTemplate
```
{% /tab %}
{% tab label="Component with OnPush Change Detection Strategy" %}
Create a component named `my-component` with OnPush Change Detection Strategy:
Create a component named `Card` with `OnPush` Change Detection Strategy:
```bash
nx g @nx/angular:component apps/my-app/src/lib/my-component/my-component --changeDetection=OnPush
nx g @nx/angular:component apps/my-app/src/lib/card/card --changeDetection=OnPush
```
{% /tab %}

View File

@ -50,12 +50,6 @@ module.exports = (config) => {
{% tab label="Providing HTTP request middleware function" %}
{% callout type="warning" title="Overrides" }
Available for workspaces using Angular version 17.0.0 or greater and with `build` targets using an esbuild-based executor.
{% /callout %}
The executor accepts an `esbuildMiddleware` option that allows you to provide HTTP require middleware functions that will be used by the Vite development server.
```json {% fileName="apps/my-app/project.json" highlightLines=[8] %}

View File

@ -302,6 +302,47 @@
},
"description": "Change the data persistence operator imports to '@ngrx/router-store/data-persistence'.",
"factory": "./src/migrations/update-21-0-0/change-data-persistence-operators-imports-to-ngrx-router-store-data-persistence"
},
"update-angular-cli-version-20-0-0-rc-3": {
"cli": "nx",
"version": "21.2.0-beta.0",
"requires": {
"@angular/core": ">=20.0.0-rc.2"
},
"description": "Update the @angular/cli package version to 20.0.0-rc.3.",
"factory": "./src/migrations/update-21-2-0/update-angular-cli"
},
"migrate-provide-server-rendering-import": {
"version": "21.2.0-beta.0",
"requires": {
"@angular/core": ">=20.0.0-rc.2"
},
"description": "Migrate imports of `provideServerRendering` from `@angular/platform-server` to `@angular/ssr`.",
"factory": "./src/migrations/update-21-2-0/migrate-provide-server-rendering-import"
},
"replace-provide-server-routing": {
"version": "21.2.0-beta.0",
"requires": {
"@angular/core": ">=20.0.0-rc.2"
},
"description": "Replace `provideServerRouting` with `provideServerRendering` using `withRoutes`.",
"factory": "./src/migrations/update-21-2-0/replace-provide-server-routing"
},
"set-generator-defaults-for-previous-style-guide": {
"version": "21.2.0-beta.0",
"requires": {
"@angular/core": ">=20.0.0-rc.2"
},
"description": "Update the generator defaults to maintain the previous style guide behavior.",
"factory": "./src/migrations/update-21-2-0/set-generator-defaults-for-previous-style-guide"
},
"update-module-resolution": {
"version": "21.2.0-beta.0",
"requires": {
"@angular/core": ">=20.0.0-rc.2"
},
"description": "Update 'moduleResolution' to 'bundler' in TypeScript configurations. You can read more about this here: https://www.typescriptlang.org/tsconfig/#moduleResolution.",
"factory": "./src/migrations/update-21-2-0/update-module-resolution"
}
},
"packageJsonUpdates": {
@ -1624,6 +1665,71 @@
"alwaysAddToPackageJson": false
}
}
},
"21.2.0": {
"version": "21.2.0-beta.0",
"x-prompt": "Do you want to update the Angular version to v20?",
"requires": {
"@angular/core": ">=19.2.0 <20.0.0-rc.2"
},
"packages": {
"@angular-devkit/build-angular": {
"version": "20.0.0-rc.3",
"alwaysAddToPackageJson": false
},
"@angular-devkit/core": {
"version": "20.0.0-rc.3",
"alwaysAddToPackageJson": false
},
"@angular-devkit/schematics": {
"version": "20.0.0-rc.3",
"alwaysAddToPackageJson": false
},
"@angular/build": {
"version": "20.0.0-rc.3",
"alwaysAddToPackageJson": false
},
"@angular/pwa": {
"version": "20.0.0-rc.3",
"alwaysAddToPackageJson": false
},
"@angular/ssr": {
"version": "20.0.0-rc.3",
"alwaysAddToPackageJson": false
},
"@schematics/angular": {
"version": "20.0.0-rc.3",
"alwaysAddToPackageJson": false
},
"@angular-devkit/architect": {
"version": "0.2000.0-rc.3",
"alwaysAddToPackageJson": false
},
"@angular-devkit/build-webpack": {
"version": "0.2000.0-rc.3",
"alwaysAddToPackageJson": false
},
"@angular/core": {
"version": "20.0.0-rc.2",
"alwaysAddToPackageJson": true
},
"@angular/material": {
"version": "20.0.0-rc.2",
"alwaysAddToPackageJson": false
},
"@angular/cdk": {
"version": "20.0.0-rc.2",
"alwaysAddToPackageJson": false
},
"@angular/google-maps": {
"version": "20.0.0-rc.2",
"alwaysAddToPackageJson": false
},
"ng-packagr": {
"version": "20.0.0-rc.1",
"alwaysAddToPackageJson": false
}
}
}
}
}

View File

@ -28,7 +28,6 @@
"magic-string",
"enquirer",
"find-cache-dir",
"piscina",
"webpack"
],
"keepLifecycleScripts": true

View File

@ -47,32 +47,44 @@
"migrations": "./migrations.json"
},
"dependencies": {
"@nx/devkit": "file:../devkit",
"@nx/eslint": "file:../eslint",
"@nx/js": "file:../js",
"@nx/module-federation": "file:../module-federation",
"@nx/rspack": "file:../rspack",
"@nx/web": "file:../web",
"@nx/webpack": "file:../webpack",
"@nx/workspace": "file:../workspace",
"@phenomnomnominal/tsquery": "~5.0.1",
"@typescript-eslint/type-utils": "^8.0.0",
"enquirer": "~2.3.6",
"magic-string": "~0.30.2",
"picocolors": "^1.1.0",
"picomatch": "4.0.2",
"magic-string": "~0.30.2",
"semver": "^7.5.3",
"tslib": "^2.3.0",
"webpack-merge": "^5.8.0",
"@nx/devkit": "file:../devkit",
"@nx/js": "file:../js",
"@nx/eslint": "file:../eslint",
"@nx/webpack": "file:../webpack",
"@nx/rspack": "file:../rspack",
"@nx/module-federation": "file:../module-federation",
"@nx/web": "file:../web",
"@nx/workspace": "file:../workspace",
"piscina": "^4.4.0"
"webpack-merge": "^5.8.0"
},
"peerDependencies": {
"@angular-devkit/build-angular": ">= 17.0.0 < 20.0.0",
"@angular-devkit/core": ">= 17.0.0 < 20.0.0",
"@angular-devkit/schematics": ">= 17.0.0 < 20.0.0",
"@schematics/angular": ">= 17.0.0 < 20.0.0",
"@angular/build": ">= 18.0.0 < 21.0.0",
"@angular-devkit/build-angular": ">= 18.0.0 < 21.0.0",
"@angular-devkit/core": ">= 18.0.0 < 21.0.0",
"@angular-devkit/schematics": ">= 18.0.0 < 21.0.0",
"@schematics/angular": ">= 18.0.0 < 21.0.0",
"ng-packagr": ">= 18.0.0 < 21.0.0",
"rxjs": "^6.5.3 || ^7.5.0"
},
"peerDependenciesMeta": {
"@angular/build": {
"optional": true
},
"@angular-devkit/build-angular": {
"optional": true
},
"ng-packagr": {
"optional": true
}
},
"publishConfig": {
"access": "public"
}

View File

@ -195,7 +195,9 @@ function normalizeBuildTargetOptions(
},
buildContext
);
const buildOptions = withSchemaDefaults(options);
const project =
buildContext.projectsConfigurations.projects[buildContext.projectName];
const buildOptions = withSchemaDefaults(options, project, buildContext.root);
// cypress creates a tsconfig if one isn't preset
// that contains all the support required for angular and component tests
@ -305,10 +307,19 @@ Note: this may fail, setting the correct 'sourceRoot' for ${buildContext.project
};
}
function withSchemaDefaults(options: any): BrowserBuilderSchema {
function withSchemaDefaults(
options: any,
project: ProjectConfiguration,
workspaceRoot: string
): BrowserBuilderSchema {
if (!options.main && !options.browser) {
const sourceRoot =
project.sourceRoot ?? joinPathFragments(project.root, 'src');
options.browser = joinPathFragments(sourceRoot, 'main.ts');
if (!existsSync(join(workspaceRoot, options.browser))) {
throw new Error('Missing executor options "main" and "browser"');
}
}
if (!options.index) {
throw new Error('Missing executor options "index"');
}

View File

@ -12,12 +12,13 @@ import { getRootTsConfigPath } from '@nx/js';
import type { DependentBuildableProjectNode } from '@nx/js/src/utils/buildable-libs-utils';
import { WebpackNxBuildCoordinationPlugin } from '@nx/webpack/src/plugins/webpack-nx-build-coordination-plugin';
import { existsSync } from 'fs';
import { readNxJson } from 'nx/src/config/configuration';
import { isNpmProject } from 'nx/src/project-graph/operators';
import { readCachedProjectConfiguration } from 'nx/src/project-graph/project-graph';
import { relative } from 'path';
import { combineLatest, from } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import { getInstalledAngularVersionInfo } from '../../executors/utilities/angular-version-utils';
import { assertBuilderPackageIsInstalled } from '../../executors/utilities/builder-package';
import {
loadIndexHtmlTransformer,
loadMiddleware,
@ -31,13 +32,7 @@ import {
resolveIndexHtmlTransformer,
} from '../utilities/webpack';
import { normalizeOptions, validateOptions } from './lib';
import type {
NormalizedSchema,
Schema,
SchemaWithBrowserTarget,
} from './schema';
import { readNxJson } from 'nx/src/config/configuration';
import type { NormalizedSchema, Schema } from './schema';
type BuildTargetOptions = {
tsConfig: string;
buildLibsFromSource?: boolean;
@ -158,6 +153,7 @@ export function executeDevServerBuilder(
const delegateBuilderOptions = getDelegateBuilderOptions(options);
const isUsingWebpackBuilder = ![
'@angular/build:application',
'@angular-devkit/build-angular:application',
'@angular-devkit/build-angular:browser-esbuild',
'@nx/angular:application',
@ -171,6 +167,7 @@ export function executeDevServerBuilder(
* handle `@nx/angular:*` executors.
*/
patchBuilderContext(context, !isUsingWebpackBuilder, parsedBuildTarget);
assertBuilderPackageIsInstalled('@angular-devkit/build-angular');
return combineLatest([
from(import('@angular-devkit/build-angular')),
@ -259,14 +256,6 @@ function getDelegateBuilderOptions(
...options,
};
const { major: angularMajorVersion } = getInstalledAngularVersionInfo();
if (angularMajorVersion <= 17) {
(
delegateBuilderOptions as unknown as SchemaWithBrowserTarget
).browserTarget = delegateBuilderOptions.buildTarget;
delete delegateBuilderOptions.buildTarget;
}
// delete extra option not supported by the delegate builder
delete delegateBuilderOptions.buildLibsFromSource;
delete delegateBuilderOptions.watchDependencies;

View File

@ -1,23 +1,11 @@
import { getInstalledAngularVersionInfo } from '../../../executors/utilities/angular-version-utils';
import type {
NormalizedSchema,
Schema,
SchemaWithBrowserTarget,
SchemaWithBuildTarget,
} from '../schema';
import type { NormalizedSchema, Schema } from '../schema';
export function normalizeOptions(schema: Schema): NormalizedSchema {
let buildTarget = (schema as SchemaWithBuildTarget).buildTarget;
if ((schema as SchemaWithBrowserTarget).browserTarget) {
buildTarget ??= (schema as SchemaWithBrowserTarget).browserTarget;
delete (schema as SchemaWithBrowserTarget).browserTarget;
}
const { major: angularMajorVersion } = getInstalledAngularVersionInfo();
return {
...schema,
buildTarget,
host: schema.host ?? 'localhost',
port: schema.port ?? 4200,
liveReload: schema.liveReload ?? true,

View File

@ -6,11 +6,6 @@ import type { Schema } from '../schema';
export function validateOptions(options: Schema): void {
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}".
You can resolve this error by removing the "prebundle" option or by migrating to Angular 17.2.0.`);
}
if (lt(angularVersion, '18.1.0') && options.inspect) {
throw new Error(stripIndents`The "inspect" option is only supported in Angular >= 18.1.0. You are currently using "${angularVersion}".
You can resolve this error by removing the "inspect" option or by migrating to Angular 18.1.0.`);

View File

@ -1,4 +1,5 @@
interface BaseSchema {
interface Schema {
buildTarget: string;
port?: number;
host?: string;
proxyConfig?: string;
@ -24,20 +25,7 @@ interface BaseSchema {
watchDependencies?: boolean;
}
export type SchemaWithBrowserTarget = BaseSchema & {
/**
* @deprecated Use `buildTarget` instead. It will be removed when Angular v20 is released.
*/
browserTarget: string;
};
export type SchemaWithBuildTarget = BaseSchema & {
buildTarget: string;
};
export type Schema = SchemaWithBrowserTarget | SchemaWithBuildTarget;
export type NormalizedSchema = SchemaWithBuildTarget & {
export type NormalizedSchema = Schema & {
liveReload: boolean;
open: boolean;
ssl: boolean;

View File

@ -14,12 +14,6 @@
}
],
"properties": {
"browserTarget": {
"type": "string",
"description": "A browser builder target to serve in the format of `project:target[:configuration]`. Ignored if `buildTarget` is set.",
"pattern": "^[^:\\s]+:[^:\\s]+(:[^\\s]+)?$",
"x-deprecated": "Use 'buildTarget' instead. It will be removed when Angular v20 is released."
},
"buildTarget": {
"type": "string",
"description": "A build builder target to serve in the format of `project:target[:configuration]`.",
@ -128,7 +122,7 @@
]
},
"prebundle": {
"description": "Enable and control the Vite-based development server's prebundling capabilities. To enable prebundling, the Angular CLI cache must also be enabled. This option has no effect when using the 'browser' or other Webpack-based builders. _Note: this is only supported in Angular versions >= 17.2.0_.",
"description": "Enable and control the Vite-based development server's prebundling capabilities. To enable prebundling, the Angular CLI cache must also be enabled. This option has no effect when using the 'browser' or other Webpack-based builders.",
"oneOf": [
{ "type": "boolean" },
{
@ -147,7 +141,7 @@
},
"buildLibsFromSource": {
"type": "boolean",
"description": "Read buildable libraries from source instead of building them separately. If not set, it will take the value specified in the `browserTarget` options, or it will default to `true` if it's also not set in the `browserTarget` options.",
"description": "Read buildable libraries from source instead of building them separately. If not set, it will take the value specified in the `buildTarget` options, or it will default to `true` if it's also not set in the `buildTarget` options.",
"x-priority": "important"
},
"esbuildMiddleware": {
@ -165,5 +159,5 @@
}
},
"additionalProperties": false,
"anyOf": [{ "required": ["buildTarget"] }, { "required": ["browserTarget"] }]
"required": ["buildTarget"]
}

View File

@ -13,6 +13,7 @@ import { getDependencyConfigs } from 'nx/src/tasks-runner/utils';
import { relative } from 'path';
import { from, Observable } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import { assertBuilderPackageIsInstalled } from '../../executors/utilities/builder-package';
import { createTmpTsConfigForBuildableLibs } from '../utilities/buildable-libs';
import {
mergeCustomWebpackConfig,
@ -98,6 +99,7 @@ export function executeWebpackBrowserBuilder(
);
}
assertBuilderPackageIsInstalled('@angular-devkit/build-angular');
return from(import('@angular-devkit/build-angular')).pipe(
switchMap(({ executeBrowserBuilder }) =>
executeBrowserBuilder(delegateBuilderOptions, context as any, {

View File

@ -1,3 +1,5 @@
import type { BuilderContext } from '@angular-devkit/architect';
import type { ServerBuilderOutput } from '@angular-devkit/build-angular';
import {
joinPathFragments,
normalizePath,
@ -7,14 +9,17 @@ import { existsSync } from 'fs';
import { relative } from 'path';
import { Observable, from } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import { assertBuilderPackageIsInstalled } from '../../executors/utilities/builder-package';
import { createTmpTsConfigForBuildableLibs } from '../utilities/buildable-libs';
import { mergeCustomWebpackConfig } from '../utilities/webpack';
import { Schema } from './schema';
function buildServerApp(
options: Schema,
context: import('@angular-devkit/architect').BuilderContext
): Observable<import('@angular-devkit/build-angular').ServerBuilderOutput> {
context: BuilderContext
): Observable<ServerBuilderOutput> {
assertBuilderPackageIsInstalled('@angular-devkit/build-angular');
const { buildLibsFromSource, customWebpackConfig, ...delegateOptions } =
options;
// If there is a path to custom webpack config
@ -47,7 +52,7 @@ function buildServerApp(
function buildServerAppWithCustomWebpackConfiguration(
options: Schema,
context: import('@angular-devkit/architect').BuilderContext,
context: BuilderContext,
pathToWebpackConfig: string
) {
return from(import('@angular-devkit/build-angular')).pipe(
@ -89,8 +94,8 @@ function buildServerAppWithCustomWebpackConfiguration(
export function executeWebpackServerBuilder(
options: Schema,
context: import('@angular-devkit/architect').BuilderContext
): Observable<import('@angular-devkit/build-angular').ServerBuilderOutput> {
context: BuilderContext
): Observable<ServerBuilderOutput> {
options.buildLibsFromSource ??= true;
process.env.NX_BUILD_LIBS_FROM_SOURCE = `${options.buildLibsFromSource}`;

View File

@ -1,10 +1,10 @@
import type { buildApplication as buildApplicationFn } from '@angular-devkit/build-angular';
import type { BuilderOutput } from '@angular-devkit/architect';
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 { gte } from 'semver';
import { getInstalledAngularVersionInfo } from '../utilities/angular-version-utils';
import { createTmpTsConfigForBuildableLibs } from '../utilities/buildable-libs';
import { assertBuilderPackageIsInstalled } from '../utilities/builder-package';
import {
loadIndexHtmlTransformer,
loadPlugins,
@ -16,7 +16,7 @@ import { validateOptions } from './utils/validate-options';
export default async function* applicationExecutor(
options: ApplicationExecutorOptions,
context: ExecutorContext
): ReturnType<typeof buildApplicationFn> {
): AsyncIterable<BuilderOutput> {
validateOptions(options);
options = normalizeOptions(options);
@ -53,19 +53,20 @@ export default async function* applicationExecutor(
context
);
const { version: angularVersion } = getInstalledAngularVersionInfo();
if (gte(angularVersion, '17.1.0')) {
const { buildApplication } = await import('@angular-devkit/build-angular');
const { major: angularMajorVersion } = getInstalledAngularVersionInfo();
if (angularMajorVersion >= 20) {
assertBuilderPackageIsInstalled('@angular/build');
const { buildApplication } = await import('@angular/build');
return yield* buildApplication(delegateExecutorOptions, builderContext, {
codePlugins: plugins,
indexHtmlTransformer,
});
}
const { buildApplication } = require('@angular-devkit/build-angular');
return yield* buildApplication(
delegateExecutorOptions,
builderContext,
plugins
);
assertBuilderPackageIsInstalled('@angular-devkit/build-angular');
const { buildApplication } = await import('@angular-devkit/build-angular');
return yield* buildApplication(delegateExecutorOptions, builderContext, {
codePlugins: plugins,
indexHtmlTransformer,
});
}

View File

@ -1,4 +1,4 @@
import type { ApplicationBuilderOptions } from '@angular-devkit/build-angular';
import type { ApplicationBuilderOptions } from '@angular/build';
import type { PluginSpec } from '../utilities/esbuild-extensions';
export interface ApplicationExecutorOptions extends ApplicationBuilderOptions {

View File

@ -19,7 +19,6 @@
"description": "The full path for the browser entry point to the application, relative to the current workspace."
},
"server": {
"type": "string",
"description": "The full path for the server entry point to the application, relative to the current workspace.",
"oneOf": [
{
@ -48,7 +47,7 @@
},
"deployUrl": {
"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_."
"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."
},
"security": {
"description": "Security features to protect against XSS and other common attacks. _Note: this is only supported in Angular versions >= 19.0.0_.",
@ -206,7 +205,7 @@
"clearScreen": {
"type": "boolean",
"default": false,
"description": "Automatically clear the terminal screen during rebuilds. _Note: this is only supported in Angular versions >= 17.2.0_."
"description": "Automatically clear the terminal screen during rebuilds."
},
"optimization": {
"description": "Enables optimization of the build output. Including minification of scripts and styles, tree-shaking, dead-code elimination, inlining of critical CSS and fonts inlining. For more information, see https://angular.dev/reference/configs/workspace-config#optimization-configuration.",
@ -239,7 +238,7 @@
},
"removeSpecialComments": {
"type": "boolean",
"description": "Remove comments in global CSS that contains '@license' or '@preserve' or that starts with '//!' or '/*!'. _Note: this is only supported in Angular versions >= 17.1.0_.",
"description": "Remove comments in global CSS that contains '@license' or '@preserve' or that starts with '//!' or '/*!'.",
"default": true
}
},
@ -279,19 +278,26 @@
]
},
"loader": {
"description": "Defines the type of loader to use with a specified file extension when used with a JavaScript `import`. `text` inlines the content as a string; `binary` inlines the content as a Uint8Array; `file` emits the file and provides the runtime location of the file; `empty` considers the content to be empty and not include it in bundles. _Note: this is only supported in Angular versions >= 17.1.0_.",
"description": "Defines the type of loader to use with a specified file extension when used with a JavaScript `import`. `text` inlines the content as a string; `binary` inlines the content as a Uint8Array; `file` emits the file and provides the runtime location of the file; `empty` considers the content to be empty and not include it in bundles.",
"type": "object",
"patternProperties": {
"^\\.\\S+$": { "enum": ["text", "binary", "file", "empty"] }
}
},
"define": {
"description": "Defines global identifiers that will be replaced with a specified constant value when found in any JavaScript or TypeScript code including libraries. The value will be used directly. String values must be put in quotes. Identifiers within Angular metadata such as Component Decorators will not be replaced. _Note: this is only supported in Angular versions >= 17.2.0_.",
"description": "Defines global identifiers that will be replaced with a specified constant value when found in any JavaScript or TypeScript code including libraries. The value will be used directly. String values must be put in quotes. Identifiers within Angular metadata such as Component Decorators will not be replaced.",
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"conditions": {
"description": "Custom package resolution conditions used to resolve conditional exports/imports. Defaults to ['module', 'development'/'production']. The following special conditions are always present if the requirements are satisfied: 'default', 'import', 'require', 'browser', 'node'. _Note: this is only supported in Angular versions >= 20.0.0_.",
"type": "array",
"items": {
"type": "string"
}
},
"fileReplacements": {
"description": "Replace compilation source files with other compilation source files in the build.",
"type": "array",
@ -301,7 +307,7 @@
"default": []
},
"outputPath": {
"description": "Specify the output path relative to workspace root. _Note: the object notation is only supported in Angular versions >= 17.1.0_.",
"description": "Specify the output path relative to workspace root.",
"oneOf": [
{
"type": "object",
@ -368,6 +374,11 @@
"type": "boolean",
"description": "Resolve vendor packages source maps.",
"default": false
},
"sourcesContent": {
"type": "boolean",
"description": "Output original source content for files within the source map. _Note: this is only supported in Angular versions >= 20.0.0_.",
"default": true
}
},
"additionalProperties": false
@ -500,7 +511,7 @@
"preloadInitial": {
"type": "boolean",
"default": true,
"description": "Generates 'preload', 'modulepreload', and 'preconnect' link elements for initial application files and resources. _Note: this is only supported in Angular versions >= 17.1.0_."
"description": "Generates 'preload', 'modulepreload', and 'preconnect' link elements for initial application files and resources."
}
},
"required": ["input"]
@ -636,12 +647,12 @@
}
},
"indexHtmlTransformer": {
"description": "Path to a file exposing a default function to transform the `index.html` file. _Note: this is only supported in Angular versions >= 17.1.0_.",
"description": "Path to a file exposing a default function to transform the `index.html` file.",
"type": "string"
}
},
"additionalProperties": false,
"required": ["outputPath", "index", "browser", "tsConfig"],
"required": ["outputPath", "tsConfig"],
"definitions": {
"assetPattern": {
"oneOf": [

View File

@ -35,5 +35,15 @@ export function normalizeOptions(
prerender ??= false;
}
return { ...options, appShell, prerender, security };
let sourceMap = options.sourceMap;
if (
sourceMap &&
typeof sourceMap === 'object' &&
sourceMap.sourcesContent !== undefined &&
angularMajorVersion < 20
) {
delete sourceMap.sourcesContent;
}
return { ...options, appShell, prerender, security, sourceMap };
}

View File

@ -5,73 +5,6 @@ import type { ApplicationExecutorOptions } from '../schema';
export function validateOptions(options: ApplicationExecutorOptions): void {
const { version: angularVersion } = getInstalledAngularVersionInfo();
if (lt(angularVersion, '17.1.0')) {
if (options.loader) {
throw new Error(
`The "loader" option requires Angular version 17.1.0 or greater. You are currently using version ${angularVersion}.`
);
}
if (options.indexHtmlTransformer) {
throw new Error(
`The "indexHtmlTransformer" option requires Angular version 17.1.0 or greater. You are currently using version ${angularVersion}.`
);
}
if (
typeof options.index === 'object' &&
options.index.preloadInitial !== undefined
) {
throw new Error(
`The "index.preloadInitial" option requires Angular version 17.1.0 or greater. You are currently using version ${angularVersion}.`
);
}
if (
options.optimization &&
typeof options.optimization !== 'boolean' &&
options.optimization.styles &&
typeof options.optimization.styles !== 'boolean'
) {
if (options.optimization.styles.removeSpecialComments === false) {
throw new Error(
`The "optimization.styles.removeSpecialComments" option requires Angular version 17.1.0 or greater. You are currently using version ${angularVersion}.`
);
} else if (options.optimization.styles.removeSpecialComments === true) {
// silently remove the option, as it was the default before 17.1.0
delete options.optimization.styles.removeSpecialComments;
}
}
if (typeof options.outputPath === 'object') {
throw new Error(
`The "outputPath" option as an object requires Angular version 17.1.0 or greater. You are currently using version ${angularVersion}.`
);
}
}
if (lt(angularVersion, '17.2.0')) {
if (options.define) {
throw new Error(
`The "define" option requires Angular version 17.2.0 or greater. You are currently using version ${angularVersion}.`
);
}
if (options.clearScreen !== undefined) {
throw new Error(
`The "clearScreen" option requires Angular version 17.2.0 or greater. You are currently using version ${angularVersion}.`
);
}
}
if (lt(angularVersion, '17.3.0')) {
if (options.deployUrl) {
throw new Error(
`The "deployUrl" option requires Angular version 17.3.0 or greater. You are currently using version ${angularVersion}.`
);
}
}
if (lt(angularVersion, '19.0.0')) {
if (options.outputMode) {
throw new Error(
@ -103,4 +36,22 @@ export function validateOptions(options: ApplicationExecutorOptions): void {
);
}
}
if (lt(angularVersion, '20.0.0')) {
if (
options.sourceMap &&
typeof options.sourceMap === 'object' &&
options.sourceMap.sourcesContent === false
) {
throw new Error(
`The "sourceMap.sourcesContent" option requires Angular version 20.0.0 or greater. You are currently using version ${angularVersion}.`
);
}
if (options.conditions) {
throw new Error(
`The "conditions" option requires Angular version 20.0.0 or greater. You are currently using version ${angularVersion}.`
);
}
}
}

View File

@ -1,8 +1,7 @@
import type { ExtractI18nBuilderOptions } from '@angular-devkit/build-angular';
import { parseTargetString, type ExecutorContext } from '@nx/devkit';
import { createBuilderContext } from 'nx/src/adapter/ngcli-adapter';
import { readCachedProjectConfiguration } from 'nx/src/project-graph/project-graph';
import { getInstalledAngularVersionInfo } from '../utilities/angular-version-utils';
import { assertBuilderPackageIsInstalled } from '../utilities/builder-package';
import { patchBuilderContext } from '../utilities/patch-builder-context';
import type { ExtractI18nExecutorOptions } from './schema';
@ -11,14 +10,15 @@ export default async function* extractI18nExecutor(
context: ExecutorContext
) {
const parsedBuildTarget = parseTargetString(options.buildTarget, context);
const browserTargetProjectConfiguration = readCachedProjectConfiguration(
const buildTargetProjectConfiguration = readCachedProjectConfiguration(
parsedBuildTarget.project
);
const buildTarget =
browserTargetProjectConfiguration.targets[parsedBuildTarget.target];
buildTargetProjectConfiguration.targets[parsedBuildTarget.target];
const isUsingEsbuildBuilder = [
'@angular/build:application',
'@angular-devkit/build-angular:application',
'@angular-devkit/build-angular:browser-esbuild',
'@nx/angular:application',
@ -42,29 +42,10 @@ export default async function* extractI18nExecutor(
*/
patchBuilderContext(builderContext, isUsingEsbuildBuilder, parsedBuildTarget);
assertBuilderPackageIsInstalled('@angular-devkit/build-angular');
const { executeExtractI18nBuilder } = await import(
'@angular-devkit/build-angular'
);
const delegateBuilderOptions = getDelegateBuilderOptions(options);
return await executeExtractI18nBuilder(
delegateBuilderOptions,
builderContext
);
}
function getDelegateBuilderOptions(
options: ExtractI18nExecutorOptions
): ExtractI18nBuilderOptions {
const delegateBuilderOptions: ExtractI18nBuilderOptions & {
browserTarget?: string;
} = { ...options };
const { major: angularMajorVersion } = getInstalledAngularVersionInfo();
if (angularMajorVersion <= 17) {
delegateBuilderOptions.browserTarget = delegateBuilderOptions.buildTarget;
delete delegateBuilderOptions.buildTarget;
}
return delegateBuilderOptions;
return await executeExtractI18nBuilder(options, builderContext);
}

View File

@ -1,8 +1,3 @@
import type { ExtractI18nBuilderOptions } from '@angular-devkit/build-angular';
export type ExtractI18nExecutorOptions = Omit<
ExtractI18nBuilderOptions,
'browserTarget'
> & {
buildTarget: string;
};
export type ExtractI18nExecutorOptions = ExtractI18nBuilderOptions;

View File

@ -38,6 +38,11 @@
"outFile": {
"type": "string",
"description": "Name of the file to output."
},
"i18nDuplicateTranslation": {
"type": "string",
"description": "How to handle duplicate translations. _Note: this is only available in Angular 20.0.0 and above._",
"enum": ["error", "warning", "ignore"]
}
},
"additionalProperties": false,

View File

@ -0,0 +1,15 @@
import { lt } from 'semver';
import { getInstalledAngularVersionInfo } from '../../utilities/angular-version-utils';
import type { ExtractI18nExecutorOptions } from '../schema';
export function validateOptions(options: ExtractI18nExecutorOptions): void {
const { version: angularVersion } = getInstalledAngularVersionInfo();
if (lt(angularVersion, '20.0.0')) {
if (options.i18nDuplicateTranslation) {
throw new Error(
`The "i18nDuplicateTranslation" option requires Angular version 20.0.0 or greater. You are currently using version ${angularVersion}.`
);
}
}
}

View File

@ -1,25 +1,14 @@
import type {
NormalizedSchema,
Schema,
SchemaWithBrowserTarget,
SchemaWithBuildTarget,
} from '../schema';
import type { NormalizedSchema, Schema } from '../schema';
import { join } from 'path';
import { workspaceRoot } from '@nx/devkit';
export function normalizeOptions(schema: Schema): NormalizedSchema {
let buildTarget = (schema as SchemaWithBuildTarget).buildTarget;
if ((schema as SchemaWithBrowserTarget).browserTarget) {
buildTarget ??= (schema as SchemaWithBrowserTarget).browserTarget;
delete (schema as SchemaWithBrowserTarget).browserTarget;
}
schema.buildLibsFromSource ??= true;
process.env.NX_BUILD_LIBS_FROM_SOURCE = `${schema.buildLibsFromSource}`;
process.env.NX_BUILD_TARGET = `${buildTarget}`;
process.env.NX_BUILD_TARGET = `${schema.buildTarget}`;
return {
...schema,
buildTarget,
devRemotes: schema.devRemotes ?? [],
host: schema.host ?? 'localhost',
port: schema.port ?? 4200,

View File

@ -1,6 +1,7 @@
import type { DevRemoteDefinition } from '../../builders/utilities/module-federation';
interface BaseSchema {
interface Schema {
buildTarget: string;
port?: number;
host?: string;
proxyConfig?: string;
@ -28,20 +29,7 @@ interface BaseSchema {
buildLibsFromSource?: boolean;
}
export type SchemaWithBrowserTarget = BaseSchema & {
/**
* @deprecated Use `buildTarget` instead. It will be removed when Angular v20 is released.
*/
browserTarget: string;
};
export type SchemaWithBuildTarget = BaseSchema & {
buildTarget: string;
};
export type Schema = SchemaWithBrowserTarget | SchemaWithBuildTarget;
export type NormalizedSchema = SchemaWithBuildTarget & {
export type NormalizedSchema = Schema & {
devRemotes: DevRemoteDefinition[];
liveReload: boolean;
open: boolean;

View File

@ -12,12 +12,6 @@
}
],
"properties": {
"browserTarget": {
"type": "string",
"description": "A browser builder target to serve in the format of `project:target[:configuration]`.",
"pattern": "^[^:\\s]+:[^:\\s]+(:[^\\s]+)?$",
"x-deprecated": "Use 'buildTarget' instead. It will be removed when Angular v20 is released."
},
"buildTarget": {
"type": "string",
"description": "A build builder target to serve in the format of `project:target[:configuration]`.",
@ -166,18 +160,11 @@
},
"buildLibsFromSource": {
"type": "boolean",
"description": "Read buildable libraries from source instead of building them separately. If not set, it will take the value specified in the `browserTarget` options, or it will default to `true` if it's also not set in the `browserTarget` options.",
"description": "Read buildable libraries from source instead of building them separately. If not set, it will take the value specified in the `buildTarget` options, or it will default to `true` if it's also not set in the `buildTarget` options.",
"x-priority": "important"
}
},
"additionalProperties": false,
"anyOf": [
{
"required": ["buildTarget"]
},
{
"required": ["browserTarget"]
}
],
"required": ["buildTarget"],
"examplesFile": "../../../docs/module-federation-dev-server-examples.md"
}

View File

@ -1,24 +1,24 @@
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';
import { startRemoteIterators } from '@nx/module-federation/src/executors/utils';
import { startRemotes } from './lib/start-dev-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 { startRemoteIterators } from '@nx/module-federation/src/executors/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 { assertBuilderPackageIsInstalled } from '../utilities/builder-package';
import { normalizeOptions } from './lib/normalize-options';
import { startRemotes } from './lib/start-dev-remotes';
import type { Schema } from './schema';
export async function* moduleFederationSsrDevServerExecutor(
schema: Schema,
@ -26,6 +26,11 @@ export async function* moduleFederationSsrDevServerExecutor(
) {
const options = normalizeOptions(schema);
assertBuilderPackageIsInstalled('@angular-devkit/build-angular');
const { executeSSRDevServerBuilder } = await import(
'@angular-devkit/build-angular'
);
const currIter = eachValueFrom(
executeSSRDevServerBuilder(
options,

View File

@ -1,24 +1,32 @@
import type {
DestinationFiles,
NgEntryPoint as NgEntryPointBase,
} from 'ng-packagr/lib/ng-package/entry-point/entry-point';
import type { NgPackageConfig } from 'ng-packagr/ng-package.schema';
import { dirname } from 'node:path';
} from 'ng-packagr/src/lib/ng-package/entry-point/entry-point';
import type { NgPackageConfig } from 'ng-packagr/src/ng-package.schema';
import { dirname, join, relative } from 'node:path';
import { getNgPackagrVersionInfo } from '../../../../utilities/ng-packagr/ng-packagr-version';
import { importNgPackagrPath } from '../../../../utilities/ng-packagr/package-imports';
export type NgEntryPointType = NgEntryPointBase & {
primaryDestinationPath?: string;
};
export function createNgEntryPoint(
packageJson: Record<string, any>,
ngPackageJson: NgPackageConfig,
basePath: string,
secondaryData?: Record<string, any>
): NgEntryPointBase {
): NgEntryPointType {
const { major: ngPackagrMajorVersion } = getNgPackagrVersionInfo();
const { NgEntryPoint: NgEntryPointBase } = importNgPackagrPath<
typeof import('ng-packagr/lib/ng-package/entry-point/entry-point')
>('ng-packagr/lib/ng-package/entry-point/entry-point', ngPackagrMajorVersion);
typeof import('ng-packagr/src/lib/ng-package/entry-point/entry-point')
>(
'ng-packagr/src/lib/ng-package/entry-point/entry-point',
ngPackagrMajorVersion
);
if (ngPackagrMajorVersion < 20) {
class NgEntryPoint extends NgEntryPointBase {
/**
* Point the FESM2022 files to the ESM2022 files.
@ -32,5 +40,80 @@ export function createNgEntryPoint(
}
}
return new NgEntryPoint(
packageJson,
ngPackageJson,
basePath,
secondaryData
);
}
const { ensureUnixPath } = importNgPackagrPath<
typeof import('ng-packagr/src/lib/utils/path')
>('ng-packagr/src/lib/utils/path', ngPackagrMajorVersion);
class NgEntryPoint extends NgEntryPointBase {
constructor(
public readonly packageJson: Record<string, any>,
public readonly ngPackageJson: NgPackageConfig,
public readonly basePath: string,
private readonly _secondaryData?: Record<string, any>
) {
super(packageJson, ngPackageJson, basePath, _secondaryData);
}
public get primaryDestinationPath(): string {
return (
this._secondaryData?.primaryDestinationPath ?? this.destinationPath
);
}
public get destinationFiles(): DestinationFiles {
let primaryDestPath = this.destinationPath;
let secondaryDir = '';
if (this._secondaryData) {
primaryDestPath = this._secondaryData.primaryDestinationPath;
secondaryDir = relative(
primaryDestPath,
this._secondaryData.destinationPath
);
}
const flatModuleFile = this.flatModuleFile;
const pathJoinWithDest = (...paths: string[]) =>
join(primaryDestPath, ...paths);
return {
directory: ensureUnixPath(secondaryDir),
declarations: pathJoinWithDest(
'tmp-typings',
secondaryDir,
`${flatModuleFile}.d.ts`
),
// changed to use esm2022
declarationsBundled: pathJoinWithDest(
'esm2022',
secondaryDir,
`${flatModuleFile}.d.ts`
),
declarationsDir: pathJoinWithDest(secondaryDir),
esm2022: pathJoinWithDest(
'tmp-esm2022',
secondaryDir,
`${flatModuleFile}.js`
),
// changed to use esm2022
fesm2022: pathJoinWithDest(
'esm2022',
secondaryDir,
`${flatModuleFile}.js`
),
// changed to use esm2022
fesm2022Dir: pathJoinWithDest('esm2022'),
};
}
}
return new NgEntryPoint(packageJson, ngPackageJson, basePath, secondaryData);
}

View File

@ -1,4 +1,4 @@
import type { TransformProvider } from 'ng-packagr/lib/graph/transform.di';
import type { TransformProvider } from 'ng-packagr/src/lib/graph/transform.di';
import { getNgPackagrVersionInfo } from '../../../../utilities/ng-packagr/ng-packagr-version';
import { importNgPackagrPath } from '../../../../utilities/ng-packagr/package-imports';
import { writeBundlesTransform } from './write-bundles.transform';
@ -7,17 +7,17 @@ export function getWriteBundlesTransformProvider(): TransformProvider {
const { major: ngPackagrMajorVersion } = getNgPackagrVersionInfo();
const { provideTransform } = importNgPackagrPath<
typeof import('ng-packagr/lib/graph/transform.di')
>('ng-packagr/lib/graph/transform.di', ngPackagrMajorVersion);
typeof import('ng-packagr/src/lib/graph/transform.di')
>('ng-packagr/src/lib/graph/transform.di', ngPackagrMajorVersion);
const { WRITE_BUNDLES_TRANSFORM_TOKEN } = importNgPackagrPath<
typeof import('ng-packagr/lib/ng-package/entry-point/write-bundles.di')
typeof import('ng-packagr/src/lib/ng-package/entry-point/write-bundles.di')
>(
'ng-packagr/lib/ng-package/entry-point/write-bundles.di',
'ng-packagr/src/lib/ng-package/entry-point/write-bundles.di',
ngPackagrMajorVersion
);
const { OPTIONS_TOKEN } = importNgPackagrPath<
typeof import('ng-packagr/lib/ng-package/options.di')
>('ng-packagr/lib/ng-package/options.di', ngPackagrMajorVersion);
typeof import('ng-packagr/src/lib/ng-package/options.di')
>('ng-packagr/src/lib/ng-package/options.di', ngPackagrMajorVersion);
return provideTransform({
provide: WRITE_BUNDLES_TRANSFORM_TOKEN,

View File

@ -7,29 +7,29 @@
* - Fake the FESM2022 outputs pointing them to the ESM2022 outputs.
*/
import type { NgEntryPoint } from 'ng-packagr/lib/ng-package/entry-point/entry-point';
import type { NgPackagrOptions } from 'ng-packagr/lib/ng-package/options.di';
import type { NgEntryPoint } from 'ng-packagr/src/lib/ng-package/entry-point/entry-point';
import type { NgPackagrOptions } from 'ng-packagr/src/lib/ng-package/options.di';
import { mkdir, writeFile } from 'node:fs/promises';
import { dirname } from 'node:path';
import { dirname, join } from 'node:path';
import { getNgPackagrVersionInfo } from '../../../../utilities/ng-packagr/ng-packagr-version';
import { importNgPackagrPath } from '../../../../utilities/ng-packagr/package-imports';
import { createNgEntryPoint } from './entry-point';
import { createNgEntryPoint, type NgEntryPointType } from './entry-point';
export const writeBundlesTransform = (_options: NgPackagrOptions) => {
const { major: ngPackagrMajorVersion } = getNgPackagrVersionInfo();
const { BuildGraph } = importNgPackagrPath<
typeof import('ng-packagr/lib/graph/build-graph')
>('ng-packagr/lib/graph/build-graph', ngPackagrMajorVersion);
typeof import('ng-packagr/src/lib/graph/build-graph')
>('ng-packagr/src/lib/graph/build-graph', ngPackagrMajorVersion);
const { transformFromPromise } = importNgPackagrPath<
typeof import('ng-packagr/lib/graph/transform')
>('ng-packagr/lib/graph/transform', ngPackagrMajorVersion);
typeof import('ng-packagr/src/lib/graph/transform')
>('ng-packagr/src/lib/graph/transform', ngPackagrMajorVersion);
const { isEntryPoint, isPackage } = importNgPackagrPath<
typeof import('ng-packagr/lib/ng-package/nodes')
>('ng-packagr/lib/ng-package/nodes', ngPackagrMajorVersion);
typeof import('ng-packagr/src/lib/ng-package/nodes')
>('ng-packagr/src/lib/ng-package/nodes', ngPackagrMajorVersion);
const { NgPackage } = importNgPackagrPath<
typeof import('ng-packagr/lib/ng-package/package')
>('ng-packagr/lib/ng-package/package', ngPackagrMajorVersion);
typeof import('ng-packagr/src/lib/ng-package/package')
>('ng-packagr/src/lib/ng-package/package', ngPackagrMajorVersion);
return transformFromPromise(async (graph) => {
const updatedGraph = new BuildGraph();
@ -41,9 +41,13 @@ export const writeBundlesTransform = (_options: NgPackagrOptions) => {
entry.data.destinationFiles = entryPoint.destinationFiles;
for (const [path, outputCache] of entry.cache.outputCache.entries()) {
const normalizedPath = normalizeEsm2022Path(path, entryPoint);
// write the outputs to the file system
await mkdir(dirname(path), { recursive: true });
await writeFile(path, outputCache.content);
await mkdir(dirname(normalizedPath), { recursive: true });
await writeFile(normalizedPath, outputCache.content);
}
if (!entry.cache.outputCache.size && entryPoint.isSecondaryEntryPoint) {
await mkdir(entryPoint.destinationPath, { recursive: true });
}
} else if (isPackage(entry)) {
entry.data = new NgPackage(
@ -61,7 +65,26 @@ export const writeBundlesTransform = (_options: NgPackagrOptions) => {
});
};
function toCustomNgEntryPoint(entryPoint: NgEntryPoint): NgEntryPoint {
function normalizeEsm2022Path(
path: string,
entryPoint: NgEntryPointType
): string {
if (!entryPoint.primaryDestinationPath) {
return path;
}
if (path.startsWith(join(entryPoint.primaryDestinationPath, 'tmp-esm2022'))) {
return path.replace('tmp-esm2022', 'esm2022');
}
if (path.startsWith(join(entryPoint.primaryDestinationPath, 'tmp-typings'))) {
return path.replace('tmp-typings', 'esm2022');
}
return path;
}
function toCustomNgEntryPoint(entryPoint: NgEntryPoint): NgEntryPointType {
return createNgEntryPoint(
entryPoint.packageJson,
entryPoint.ngPackageJson,

View File

@ -31,9 +31,8 @@
},
"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_."
"description": "Enable and define the file watching poll time period in milliseconds."
}
},
"additionalProperties": false,
"required": ["project"]
"additionalProperties": false
}

View File

@ -9,7 +9,6 @@ import type { NgPackagr } from 'ng-packagr';
import { join, resolve } from 'path';
import { from } from 'rxjs';
import { mapTo, switchMap } from 'rxjs/operators';
import { getInstalledAngularVersionInfo } from '../utilities/angular-version-utils';
import { parseRemappedTsConfigAndMergeDefaults } from '../utilities/typescript';
import { getNgPackagrInstance } from './ng-packagr-adjustments/ng-packagr';
import type { BuildAngularLibraryExecutorOptions } from './schema';
@ -56,13 +55,10 @@ export function createLibraryExecutor(
options: BuildAngularLibraryExecutorOptions,
context: ExecutorContext
) {
const { major: angularMajorVersion, version: angularVersion } =
getInstalledAngularVersionInfo();
if (angularMajorVersion < 18 && options.poll !== undefined) {
throw new Error(
`The "poll" option requires Angular version 18.0.0 or greater. You are currently using version ${angularVersion}.`
options.project ??= join(
context.projectsConfigurations.projects[context.projectName].root,
'ng-package.json'
);
}
const { dependencies } = calculateProjectBuildableDependencies(
context.taskGraph,

View File

@ -1,4 +1,6 @@
import type { NgPackagrBuilderOptions } from '@angular-devkit/build-angular';
export interface BuildAngularLibraryExecutorOptions
extends NgPackagrBuilderOptions {}
extends NgPackagrBuilderOptions {
project?: string;
}

View File

@ -31,9 +31,8 @@
},
"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_."
"description": "Enable and define the file watching poll time period in milliseconds."
}
},
"additionalProperties": false,
"required": ["project"]
"additionalProperties": false
}

View File

@ -0,0 +1,9 @@
export function assertBuilderPackageIsInstalled(packageName: string): void {
try {
require.resolve(packageName);
} catch {
throw new Error(
`This executor requires the package ${packageName} to be installed. Please make sure it is installed and try again.`
);
}
}

View File

@ -1,12 +1,11 @@
import type { buildApplication } from '@angular/build';
import { registerTsProject } from '@nx/js/src/internal';
import { loadModule } from './module-loader';
// This is a workaround to make sure we use the same esbuild version as the
// Angular DevKit uses. This is only used internally to load the plugins and
// forward them to the Angular DevKit builders.
type Plugin = Parameters<
typeof import('@angular-devkit/build-angular').buildApplication
>[2]['codePlugins'][number];
type Plugin = Parameters<typeof buildApplication>[2]['codePlugins'][number];
export type PluginSpec = {
path: string;

View File

@ -1,11 +1,13 @@
type NgPackagrImportPath = `ng-packagr/src/${string}`;
export function importNgPackagrPath<T>(
path: string,
path: NgPackagrImportPath,
ngPackagrMajorVersion: number
): T {
let finalPath = path;
let finalPath: string = path;
if (ngPackagrMajorVersion >= 20 && !path.startsWith('ng-packagr/src/')) {
finalPath = path.replace(/^ng-packagr\//, 'ng-packagr/src/');
if (ngPackagrMajorVersion < 20 && path.startsWith('ng-packagr/src/')) {
finalPath = path.replace(/^ng-packagr\/src\//, 'ng-packagr/');
}
return require(finalPath);

View File

@ -2,21 +2,15 @@
* Adapted from the original ng-packagr source.
*
* Changes made:
* - Use our own function to get the TailwindCSS config path to support a
* config at the root of the workspace.
* - Resolve `piscina` from the installed `ng-packagr` package.
* - Additionally search for the TailwindCSS config in the workspace root.
*/
import { workspaceRoot } from '@nx/devkit';
import browserslist from 'browserslist';
import { existsSync } from 'fs';
import { colors } from 'ng-packagr/src/lib/utils/color';
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 { workspaceRoot } from '@nx/devkit';
import type { PostcssConfiguration } from 'ng-packagr/lib/styles/postcss-configuration';
import { gt, gte } from 'semver';
import { getNgPackagrVersionInfo } from '../ng-packagr-version';
const maxWorkersVariable = process.env['NG_BUILD_MAX_WORKERS'];
const maxThreads =
@ -30,7 +24,7 @@ export enum CssUrl {
}
export class StylesheetProcessor {
private renderWorker: typeof Piscina | undefined;
private renderWorker: any | undefined;
constructor(
private readonly projectBasePath: string,
@ -91,33 +85,22 @@ export class StylesheetProcessor {
const browserslistData = browserslist(undefined, { path: this.basePath });
const { version: ngPackagrVersion } = getNgPackagrVersionInfo();
let tailwindConfigPath: string | undefined;
let postcssConfiguration: PostcssConfiguration | undefined;
if (gte(ngPackagrVersion, '18.0.0')) {
const {
findTailwindConfiguration,
generateSearchDirectories,
loadPostcssConfiguration,
} = require('ng-packagr/lib/styles/postcss-configuration');
let searchDirs = generateSearchDirectories([this.projectBasePath]);
postcssConfiguration = loadPostcssConfiguration(searchDirs);
const postcssConfiguration = loadPostcssConfiguration(searchDirs);
// (nx-specific): we support loading the TailwindCSS config from the root of the workspace
searchDirs = generateSearchDirectories([
this.projectBasePath,
workspaceRoot,
]);
tailwindConfigPath = findTailwindConfiguration(searchDirs);
} else if (gt(ngPackagrVersion, '17.2.0')) {
const {
loadPostcssConfiguration,
} = require('ng-packagr/lib/styles/postcss-configuration');
postcssConfiguration = loadPostcssConfiguration(this.projectBasePath);
tailwindConfigPath = getTailwindConfigPath(
this.projectBasePath,
workspaceRoot
);
}
const tailwindConfigPath = findTailwindConfiguration(searchDirs);
const Piscina = getPiscina();
this.renderWorker = new Piscina({
filename: require.resolve(
@ -143,112 +126,6 @@ export class StylesheetProcessor {
}
}
/**
* This class is used when ng-packagr version is 17.2.0. The async `loadPostcssConfiguration` function
* introduced in ng-packagr 17.2.0 causes a memory leak due to multiple workers being created. We must
* keep this class to support any workspace that might be using ng-packagr 17.2.0 where that function
* need to be awaited.
*/
export class AsyncStylesheetProcessor {
private renderWorker: typeof Piscina | undefined;
constructor(
private readonly projectBasePath: string,
private readonly basePath: string,
private readonly cssUrl?: CssUrl,
private readonly includePaths?: string[],
private readonly cacheDirectory?: string | false
) {
// By default, browserslist defaults are too inclusive
// https://github.com/browserslist/browserslist/blob/83764ea81ffaa39111c204b02c371afa44a4ff07/index.js#L516-L522
// We change the default query to browsers that Angular support.
// https://angular.io/guide/browser-support
(browserslist.defaults as string[]) = [
'last 2 Chrome versions',
'last 1 Firefox version',
'last 2 Edge major versions',
'last 2 Safari major versions',
'last 2 iOS major versions',
'Firefox ESR',
];
}
async process({
filePath,
content,
}: {
filePath: string;
content: string;
}): Promise<string> {
await this.createRenderWorker();
return this.renderWorker.run({ content, filePath });
}
/** Destory workers in pool. */
destroy(): void {
void this.renderWorker?.destroy();
}
private async createRenderWorker(): Promise<void> {
if (this.renderWorker) {
return;
}
const styleIncludePaths = [...this.includePaths];
let prevDir = null;
let currentDir = this.basePath;
while (currentDir !== prevDir) {
const p = join(currentDir, 'node_modules');
if (existsSync(p)) {
styleIncludePaths.push(p);
}
prevDir = currentDir;
currentDir = dirname(prevDir);
}
const browserslistData = browserslist(undefined, { path: this.basePath });
const { version: ngPackagrVersion } = getNgPackagrVersionInfo();
let postcssConfiguration: PostcssConfiguration | undefined;
if (ngPackagrVersion === '17.2.0') {
const {
loadPostcssConfiguration,
} = require('ng-packagr/lib/styles/postcss-configuration');
postcssConfiguration = await loadPostcssConfiguration(
this.projectBasePath
);
}
this.renderWorker = new Piscina({
filename: require.resolve(
'ng-packagr/lib/styles/stylesheet-processor-worker'
),
maxThreads,
recordTiming: false,
env: {
...process.env,
FORCE_COLOR: '' + colors.enabled,
},
workerData: {
postcssConfiguration,
tailwindConfigPath: getTailwindConfigPath(
this.projectBasePath,
workspaceRoot
),
projectBasePath: this.projectBasePath,
browserslistData,
targets: transformSupportedBrowsersToTargets(browserslistData),
cacheDirectory: this.cacheDirectory,
cssUrl: this.cssUrl,
styleIncludePaths,
},
});
}
}
function transformSupportedBrowsersToTargets(
supportedBrowsers: string[]
): string[] {
@ -288,3 +165,34 @@ function transformSupportedBrowsersToTargets(
return transformed.length ? transformed : undefined;
}
/**
* Loads the `piscina` package from the installed `ng-packagr` package.
*/
function getPiscina() {
const ngPackagrPath = getInstalledNgPackagrPath();
try {
// Resolve the main piscina module entry point
const piscinaModulePath = require.resolve('piscina', {
paths: [ngPackagrPath],
});
return require(piscinaModulePath);
} catch (error) {
throw new Error(
`Failed to load the \`piscina\` package from \`ng-packagr\` dependencies: ${error.message}`
);
}
}
function getInstalledNgPackagrPath(): string {
try {
const ngPackagrPackageJsonPath = require.resolve('ng-packagr/package.json');
return dirname(ngPackagrPackageJsonPath);
} catch (e) {
throw new Error(
'The `ng-packagr` package is not installed. The package is required to use this executor. Please install it in your workspace.'
);
}
}

View File

@ -3,12 +3,11 @@ import { getNgPackagrVersionInfo } from './ng-packagr-version';
import { importNgPackagrPath } from './package-imports';
export function getStylesheetProcessorFactoryProvider(): FactoryProvider {
const { major: ngPackagrMajorVersion, version: ngPackagrVersion } =
getNgPackagrVersionInfo();
const { major: ngPackagrMajorVersion } = getNgPackagrVersionInfo();
const { STYLESHEET_PROCESSOR_TOKEN } = importNgPackagrPath<
typeof import('ng-packagr/lib/styles/stylesheet-processor.di')
>('ng-packagr/lib/styles/stylesheet-processor.di', ngPackagrMajorVersion);
typeof import('ng-packagr/src/lib/styles/stylesheet-processor.di')
>('ng-packagr/src/lib/styles/stylesheet-processor.di', ngPackagrMajorVersion);
return {
provide: STYLESHEET_PROCESSOR_TOKEN,
@ -20,17 +19,8 @@ export function getStylesheetProcessorFactoryProvider(): FactoryProvider {
return getStylesheetProcessor();
}
if (ngPackagrVersion !== '17.2.0') {
const {
StylesheetProcessor,
} = require('./pre-v19/stylesheet-processor');
const { StylesheetProcessor } = require('./pre-v19/stylesheet-processor');
return StylesheetProcessor;
}
const {
AsyncStylesheetProcessor,
} = require('./pre-v19/stylesheet-processor');
return AsyncStylesheetProcessor;
},
deps: [],
};

View File

@ -7,7 +7,7 @@
import { workspaceRoot } from '@nx/devkit';
import browserslist from 'browserslist';
import type { NgPackageEntryConfig } from 'ng-packagr/ng-entrypoint.schema';
import type { NgPackageEntryConfig } from 'ng-packagr/src/ng-entrypoint.schema';
import { getNgPackagrVersionInfo } from '../ng-packagr-version';
import { importNgPackagrPath } from '../package-imports';
@ -30,15 +30,15 @@ export function getStylesheetProcessor(): new (
const { major: ngPackagrMajorVersion } = getNgPackagrVersionInfo();
const { ComponentStylesheetBundler } = importNgPackagrPath<
typeof import('ng-packagr/lib/styles/component-stylesheets')
>('ng-packagr/lib/styles/component-stylesheets', ngPackagrMajorVersion);
typeof import('ng-packagr/src/lib/styles/component-stylesheets')
>('ng-packagr/src/lib/styles/component-stylesheets', ngPackagrMajorVersion);
const {
generateSearchDirectories,
getTailwindConfig,
loadPostcssConfiguration,
} = importNgPackagrPath<
typeof import('ng-packagr/lib/styles/postcss-configuration')
>('ng-packagr/lib/styles/postcss-configuration', ngPackagrMajorVersion);
typeof import('ng-packagr/src/lib/styles/postcss-configuration')
>('ng-packagr/src/lib/styles/postcss-configuration', ngPackagrMajorVersion);
class StylesheetProcessor extends ComponentStylesheetBundler {
constructor(
@ -53,7 +53,12 @@ export function getStylesheetProcessor(): new (
// 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
// https://angular.dev/reference/versions#browser-support
if (ngPackagrMajorVersion >= 20) {
(browserslist.defaults as string[]) = browserslist(undefined, {
path: require.resolve('ng-packagr/.browserslistrc'),
});
} else {
(browserslist.defaults as string[]) = [
'last 2 Chrome versions',
'last 1 Firefox version',
@ -62,6 +67,7 @@ export function getStylesheetProcessor(): new (
'last 2 iOS major versions',
'Firefox ESR',
];
}
const browserslistData = browserslist(undefined, { path: basePath });
let searchDirs = generateSearchDirectories([projectBasePath]);

View File

@ -1,9 +1,7 @@
import type { BuilderContext } from '@angular-devkit/architect';
import type {
ApplicationBuilderOptions,
BrowserBuilderOptions,
} from '@angular-devkit/build-angular';
import type { BrowserBuilderOptions } from '@angular-devkit/build-angular';
import type { Schema as BrowserEsbuildBuilderOptions } from '@angular-devkit/build-angular/src/builders/browser-esbuild/schema';
import type { ApplicationBuilderOptions } from '@angular/build';
import type { Target } from '@nx/devkit';
const executorToBuilderMap = new Map<string, string>([

View File

@ -18,6 +18,7 @@ exports[`addLinting generator should correctly generate the .eslintrc.json file
"*.ts",
],
"rules": {
"@angular-eslint/component-class-suffix": "off",
"@angular-eslint/component-selector": [
"error",
{
@ -26,6 +27,7 @@ exports[`addLinting generator should correctly generate the .eslintrc.json file
"type": "element",
},
],
"@angular-eslint/directive-class-suffix": "off",
"@angular-eslint/directive-selector": [
"error",
{

View File

@ -66,7 +66,7 @@ describe('addLinting generator', () => {
const { devDependencies } = readJson(tree, 'package.json');
expect(devDependencies['@typescript-eslint/utils']).toMatchInlineSnapshot(
`"^8.19.0"`
`"^8.29.0"`
);
delete process.env.ESLINT_USE_FLAT_CONFIG;
});
@ -168,7 +168,9 @@ describe('addLinting generator', () => {
prefix: "my-org",
style: "kebab-case"
}
]
],
"@angular-eslint/component-class-suffix": "off",
"@angular-eslint/directive-class-suffix": "off"
}
},
{
@ -289,7 +291,9 @@ describe('addLinting generator', () => {
"prefix": "my-org",
"style": "kebab-case"
}
]
],
"@angular-eslint/component-class-suffix": "off",
"@angular-eslint/directive-class-suffix": "off"
}
},
{

View File

@ -82,6 +82,10 @@ export async function addLintingGenerator(
style: 'kebab-case',
},
],
// Temporary disable these rules until Angular ESLint recommended
// rules are updated with the new Style Guide
'@angular-eslint/component-class-suffix': 'off',
'@angular-eslint/directive-class-suffix': 'off',
},
});
addOverrideToLintConfig(tree, options.projectRoot, {
@ -121,6 +125,10 @@ export async function addLintingGenerator(
style: 'kebab-case',
},
],
// Temporary disable these rules until Angular ESLint recommended
// rules are updated with the new Style Guide
'@angular-eslint/component-class-suffix': 'off',
'@angular-eslint/directive-class-suffix': 'off',
},
},
{

View File

@ -27,7 +27,6 @@ exports[`app --minimal should generate a correct setup when --bundler=rspack and
"styles": [
"./src/styles.css"
],
"scripts": [],
"devServer": {},
"ssr": {
"entry": "./src/server.ts"
@ -188,7 +187,6 @@ exports[`app --minimal should generate a correct setup when --bundler=rspack inc
"styles": [
"./src/styles.css"
],
"scripts": [],
"devServer": {}
}
@ -228,54 +226,54 @@ exports[`app --minimal should generate a correct setup when --bundler=rspack inc
}});"
`;
exports[`app --minimal should skip "nx-welcome.component.ts" file and references for non-standalone apps with routing 1`] = `
"import { NgModule } from '@angular/core';
exports[`app --minimal should skip "nx-welcome.ts" file and references for non-standalone apps with routing 1`] = `
"import { NgModule, provideBrowserGlobalErrorListeners } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouterModule } from '@angular/router';
import { AppComponent } from './app.component';
import { App } from './app';
import { appRoutes } from './app.routes';
@NgModule({
declarations: [AppComponent],
declarations: [App],
imports: [
BrowserModule,
RouterModule.forRoot(appRoutes),
],
providers: [],
bootstrap: [AppComponent],
providers: [provideBrowserGlobalErrorListeners()],
bootstrap: [App],
})
export class AppModule {}
"
`;
exports[`app --minimal should skip "nx-welcome.component.ts" file and references for non-standalone apps with routing 2`] = `
exports[`app --minimal should skip "nx-welcome.ts" file and references for non-standalone apps with routing 2`] = `
"import { Component } from '@angular/core';
@Component({
selector: 'app-root',
standalone: false,
templateUrl: './app.component.html',
styleUrl: './app.component.css',
templateUrl: './app.html',
styleUrl: './app.css',
})
export class AppComponent {}
export class App {}
"
`;
exports[`app --minimal should skip "nx-welcome.component.ts" file and references for non-standalone apps with routing 3`] = `
exports[`app --minimal should skip "nx-welcome.ts" file and references for non-standalone apps with routing 3`] = `
"import { TestBed } from '@angular/core/testing';
import { AppComponent } from './app.component';
import { App } from './app';
import { RouterModule } from '@angular/router';
describe('AppComponent', () => {
describe('App', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [RouterModule.forRoot([])],
declarations: [AppComponent]
declarations: [App]
}).compileComponents();
});
it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
const compiled = fixture.nativeElement as HTMLElement;
expect(compiled.querySelector('h1')?.textContent).toContain(
@ -286,56 +284,56 @@ describe('AppComponent', () => {
"
`;
exports[`app --minimal should skip "nx-welcome.component.ts" file and references for non-standalone apps with routing 4`] = `
exports[`app --minimal should skip "nx-welcome.ts" file and references for non-standalone apps with routing 4`] = `
"<h1>Welcome plain</h1>
<router-outlet></router-outlet>
"
`;
exports[`app --minimal should skip "nx-welcome.component.ts" file and references for non-standalone apps without routing 1`] = `
"import { NgModule } from '@angular/core';
exports[`app --minimal should skip "nx-welcome.ts" file and references for non-standalone apps without routing 1`] = `
"import { NgModule, provideBrowserGlobalErrorListeners } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { App } from './app';
@NgModule({
declarations: [AppComponent],
declarations: [App],
imports: [
BrowserModule,
],
providers: [],
bootstrap: [AppComponent],
providers: [provideBrowserGlobalErrorListeners()],
bootstrap: [App],
})
export class AppModule {}
"
`;
exports[`app --minimal should skip "nx-welcome.component.ts" file and references for non-standalone apps without routing 2`] = `
exports[`app --minimal should skip "nx-welcome.ts" file and references for non-standalone apps without routing 2`] = `
"import { Component } from '@angular/core';
@Component({
selector: 'app-root',
standalone: false,
templateUrl: './app.component.html',
styleUrl: './app.component.css',
templateUrl: './app.html',
styleUrl: './app.css',
})
export class AppComponent {}
export class App {}
"
`;
exports[`app --minimal should skip "nx-welcome.component.ts" file and references for non-standalone apps without routing 3`] = `
exports[`app --minimal should skip "nx-welcome.ts" file and references for non-standalone apps without routing 3`] = `
"import { TestBed } from '@angular/core/testing';
import { AppComponent } from './app.component';
import { App } from './app';
describe('AppComponent', () => {
describe('App', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [],
declarations: [AppComponent]
declarations: [App]
}).compileComponents();
});
it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
const compiled = fixture.nativeElement as HTMLElement;
expect(compiled.querySelector('h1')?.textContent).toContain(
@ -346,39 +344,39 @@ describe('AppComponent', () => {
"
`;
exports[`app --minimal should skip "nx-welcome.component.ts" file and references for non-standalone apps without routing 4`] = `
exports[`app --minimal should skip "nx-welcome.ts" file and references for non-standalone apps without routing 4`] = `
"<h1>Welcome plain</h1>
"
`;
exports[`app --minimal should skip "nx-welcome.component.ts" file and references for standalone apps with routing 1`] = `
exports[`app --minimal should skip "nx-welcome.ts" file and references for standalone apps with routing 1`] = `
"import { Component } from '@angular/core';
import { RouterModule } from '@angular/router';
@Component({
imports: [RouterModule],
selector: 'app-root',
templateUrl: './app.component.html',
styleUrl: './app.component.css',
templateUrl: './app.html',
styleUrl: './app.css',
})
export class AppComponent {}
export class App {}
"
`;
exports[`app --minimal should skip "nx-welcome.component.ts" file and references for standalone apps with routing 2`] = `
exports[`app --minimal should skip "nx-welcome.ts" file and references for standalone apps with routing 2`] = `
"import { TestBed } from '@angular/core/testing';
import { AppComponent } from './app.component';
import { App } from './app';
import { RouterModule } from '@angular/router';
describe('AppComponent', () => {
describe('App', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [AppComponent, RouterModule.forRoot([])],
imports: [App, RouterModule.forRoot([])],
}).compileComponents();
});
it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
const compiled = fixture.nativeElement as HTMLElement;
expect(compiled.querySelector('h1')?.textContent).toContain(
@ -389,38 +387,38 @@ describe('AppComponent', () => {
"
`;
exports[`app --minimal should skip "nx-welcome.component.ts" file and references for standalone apps with routing 3`] = `
exports[`app --minimal should skip "nx-welcome.ts" file and references for standalone apps with routing 3`] = `
"<h1>Welcome plain</h1>
<router-outlet></router-outlet>
"
`;
exports[`app --minimal should skip "nx-welcome.component.ts" file and references for standalone apps without routing 1`] = `
exports[`app --minimal should skip "nx-welcome.ts" file and references for standalone apps without routing 1`] = `
"import { Component } from '@angular/core';
@Component({
imports: [],
selector: 'app-root',
templateUrl: './app.component.html',
styleUrl: './app.component.css',
templateUrl: './app.html',
styleUrl: './app.css',
})
export class AppComponent {}
export class App {}
"
`;
exports[`app --minimal should skip "nx-welcome.component.ts" file and references for standalone apps without routing 2`] = `
exports[`app --minimal should skip "nx-welcome.ts" file and references for standalone apps without routing 2`] = `
"import { TestBed } from '@angular/core/testing';
import { AppComponent } from './app.component';
import { App } from './app';
describe('AppComponent', () => {
describe('App', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [AppComponent],
imports: [App],
}).compileComponents();
});
it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
const compiled = fixture.nativeElement as HTMLElement;
expect(compiled.querySelector('h1')?.textContent).toContain(
@ -431,7 +429,7 @@ describe('AppComponent', () => {
"
`;
exports[`app --minimal should skip "nx-welcome.component.ts" file and references for standalone apps without routing 3`] = `
exports[`app --minimal should skip "nx-welcome.ts" file and references for standalone apps without routing 3`] = `
"<h1>Welcome plain</h1>
"
`;
@ -439,21 +437,25 @@ exports[`app --minimal should skip "nx-welcome.component.ts" file and references
exports[`app --standalone should generate a standalone app correctly with routing 1`] = `
"import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import { AppComponent } from './app/app.component';
import { App } from './app/app';
bootstrapApplication(AppComponent, appConfig).catch((err) =>
bootstrapApplication(App, appConfig).catch((err) =>
console.error(err)
);
"
`;
exports[`app --standalone should generate a standalone app correctly with routing 2`] = `
"import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
"import { ApplicationConfig, provideBrowserGlobalErrorListeners, provideZoneChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';
import { appRoutes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(appRoutes) ]
providers: [
provideBrowserGlobalErrorListeners(),
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(appRoutes)
]
};
"
`;
@ -468,47 +470,41 @@ export const appRoutes: Route[] = [];
exports[`app --standalone should generate a standalone app correctly with routing 4`] = `
"import { Component } from '@angular/core';
import { RouterModule } from '@angular/router';
import { NxWelcomeComponent } from './nx-welcome.component';
import { NxWelcome } from './nx-welcome';
@Component({
imports: [NxWelcomeComponent, RouterModule],
imports: [NxWelcome, RouterModule],
selector: 'app-root',
templateUrl: './app.component.html',
styleUrl: './app.component.css',
templateUrl: './app.html',
styleUrl: './app.css',
})
export class AppComponent {
title = 'standalone';
export class App {
protected title = 'standalone';
}
"
`;
exports[`app --standalone should generate a standalone app correctly with routing 5`] = `
"import { TestBed } from '@angular/core/testing';
import { AppComponent } from './app.component';
import { NxWelcomeComponent } from './nx-welcome.component';
import { App } from './app';
import { NxWelcome } from './nx-welcome';
import { RouterModule } from '@angular/router';
describe('AppComponent', () => {
describe('App', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [AppComponent, NxWelcomeComponent, RouterModule.forRoot([])],
imports: [App, NxWelcome, RouterModule.forRoot([])],
}).compileComponents();
});
it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
const compiled = fixture.nativeElement as HTMLElement;
expect(compiled.querySelector('h1')?.textContent).toContain(
'Welcome standalone'
);
});
it(\`should have as title 'standalone'\`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app.title).toEqual('standalone');
});
});
"
`;
@ -516,77 +512,63 @@ describe('AppComponent', () => {
exports[`app --standalone should generate a standalone app correctly without routing 1`] = `
"import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import { AppComponent } from './app/app.component';
import { App } from './app/app';
bootstrapApplication(AppComponent, appConfig).catch((err) =>
bootstrapApplication(App, appConfig).catch((err) =>
console.error(err)
);
"
`;
exports[`app --standalone should generate a standalone app correctly without routing 2`] = `
"import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
"import { ApplicationConfig, provideBrowserGlobalErrorListeners, provideZoneChangeDetection } from '@angular/core';
export const appConfig: ApplicationConfig = {
providers: [provideZoneChangeDetection({ eventCoalescing: true }), ]
providers: [
provideBrowserGlobalErrorListeners(),
provideZoneChangeDetection({ eventCoalescing: true })
]
};
"
`;
exports[`app --standalone should generate a standalone app correctly without routing 3`] = `
"import { Component } from '@angular/core';
import { NxWelcomeComponent } from './nx-welcome.component';
import { NxWelcome } from './nx-welcome';
@Component({
imports: [NxWelcomeComponent, ],
imports: [NxWelcome, ],
selector: 'app-root',
templateUrl: './app.component.html',
styleUrl: './app.component.css',
templateUrl: './app.html',
styleUrl: './app.css',
})
export class AppComponent {
title = 'standalone';
export class App {
protected title = 'standalone';
}
"
`;
exports[`app --standalone should generate a standalone app correctly without routing 4`] = `
"import { TestBed } from '@angular/core/testing';
import { AppComponent } from './app.component';
import { NxWelcomeComponent } from './nx-welcome.component';
import { App } from './app';
import { NxWelcome } from './nx-welcome';
describe('AppComponent', () => {
describe('App', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [AppComponent, NxWelcomeComponent],
imports: [App, NxWelcome],
}).compileComponents();
});
it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
const compiled = fixture.nativeElement as HTMLElement;
expect(compiled.querySelector('h1')?.textContent).toContain(
'Welcome standalone'
);
});
it(\`should have as title 'standalone'\`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app.title).toEqual('standalone');
});
});
"
`;
exports[`app --standalone should should not use event coalescing in versions lower than v18 1`] = `
"import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { appRoutes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [provideRouter(appRoutes) ]
};
"
`;
@ -597,14 +579,19 @@ exports[`app --strict should enable strict type checking: app tsconfig.json 1`]
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"strictTemplates": true,
"typeCheckHostBindings": true,
},
"compilerOptions": {
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"experimentalDecorators": true,
"importHelpers": true,
"isolatedModules": true,
"module": "preserve",
"moduleResolution": "bundler",
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noPropertyAccessFromIndexSignature": true,
"skipLibCheck": true,
"strict": true,
"target": "es2022",
},
@ -629,7 +616,6 @@ exports[`app --strict should enable strict type checking: e2e tsconfig.json 1`]
{
"compilerOptions": {
"allowJs": true,
"forceConsistentCasingInFileNames": true,
"module": "commonjs",
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true,
@ -689,17 +675,18 @@ exports[`app --unit-test-runner vitest should add tsconfig.spec.json 1`] = `
`;
exports[`app --unit-test-runner vitest should generate src/test-setup.ts 1`] = `
"import '@analogjs/vitest-angular/setup-zone';
"import '@angular/compiler';
import '@analogjs/vitest-angular/setup-zone';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting,
} from '@angular/platform-browser-dynamic/testing';
BrowserTestingModule,
platformBrowserTesting,
} from '@angular/platform-browser/testing';
import { getTestBed } from '@angular/core/testing';
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
BrowserTestingModule,
platformBrowserTesting()
);
"
`;
@ -735,32 +722,86 @@ export default defineConfig(() => ({
"
`;
exports[`app angular compat support should import "ApplicationConfig" from "@angular/platform-browser" 1`] = `
"import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { appRoutes } from './app.routes';
exports[`app angular compat support should generate components with the "component" type for versions lower than v20 1`] = `
"import { Component } from '@angular/core';
import { RouterModule } from '@angular/router';
import { NxWelcomeComponent } from './nx-welcome.component';
export const appConfig: ApplicationConfig = {
providers: [provideRouter(appRoutes) ]
};
@Component({
imports: [NxWelcomeComponent, RouterModule],
selector: 'app-root',
templateUrl: './app.component.html',
styleUrl: './app.component.css',
})
export class AppComponent {
title = 'myapp';
}
"
`;
exports[`app angular compat support should generate components with the "component" type for versions lower than v20 2`] = `
"<app-nx-welcome></app-nx-welcome>
<router-outlet></router-outlet>
"
`;
exports[`app angular compat support should generate components with the "component" type for versions lower than v20 3`] = `
"import { TestBed } from '@angular/core/testing';
import { AppComponent } from './app.component';
import { NxWelcomeComponent } from './nx-welcome.component';
import { RouterModule } from '@angular/router';
describe('AppComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [AppComponent, NxWelcomeComponent, RouterModule.forRoot([])],
}).compileComponents();
});
it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.nativeElement as HTMLElement;
expect(compiled.querySelector('h1')?.textContent).toContain(
'Welcome myapp'
);
});
it(\`should have as title 'myapp'\`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app.title).toEqual('myapp');
});
});
"
`;
exports[`app angular compat support should generate components with the "component" type for versions lower than v20 4`] = `
"import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import { AppComponent } from './app/app.component';
bootstrapApplication(AppComponent, appConfig).catch((err) =>
console.error(err)
);
"
`;
exports[`app at the root should accept numbers in the path 1`] = `"src/9-websites/my-app"`;
exports[`app format files should format files 1`] = `
"import { NgModule } from '@angular/core';
"import { NgModule, provideBrowserGlobalErrorListeners } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouterModule } from '@angular/router';
import { AppComponent } from './app.component';
import { App } from './app';
import { appRoutes } from './app.routes';
import { NxWelcomeComponent } from './nx-welcome.component';
import { NxWelcome } from './nx-welcome';
@NgModule({
declarations: [AppComponent, NxWelcomeComponent],
declarations: [App, NxWelcome],
imports: [BrowserModule, RouterModule.forRoot(appRoutes)],
providers: [],
bootstrap: [AppComponent],
providers: [provideBrowserGlobalErrorListeners()],
bootstrap: [App],
})
export class AppModule {}
"
@ -772,43 +813,37 @@ exports[`app format files should format files 2`] = `
@Component({
selector: 'app-root',
standalone: false,
templateUrl: './app.component.html',
styleUrl: './app.component.css',
templateUrl: './app.html',
styleUrl: './app.css',
})
export class AppComponent {
title = 'my-app';
export class App {
protected title = 'my-app';
}
"
`;
exports[`app format files should format files 3`] = `
"import { TestBed } from '@angular/core/testing';
import { AppComponent } from './app.component';
import { NxWelcomeComponent } from './nx-welcome.component';
import { App } from './app';
import { NxWelcome } from './nx-welcome';
import { RouterModule } from '@angular/router';
describe('AppComponent', () => {
describe('App', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [RouterModule.forRoot([])],
declarations: [AppComponent, NxWelcomeComponent],
declarations: [App, NxWelcome],
}).compileComponents();
});
it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
const compiled = fixture.nativeElement as HTMLElement;
expect(compiled.querySelector('h1')?.textContent).toContain(
'Welcome my-app'
);
});
it(\`should have as title 'my-app'\`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app.title).toEqual('my-app');
});
});
"
`;
@ -854,7 +889,7 @@ exports[`app nested should create project configs 1`] = `
},
},
"defaultConfiguration": "production",
"executor": "@angular-devkit/build-angular:application",
"executor": "@angular/build:application",
"options": {
"assets": [
{
@ -863,12 +898,10 @@ exports[`app nested should create project configs 1`] = `
},
],
"browser": "my-dir/my-app/src/main.ts",
"index": "my-dir/my-app/src/index.html",
"outputPath": "dist/my-dir/my-app",
"polyfills": [
"zone.js",
],
"scripts": [],
"styles": [
"my-dir/my-app/src/styles.css",
],
@ -879,7 +912,7 @@ exports[`app nested should create project configs 1`] = `
],
},
"extract-i18n": {
"executor": "@angular-devkit/build-angular:extract-i18n",
"executor": "@angular/build:extract-i18n",
"options": {
"buildTarget": "my-app:build",
},
@ -898,7 +931,7 @@ exports[`app nested should create project configs 1`] = `
},
"continuous": true,
"defaultConfiguration": "development",
"executor": "@angular-devkit/build-angular:dev-server",
"executor": "@angular/build:dev-server",
},
"serve-static": {
"continuous": true,
@ -972,7 +1005,7 @@ exports[`app not nested should create project configs 1`] = `
},
},
"defaultConfiguration": "production",
"executor": "@angular-devkit/build-angular:application",
"executor": "@angular/build:application",
"options": {
"assets": [
{
@ -981,12 +1014,10 @@ exports[`app not nested should create project configs 1`] = `
},
],
"browser": "my-app/src/main.ts",
"index": "my-app/src/index.html",
"outputPath": "dist/my-app",
"polyfills": [
"zone.js",
],
"scripts": [],
"styles": [
"my-app/src/styles.css",
],
@ -997,7 +1028,7 @@ exports[`app not nested should create project configs 1`] = `
],
},
"extract-i18n": {
"executor": "@angular-devkit/build-angular:extract-i18n",
"executor": "@angular/build:extract-i18n",
"options": {
"buildTarget": "my-app:build",
},
@ -1016,7 +1047,7 @@ exports[`app not nested should create project configs 1`] = `
},
"continuous": true,
"defaultConfiguration": "development",
"executor": "@angular-devkit/build-angular:dev-server",
"executor": "@angular/build:dev-server",
},
"serve-static": {
"continuous": true,
@ -1060,7 +1091,6 @@ exports[`app not nested should generate files: e2e tsconfig.json 1`] = `
{
"compilerOptions": {
"allowJs": true,
"forceConsistentCasingInFileNames": true,
"module": "commonjs",
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true,
@ -1094,15 +1124,13 @@ exports[`app not nested should generate files: tsconfig.app.json 1`] = `
},
"exclude": [
"jest.config.ts",
"src/test-setup.ts",
"src/**/*.test.ts",
"src/**/*.spec.ts",
],
"extends": "./tsconfig.json",
"files": [
"src/main.ts",
],
"include": [
"src/**/*.d.ts",
"src/**/*.ts",
],
}
`;
@ -1114,14 +1142,19 @@ exports[`app not nested should generate files: tsconfig.json 1`] = `
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"strictTemplates": true,
"typeCheckHostBindings": true,
},
"compilerOptions": {
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"experimentalDecorators": true,
"importHelpers": true,
"isolatedModules": true,
"module": "preserve",
"moduleResolution": "bundler",
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noPropertyAccessFromIndexSignature": true,
"skipLibCheck": true,
"strict": true,
"target": "es2022",
},
@ -1142,17 +1175,61 @@ exports[`app not nested should generate files: tsconfig.json 1`] = `
}
`;
exports[`app should generate correct tsconfig.editor.json 1`] = `
{
"compilerOptions": {},
"exclude": [
"jest.config.ts",
"src/**/*.test.ts",
"src/**/*.spec.ts",
],
"extends": "./tsconfig.json",
"include": [
"src/**/*.ts",
],
exports[`app template generation mode should respect the "type" configured in the component generator defaults 1`] = `
"import { Component } from '@angular/core';
import { RouterModule } from '@angular/router';
import { NxWelcomeComponent } from './nx-welcome.component';
@Component({
imports: [NxWelcomeComponent, RouterModule],
selector: 'app-root',
templateUrl: './app.component.html',
styleUrl: './app.component.css',
})
export class AppComponent {
protected title = 'myapp';
}
"
`;
exports[`app template generation mode should respect the "type" configured in the component generator defaults 2`] = `
"<app-nx-welcome></app-nx-welcome>
<router-outlet></router-outlet>
"
`;
exports[`app template generation mode should respect the "type" configured in the component generator defaults 3`] = `
"import { TestBed } from '@angular/core/testing';
import { AppComponent } from './app.component';
import { NxWelcomeComponent } from './nx-welcome.component';
import { RouterModule } from '@angular/router';
describe('AppComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [AppComponent, NxWelcomeComponent, RouterModule.forRoot([])],
}).compileComponents();
});
it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.nativeElement as HTMLElement;
expect(compiled.querySelector('h1')?.textContent).toContain(
'Welcome myapp'
);
});
});
"
`;
exports[`app template generation mode should respect the "type" configured in the component generator defaults 4`] = `
"import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import { AppComponent } from './app/app.component';
bootstrapApplication(AppComponent, appConfig).catch((err) =>
console.error(err)
);
"
`;

View File

@ -1,4 +1,5 @@
import {
addDependenciesToPackageJson,
formatFiles,
generateFiles,
GeneratorCallback,
@ -12,11 +13,16 @@ import {
import { logShowProjectCommand } from '@nx/devkit/src/utils/log-show-project-command';
import { initGenerator as jsInitGenerator } from '@nx/js';
import { assertNotUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup';
import { angularInitGenerator } from '../init/init';
import { convertToRspack } from '../convert-to-rspack/convert-to-rspack';
import { angularInitGenerator } from '../init/init';
import { setupSsr } from '../setup-ssr/setup-ssr';
import { setupTailwindGenerator } from '../setup-tailwind/setup-tailwind';
import { ensureAngularDependencies } from '../utils/ensure-angular-dependencies';
import {
getInstalledAngularDevkitVersion,
getInstalledAngularVersionInfo,
versions,
} from '../utils/version-utils';
import {
addE2e,
addLinting,
@ -25,11 +31,9 @@ import {
addUnitTestRunner,
createFiles,
createProject,
enableStrictTypeChecking,
normalizeOptions,
setApplicationStrictDefault,
setGeneratorDefaults,
updateEditorTsConfig,
updateTsconfigFiles,
} from './lib';
import type { Schema } from './schema';
@ -82,7 +86,7 @@ export async function applicationGenerator(
options,
options.e2eTestRunner !== 'none' ? e2ePort : options.port
);
updateEditorTsConfig(tree, options);
updateTsconfigFiles(tree, options);
setGeneratorDefaults(tree, options);
if (options.rootProject) {
@ -95,12 +99,6 @@ export async function applicationGenerator(
addProxyConfig(tree, options);
}
if (options.strict) {
enableStrictTypeChecking(tree, options);
} else {
setApplicationStrictDefault(tree, false);
}
if (options.ssr) {
await setupSsr(tree, {
project: options.name,
@ -134,6 +132,27 @@ export async function applicationGenerator(
}
}
if (!options.skipPackageJson) {
const { major: angularMajorVersion } = getInstalledAngularVersionInfo(tree);
if (angularMajorVersion >= 20) {
const angularDevkitVersion =
getInstalledAngularDevkitVersion(tree) ??
versions(tree).angularDevkitVersion;
const devDependencies: Record<string, string> = {};
if (options.bundler === 'esbuild') {
devDependencies['@angular/build'] = angularDevkitVersion;
} else if (isRspack) {
devDependencies['@angular/build'] = angularDevkitVersion;
devDependencies['@angular-devkit/build-angular'] = angularDevkitVersion;
} else {
devDependencies['@angular-devkit/build-angular'] = angularDevkitVersion;
}
addDependenciesToPackageJson(tree, {}, devDependencies, undefined, true);
}
}
if (!options.skipFormat) {
await formatFiles(tree);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View File

@ -4,7 +4,16 @@
"outDir": "<%= rootOffset %>dist/out-tsc",
"types": []
},
<%_ if (angularMajorVersion < 20) { _%>
"files": ["src/main.ts"],
"include": ["src/**/*.d.ts"],
"exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"]
<%_ } else { _%>
"include": ["src/**/*.ts"],
<%_ } _%>
"exclude": [
"jest.config.ts",
"src/test-setup.ts",
"src/**/*.test.ts",
"src/**/*.spec.ts"
]
}

View File

@ -1,8 +1,8 @@
{
"compilerOptions": {
"target": "es2022"<% if (disableModernClassFieldsBehavior) { %>,
"useDefineForClassFields": false<% } %><% if (isUsingApplicationBuilder) { %>,
"esModuleInterop": true<% } %>
"extends": "<%= rootTsConfig %>",
"compilerOptions": {},
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false
},
"files": [],
"include": [],
@ -13,6 +13,5 @@
{
"path": "./tsconfig.app.json"
}
],
"extends": "<%= rootTsConfig %>"
]
}

View File

@ -1,29 +0,0 @@
import { TestBed } from '@angular/core/testing';
import { AppComponent } from './app.component';<% if(!minimal) { %>
import { NxWelcomeComponent } from './nx-welcome.component';<% } %><% if(routing && useRouterTestingModule) { %>
import { RouterTestingModule } from '@angular/router/testing';<% } %><% if(routing && !useRouterTestingModule) { %>
import { RouterModule } from '@angular/router';<% } %>
describe('AppComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [<% if(routing && useRouterTestingModule) { %>RouterTestingModule<% } %><% if(routing && !useRouterTestingModule) { %>RouterModule.forRoot([])<% } %>],
declarations: [AppComponent<% if(!minimal) { %>, NxWelcomeComponent<% } %>]
}).compileComponents();
});
it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.nativeElement as HTMLElement;
expect(compiled.querySelector('h1')?.textContent).toContain(
'Welcome <%= appName %>'
);
});<% if(!minimal) { %>
it(`should have as title '<%= appName %>'`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app.title).toEqual('<%= appName %>');
});<% } %>
});

View File

@ -1,17 +0,0 @@
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';<% if(routing) { %>
import { RouterModule } from '@angular/router';<% } %>
import { AppComponent } from './app.component';<% if(routing) { %>
import { appRoutes } from './app.routes';<% } %><% if(!minimal) { %>
import { NxWelcomeComponent } from './nx-welcome.component';<% } %>
@NgModule({
declarations: [AppComponent<% if(!minimal) { %>, NxWelcomeComponent<% } %>],
imports: [
BrowserModule,<% if(routing) { %>
RouterModule.forRoot(appRoutes),<% } %>
],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}

Some files were not shown because too many files have changed in this diff Show More