feat(angular): support angular 18.0.0 (#22509)
<!-- Please make sure you have read the submission guidelines before posting an PR --> <!-- https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr --> <!-- Please make sure that your commit message follows our format --> <!-- Example: `fix(nx): must begin with lowercase` --> ## Current Behavior <!-- This is the behavior we have today --> ## Expected Behavior <!-- This is the behavior we should expect with the changes in this PR --> ## Related Issue(s) <!-- Please link the issue being fixed so it gets closed when this is merged. --> Fixes #25284
This commit is contained in:
parent
881adfb185
commit
37f02f7e6b
@ -63,7 +63,7 @@
|
||||
"type": "executor"
|
||||
},
|
||||
"/nx-api/angular/executors/ng-packagr-lite": {
|
||||
"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 with some key differences:\n- For supported Angular versions lower than v16, it doesn't run `ngcc` automatically (`ngcc` is no longer available from Angular v16 onwards, for lower versions, it needs to be run separately beforehand if needed, this can be done in a `postinstall` hook on `package.json`).\n- It only produces ESM2022 bundles (ESM2020 for supported Angular versions lower than v16).",
|
||||
"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.",
|
||||
"file": "generated/packages/angular/executors/ng-packagr-lite.json",
|
||||
"hidden": false,
|
||||
"name": "ng-packagr-lite",
|
||||
|
||||
@ -58,7 +58,7 @@
|
||||
"type": "executor"
|
||||
},
|
||||
{
|
||||
"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 with some key differences:\n- For supported Angular versions lower than v16, it doesn't run `ngcc` automatically (`ngcc` is no longer available from Angular v16 onwards, for lower versions, it needs to be run separately beforehand if needed, this can be done in a `postinstall` hook on `package.json`).\n- It only produces ESM2022 bundles (ESM2020 for supported Angular versions lower than v16).",
|
||||
"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.",
|
||||
"file": "generated/packages/angular/executors/ng-packagr-lite.json",
|
||||
"hidden": false,
|
||||
"name": "ng-packagr-lite",
|
||||
|
||||
@ -38,11 +38,12 @@
|
||||
},
|
||||
"output": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "Absolute path within the output."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["glob", "input", "output"]
|
||||
"required": ["glob", "input"]
|
||||
},
|
||||
{ "type": "string" }
|
||||
]
|
||||
@ -663,11 +664,12 @@
|
||||
},
|
||||
"output": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "Absolute path within the output."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["glob", "input", "output"]
|
||||
"required": ["glob", "input"]
|
||||
},
|
||||
{ "type": "string" }
|
||||
]
|
||||
|
||||
@ -38,11 +38,12 @@
|
||||
},
|
||||
"output": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "Absolute path within the output."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["glob", "input", "output"]
|
||||
"required": ["glob", "input"]
|
||||
},
|
||||
{ "type": "string" }
|
||||
]
|
||||
@ -572,11 +573,12 @@
|
||||
},
|
||||
"output": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "Absolute path within the output."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["glob", "input", "output"]
|
||||
"required": ["glob", "input"]
|
||||
},
|
||||
{ "type": "string" }
|
||||
]
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
"outputCapture": "direct-nodejs",
|
||||
"$schema": "https://json-schema.org/schema",
|
||||
"title": "ng-packagr Target",
|
||||
"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 with some key differences:\n- For supported Angular versions lower than v16, it doesn't run `ngcc` automatically (`ngcc` is no longer available from Angular v16 onwards, for lower versions, it needs to be run separately beforehand if needed, this can be done in a `postinstall` hook on `package.json`).\n- It only produces ESM2022 bundles (ESM2020 for supported Angular versions lower than v16).",
|
||||
"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.",
|
||||
"cli": "nx",
|
||||
"type": "object",
|
||||
"presets": [
|
||||
@ -44,7 +44,7 @@
|
||||
"additionalProperties": false,
|
||||
"required": ["project"]
|
||||
},
|
||||
"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 with some key differences:\n- For supported Angular versions lower than v16, it doesn't run `ngcc` automatically (`ngcc` is no longer available from Angular v16 onwards, for lower versions, it needs to be run separately beforehand if needed, this can be done in a `postinstall` hook on `package.json`).\n- It only produces ESM2022 bundles (ESM2020 for supported Angular versions lower than v16).",
|
||||
"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": [],
|
||||
"hidden": false,
|
||||
"path": "/packages/angular/src/executors/ng-packagr-lite/schema.json",
|
||||
|
||||
@ -56,11 +56,12 @@
|
||||
},
|
||||
"output": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "Absolute path within the output."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["glob", "input", "output"]
|
||||
"required": ["glob", "input"]
|
||||
},
|
||||
{ "type": "string" }
|
||||
]
|
||||
@ -592,11 +593,12 @@
|
||||
},
|
||||
"output": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "Absolute path within the output."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["glob", "input", "output"]
|
||||
"required": ["glob", "input"]
|
||||
},
|
||||
{ "type": "string" }
|
||||
]
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
"properties": {
|
||||
"assets": {
|
||||
"type": "array",
|
||||
"description": "List of static application assets. _Note: only supported in Angular versions >= 15.1.0_",
|
||||
"description": "List of static application assets.",
|
||||
"default": [],
|
||||
"items": {
|
||||
"oneOf": [
|
||||
@ -38,11 +38,12 @@
|
||||
},
|
||||
"output": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "Absolute path within the output."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["glob", "input", "output"]
|
||||
"required": ["glob", "input"]
|
||||
},
|
||||
{ "type": "string" }
|
||||
]
|
||||
@ -186,7 +187,7 @@
|
||||
},
|
||||
"vendorChunk": {
|
||||
"type": "boolean",
|
||||
"description": "Generate a separate bundle containing only vendor libraries. This option should only be used for development to reduce the incremental compilation time. _Note: supported in Angular versions >= 15.1.0_",
|
||||
"description": "Generate a separate bundle containing only vendor libraries. This option should only be used for development to reduce the incremental compilation time.",
|
||||
"default": false
|
||||
},
|
||||
"verbose": {
|
||||
@ -248,7 +249,7 @@
|
||||
},
|
||||
"buildOptimizer": {
|
||||
"type": "boolean",
|
||||
"description": "Enables advanced build optimizations. _Note: only supported in Angular versions >= 16.0.0_.",
|
||||
"description": "Enables advanced build optimizations.",
|
||||
"default": true
|
||||
},
|
||||
"namedChunks": {
|
||||
@ -323,11 +324,12 @@
|
||||
},
|
||||
"output": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "Absolute path within the output."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["glob", "input", "output"]
|
||||
"required": ["glob", "input"]
|
||||
},
|
||||
{ "type": "string" }
|
||||
]
|
||||
|
||||
@ -19,8 +19,9 @@
|
||||
"appId": {
|
||||
"type": "string",
|
||||
"format": "html-selector",
|
||||
"description": "The `appId` to use with `withServerTransition`. _Note: This is only used in Angular versions <16.0.0. It's deprecated since Angular 16 and not supported since Angular 17._",
|
||||
"default": "serverApp"
|
||||
"description": "The `appId` to use with `withServerTransition`.",
|
||||
"default": "serverApp",
|
||||
"x-deprecated": "This is deprecated and ignored since Angular 16 and not supported since Angular 17."
|
||||
},
|
||||
"main": {
|
||||
"type": "string",
|
||||
@ -55,7 +56,7 @@
|
||||
},
|
||||
"hydration": {
|
||||
"type": "boolean",
|
||||
"description": "Set up Hydration for the SSR application. It defaults to `true` for Angular versions >= 17.0.0. Otherwise, it defaults to `false`. _Note: This is only supported in Angular versions >= 16.0.0_."
|
||||
"description": "Set up Hydration for the SSR application. It defaults to `true` for Angular versions >= 17.0.0. Otherwise, it defaults to `false`."
|
||||
},
|
||||
"skipFormat": {
|
||||
"type": "boolean",
|
||||
|
||||
@ -42,7 +42,7 @@ describe('Move Angular Project', () => {
|
||||
expect(moveOutput).toContain(`CREATE ${newPath}/tsconfig.json`);
|
||||
expect(moveOutput).toContain(`CREATE ${newPath}/tsconfig.spec.json`);
|
||||
expect(moveOutput).toContain(`CREATE ${newPath}/.eslintrc.json`);
|
||||
expect(moveOutput).toContain(`CREATE ${newPath}/src/favicon.ico`);
|
||||
expect(moveOutput).toContain(`CREATE ${newPath}/public/favicon.ico`);
|
||||
expect(moveOutput).toContain(`CREATE ${newPath}/src/index.html`);
|
||||
expect(moveOutput).toContain(`CREATE ${newPath}/src/main.ts`);
|
||||
expect(moveOutput).toContain(`CREATE ${newPath}/src/styles.css`);
|
||||
@ -52,7 +52,6 @@ describe('Move Angular Project', () => {
|
||||
);
|
||||
expect(moveOutput).toContain(`CREATE ${newPath}/src/app/app.component.ts`);
|
||||
expect(moveOutput).toContain(`CREATE ${newPath}/src/app/app.config.ts`);
|
||||
expect(moveOutput).toContain(`CREATE ${newPath}/src/assets/.gitkeep`);
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
@ -185,10 +185,7 @@ describe('convert Angular CLI workspace to an Nx workspace', () => {
|
||||
browser: `apps/${project}/src/main.ts`,
|
||||
polyfills: [`zone.js`],
|
||||
tsConfig: `apps/${project}/tsconfig.app.json`,
|
||||
assets: [
|
||||
`apps/${project}/src/favicon.ico`,
|
||||
`apps/${project}/src/assets`,
|
||||
],
|
||||
assets: [{ glob: '**/*', input: `apps/${project}/public` }],
|
||||
styles: [`apps/${project}/src/styles.css`],
|
||||
scripts: [`apps/${project}/src/scripts.js`],
|
||||
},
|
||||
@ -197,13 +194,13 @@ describe('convert Angular CLI workspace to an Nx workspace', () => {
|
||||
budgets: [
|
||||
{
|
||||
type: 'initial',
|
||||
maximumWarning: '500kb',
|
||||
maximumError: '1mb',
|
||||
maximumWarning: '500kB',
|
||||
maximumError: '1MB',
|
||||
},
|
||||
{
|
||||
type: 'anyComponentStyle',
|
||||
maximumWarning: '2kb',
|
||||
maximumError: '4kb',
|
||||
maximumWarning: '2kB',
|
||||
maximumError: '4kB',
|
||||
},
|
||||
],
|
||||
outputHashing: 'all',
|
||||
@ -229,10 +226,7 @@ describe('convert Angular CLI workspace to an Nx workspace', () => {
|
||||
options: {
|
||||
polyfills: [`zone.js`, `zone.js/testing`],
|
||||
tsConfig: `apps/${project}/tsconfig.spec.json`,
|
||||
assets: [
|
||||
`apps/${project}/src/favicon.ico`,
|
||||
`apps/${project}/src/assets`,
|
||||
],
|
||||
assets: [{ glob: '**/*', input: `apps/${project}/public` }],
|
||||
styles: [`apps/${project}/src/styles.css`],
|
||||
scripts: [`apps/${project}/src/scripts.js`],
|
||||
},
|
||||
|
||||
30
package.json
30
package.json
@ -25,19 +25,19 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@actions/core": "^1.10.0",
|
||||
"@angular-devkit/architect": "~0.1703.0",
|
||||
"@angular-devkit/build-angular": "~17.3.0",
|
||||
"@angular-devkit/core": "~17.3.0",
|
||||
"@angular-devkit/schematics": "~17.3.0",
|
||||
"@angular-eslint/eslint-plugin": "17.3.0",
|
||||
"@angular-eslint/eslint-plugin-template": "17.3.0",
|
||||
"@angular-eslint/template-parser": "17.3.0",
|
||||
"@angular/cli": "~17.3.0",
|
||||
"@angular/common": "~17.3.0",
|
||||
"@angular/compiler": "~17.3.0",
|
||||
"@angular/compiler-cli": "~17.3.0",
|
||||
"@angular/core": "~17.3.0",
|
||||
"@angular/router": "~17.3.0",
|
||||
"@angular-devkit/architect": "~0.1800.0",
|
||||
"@angular-devkit/build-angular": "~18.0.0",
|
||||
"@angular-devkit/core": "~18.0.0",
|
||||
"@angular-devkit/schematics": "~18.0.0",
|
||||
"@angular-eslint/eslint-plugin": "~17.3.0",
|
||||
"@angular-eslint/eslint-plugin-template": "~17.3.0",
|
||||
"@angular-eslint/template-parser": "~17.3.0",
|
||||
"@angular/cli": "~18.0.0",
|
||||
"@angular/common": "~18.0.0",
|
||||
"@angular/compiler": "~18.0.0",
|
||||
"@angular/compiler-cli": "~18.0.0",
|
||||
"@angular/core": "~18.0.0",
|
||||
"@angular/router": "~18.0.0",
|
||||
"@babel/core": "^7.23.2",
|
||||
"@babel/helper-create-regexp-features-plugin": "^7.22.9",
|
||||
"@babel/plugin-transform-runtime": "^7.23.2",
|
||||
@ -92,7 +92,7 @@
|
||||
"@rollup/plugin-json": "^6.1.0",
|
||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||
"@rollup/plugin-url": "^8.0.2",
|
||||
"@schematics/angular": "~17.3.0",
|
||||
"@schematics/angular": "~18.0.0",
|
||||
"@storybook/addon-essentials": "7.5.3",
|
||||
"@storybook/core-server": "7.5.3",
|
||||
"@storybook/react": "7.5.3",
|
||||
@ -226,7 +226,7 @@
|
||||
"mini-css-extract-plugin": "~2.4.7",
|
||||
"minimatch": "9.0.3",
|
||||
"next-sitemap": "^3.1.10",
|
||||
"ng-packagr": "~17.3.0",
|
||||
"ng-packagr": "~18.0.0",
|
||||
"node-fetch": "^2.6.7",
|
||||
"npm-package-arg": "11.0.1",
|
||||
"nuxt": "^3.10.0",
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
"ng-packagr-lite": {
|
||||
"implementation": "./src/executors/ng-packagr-lite/ng-packagr-lite.impl",
|
||||
"schema": "./src/executors/ng-packagr-lite/schema.json",
|
||||
"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 with some key differences:\n- For supported Angular versions lower than v16, it doesn't run `ngcc` automatically (`ngcc` is no longer available from Angular v16 onwards, for lower versions, it needs to be run separately beforehand if needed, this can be done in a `postinstall` hook on `package.json`).\n- It only produces ESM2022 bundles (ESM2020 for supported Angular versions lower than v16)."
|
||||
"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."
|
||||
},
|
||||
"package": {
|
||||
"implementation": "./src/executors/package/package.impl",
|
||||
|
||||
@ -386,6 +386,15 @@
|
||||
},
|
||||
"description": "Update the @angular/cli package version to ~17.3.0.",
|
||||
"factory": "./src/migrations/update-18-2-0/update-angular-cli"
|
||||
},
|
||||
"update-angular-cli-version-18-0-0": {
|
||||
"cli": "nx",
|
||||
"version": "19.1.0-beta.2",
|
||||
"requires": {
|
||||
"@angular/core": ">=18.0.0"
|
||||
},
|
||||
"description": "Update the @angular/cli package version to ~18.0.0.",
|
||||
"factory": "./src/migrations/update-19-1-0/update-angular-cli"
|
||||
}
|
||||
},
|
||||
"packageJsonUpdates": {
|
||||
@ -1805,6 +1814,79 @@
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"19.1.0": {
|
||||
"version": "19.1.0-beta.2",
|
||||
"x-prompt": "Do you want to update the Angular version to v18?",
|
||||
"requires": {
|
||||
"@angular/core": ">=17.3.0 <18.0.0"
|
||||
},
|
||||
"packages": {
|
||||
"@angular-devkit/build-angular": {
|
||||
"version": "~18.0.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@angular-devkit/core": {
|
||||
"version": "~18.0.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@angular-devkit/schematics": {
|
||||
"version": "~18.0.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@angular/pwa": {
|
||||
"version": "~18.0.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@angular/ssr": {
|
||||
"version": "~18.0.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@schematics/angular": {
|
||||
"version": "~18.0.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@angular-devkit/architect": {
|
||||
"version": "~0.1800.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@angular-devkit/build-webpack": {
|
||||
"version": "~0.1800.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@angular/core": {
|
||||
"version": "~18.0.0",
|
||||
"alwaysAddToPackageJson": true
|
||||
},
|
||||
"@angular/material": {
|
||||
"version": "~18.0.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@angular/cdk": {
|
||||
"version": "~18.0.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"ng-packagr": {
|
||||
"version": "~18.0.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"19.1.0-jest": {
|
||||
"version": "19.1.0-beta.2",
|
||||
"requires": {
|
||||
"@angular-devkit/build-angular": ">=15.0.0 <19.0.0",
|
||||
"@angular/compiler-cli": ">=15.0.0 <19.0.0",
|
||||
"@angular/core": ">=15.0.0 <19.0.0",
|
||||
"@angular/platform-browser-dynamic": ">=15.0.0 <19.0.0",
|
||||
"jest": "^29.0.0"
|
||||
},
|
||||
"packages": {
|
||||
"jest-preset-angular": {
|
||||
"version": "~14.1.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,10 +67,10 @@
|
||||
"piscina": "^4.4.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular-devkit/build-angular": ">= 15.0.0 < 18.0.0",
|
||||
"@angular-devkit/core": ">= 15.0.0 < 18.0.0",
|
||||
"@angular-devkit/schematics": ">= 15.0.0 < 18.0.0",
|
||||
"@schematics/angular": ">= 15.0.0 < 18.0.0",
|
||||
"@angular-devkit/build-angular": ">= 16.0.0 < 19.0.0",
|
||||
"@angular-devkit/core": ">= 16.0.0 < 19.0.0",
|
||||
"@angular-devkit/schematics": ">= 16.0.0 < 19.0.0",
|
||||
"@schematics/angular": ">= 16.0.0 < 19.0.0",
|
||||
"esbuild": "^0.19.2",
|
||||
"rxjs": "^6.5.3 || ^7.5.0"
|
||||
},
|
||||
|
||||
@ -13,6 +13,7 @@ import { from } from 'rxjs';
|
||||
import { switchMap, tap } from 'rxjs/operators';
|
||||
import { getInstalledAngularVersionInfo } from '../../executors/utilities/angular-version-utils';
|
||||
import {
|
||||
getDynamicMfManifestFile,
|
||||
getDynamicRemotes,
|
||||
getStaticRemotes,
|
||||
validateDevRemotes,
|
||||
@ -29,11 +30,7 @@ export function executeModuleFederationDevSSRBuilder(
|
||||
readProjectsConfigurationFromProjectGraph(projectGraph);
|
||||
const project = workspaceProjects[context.target.project];
|
||||
|
||||
let pathToManifestFile = join(
|
||||
context.workspaceRoot,
|
||||
project.sourceRoot,
|
||||
'assets/module-federation.manifest.json'
|
||||
);
|
||||
let pathToManifestFile: string;
|
||||
if (options.pathToManifestFile) {
|
||||
const userPathToManifestFile = join(
|
||||
context.workspaceRoot,
|
||||
@ -50,6 +47,11 @@ export function executeModuleFederationDevSSRBuilder(
|
||||
}
|
||||
|
||||
pathToManifestFile = userPathToManifestFile;
|
||||
} else {
|
||||
pathToManifestFile = getDynamicMfManifestFile(
|
||||
project,
|
||||
context.workspaceRoot
|
||||
);
|
||||
}
|
||||
|
||||
validateDevRemotes(options, workspaceProjects);
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { basename, dirname, join } from 'path';
|
||||
import { join } from 'path';
|
||||
import { existsSync, readFileSync } from 'fs';
|
||||
import { logger, ProjectConfiguration } from '@nx/devkit';
|
||||
import { registerTsProject } from '@nx/js/src/internal';
|
||||
@ -8,17 +8,18 @@ export function getDynamicRemotes(
|
||||
context: import('@angular-devkit/architect').BuilderContext,
|
||||
workspaceProjects: Record<string, ProjectConfiguration>,
|
||||
remotesToSkip: Set<string>,
|
||||
pathToManifestFile = join(
|
||||
context.workspaceRoot,
|
||||
project.sourceRoot,
|
||||
'assets/module-federation.manifest.json'
|
||||
)
|
||||
pathToManifestFile: string | undefined
|
||||
): string[] {
|
||||
pathToManifestFile ??= getDynamicMfManifestFile(
|
||||
project,
|
||||
context.workspaceRoot
|
||||
);
|
||||
|
||||
// check for dynamic remotes
|
||||
// we should only check for dynamic based on what we generate
|
||||
// and fallback to empty array
|
||||
|
||||
if (!existsSync(pathToManifestFile)) {
|
||||
if (!pathToManifestFile || !existsSync(pathToManifestFile)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@ -182,3 +183,23 @@ export function validateDevRemotes(
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function getDynamicMfManifestFile(
|
||||
project: ProjectConfiguration,
|
||||
workspaceRoot: string
|
||||
): string | undefined {
|
||||
// {sourceRoot}/assets/module-federation.manifest.json was the generated
|
||||
// path for the manifest file in the past. We now generate the manifest
|
||||
// file at {root}/public/module-federation.manifest.json. This check
|
||||
// ensures that we can still support the old path for backwards
|
||||
// compatibility since old projects may still have the manifest file
|
||||
// at the old path.
|
||||
return [
|
||||
join(workspaceRoot, project.root, 'public/module-federation.manifest.json'),
|
||||
join(
|
||||
workspaceRoot,
|
||||
project.sourceRoot,
|
||||
'assets/module-federation.manifest.json'
|
||||
),
|
||||
].find((path) => existsSync(path));
|
||||
}
|
||||
|
||||
@ -498,11 +498,12 @@
|
||||
},
|
||||
"output": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "Absolute path within the output."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["glob", "input", "output"]
|
||||
"required": ["glob", "input"]
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
"properties": {
|
||||
"assets": {
|
||||
"type": "array",
|
||||
"description": "List of static application assets. _Note: only supported in Angular versions >= 15.1.0_",
|
||||
"description": "List of static application assets.",
|
||||
"default": [],
|
||||
"items": {
|
||||
"$ref": "#/definitions/assetPattern"
|
||||
@ -127,7 +127,7 @@
|
||||
},
|
||||
"vendorChunk": {
|
||||
"type": "boolean",
|
||||
"description": "Generate a separate bundle containing only vendor libraries. This option should only be used for development to reduce the incremental compilation time. _Note: supported in Angular versions >= 15.1.0_",
|
||||
"description": "Generate a separate bundle containing only vendor libraries. This option should only be used for development to reduce the incremental compilation time.",
|
||||
"default": false
|
||||
},
|
||||
"verbose": {
|
||||
@ -192,7 +192,7 @@
|
||||
},
|
||||
"buildOptimizer": {
|
||||
"type": "boolean",
|
||||
"description": "Enables advanced build optimizations. _Note: only supported in Angular versions >= 16.0.0_.",
|
||||
"description": "Enables advanced build optimizations.",
|
||||
"default": true
|
||||
},
|
||||
"namedChunks": {
|
||||
@ -271,11 +271,12 @@
|
||||
},
|
||||
"output": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "Absolute path within the output."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["glob", "input", "output"]
|
||||
"required": ["glob", "input"]
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
|
||||
@ -1,40 +0,0 @@
|
||||
import { stripIndents } from '@nx/devkit';
|
||||
import { lt } from 'semver';
|
||||
import type { VersionInfo } from '../../executors/utilities/angular-version-utils';
|
||||
import { getInstalledAngularVersionInfo } from '../../executors/utilities/angular-version-utils';
|
||||
import type { Schema } from './schema';
|
||||
|
||||
export function validateOptions(options: Schema): void {
|
||||
const angularVersionInfo = getInstalledAngularVersionInfo();
|
||||
validateAssets(options, angularVersionInfo);
|
||||
validateBuildOptimizer(options, angularVersionInfo);
|
||||
validateVendorChunk(options, angularVersionInfo);
|
||||
}
|
||||
|
||||
function validateAssets(options: Schema, { version }: VersionInfo): void {
|
||||
if (
|
||||
lt(version, '15.1.0') &&
|
||||
Array.isArray(options.assets) &&
|
||||
options.assets.length > 0
|
||||
) {
|
||||
throw new Error(stripIndents`The "assets" option is supported from Angular >= 15.1.0. You are currently using "${version}".
|
||||
You can resolve this error by removing the "assets" option or by migrating to Angular 15.1.0.`);
|
||||
}
|
||||
}
|
||||
|
||||
function validateBuildOptimizer(
|
||||
options: Schema,
|
||||
{ major, version }: VersionInfo
|
||||
): void {
|
||||
if (major < 16 && options.buildOptimizer) {
|
||||
throw new Error(stripIndents`The "buildOptimizer" option is supported from Angular >= 16.0.0. You are currently using "${version}".
|
||||
You can resolve this error by removing the "buildOptimizer" option.`);
|
||||
}
|
||||
}
|
||||
|
||||
function validateVendorChunk(options: Schema, { version }: VersionInfo): void {
|
||||
if (lt(version, '15.1.0') && options.vendorChunk) {
|
||||
throw new Error(stripIndents`The "vendorChunk" option is supported from Angular >= 15.1.0. You are currently using "${version}".
|
||||
You can resolve this error by removing the "vendorChunk" option or by migrating to Angular 15.1.0.`);
|
||||
}
|
||||
}
|
||||
@ -10,7 +10,6 @@ import { switchMap } from 'rxjs/operators';
|
||||
import { createTmpTsConfigForBuildableLibs } from '../utilities/buildable-libs';
|
||||
import { mergeCustomWebpackConfig } from '../utilities/webpack';
|
||||
import { Schema } from './schema';
|
||||
import { validateOptions } from './validate-options';
|
||||
|
||||
function buildServerApp(
|
||||
options: Schema,
|
||||
@ -55,7 +54,7 @@ function buildServerAppWithCustomWebpackConfiguration(
|
||||
switchMap(({ executeServerBuilder }) =>
|
||||
executeServerBuilder(options, context as any, {
|
||||
webpackConfiguration: async (baseWebpackConfig) => {
|
||||
// Angular 15 auto includes code from @angular/platform-server
|
||||
// Angular auto includes code from @angular/platform-server
|
||||
// This includes the code outside the shared scope created by ModuleFederation
|
||||
// This code will be included in the generated code from our generators,
|
||||
// maintaining it within the shared scope.
|
||||
@ -96,8 +95,6 @@ export function executeWebpackServerBuilder(
|
||||
options: Schema,
|
||||
context: import('@angular-devkit/architect').BuilderContext
|
||||
): Observable<import('@angular-devkit/build-angular').ServerBuilderOutput> {
|
||||
validateOptions(options);
|
||||
|
||||
options.buildLibsFromSource ??= true;
|
||||
|
||||
process.env.NX_BUILD_LIBS_FROM_SOURCE = `${options.buildLibsFromSource}`;
|
||||
|
||||
@ -10,11 +10,12 @@ import {
|
||||
} from '../utilities/esbuild-extensions';
|
||||
import type { ApplicationExecutorOptions } from './schema';
|
||||
import { validateOptions } from './utils/validate-options';
|
||||
import type { buildApplication as buildApplicationFn } from '@angular-devkit/build-angular';
|
||||
|
||||
export default async function* applicationExecutor(
|
||||
options: ApplicationExecutorOptions,
|
||||
context: ExecutorContext
|
||||
) {
|
||||
): ReturnType<typeof buildApplicationFn> {
|
||||
validateOptions(options);
|
||||
|
||||
const {
|
||||
@ -46,7 +47,7 @@ export default async function* applicationExecutor(
|
||||
{
|
||||
builderName: 'application',
|
||||
description: 'Build an application.',
|
||||
optionSchema: await import('./schema.json'),
|
||||
optionSchema: require('./schema.json'),
|
||||
},
|
||||
context
|
||||
);
|
||||
|
||||
@ -600,11 +600,12 @@
|
||||
},
|
||||
"output": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "Absolute path within the output."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["glob", "input", "output"]
|
||||
"required": ["glob", "input"]
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import type { buildEsbuildBrowser as buildEsbuildBrowserFn } from '@angular-devkit/build-angular/src/builders/browser-esbuild';
|
||||
import { stripIndents, type ExecutorContext } from '@nx/devkit';
|
||||
import type { DependentBuildableProjectNode } from '@nx/js/src/utils/buildable-libs-utils';
|
||||
import { createBuilderContext } from 'nx/src/adapter/ngcli-adapter';
|
||||
@ -9,7 +10,7 @@ import type { EsBuildSchema } from './schema';
|
||||
export default async function* esbuildExecutor(
|
||||
options: EsBuildSchema,
|
||||
context: ExecutorContext
|
||||
) {
|
||||
): ReturnType<typeof buildEsbuildBrowserFn> {
|
||||
if (options.plugins) {
|
||||
const { major: angularMajorVersion, version: angularVersion } =
|
||||
getInstalledAngularVersionInfo();
|
||||
@ -41,17 +42,15 @@ export default async function* esbuildExecutor(
|
||||
|
||||
const plugins = await loadPlugins(pluginPaths, options.tsConfig);
|
||||
|
||||
const { buildEsbuildBrowser } = await import(
|
||||
'@angular-devkit/build-angular/src/builders/browser-esbuild/index'
|
||||
);
|
||||
const { buildEsbuildBrowser } = <
|
||||
typeof import('@angular-devkit/build-angular/src/builders/browser-esbuild')
|
||||
>require('@angular-devkit/build-angular/src/builders/browser-esbuild');
|
||||
|
||||
const builderContext = await createBuilderContext(
|
||||
{
|
||||
builderName: 'browser-esbuild',
|
||||
description: 'Build a browser application',
|
||||
optionSchema: await import(
|
||||
'@angular-devkit/build-angular/src/builders/browser-esbuild/schema.json'
|
||||
),
|
||||
optionSchema: require('@angular-devkit/build-angular/src/builders/browser-esbuild/schema.json'),
|
||||
},
|
||||
context
|
||||
);
|
||||
|
||||
@ -502,11 +502,12 @@
|
||||
},
|
||||
"output": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "Absolute path within the output."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["glob", "input", "output"]
|
||||
"required": ["glob", "input"]
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
|
||||
@ -29,7 +29,7 @@ export default async function* extractI18nExecutor(
|
||||
{
|
||||
builderName: 'extrct-i18n',
|
||||
description: 'Extracts i18n messages from source code.',
|
||||
optionSchema: await import('./schema.json'),
|
||||
optionSchema: require('./schema.json'),
|
||||
},
|
||||
context
|
||||
);
|
||||
|
||||
@ -25,7 +25,10 @@ import { waitForPortOpen } from '@nx/web/src/utils/wait-for-port-open';
|
||||
import fileServerExecutor from '@nx/web/src/executors/file-server/file-server.impl';
|
||||
import { createBuilderContext } from 'nx/src/adapter/ngcli-adapter';
|
||||
import { executeDevServerBuilder } from '../../builders/dev-server/dev-server.impl';
|
||||
import { validateDevRemotes } from '../../builders/utilities/module-federation';
|
||||
import {
|
||||
getDynamicMfManifestFile,
|
||||
validateDevRemotes,
|
||||
} from '../../builders/utilities/module-federation';
|
||||
import { extname, join } from 'path';
|
||||
import { existsSync } from 'fs';
|
||||
|
||||
@ -63,9 +66,7 @@ export async function* moduleFederationDevServerExecutor(
|
||||
{
|
||||
builderName: '@nx/angular:webpack-browser',
|
||||
description: 'Build a browser application',
|
||||
optionSchema: await import(
|
||||
'../../builders/webpack-browser/schema.json'
|
||||
),
|
||||
optionSchema: require('../../builders/webpack-browser/schema.json'),
|
||||
},
|
||||
context
|
||||
)
|
||||
@ -76,12 +77,10 @@ export async function* moduleFederationDevServerExecutor(
|
||||
return yield* currIter;
|
||||
}
|
||||
|
||||
let pathToManifestFile = join(
|
||||
context.root,
|
||||
project.sourceRoot,
|
||||
'assets/module-federation.manifest.json'
|
||||
);
|
||||
if (options.pathToManifestFile) {
|
||||
let pathToManifestFile: string;
|
||||
if (!options.pathToManifestFile) {
|
||||
pathToManifestFile = getDynamicMfManifestFile(project, context.root);
|
||||
} else {
|
||||
const userPathToManifestFile = join(
|
||||
context.root,
|
||||
options.pathToManifestFile
|
||||
|
||||
@ -8,13 +8,13 @@ export async function getNgPackagrInstance(
|
||||
const { major: angularMajorVersion } = getInstalledAngularVersionInfo();
|
||||
if (angularMajorVersion >= 17) {
|
||||
const { WRITE_BUNDLES_TRANSFORM } = await import(
|
||||
'./v17+/ng-package/entry-point/write-bundles.di'
|
||||
'./v17+/ng-package/entry-point/write-bundles.di.js'
|
||||
);
|
||||
const { WRITE_PACKAGE_TRANSFORM } = await import(
|
||||
'./v17+/ng-package/entry-point/write-package.di'
|
||||
'./v17+/ng-package/entry-point/write-package.di.js'
|
||||
);
|
||||
const { STYLESHEET_PROCESSOR } = await import(
|
||||
'../../utilities/ng-packagr/stylesheet-processor.di'
|
||||
'../../utilities/ng-packagr/stylesheet-processor.di.js'
|
||||
);
|
||||
|
||||
const packagr = ngPackagr();
|
||||
@ -28,11 +28,13 @@ export async function getNgPackagrInstance(
|
||||
}
|
||||
|
||||
const { NX_ENTRY_POINT_PROVIDERS } = await import(
|
||||
'./pre-v17/ng-package/entry-point/entry-point.di'
|
||||
'./pre-v17/ng-package/entry-point/entry-point.di.js'
|
||||
);
|
||||
const { nxProvideOptions } = await import(
|
||||
'./pre-v17/ng-package/options.di.js'
|
||||
);
|
||||
const { nxProvideOptions } = await import('./pre-v17/ng-package/options.di');
|
||||
const { NX_PACKAGE_PROVIDERS, NX_PACKAGE_TRANSFORM } = await import(
|
||||
'./pre-v17/ng-package/package.di'
|
||||
'./pre-v17/ng-package/package.di.js'
|
||||
);
|
||||
|
||||
const packagr = new NgPackagr([
|
||||
|
||||
@ -6,10 +6,6 @@
|
||||
* - Support Angular Compiler `incrementalDriver` for Angular < 16.
|
||||
*/
|
||||
|
||||
import type {
|
||||
CompilerOptions,
|
||||
ParsedConfiguration,
|
||||
} from '@angular/compiler-cli';
|
||||
import { BuildGraph } from 'ng-packagr/lib/graph/build-graph';
|
||||
import {
|
||||
EntryPointNode,
|
||||
@ -25,21 +21,23 @@ import * as log from 'ng-packagr/lib/utils/log';
|
||||
import { join } from 'node:path';
|
||||
import * as ts from 'typescript';
|
||||
import { getInstalledAngularVersionInfo } from '../../../../utilities/angular-version-utils';
|
||||
import { ngCompilerCli } from '../../../../utilities/ng-compiler-cli';
|
||||
import { loadEsmModule } from '../../../../utilities/module-loader';
|
||||
import { NgPackagrOptions } from '../ng-package/options.di';
|
||||
import { StylesheetProcessor } from '../styles/stylesheet-processor';
|
||||
|
||||
export async function compileSourceFiles(
|
||||
graph: BuildGraph,
|
||||
tsConfig: ParsedConfiguration,
|
||||
tsConfig: any,
|
||||
moduleResolutionCache: ts.ModuleResolutionCache,
|
||||
options: NgPackagrOptions,
|
||||
extraOptions?: Partial<CompilerOptions>,
|
||||
extraOptions?: Partial<ts.CompilerOptions>,
|
||||
stylesheetProcessor?: StylesheetProcessor
|
||||
) {
|
||||
const { NgtscProgram, formatDiagnostics } = await ngCompilerCli();
|
||||
const { NgtscProgram, formatDiagnostics } = await loadEsmModule(
|
||||
'@angular/compiler-cli'
|
||||
);
|
||||
const { cacheDirectory, watch, cacheEnabled } = options;
|
||||
const tsConfigOptions: CompilerOptions = {
|
||||
const tsConfigOptions: ts.CompilerOptions = {
|
||||
...tsConfig.options,
|
||||
...extraOptions,
|
||||
};
|
||||
|
||||
@ -6,9 +6,8 @@
|
||||
* - Added PostCSS plugin needed to support TailwindCSS.
|
||||
*/
|
||||
|
||||
import * as browserslist from 'browserslist';
|
||||
import browserslist from 'browserslist';
|
||||
import { existsSync } from 'fs';
|
||||
import { EsbuildExecutor } from 'ng-packagr/lib/esbuild/esbuild-executor';
|
||||
import {
|
||||
generateKey,
|
||||
readCacheEntry,
|
||||
@ -16,7 +15,7 @@ import {
|
||||
} from 'ng-packagr/lib/utils/cache';
|
||||
import * as log from 'ng-packagr/lib/utils/log';
|
||||
import { dirname, extname, join } from 'path';
|
||||
import * as autoprefixer from 'autoprefixer';
|
||||
import autoprefixer from 'autoprefixer';
|
||||
import * as postcssUrl from 'postcss-url';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
import {
|
||||
@ -49,7 +48,8 @@ export class StylesheetProcessor {
|
||||
private browserslistData: string[];
|
||||
private targets: string[];
|
||||
private postCssProcessor: ReturnType<typeof postcss>;
|
||||
private esbuild = new EsbuildExecutor();
|
||||
private esbuild =
|
||||
new (require('ng-packagr/lib/esbuild/esbuild-executor').EsbuildExecutor)();
|
||||
private styleIncludePaths: string[];
|
||||
|
||||
constructor(
|
||||
|
||||
@ -354,12 +354,6 @@ type ConditionalExport = {
|
||||
esm2022?: string;
|
||||
esm?: string;
|
||||
default?: string;
|
||||
|
||||
// backward compat for Angular < 16
|
||||
node?: string;
|
||||
esm2020?: string;
|
||||
es2020?: string;
|
||||
es2015?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
"outputCapture": "direct-nodejs",
|
||||
"$schema": "https://json-schema.org/schema",
|
||||
"title": "ng-packagr Target",
|
||||
"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 with some key differences:\n- For supported Angular versions lower than v16, it doesn't run `ngcc` automatically (`ngcc` is no longer available from Angular v16 onwards, for lower versions, it needs to be run separately beforehand if needed, this can be done in a `postinstall` hook on `package.json`).\n- It only produces ESM2022 bundles (ESM2020 for supported Angular versions lower than v16).",
|
||||
"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.",
|
||||
"cli": "nx",
|
||||
"type": "object",
|
||||
"presets": [
|
||||
|
||||
@ -19,7 +19,7 @@ import {
|
||||
PackageNode,
|
||||
} from 'ng-packagr/lib/ng-package/nodes';
|
||||
import { setDependenciesTsConfigPaths } from 'ng-packagr/lib/ts/tsconfig';
|
||||
import * as ora from 'ora';
|
||||
import ora from 'ora';
|
||||
import * as path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
import { getInstalledAngularVersionInfo } from '../../../../utilities/angular-version-utils';
|
||||
|
||||
@ -8,7 +8,7 @@ export async function getNgPackagrInstance(
|
||||
const { major: angularMajorVersion } = getInstalledAngularVersionInfo();
|
||||
if (angularMajorVersion >= 17) {
|
||||
const { STYLESHEET_PROCESSOR } = await import(
|
||||
'../../utilities/ng-packagr/stylesheet-processor.di'
|
||||
'../../utilities/ng-packagr/stylesheet-processor.di.js'
|
||||
);
|
||||
|
||||
const packagr = ngPackagr();
|
||||
@ -17,11 +17,11 @@ export async function getNgPackagrInstance(
|
||||
}
|
||||
|
||||
const { NX_ENTRY_POINT_PROVIDERS } = await import(
|
||||
'./ng-package/entry-point/entry-point.di'
|
||||
'./ng-package/entry-point/entry-point.di.js'
|
||||
);
|
||||
const { nxProvideOptions } = await import('./ng-package/options.di');
|
||||
const { nxProvideOptions } = await import('./ng-package/options.di.js');
|
||||
const { NX_PACKAGE_PROVIDERS, NX_PACKAGE_TRANSFORM } = await import(
|
||||
'./ng-package/package.di'
|
||||
'./ng-package/package.di.js'
|
||||
);
|
||||
|
||||
const packagr = new NgPackagr([
|
||||
|
||||
@ -7,10 +7,6 @@
|
||||
* - Support Angular Compiler `incrementalDriver` for Angular < 16.
|
||||
*/
|
||||
|
||||
import type {
|
||||
CompilerOptions,
|
||||
ParsedConfiguration,
|
||||
} from '@angular/compiler-cli';
|
||||
import { BuildGraph } from 'ng-packagr/lib/graph/build-graph';
|
||||
import {
|
||||
EntryPointNode,
|
||||
@ -26,22 +22,24 @@ import {
|
||||
import { join } from 'node:path';
|
||||
import * as ts from 'typescript';
|
||||
import { getInstalledAngularVersionInfo } from '../../../utilities/angular-version-utils';
|
||||
import { ngCompilerCli } from '../../../utilities/ng-compiler-cli';
|
||||
import { loadEsmModule } from '../../../utilities/module-loader';
|
||||
import { NgPackagrOptions } from '../ng-package/options.di';
|
||||
import { StylesheetProcessor } from '../styles/stylesheet-processor';
|
||||
|
||||
export async function compileSourceFiles(
|
||||
graph: BuildGraph,
|
||||
tsConfig: ParsedConfiguration,
|
||||
tsConfig: any,
|
||||
moduleResolutionCache: ts.ModuleResolutionCache,
|
||||
options: NgPackagrOptions,
|
||||
extraOptions?: Partial<CompilerOptions>,
|
||||
extraOptions?: Partial<ts.CompilerOptions>,
|
||||
stylesheetProcessor?: StylesheetProcessor,
|
||||
ngccProcessor?: any
|
||||
) {
|
||||
const { NgtscProgram, formatDiagnostics } = await ngCompilerCli();
|
||||
const { NgtscProgram, formatDiagnostics } = await loadEsmModule(
|
||||
'@angular/compiler-cli'
|
||||
);
|
||||
const { cacheDirectory, watch, cacheEnabled } = options;
|
||||
const tsConfigOptions: CompilerOptions = {
|
||||
const tsConfigOptions: ts.CompilerOptions = {
|
||||
...tsConfig.options,
|
||||
...extraOptions,
|
||||
};
|
||||
|
||||
@ -6,9 +6,8 @@
|
||||
* - Added PostCSS plugin needed to support TailwindCSS.
|
||||
*/
|
||||
|
||||
import * as browserslist from 'browserslist';
|
||||
import browserslist from 'browserslist';
|
||||
import { existsSync } from 'fs';
|
||||
import { EsbuildExecutor } from 'ng-packagr/lib/esbuild/esbuild-executor';
|
||||
import {
|
||||
generateKey,
|
||||
readCacheEntry,
|
||||
@ -16,7 +15,7 @@ import {
|
||||
} from 'ng-packagr/lib/utils/cache';
|
||||
import * as log from 'ng-packagr/lib/utils/log';
|
||||
import { dirname, extname, join } from 'path';
|
||||
import * as autoprefixer from 'autoprefixer';
|
||||
import autoprefixer from 'autoprefixer';
|
||||
import * as postcssUrl from 'postcss-url';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
import {
|
||||
@ -42,7 +41,8 @@ export class StylesheetProcessor {
|
||||
private browserslistData: string[];
|
||||
private targets: string[];
|
||||
private postCssProcessor: ReturnType<typeof postcss>;
|
||||
private esbuild = new EsbuildExecutor();
|
||||
private esbuild =
|
||||
new (require('ng-packagr/lib/esbuild/esbuild-executor').EsbuildExecutor)();
|
||||
private styleIncludePaths: string[];
|
||||
|
||||
constructor(
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
import type { IndexHtmlTransform } from '@angular-devkit/build-angular/src/utils/index-file/index-html-generator';
|
||||
import { registerTsProject } from '@nx/js/src/internal';
|
||||
import type { Plugin } from 'esbuild';
|
||||
import type { Connect } from 'vite';
|
||||
import { loadModule } from './module-loader';
|
||||
|
||||
export type PluginSpec = {
|
||||
@ -45,7 +43,7 @@ async function loadPlugin(pluginSpec: string | PluginSpec): Promise<Plugin> {
|
||||
export async function loadMiddleware(
|
||||
middlewareFns: string[] | undefined,
|
||||
tsConfig: string
|
||||
): Promise<Connect.NextHandleFunction[]> {
|
||||
): Promise<any[]> {
|
||||
if (!middlewareFns?.length) {
|
||||
return [];
|
||||
}
|
||||
@ -61,7 +59,7 @@ export async function loadMiddleware(
|
||||
export async function loadIndexHtmlTransformer(
|
||||
indexHtmlTransformerPath: string,
|
||||
tsConfig: string
|
||||
): Promise<IndexHtmlTransform> {
|
||||
): Promise<any> {
|
||||
const cleanupTranspiler = registerTsProject(tsConfig);
|
||||
|
||||
try {
|
||||
|
||||
@ -45,7 +45,7 @@ let load: (<T>(modulePath: string | URL) => Promise<T>) | undefined;
|
||||
* @param modulePath The path of the module to load.
|
||||
* @returns A Promise that resolves to the dynamically imported module.
|
||||
*/
|
||||
export function loadEsmModule<T>(modulePath: string | URL): Promise<T> {
|
||||
export function loadEsmModule<T = any>(modulePath: string | URL): Promise<T> {
|
||||
load ??= new Function('modulePath', `return import(modulePath);`) as Exclude<
|
||||
typeof load,
|
||||
undefined
|
||||
|
||||
@ -1,7 +0,0 @@
|
||||
import { loadEsmModule } from './module-loader';
|
||||
|
||||
export function ngCompilerCli(): Promise<
|
||||
typeof import('@angular/compiler-cli')
|
||||
> {
|
||||
return loadEsmModule('@angular/compiler-cli');
|
||||
}
|
||||
@ -6,7 +6,7 @@
|
||||
* config at the root of the workspace.
|
||||
*/
|
||||
|
||||
import * as browserslist from 'browserslist';
|
||||
import browserslist from 'browserslist';
|
||||
import { existsSync } from 'fs';
|
||||
import { dirname, join } from 'path';
|
||||
const Piscina = require('piscina');
|
||||
@ -15,7 +15,7 @@ import { colors } from 'ng-packagr/lib/utils/color';
|
||||
import { getTailwindConfigPath } from './tailwindcss';
|
||||
import { workspaceRoot } from '@nx/devkit';
|
||||
import type { PostcssConfiguration } from 'ng-packagr/lib/styles/postcss-configuration';
|
||||
import { gt } from 'semver';
|
||||
import { gt, gte } from 'semver';
|
||||
import { getInstalledPackageVersionInfo } from '../angular-version-utils';
|
||||
|
||||
const maxWorkersVariable = process.env['NG_BUILD_MAX_WORKERS'];
|
||||
@ -93,12 +93,31 @@ export class StylesheetProcessor {
|
||||
|
||||
const { version: ngPackagrVersion } =
|
||||
getInstalledPackageVersionInfo('ng-packagr');
|
||||
let tailwindConfigPath: string | undefined;
|
||||
let postcssConfiguration: PostcssConfiguration | undefined;
|
||||
if (gt(ngPackagrVersion, '17.2.0')) {
|
||||
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);
|
||||
// (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
|
||||
);
|
||||
}
|
||||
|
||||
this.renderWorker = new Piscina({
|
||||
@ -113,10 +132,7 @@ export class StylesheetProcessor {
|
||||
},
|
||||
workerData: {
|
||||
postcssConfiguration,
|
||||
tailwindConfigPath: getTailwindConfigPath(
|
||||
this.projectBasePath,
|
||||
workspaceRoot
|
||||
),
|
||||
tailwindConfigPath,
|
||||
projectBasePath: this.projectBasePath,
|
||||
browserslistData,
|
||||
targets: transformSupportedBrowsersToTargets(browserslistData),
|
||||
@ -200,9 +216,9 @@ export class AsyncStylesheetProcessor {
|
||||
getInstalledPackageVersionInfo('ng-packagr');
|
||||
let postcssConfiguration: PostcssConfiguration | undefined;
|
||||
if (ngPackagrVersion === '17.2.0') {
|
||||
const { loadPostcssConfiguration } = await import(
|
||||
'ng-packagr/lib/styles/postcss-configuration'
|
||||
);
|
||||
const {
|
||||
loadPostcssConfiguration,
|
||||
} = require('ng-packagr/lib/styles/postcss-configuration');
|
||||
postcssConfiguration = await loadPostcssConfiguration(
|
||||
this.projectBasePath
|
||||
);
|
||||
|
||||
@ -7,19 +7,13 @@
|
||||
* to be the original tsconfig file.
|
||||
*/
|
||||
|
||||
import type {
|
||||
CompilerOptions,
|
||||
ParsedConfiguration,
|
||||
} from '@angular/compiler-cli';
|
||||
import { resolve } from 'path';
|
||||
import * as ts from 'typescript';
|
||||
import { ngCompilerCli } from './ng-compiler-cli';
|
||||
import { loadEsmModule } from './module-loader';
|
||||
|
||||
async function readDefaultTsConfig(
|
||||
fileName: string
|
||||
): Promise<ParsedConfiguration> {
|
||||
async function readDefaultTsConfig(fileName: string) {
|
||||
// these options are mandatory
|
||||
const extraOptions: CompilerOptions = {
|
||||
const extraOptions: ts.CompilerOptions = {
|
||||
target: ts.ScriptTarget.ES2020,
|
||||
experimentalDecorators: true,
|
||||
|
||||
@ -39,7 +33,7 @@ async function readDefaultTsConfig(
|
||||
flatModuleOutFile: 'AUTOGENERATED',
|
||||
};
|
||||
|
||||
const { readConfiguration } = await ngCompilerCli();
|
||||
const { readConfiguration } = await loadEsmModule('@angular/compiler-cli');
|
||||
|
||||
return readConfiguration(fileName, extraOptions);
|
||||
}
|
||||
@ -51,7 +45,7 @@ export async function parseRemappedTsConfigAndMergeDefaults(
|
||||
workspaceRoot: string,
|
||||
originalFilePath: string,
|
||||
remappedFilePath: string
|
||||
): Promise<ParsedConfiguration> {
|
||||
) {
|
||||
const parsedConfiguration = await readDefaultTsConfig(remappedFilePath);
|
||||
parsedConfiguration.options.configFilePath = resolve(
|
||||
workspaceRoot,
|
||||
|
||||
@ -243,8 +243,10 @@ exports[`app --project-name-and-root-format=derived should generate correctly wh
|
||||
"executor": "@angular-devkit/build-angular:application",
|
||||
"options": {
|
||||
"assets": [
|
||||
"apps/my-dir/my-app/src/favicon.ico",
|
||||
"apps/my-dir/my-app/src/assets",
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "apps/my-dir/my-app/public",
|
||||
},
|
||||
],
|
||||
"browser": "apps/my-dir/my-app/src/main.ts",
|
||||
"index": "apps/my-dir/my-app/src/index.html",
|
||||
@ -444,8 +446,10 @@ exports[`app --project-name-and-root-format=derived should generate correctly wh
|
||||
"executor": "@angular-devkit/build-angular:application",
|
||||
"options": {
|
||||
"assets": [
|
||||
"apps/my-app/src/favicon.ico",
|
||||
"apps/my-app/src/assets",
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "apps/my-app/public",
|
||||
},
|
||||
],
|
||||
"browser": "apps/my-app/src/main.ts",
|
||||
"index": "apps/my-app/src/index.html",
|
||||
@ -620,12 +624,12 @@ bootstrapApplication(AppComponent, appConfig).catch((err) =>
|
||||
`;
|
||||
|
||||
exports[`app --standalone should generate a standalone app correctly with routing 2`] = `
|
||||
"import { ApplicationConfig } from '@angular/core';
|
||||
"import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
|
||||
import { provideRouter } from '@angular/router';
|
||||
import { appRoutes } from './app.routes';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [provideRouter(appRoutes) ]
|
||||
providers: [provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(appRoutes) ]
|
||||
};
|
||||
"
|
||||
`;
|
||||
@ -698,10 +702,10 @@ bootstrapApplication(AppComponent, appConfig).catch((err) =>
|
||||
`;
|
||||
|
||||
exports[`app --standalone should generate a standalone app correctly without routing 2`] = `
|
||||
"import { ApplicationConfig } from '@angular/core';
|
||||
"import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: []
|
||||
providers: [provideZoneChangeDetection({ eventCoalescing: true }), ]
|
||||
};
|
||||
"
|
||||
`;
|
||||
@ -753,6 +757,17 @@ describe('AppComponent', () => {
|
||||
"
|
||||
`;
|
||||
|
||||
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) ]
|
||||
};
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`app --strict should enable strict type checking: app tsconfig.json 1`] = `
|
||||
{
|
||||
"angularCompilerOptions": {
|
||||
@ -819,8 +834,8 @@ exports[`app --strict should enable strict type checking: e2e tsconfig.json 1`]
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`app angular v15 support should import "ApplicationConfig" from "@angular/platform-browser" 1`] = `
|
||||
"import { ApplicationConfig } from '@angular/platform-browser';
|
||||
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';
|
||||
|
||||
@ -940,8 +955,10 @@ exports[`app nested should create project configs 1`] = `
|
||||
"executor": "@angular-devkit/build-angular:application",
|
||||
"options": {
|
||||
"assets": [
|
||||
"my-dir/my-app/src/favicon.ico",
|
||||
"my-dir/my-app/src/assets",
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "my-dir/my-app/public",
|
||||
},
|
||||
],
|
||||
"browser": "my-dir/my-app/src/main.ts",
|
||||
"index": "my-dir/my-app/src/index.html",
|
||||
@ -1054,8 +1071,10 @@ exports[`app not nested should create project configs 1`] = `
|
||||
"executor": "@angular-devkit/build-angular:application",
|
||||
"options": {
|
||||
"assets": [
|
||||
"my-app/src/favicon.ico",
|
||||
"my-app/src/assets",
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "my-app/public",
|
||||
},
|
||||
],
|
||||
"browser": "my-app/src/main.ts",
|
||||
"index": "my-app/src/index.html",
|
||||
|
||||
@ -896,6 +896,19 @@ describe('app', () => {
|
||||
appTree.read('standalone/src/app/nx-welcome.component.ts', 'utf-8')
|
||||
).toContain('standalone: true');
|
||||
});
|
||||
|
||||
it('should should not use event coalescing in versions lower than v18', async () => {
|
||||
updateJson(appTree, 'package.json', (json) => ({
|
||||
...json,
|
||||
dependencies: { ...json.dependencies, '@angular/core': '~17.0.0' },
|
||||
}));
|
||||
|
||||
await generateApp(appTree, 'standalone', { standalone: true });
|
||||
|
||||
expect(
|
||||
appTree.read('standalone/src/app/app.config.ts', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
it('should generate correct main.ts', async () => {
|
||||
@ -903,6 +916,27 @@ describe('app', () => {
|
||||
await generateApp(appTree, 'myapp');
|
||||
|
||||
// ASSERT
|
||||
expect(appTree.read('myapp/src/main.ts', 'utf-8')).toMatchInlineSnapshot(`
|
||||
"import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
import { AppModule } from './app/app.module';
|
||||
|
||||
platformBrowserDynamic()
|
||||
.bootstrapModule(AppModule, {
|
||||
ngZoneEventCoalescing: true
|
||||
})
|
||||
.catch((err) => console.error(err));
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should should not use event coalescing in versions lower than v18', async () => {
|
||||
updateJson(appTree, 'package.json', (json) => ({
|
||||
...json,
|
||||
dependencies: { ...json.dependencies, '@angular/core': '~17.0.0' },
|
||||
}));
|
||||
|
||||
await generateApp(appTree, 'myapp');
|
||||
|
||||
expect(appTree.read('myapp/src/main.ts', 'utf-8')).toMatchInlineSnapshot(`
|
||||
"import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
import { AppModule } from './app/app.module';
|
||||
@ -1190,14 +1224,14 @@ describe('app', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('angular v15 support', () => {
|
||||
describe('angular compat support', () => {
|
||||
beforeEach(() => {
|
||||
appTree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
updateJson(appTree, 'package.json', (json) => ({
|
||||
...json,
|
||||
dependencies: {
|
||||
...json.dependencies,
|
||||
'@angular/core': '~15.2.0',
|
||||
'@angular/core': '~16.2.0',
|
||||
},
|
||||
}));
|
||||
});
|
||||
@ -1207,13 +1241,13 @@ describe('app', () => {
|
||||
|
||||
const { devDependencies } = readJson(appTree, 'package.json');
|
||||
expect(devDependencies['@angular-devkit/build-angular']).toEqual(
|
||||
backwardCompatibleVersions.angularV15.angularDevkitVersion
|
||||
backwardCompatibleVersions.angularV16.angularDevkitVersion
|
||||
);
|
||||
expect(devDependencies['@angular-devkit/schematics']).toEqual(
|
||||
backwardCompatibleVersions.angularV15.angularDevkitVersion
|
||||
backwardCompatibleVersions.angularV16.angularDevkitVersion
|
||||
);
|
||||
expect(devDependencies['@schematics/angular']).toEqual(
|
||||
backwardCompatibleVersions.angularV15.angularDevkitVersion
|
||||
backwardCompatibleVersions.angularV16.angularDevkitVersion
|
||||
);
|
||||
});
|
||||
|
||||
@ -1264,6 +1298,21 @@ describe('app', () => {
|
||||
.esModuleInterop
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should configure the correct assets for versions lower than v18', async () => {
|
||||
updateJson(appTree, 'package.json', (json) => ({
|
||||
...json,
|
||||
dependencies: { ...json.dependencies, '@angular/core': '~17.0.0' },
|
||||
}));
|
||||
|
||||
await generateApp(appTree, 'my-app', { rootProject: true });
|
||||
|
||||
const project = readProjectConfiguration(appTree, 'my-app');
|
||||
expect(project.targets.build.options.assets).toStrictEqual([
|
||||
'./src/favicon.ico',
|
||||
'./src/assets',
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
@ -2,5 +2,7 @@ import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
import { AppModule } from './app/app.module';
|
||||
|
||||
platformBrowserDynamic()
|
||||
.bootstrapModule(AppModule)
|
||||
.bootstrapModule(AppModule<% if (useEventCoalescing) { %>, {
|
||||
ngZoneEventCoalescing: true
|
||||
}<% } %>)
|
||||
.catch((err) => console.error(err));
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { ApplicationConfig } from <% if (angularMajorVersion >= 16) { %>'@angular/core';<% } else { %>'@angular/platform-browser';<% } %><% if (routing) { %>
|
||||
import { ApplicationConfig<% if (useEventCoalescing) { %>, provideZoneChangeDetection<% } %> } from '@angular/core';<% if (routing) { %>
|
||||
import { provideRouter } from '@angular/router';
|
||||
import { appRoutes } from './app.routes';<% } %>
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [<% if (routing) { %>provideRouter(appRoutes) <% } %>]
|
||||
providers: [<% if (useEventCoalescing) { %>provideZoneChangeDetection({ eventCoalescing: true }), <% } %><% if (routing) { %>provideRouter(appRoutes) <% } %>]
|
||||
};
|
||||
|
||||
@ -36,6 +36,7 @@ export async function createFiles(
|
||||
angularMajorVersion,
|
||||
rootOffset,
|
||||
isUsingApplicationBuilder,
|
||||
useEventCoalescing: angularMajorVersion >= 18,
|
||||
tpl: '',
|
||||
};
|
||||
|
||||
@ -46,6 +47,22 @@ export async function createFiles(
|
||||
substitutions
|
||||
);
|
||||
|
||||
if (angularMajorVersion >= 18) {
|
||||
generateFiles(
|
||||
tree,
|
||||
joinPathFragments(__dirname, '../files/base-18+'),
|
||||
options.appProjectRoot,
|
||||
substitutions
|
||||
);
|
||||
} else {
|
||||
generateFiles(
|
||||
tree,
|
||||
joinPathFragments(__dirname, '../files/base-pre18'),
|
||||
options.appProjectRoot,
|
||||
substitutions
|
||||
);
|
||||
}
|
||||
|
||||
if (options.standalone) {
|
||||
generateFiles(
|
||||
tree,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { addProjectConfiguration, Tree } from '@nx/devkit';
|
||||
import { addProjectConfiguration, joinPathFragments, Tree } from '@nx/devkit';
|
||||
import type { AngularProjectConfiguration } from '../../../utils/types';
|
||||
import { getInstalledAngularVersionInfo } from '../../utils/version-utils';
|
||||
import type { NormalizedSchema } from './normalized-schema';
|
||||
@ -64,12 +64,23 @@ export function createProject(tree: Tree, options: NormalizedSchema) {
|
||||
index: `${options.appProjectSourceRoot}/index.html`,
|
||||
[buildMainOptionName]: `${options.appProjectSourceRoot}/main.ts`,
|
||||
polyfills: ['zone.js'],
|
||||
tsConfig: `${options.appProjectRoot}/tsconfig.app.json`,
|
||||
tsConfig: joinPathFragments(
|
||||
options.appProjectRoot,
|
||||
'tsconfig.app.json'
|
||||
),
|
||||
inlineStyleLanguage,
|
||||
assets: [
|
||||
`${options.appProjectSourceRoot}/favicon.ico`,
|
||||
`${options.appProjectSourceRoot}/assets`,
|
||||
],
|
||||
assets:
|
||||
angularMajorVersion >= 18
|
||||
? [
|
||||
{
|
||||
glob: '**/*',
|
||||
input: joinPathFragments(options.appProjectRoot, 'public'),
|
||||
},
|
||||
]
|
||||
: [
|
||||
`${options.appProjectSourceRoot}/favicon.ico`,
|
||||
`${options.appProjectSourceRoot}/assets`,
|
||||
],
|
||||
styles: [`${options.appProjectSourceRoot}/styles.${options.style}`],
|
||||
scripts: [],
|
||||
},
|
||||
|
||||
@ -18,9 +18,9 @@ export async function componentTestGenerator(
|
||||
options: ComponentTestSchema
|
||||
) {
|
||||
ensurePackage('@nx/cypress', nxVersion);
|
||||
const { assertMinimumCypressVersion } = await import(
|
||||
'@nx/cypress/src/utils/cypress-version'
|
||||
);
|
||||
const { assertMinimumCypressVersion } = <
|
||||
typeof import('@nx/cypress/src/utils/cypress-version')
|
||||
>require('@nx/cypress/src/utils/cypress-version');
|
||||
assertMinimumCypressVersion(10);
|
||||
const { root } = readProjectConfiguration(tree, options.project);
|
||||
const componentDirPath = joinPathFragments(root, options.componentDir);
|
||||
|
||||
@ -74,7 +74,9 @@ async function addFiles(
|
||||
'support',
|
||||
'component.ts'
|
||||
);
|
||||
const { addMountDefinition } = await import('@nx/cypress/src/utils/config');
|
||||
const { addMountDefinition } = <
|
||||
typeof import('@nx/cypress/src/utils/config')
|
||||
>require('@nx/cypress/src/utils/config');
|
||||
const updatedCmpContents = await addMountDefinition(
|
||||
tree.read(componentFile, 'utf-8')
|
||||
);
|
||||
@ -126,9 +128,9 @@ async function configureCypressCT(
|
||||
let found: FoundTarget = { target: options.buildTarget, config: undefined };
|
||||
|
||||
if (!options.buildTarget) {
|
||||
const { findBuildConfig } = await import(
|
||||
'@nx/cypress/src/utils/find-target-options'
|
||||
);
|
||||
const { findBuildConfig } = <
|
||||
typeof import('@nx/cypress/src/utils/find-target-options')
|
||||
>require('@nx/cypress/src/utils/find-target-options');
|
||||
found = await findBuildConfig(tree, {
|
||||
project: options.project,
|
||||
buildTarget: options.buildTarget,
|
||||
@ -158,9 +160,9 @@ async function configureCypressCT(
|
||||
ctConfigOptions.buildTarget = found.target;
|
||||
}
|
||||
|
||||
const { addDefaultCTConfig, getProjectCypressConfigPath } = await import(
|
||||
'@nx/cypress/src/utils/config'
|
||||
);
|
||||
const { addDefaultCTConfig, getProjectCypressConfigPath } = <
|
||||
typeof import('@nx/cypress/src/utils/config')
|
||||
>require('@nx/cypress/src/utils/config');
|
||||
const cypressConfigPath = getProjectCypressConfigPath(
|
||||
tree,
|
||||
projectConfig.root
|
||||
|
||||
@ -96,7 +96,9 @@ exports[`Host App Generator --ssr should generate the correct files 2`] = `
|
||||
import { AppModule } from './app/app.module';
|
||||
|
||||
platformBrowserDynamic()
|
||||
.bootstrapModule(AppModule)
|
||||
.bootstrapModule(AppModule, {
|
||||
ngZoneEventCoalescing: true,
|
||||
})
|
||||
.catch((err) => console.error(err));
|
||||
"
|
||||
`;
|
||||
@ -408,13 +410,17 @@ export const appRoutes: Route[] = [
|
||||
`;
|
||||
|
||||
exports[`Host App Generator --ssr should generate the correct files for standalone 8`] = `
|
||||
"import { ApplicationConfig } from '@angular/core';
|
||||
"import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
|
||||
import { provideRouter } from '@angular/router';
|
||||
import { appRoutes } from './app.routes';
|
||||
import { provideClientHydration } from '@angular/platform-browser';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [provideClientHydration(), provideRouter(appRoutes)],
|
||||
providers: [
|
||||
provideClientHydration(),
|
||||
provideZoneChangeDetection({ eventCoalescing: true }),
|
||||
provideRouter(appRoutes),
|
||||
],
|
||||
};
|
||||
"
|
||||
`;
|
||||
@ -619,13 +625,17 @@ export const appRoutes: Route[] = [
|
||||
`;
|
||||
|
||||
exports[`Host App Generator --ssr should generate the correct files for standalone when --typescript=true 8`] = `
|
||||
"import { ApplicationConfig } from '@angular/core';
|
||||
"import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
|
||||
import { provideRouter } from '@angular/router';
|
||||
import { appRoutes } from './app.routes';
|
||||
import { provideClientHydration } from '@angular/platform-browser';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [provideClientHydration(), provideRouter(appRoutes)],
|
||||
providers: [
|
||||
provideClientHydration(),
|
||||
provideZoneChangeDetection({ eventCoalescing: true }),
|
||||
provideRouter(appRoutes),
|
||||
],
|
||||
};
|
||||
"
|
||||
`;
|
||||
@ -716,7 +726,9 @@ exports[`Host App Generator --ssr should generate the correct files when --types
|
||||
import { AppModule } from './app/app.module';
|
||||
|
||||
platformBrowserDynamic()
|
||||
.bootstrapModule(AppModule)
|
||||
.bootstrapModule(AppModule, {
|
||||
ngZoneEventCoalescing: true,
|
||||
})
|
||||
.catch((err) => console.error(err));
|
||||
"
|
||||
`;
|
||||
|
||||
@ -73,8 +73,9 @@ export class AngularDevkitKarmaMigrator extends BuilderMigrator {
|
||||
target.options.main =
|
||||
target.options.main && this.convertAsset(target.options.main);
|
||||
target.options.polyfills = Array.isArray(target.options.polyfills)
|
||||
? target.options.polyfills.map((p) => this.convertAsset(p))
|
||||
: target.options.polyfills && this.convertAsset(target.options.polyfills);
|
||||
? target.options.polyfills.map((p) => this.convertSourceRootPath(p))
|
||||
: target.options.polyfills &&
|
||||
this.convertSourceRootPath(target.options.polyfills);
|
||||
target.options.tsConfig =
|
||||
target.options.tsConfig &&
|
||||
joinPathFragments(
|
||||
|
||||
@ -36,9 +36,9 @@ export abstract class Migrator {
|
||||
|
||||
protected convertAsset(asset: string | any): string | any {
|
||||
if (typeof asset === 'string') {
|
||||
return this.convertSourceRootPath(asset);
|
||||
return this.convertRootPath(asset);
|
||||
} else {
|
||||
return { ...asset, input: this.convertSourceRootPath(asset.input) };
|
||||
return { ...asset, input: this.convertRootPath(asset.input) };
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,6 +51,15 @@ export abstract class Migrator {
|
||||
: originalPath;
|
||||
}
|
||||
|
||||
protected convertSourceRootPath(originalPath: string): string {
|
||||
return originalPath?.startsWith(this.project.oldSourceRoot)
|
||||
? joinPathFragments(
|
||||
this.project.newSourceRoot,
|
||||
originalPath.replace(this.project.oldSourceRoot, '')
|
||||
)
|
||||
: originalPath;
|
||||
}
|
||||
|
||||
protected moveFile(from: string, to: string, required: boolean = true): void {
|
||||
if (!this.tree.exists(from)) {
|
||||
if (required) {
|
||||
@ -120,15 +129,6 @@ export abstract class Migrator {
|
||||
});
|
||||
}
|
||||
|
||||
private convertSourceRootPath(originalPath: string): string {
|
||||
return originalPath?.startsWith(this.project.oldSourceRoot)
|
||||
? joinPathFragments(
|
||||
this.project.newSourceRoot,
|
||||
originalPath.replace(this.project.oldSourceRoot, '')
|
||||
)
|
||||
: originalPath;
|
||||
}
|
||||
|
||||
private getTargetValuesForOption(
|
||||
target: TargetConfiguration,
|
||||
optionPath: string
|
||||
|
||||
@ -257,8 +257,10 @@ export class AppMigrator extends ProjectMigrator<SupportedTargets> {
|
||||
buildOptions.polyfills =
|
||||
buildOptions.polyfills &&
|
||||
(Array.isArray(buildOptions.polyfills)
|
||||
? buildOptions.polyfills.map((asset) => this.convertAsset(asset))
|
||||
: this.convertAsset(buildOptions.polyfills as string));
|
||||
? buildOptions.polyfills.map((asset) =>
|
||||
this.convertSourceRootPath(asset)
|
||||
)
|
||||
: this.convertSourceRootPath(buildOptions.polyfills));
|
||||
buildOptions.tsConfig =
|
||||
buildOptions.tsConfig &&
|
||||
joinPathFragments(this.project.newRoot, basename(buildOptions.tsConfig));
|
||||
|
||||
@ -343,7 +343,9 @@ export class E2eMigrator extends ProjectMigrator<SupportedTargets> {
|
||||
const addPlugin =
|
||||
process.env.NX_ADD_PLUGINS !== 'false' &&
|
||||
nxJson.useInferencePlugins !== false;
|
||||
const { configurationGenerator } = await import('@nx/cypress');
|
||||
const { configurationGenerator } = <typeof import('@nx/cypress')>(
|
||||
require('@nx/cypress')
|
||||
);
|
||||
await configurationGenerator(this.tree, {
|
||||
project: this.project.name,
|
||||
linter: this.isProjectUsingEsLint ? Linter.EsLint : Linter.None,
|
||||
|
||||
@ -426,7 +426,7 @@ export class AppModule {}
|
||||
`;
|
||||
|
||||
exports[`NgRxRootStoreGenerator Standalone APIs should add a facade when --facade=true 1`] = `
|
||||
"import { ApplicationConfig } from '@angular/core';
|
||||
"import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
|
||||
import { provideRouter } from '@angular/router';
|
||||
import { appRoutes } from './app.routes';
|
||||
import { provideStore, provideState } from '@ngrx/store';
|
||||
@ -436,7 +436,7 @@ import { UsersEffects } from './+state/users.effects';
|
||||
import { UsersFacade } from './+state/users.facade';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [provideEffects(UsersEffects),provideState(fromUsers.USERS_FEATURE_KEY, fromUsers.usersReducer),UsersFacade,provideEffects(),provideStore(),provideRouter(appRoutes) ]
|
||||
providers: [provideEffects(UsersEffects),provideState(fromUsers.USERS_FEATURE_KEY, fromUsers.usersReducer),UsersFacade,provideEffects(),provideStore(),provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(appRoutes) ]
|
||||
};
|
||||
"
|
||||
`;
|
||||
@ -473,7 +473,7 @@ export class UsersFacade {
|
||||
`;
|
||||
|
||||
exports[`NgRxRootStoreGenerator Standalone APIs should add a root module and root state when --minimal=false 1`] = `
|
||||
"import { ApplicationConfig } from '@angular/core';
|
||||
"import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
|
||||
import { provideRouter } from '@angular/router';
|
||||
import { appRoutes } from './app.routes';
|
||||
import { provideStore, provideState } from '@ngrx/store';
|
||||
@ -482,7 +482,7 @@ import * as fromUsers from './+state/users.reducer';
|
||||
import { UsersEffects } from './+state/users.effects';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [provideEffects(UsersEffects),provideState(fromUsers.USERS_FEATURE_KEY, fromUsers.usersReducer),provideEffects(),provideStore(),provideRouter(appRoutes) ]
|
||||
providers: [provideEffects(UsersEffects),provideState(fromUsers.USERS_FEATURE_KEY, fromUsers.usersReducer),provideEffects(),provideStore(),provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(appRoutes) ]
|
||||
};
|
||||
"
|
||||
`;
|
||||
@ -722,20 +722,20 @@ describe('Users Selectors', () => {
|
||||
`;
|
||||
|
||||
exports[`NgRxRootStoreGenerator Standalone APIs should add an empty root module when --minimal=true 1`] = `
|
||||
"import { ApplicationConfig } from '@angular/core';
|
||||
"import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
|
||||
import { provideRouter } from '@angular/router';
|
||||
import { appRoutes } from './app.routes';
|
||||
import { provideStore } from '@ngrx/store';
|
||||
import { provideEffects } from '@ngrx/effects';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [provideEffects(),provideStore(),provideRouter(appRoutes) ]
|
||||
providers: [provideEffects(),provideStore(),provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(appRoutes) ]
|
||||
};
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`NgRxRootStoreGenerator Standalone APIs should instrument the store devtools when "addDevTools: true" 1`] = `
|
||||
"import { ApplicationConfig, isDevMode } from '@angular/core';
|
||||
"import { ApplicationConfig, provideZoneChangeDetection, isDevMode } from '@angular/core';
|
||||
import { provideRouter } from '@angular/router';
|
||||
import { appRoutes } from './app.routes';
|
||||
import { provideStore } from '@ngrx/store';
|
||||
@ -743,7 +743,7 @@ import { provideEffects } from '@ngrx/effects';
|
||||
import { provideStoreDevtools } from '@ngrx/store-devtools';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [provideStoreDevtools({ logOnly: !isDevMode() }),provideEffects(),provideStore(),provideRouter(appRoutes) ]
|
||||
providers: [provideStoreDevtools({ logOnly: !isDevMode() }),provideEffects(),provideStore(),provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(appRoutes) ]
|
||||
};
|
||||
"
|
||||
`;
|
||||
|
||||
@ -756,7 +756,7 @@ bootstrapApplication(AppComponent, appConfig).catch((err) =>
|
||||
`;
|
||||
|
||||
exports[`ngrx Standalone APIs should add a root module with feature module when minimal is set to false 2`] = `
|
||||
"import { ApplicationConfig } from '@angular/core';
|
||||
"import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
|
||||
import { provideRouter } from '@angular/router';
|
||||
import { appRoutes } from './app.routes';
|
||||
import { provideStore, provideState } from '@ngrx/store';
|
||||
@ -765,7 +765,7 @@ import * as fromUsers from './+state/users.reducer';
|
||||
import { UsersEffects } from './+state/users.effects';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [provideEffects(UsersEffects),provideState(fromUsers.USERS_FEATURE_KEY, fromUsers.usersReducer),provideEffects(),provideStore(),provideRouter(appRoutes) ]
|
||||
providers: [provideEffects(UsersEffects),provideState(fromUsers.USERS_FEATURE_KEY, fromUsers.usersReducer),provideEffects(),provideStore(),provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(appRoutes) ]
|
||||
};
|
||||
"
|
||||
`;
|
||||
@ -782,14 +782,14 @@ bootstrapApplication(AppComponent, appConfig).catch((err) =>
|
||||
`;
|
||||
|
||||
exports[`ngrx Standalone APIs should add an empty provideStore when minimal and root are set to true 2`] = `
|
||||
"import { ApplicationConfig } from '@angular/core';
|
||||
"import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
|
||||
import { provideRouter } from '@angular/router';
|
||||
import { appRoutes } from './app.routes';
|
||||
import { provideStore, provideState } from '@ngrx/store';
|
||||
import { provideEffects } from '@ngrx/effects';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [provideEffects(),provideStore(),provideRouter(appRoutes) ]
|
||||
providers: [provideEffects(),provideStore(),provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(appRoutes) ]
|
||||
};
|
||||
"
|
||||
`;
|
||||
@ -806,7 +806,7 @@ bootstrapApplication(AppComponent, appConfig).catch((err) =>
|
||||
`;
|
||||
|
||||
exports[`ngrx Standalone APIs should add facade provider when facade is true 2`] = `
|
||||
"import { ApplicationConfig } from '@angular/core';
|
||||
"import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
|
||||
import { provideRouter } from '@angular/router';
|
||||
import { appRoutes } from './app.routes';
|
||||
import { provideStore, provideState } from '@ngrx/store';
|
||||
@ -816,7 +816,7 @@ import { UsersEffects } from './+state/users.effects';
|
||||
import { UsersFacade } from './+state/users.facade';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [provideEffects(UsersEffects),provideState(fromUsers.USERS_FEATURE_KEY, fromUsers.usersReducer),provideEffects(),provideStore(),UsersFacade,provideRouter(appRoutes) ]
|
||||
providers: [provideEffects(UsersEffects),provideState(fromUsers.USERS_FEATURE_KEY, fromUsers.usersReducer),provideEffects(),provideStore(),UsersFacade,provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(appRoutes) ]
|
||||
};
|
||||
"
|
||||
`;
|
||||
|
||||
@ -674,35 +674,35 @@ export const appRoutes: Routes = [{ path: 'home', component: NxWelcomeComponent
|
||||
...json,
|
||||
dependencies: {
|
||||
...json.dependencies,
|
||||
'@angular/core': '15.0.0',
|
||||
'@angular/core': '16.0.0',
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
it('should install the ngrx 15 packages', async () => {
|
||||
it('should install the ngrx 16 packages', async () => {
|
||||
await ngrxGenerator(tree, defaultOptions);
|
||||
|
||||
const packageJson = devkit.readJson(tree, 'package.json');
|
||||
expect(packageJson.dependencies['@ngrx/store']).toEqual(
|
||||
backwardCompatibleVersions.angularV15.ngrxVersion
|
||||
backwardCompatibleVersions.angularV16.ngrxVersion
|
||||
);
|
||||
expect(packageJson.dependencies['@ngrx/effects']).toEqual(
|
||||
backwardCompatibleVersions.angularV15.ngrxVersion
|
||||
backwardCompatibleVersions.angularV16.ngrxVersion
|
||||
);
|
||||
expect(packageJson.dependencies['@ngrx/entity']).toEqual(
|
||||
backwardCompatibleVersions.angularV15.ngrxVersion
|
||||
backwardCompatibleVersions.angularV16.ngrxVersion
|
||||
);
|
||||
expect(packageJson.dependencies['@ngrx/router-store']).toEqual(
|
||||
backwardCompatibleVersions.angularV15.ngrxVersion
|
||||
backwardCompatibleVersions.angularV16.ngrxVersion
|
||||
);
|
||||
expect(packageJson.dependencies['@ngrx/component-store']).toEqual(
|
||||
backwardCompatibleVersions.angularV15.ngrxVersion
|
||||
backwardCompatibleVersions.angularV16.ngrxVersion
|
||||
);
|
||||
expect(packageJson.devDependencies['@ngrx/schematics']).toEqual(
|
||||
backwardCompatibleVersions.angularV15.ngrxVersion
|
||||
backwardCompatibleVersions.angularV16.ngrxVersion
|
||||
);
|
||||
expect(packageJson.devDependencies['@ngrx/store-devtools']).toEqual(
|
||||
backwardCompatibleVersions.angularV15.ngrxVersion
|
||||
backwardCompatibleVersions.angularV16.ngrxVersion
|
||||
);
|
||||
expect(packageJson.devDependencies['jasmine-marbles']).toBeDefined();
|
||||
});
|
||||
|
||||
@ -111,7 +111,9 @@ exports[`MF Remote App Generator --ssr should generate the correct files 2`] = `
|
||||
import { AppModule } from './app/app.module';
|
||||
|
||||
platformBrowserDynamic()
|
||||
.bootstrapModule(AppModule)
|
||||
.bootstrapModule(AppModule, {
|
||||
ngZoneEventCoalescing: true,
|
||||
})
|
||||
.catch((err) => console.error(err));
|
||||
"
|
||||
`;
|
||||
@ -333,7 +335,9 @@ exports[`MF Remote App Generator --ssr should generate the correct files when --
|
||||
import { AppModule } from './app/app.module';
|
||||
|
||||
platformBrowserDynamic()
|
||||
.bootstrapModule(AppModule)
|
||||
.bootstrapModule(AppModule, {
|
||||
ngZoneEventCoalescing: true,
|
||||
})
|
||||
.catch((err) => console.error(err));
|
||||
"
|
||||
`;
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
exports[`Init MF --federationType=dynamic should create a host with the correct configurations 1`] = `
|
||||
"import { setRemoteDefinitions } from '@nx/angular/mf';
|
||||
|
||||
fetch('/assets/module-federation.manifest.json')
|
||||
fetch('/module-federation.manifest.json')
|
||||
.then((res) => res.json())
|
||||
.then(definitions => setRemoteDefinitions(definitions))
|
||||
.then(() => import('./bootstrap').catch(err => console.error(err)));"
|
||||
@ -12,7 +12,7 @@ fetch('/assets/module-federation.manifest.json')
|
||||
exports[`Init MF --federationType=dynamic should create a host with the correct configurations when --typescriptConfiguration=true 1`] = `
|
||||
"import { setRemoteDefinitions } from '@nx/angular/mf';
|
||||
|
||||
fetch('/assets/module-federation.manifest.json')
|
||||
fetch('/module-federation.manifest.json')
|
||||
.then((res) => res.json())
|
||||
.then(definitions => setRemoteDefinitions(definitions))
|
||||
.then(() => import('./bootstrap').catch(err => console.error(err)));"
|
||||
|
||||
@ -32,14 +32,8 @@ export function checkIsCommaNeeded(mfRemoteText: string) {
|
||||
export function addRemoteToHost(tree: Tree, options: AddRemoteOptions) {
|
||||
if (options.host) {
|
||||
const hostProject = readProjectConfiguration(tree, options.host);
|
||||
const pathToMFManifest = joinPathFragments(
|
||||
hostProject.sourceRoot,
|
||||
'assets/module-federation.manifest.json'
|
||||
);
|
||||
const hostFederationType = determineHostFederationType(
|
||||
tree,
|
||||
pathToMFManifest
|
||||
);
|
||||
const pathToMFManifest = getDynamicManifestFile(tree, hostProject);
|
||||
const hostFederationType = !!pathToMFManifest ? 'dynamic' : 'static';
|
||||
|
||||
const isHostUsingTypescriptConfig = tree.exists(
|
||||
joinPathFragments(hostProject.root, 'module-federation.config.ts')
|
||||
@ -60,11 +54,23 @@ export function addRemoteToHost(tree: Tree, options: AddRemoteOptions) {
|
||||
}
|
||||
}
|
||||
|
||||
function determineHostFederationType(
|
||||
function getDynamicManifestFile(
|
||||
tree: Tree,
|
||||
pathToMfManifest: string
|
||||
): 'dynamic' | 'static' {
|
||||
return tree.exists(pathToMfManifest) ? 'dynamic' : 'static';
|
||||
project: ProjectConfiguration
|
||||
): string | undefined {
|
||||
// {sourceRoot}/assets/module-federation.manifest.json was the generated
|
||||
// path for the manifest file in the past. We now generate the manifest
|
||||
// file at {root}/public/module-federation.manifest.json. This check
|
||||
// ensures that we can still support the old path for backwards
|
||||
// compatibility since old projects may still have the manifest file
|
||||
// at the old path.
|
||||
return [
|
||||
joinPathFragments(project.root, 'public/module-federation.manifest.json'),
|
||||
joinPathFragments(
|
||||
project.sourceRoot,
|
||||
'assets/module-federation.manifest.json'
|
||||
),
|
||||
].find((path) => tree.exists(path));
|
||||
}
|
||||
|
||||
function addRemoteToStaticHost(
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import type { Tree } from '@nx/devkit';
|
||||
import { joinPathFragments } from '@nx/devkit';
|
||||
import { joinPathFragments, type Tree } from '@nx/devkit';
|
||||
import type { Schema } from '../schema';
|
||||
|
||||
export function fixBootstrap(tree: Tree, appRoot: string, options: Schema) {
|
||||
@ -12,20 +11,27 @@ export function fixBootstrap(tree: Tree, appRoot: string, options: Schema) {
|
||||
}
|
||||
|
||||
const bootstrapImportCode = `import('./bootstrap').catch(err => console.error(err))`;
|
||||
if (options.mfType === 'remote' || options.federationType === 'static') {
|
||||
tree.write(mainFilePath, `${bootstrapImportCode};`);
|
||||
} else {
|
||||
let manifestPath = '/assets/module-federation.manifest.json';
|
||||
if (
|
||||
tree.exists(
|
||||
joinPathFragments(appRoot, 'public/module-federation.manifest.json')
|
||||
)
|
||||
) {
|
||||
manifestPath = '/module-federation.manifest.json';
|
||||
}
|
||||
|
||||
const fetchMFManifestCode = `import { setRemoteDefinitions } from '@nx/angular/mf';
|
||||
const fetchMFManifestCode = `import { setRemoteDefinitions } from '@nx/angular/mf';
|
||||
|
||||
fetch('/assets/module-federation.manifest.json')
|
||||
fetch('${manifestPath}')
|
||||
.then((res) => res.json())
|
||||
.then(definitions => setRemoteDefinitions(definitions))
|
||||
.then(() => ${bootstrapImportCode});`;
|
||||
|
||||
tree.write(
|
||||
mainFilePath,
|
||||
options.mfType === 'host' && options.federationType === 'dynamic'
|
||||
? fetchMFManifestCode
|
||||
: `${bootstrapImportCode};`
|
||||
);
|
||||
tree.write(mainFilePath, fetchMFManifestCode);
|
||||
}
|
||||
}
|
||||
|
||||
const standaloneBootstrapCode =
|
||||
|
||||
@ -5,17 +5,25 @@ import {
|
||||
updateProjectConfiguration,
|
||||
} from '@nx/devkit';
|
||||
import type { Schema } from '../schema';
|
||||
import { getInstalledAngularVersionInfo } from '../../utils/version-utils';
|
||||
|
||||
export function setupHostIfDynamic(tree: Tree, options: Schema) {
|
||||
if (options.federationType === 'static') {
|
||||
return;
|
||||
}
|
||||
|
||||
const { major: angularMajorVersion } = getInstalledAngularVersionInfo(tree);
|
||||
const project = readProjectConfiguration(tree, options.appName);
|
||||
const pathToMFManifest = joinPathFragments(
|
||||
project.sourceRoot,
|
||||
'assets/module-federation.manifest.json'
|
||||
);
|
||||
const pathToMFManifest =
|
||||
angularMajorVersion >= 18
|
||||
? joinPathFragments(
|
||||
project.root,
|
||||
'public/module-federation.manifest.json'
|
||||
)
|
||||
: joinPathFragments(
|
||||
project.sourceRoot,
|
||||
'assets/module-federation.manifest.json'
|
||||
);
|
||||
|
||||
if (!tree.exists(pathToMFManifest)) {
|
||||
tree.write(pathToMFManifest, '{}');
|
||||
|
||||
@ -518,7 +518,7 @@ describe('Init MF', () => {
|
||||
'remotes: []'
|
||||
);
|
||||
expect(
|
||||
tree.exists('app1/src/assets/module-federation.manifest.json')
|
||||
tree.exists('app1/public/module-federation.manifest.json')
|
||||
).toBeTruthy();
|
||||
expect(tree.read('app1/src/main.ts', 'utf-8')).toMatchSnapshot();
|
||||
});
|
||||
@ -540,7 +540,7 @@ describe('Init MF', () => {
|
||||
'remotes: []'
|
||||
);
|
||||
expect(
|
||||
tree.exists('app1/src/assets/module-federation.manifest.json')
|
||||
tree.exists('app1/public/module-federation.manifest.json')
|
||||
).toBeTruthy();
|
||||
expect(tree.read('app1/src/main.ts', 'utf-8')).toMatchSnapshot();
|
||||
});
|
||||
@ -571,7 +571,7 @@ describe('Init MF', () => {
|
||||
'remotes: []'
|
||||
);
|
||||
expect(
|
||||
readJson(tree, 'app1/src/assets/module-federation.manifest.json')
|
||||
readJson(tree, 'app1/public/module-federation.manifest.json')
|
||||
).toEqual({
|
||||
remote1: 'http://localhost:4201',
|
||||
});
|
||||
@ -606,7 +606,7 @@ describe('Init MF', () => {
|
||||
'remotes: []'
|
||||
);
|
||||
expect(
|
||||
readJson(tree, 'app1/src/assets/module-federation.manifest.json')
|
||||
readJson(tree, 'app1/public/module-federation.manifest.json')
|
||||
).toEqual({
|
||||
remote1: 'http://localhost:4201',
|
||||
});
|
||||
@ -645,7 +645,7 @@ describe('Init MF', () => {
|
||||
'remotes: []'
|
||||
);
|
||||
expect(
|
||||
readJson(tree, 'app1/src/assets/module-federation.manifest.json')
|
||||
readJson(tree, 'app1/public/module-federation.manifest.json')
|
||||
).toEqual({
|
||||
remote1: 'http://localhost:4201',
|
||||
});
|
||||
@ -681,7 +681,7 @@ describe('Init MF', () => {
|
||||
'remotes: []'
|
||||
);
|
||||
expect(
|
||||
readJson(tree, 'app1/src/assets/module-federation.manifest.json')
|
||||
readJson(tree, 'app1/public/module-federation.manifest.json')
|
||||
).toEqual({
|
||||
remote1: 'http://localhost:4201',
|
||||
});
|
||||
|
||||
@ -56,8 +56,6 @@ export async function setupMf(tree: Tree, rawOptions: Schema) {
|
||||
updateTsConfig(tree, options);
|
||||
setupServeTarget(tree, options);
|
||||
|
||||
fixBootstrap(tree, projectConfig.root, options);
|
||||
|
||||
if (options.mfType === 'host') {
|
||||
setupHostIfDynamic(tree, options);
|
||||
updateHostAppRoutes(tree, options);
|
||||
@ -78,6 +76,8 @@ export async function setupMf(tree: Tree, rawOptions: Schema) {
|
||||
}
|
||||
}
|
||||
|
||||
fixBootstrap(tree, projectConfig.root, options);
|
||||
|
||||
if (!options.skipE2E) {
|
||||
addCypressOnErrorWorkaround(tree, options);
|
||||
}
|
||||
|
||||
@ -91,8 +91,10 @@ exports[`setupSSR with application builder should create the files correctly for
|
||||
"executor": "@angular-devkit/build-angular:application",
|
||||
"options": {
|
||||
"assets": [
|
||||
"app1/src/favicon.ico",
|
||||
"app1/src/assets",
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "app1/public",
|
||||
},
|
||||
],
|
||||
"browser": "app1/src/main.ts",
|
||||
"index": "app1/src/index.html",
|
||||
@ -141,14 +143,15 @@ export function app(): express.Express {
|
||||
// server.get('/api/**', (req, res) => { });
|
||||
// Serve static files from /browser
|
||||
server.get(
|
||||
'*.*',
|
||||
'**',
|
||||
express.static(browserDistFolder, {
|
||||
maxAge: '1y',
|
||||
index: 'index.html',
|
||||
})
|
||||
);
|
||||
|
||||
// All regular routes use the Angular engine
|
||||
server.get('*', (req, res, next) => {
|
||||
server.get('**', (req, res, next) => {
|
||||
const { protocol, originalUrl, baseUrl, headers } = req;
|
||||
|
||||
commonEngine
|
||||
@ -208,8 +211,10 @@ exports[`setupSSR with application builder should create the files correctly for
|
||||
"executor": "@angular-devkit/build-angular:application",
|
||||
"options": {
|
||||
"assets": [
|
||||
"app1/src/favicon.ico",
|
||||
"app1/src/assets",
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "app1/public",
|
||||
},
|
||||
],
|
||||
"browser": "app1/src/main.ts",
|
||||
"index": "app1/src/index.html",
|
||||
@ -258,14 +263,15 @@ export function app(): express.Express {
|
||||
// server.get('/api/**', (req, res) => { });
|
||||
// Serve static files from /browser
|
||||
server.get(
|
||||
'*.*',
|
||||
'**',
|
||||
express.static(browserDistFolder, {
|
||||
maxAge: '1y',
|
||||
index: 'index.html',
|
||||
})
|
||||
);
|
||||
|
||||
// All regular routes use the Angular engine
|
||||
server.get('*', (req, res, next) => {
|
||||
server.get('**', (req, res, next) => {
|
||||
const { protocol, originalUrl, baseUrl, headers } = req;
|
||||
|
||||
commonEngine
|
||||
@ -350,12 +356,13 @@ export function app(): express.Express {
|
||||
// Example Express Rest API endpoints
|
||||
// server.get('/api/**', (req, res) => { });
|
||||
// Serve static files from /browser
|
||||
server.get('*.*', express.static(distFolder, {
|
||||
maxAge: '1y'
|
||||
server.get('**', express.static(distFolder, {
|
||||
maxAge: '1y',
|
||||
index: 'index.html'
|
||||
}));
|
||||
|
||||
// All regular routes use the Angular engine
|
||||
server.get('*', (req, res, next) => {
|
||||
server.get('**', (req, res, next) => {
|
||||
const { protocol, originalUrl, baseUrl, headers } = req;
|
||||
|
||||
commonEngine
|
||||
@ -450,12 +457,13 @@ export function app(): express.Express {
|
||||
// Example Express Rest API endpoints
|
||||
// server.get('/api/**', (req, res) => { });
|
||||
// Serve static files from /browser
|
||||
server.get('*.*', express.static(distFolder, {
|
||||
maxAge: '1y'
|
||||
server.get('**', express.static(distFolder, {
|
||||
maxAge: '1y',
|
||||
index: 'index.html'
|
||||
}));
|
||||
|
||||
// All regular routes use the Angular engine
|
||||
server.get('*', (req, res, next) => {
|
||||
server.get('**', (req, res, next) => {
|
||||
const { protocol, originalUrl, baseUrl, headers } = req;
|
||||
|
||||
commonEngine
|
||||
|
||||
@ -1,14 +0,0 @@
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
import { AppModule } from './app/app.module';
|
||||
|
||||
function bootstrap() {
|
||||
platformBrowserDynamic()
|
||||
.bootstrapModule(AppModule)
|
||||
.catch((err) => console.error(err));
|
||||
}
|
||||
|
||||
if (document.readyState !== 'loading') {
|
||||
bootstrap();
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', bootstrap);
|
||||
}
|
||||
@ -20,12 +20,13 @@ export function app(): express.Express {
|
||||
// Example Express Rest API endpoints
|
||||
// server.get('/api/**', (req, res) => { });
|
||||
// Serve static files from /browser
|
||||
server.get('*.*', express.static(browserDistFolder, {
|
||||
maxAge: '1y'
|
||||
server.get('**', express.static(browserDistFolder, {
|
||||
maxAge: '1y',
|
||||
index: 'index.html',
|
||||
}));
|
||||
|
||||
// All regular routes use the Angular engine
|
||||
server.get('*', (req, res, next) => {
|
||||
server.get('**', (req, res, next) => {
|
||||
const { protocol, originalUrl, baseUrl, headers } = req;
|
||||
|
||||
commonEngine
|
||||
|
||||
@ -23,12 +23,13 @@ export function app(): express.Express {
|
||||
// Example Express Rest API endpoints
|
||||
// server.get('/api/**', (req, res) => { });
|
||||
// Serve static files from /browser
|
||||
server.get('*.*', express.static(distFolder, {
|
||||
maxAge: '1y'
|
||||
server.get('**', express.static(distFolder, {
|
||||
maxAge: '1y',
|
||||
index: 'index.html'
|
||||
}));
|
||||
|
||||
// All regular routes use the Angular engine
|
||||
server.get('*', (req, res, next) => {
|
||||
server.get('**', (req, res, next) => {
|
||||
const { protocol, originalUrl, baseUrl, headers } = req;
|
||||
|
||||
commonEngine
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { addDependenciesToPackageJson, type Tree } from '@nx/devkit';
|
||||
import { gte } from 'semver';
|
||||
import type { PackageCompatVersions } from '../../../utils/backward-compatible-versions';
|
||||
import type { VersionMap } from '../../../utils/backward-compatible-versions';
|
||||
import {
|
||||
getInstalledAngularVersionInfo,
|
||||
getInstalledPackageVersionInfo,
|
||||
@ -35,10 +35,11 @@ export function addDependencies(
|
||||
} else {
|
||||
dependencies['@nguniversal/express-engine'] =
|
||||
getInstalledPackageVersionInfo(tree, '@nguniversal/express-engine')
|
||||
?.version ?? (pkgVersions as PackageCompatVersions).ngUniversalVersion;
|
||||
?.version ??
|
||||
(pkgVersions as VersionMap['angularV16']).ngUniversalVersion;
|
||||
devDependencies['@nguniversal/builders'] =
|
||||
getInstalledPackageVersionInfo(tree, '@nguniversal/builders')?.version ??
|
||||
(pkgVersions as PackageCompatVersions).ngUniversalVersion;
|
||||
(pkgVersions as VersionMap['angularV16']).ngUniversalVersion;
|
||||
}
|
||||
|
||||
addDependenciesToPackageJson(tree, dependencies, devDependencies);
|
||||
|
||||
@ -4,8 +4,6 @@ import {
|
||||
joinPathFragments,
|
||||
readProjectConfiguration,
|
||||
} from '@nx/devkit';
|
||||
import { lt } from 'semver';
|
||||
import { getInstalledAngularVersionInfo } from '../../utils/version-utils';
|
||||
import type { Schema } from '../schema';
|
||||
|
||||
export function generateSSRFiles(
|
||||
@ -27,7 +25,6 @@ export function generateSSRFiles(
|
||||
}
|
||||
|
||||
const pathToFiles = joinPathFragments(__dirname, '..', 'files');
|
||||
const { version: angularVersion } = getInstalledAngularVersionInfo(tree);
|
||||
|
||||
if (schema.standalone) {
|
||||
generateFiles(
|
||||
@ -39,18 +36,9 @@ export function generateSSRFiles(
|
||||
} else {
|
||||
generateFiles(
|
||||
tree,
|
||||
joinPathFragments(pathToFiles, 'ngmodule', 'base'),
|
||||
joinPathFragments(pathToFiles, 'ngmodule'),
|
||||
projectRoot,
|
||||
{ ...schema, tpl: '' }
|
||||
);
|
||||
|
||||
if (lt(angularVersion, '15.2.0')) {
|
||||
generateFiles(
|
||||
tree,
|
||||
joinPathFragments(pathToFiles, 'ngmodule', 'pre-v15-2'),
|
||||
projectRoot,
|
||||
{ ...schema, tpl: '' }
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,6 @@ export * from './add-server-file';
|
||||
export * from './generate-files';
|
||||
export * from './generate-server-ts-config';
|
||||
export * from './normalize-options';
|
||||
export * from './update-app-module';
|
||||
export * from './update-project-config';
|
||||
export * from './validate-options';
|
||||
export * from './add-hydration';
|
||||
|
||||
@ -1,47 +0,0 @@
|
||||
import type { Tree } from '@nx/devkit';
|
||||
import { joinPathFragments, readProjectConfiguration } from '@nx/devkit';
|
||||
import { ensureTypescript } from '@nx/js/src/utils/typescript/ensure-typescript';
|
||||
import { getInstalledAngularMajorVersion } from '../../utils/version-utils';
|
||||
import type { Schema } from '../schema';
|
||||
|
||||
export function updateAppModule(tree: Tree, schema: Schema) {
|
||||
const angularMajorVersion = getInstalledAngularMajorVersion(tree);
|
||||
if (angularMajorVersion >= 16) {
|
||||
return;
|
||||
}
|
||||
|
||||
ensureTypescript();
|
||||
const { tsquery } = require('@phenomnomnominal/tsquery');
|
||||
// read the content of app module
|
||||
const projectConfig = readProjectConfiguration(tree, schema.project);
|
||||
const pathToAppModule = joinPathFragments(
|
||||
projectConfig.sourceRoot,
|
||||
'app/app.module.ts'
|
||||
);
|
||||
const fileContents = tree.read(pathToAppModule, 'utf-8');
|
||||
|
||||
const ast = tsquery.ast(fileContents);
|
||||
const browserModuleImportNodes = tsquery(
|
||||
ast,
|
||||
'PropertyAssignment:has(Identifier[name=imports]) > ArrayLiteralExpression Identifier[name=BrowserModule]',
|
||||
{ visitAllChildren: true }
|
||||
);
|
||||
|
||||
if (browserModuleImportNodes.length === 0) {
|
||||
throw new Error(
|
||||
`Could not find BrowserModule declaration in ${pathToAppModule}. Please ensure this is correct.`
|
||||
);
|
||||
}
|
||||
|
||||
const browserModuleNode = browserModuleImportNodes[0];
|
||||
|
||||
const newFileContents = `${fileContents.slice(
|
||||
0,
|
||||
browserModuleNode.getEnd()
|
||||
)}.withServerTransition({ appId: '${schema.appId}' })${fileContents.slice(
|
||||
browserModuleNode.getEnd(),
|
||||
fileContents.length
|
||||
)}`;
|
||||
|
||||
tree.write(pathToAppModule, newFileContents);
|
||||
}
|
||||
@ -1,14 +1,11 @@
|
||||
import type { Tree } from '@nx/devkit';
|
||||
import { readProjectConfiguration, stripIndents } from '@nx/devkit';
|
||||
import { readProjectConfiguration } from '@nx/devkit';
|
||||
import { validateProject as validateExistingProject } from '../../utils/validations';
|
||||
import type { Schema } from '../schema';
|
||||
import { getInstalledAngularVersionInfo } from '../../utils/version-utils';
|
||||
import { lt } from 'semver';
|
||||
|
||||
export function validateOptions(tree: Tree, options: Schema): void {
|
||||
validateProject(tree, options.project);
|
||||
validateBuildTarget(tree, options.project);
|
||||
validateHydrationOption(tree, options.hydration);
|
||||
}
|
||||
|
||||
function validateProject(tree: Tree, project: string): void {
|
||||
@ -31,16 +28,3 @@ function validateBuildTarget(tree: Tree, project: string): void {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function validateHydrationOption(tree: Tree, hydration: boolean): void {
|
||||
if (!hydration) {
|
||||
return;
|
||||
}
|
||||
|
||||
const installedAngularVersion = getInstalledAngularVersionInfo(tree).version;
|
||||
|
||||
if (lt(installedAngularVersion, '16.0.0')) {
|
||||
throw new Error(stripIndents`The "hydration" option is only supported in Angular >= 16.0.0. You are currently using "${installedAngularVersion}".
|
||||
You can resolve this error by removing the "hydration" option or by migrating to Angular 16.0.0.`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,8 +19,9 @@
|
||||
"appId": {
|
||||
"type": "string",
|
||||
"format": "html-selector",
|
||||
"description": "The `appId` to use with `withServerTransition`. _Note: This is only used in Angular versions <16.0.0. It's deprecated since Angular 16 and not supported since Angular 17._",
|
||||
"default": "serverApp"
|
||||
"description": "The `appId` to use with `withServerTransition`.",
|
||||
"default": "serverApp",
|
||||
"x-deprecated": "This is deprecated and ignored since Angular 16 and not supported since Angular 17."
|
||||
},
|
||||
"main": {
|
||||
"type": "string",
|
||||
@ -55,7 +56,7 @@
|
||||
},
|
||||
"hydration": {
|
||||
"type": "boolean",
|
||||
"description": "Set up Hydration for the SSR application. It defaults to `true` for Angular versions >= 17.0.0. Otherwise, it defaults to `false`. _Note: This is only supported in Angular versions >= 16.0.0_."
|
||||
"description": "Set up Hydration for the SSR application. It defaults to `true` for Angular versions >= 17.0.0. Otherwise, it defaults to `false`."
|
||||
},
|
||||
"skipFormat": {
|
||||
"type": "boolean",
|
||||
|
||||
@ -48,7 +48,9 @@ describe('setupSSR', () => {
|
||||
import { AppModule } from './app/app.module';
|
||||
|
||||
platformBrowserDynamic()
|
||||
.bootstrapModule(AppModule)
|
||||
.bootstrapModule(AppModule, {
|
||||
ngZoneEventCoalescing: true,
|
||||
})
|
||||
.catch((err) => console.error(err));
|
||||
"
|
||||
`);
|
||||
@ -214,7 +216,9 @@ describe('setupSSR', () => {
|
||||
import { AppModule } from './app/app.module';
|
||||
|
||||
platformBrowserDynamic()
|
||||
.bootstrapModule(AppModule)
|
||||
.bootstrapModule(AppModule, {
|
||||
ngZoneEventCoalescing: true
|
||||
})
|
||||
.catch((err) => console.error(err));
|
||||
"
|
||||
`);
|
||||
@ -464,13 +468,13 @@ describe('setupSSR', () => {
|
||||
// ASSERT
|
||||
expect(tree.read('app1/src/app/app.config.ts', 'utf-8'))
|
||||
.toMatchInlineSnapshot(`
|
||||
"import { ApplicationConfig } from '@angular/core';
|
||||
"import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
|
||||
import { provideRouter } from '@angular/router';
|
||||
import { appRoutes } from './app.routes';
|
||||
import { provideClientHydration } from '@angular/platform-browser';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [provideClientHydration(),provideRouter(appRoutes) ]
|
||||
providers: [provideClientHydration(),provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(appRoutes) ]
|
||||
};
|
||||
"
|
||||
`);
|
||||
@ -542,12 +546,12 @@ describe('setupSSR', () => {
|
||||
|
||||
expect(tree.read('app1/src/app/app.config.ts', 'utf-8'))
|
||||
.toMatchInlineSnapshot(`
|
||||
"import { ApplicationConfig } from '@angular/core';
|
||||
"import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
|
||||
import { provideRouter, withEnabledBlockingInitialNavigation } from '@angular/router';
|
||||
import { appRoutes } from './app.routes';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [provideRouter(appRoutes, withEnabledBlockingInitialNavigation()) ]
|
||||
providers: [provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(appRoutes, withEnabledBlockingInitialNavigation()) ]
|
||||
};
|
||||
"
|
||||
`);
|
||||
@ -567,7 +571,7 @@ describe('setupSSR', () => {
|
||||
updateJson(tree, 'package.json', (json) => ({
|
||||
...json,
|
||||
dependencies: {
|
||||
'@angular/core': '15.2.0',
|
||||
'@angular/core': '16.2.0',
|
||||
},
|
||||
}));
|
||||
|
||||
@ -578,63 +582,22 @@ describe('setupSSR', () => {
|
||||
const pkgJson = readJson(tree, 'package.json');
|
||||
expect(pkgJson.dependencies['@angular/ssr']).toBeUndefined();
|
||||
expect(pkgJson.dependencies['@angular/platform-server']).toEqual(
|
||||
backwardCompatibleVersions.angularV15.angularVersion
|
||||
backwardCompatibleVersions.angularV16.angularVersion
|
||||
);
|
||||
expect(pkgJson.dependencies['@nguniversal/express-engine']).toEqual(
|
||||
backwardCompatibleVersions.angularV15.ngUniversalVersion
|
||||
backwardCompatibleVersions.angularV16.ngUniversalVersion
|
||||
);
|
||||
expect(pkgJson.devDependencies['@nguniversal/builders']).toEqual(
|
||||
backwardCompatibleVersions.angularV15.ngUniversalVersion
|
||||
backwardCompatibleVersions.angularV16.ngUniversalVersion
|
||||
);
|
||||
});
|
||||
|
||||
it('should add "withServerTransition" call to app module for angular versions lower than 16', async () => {
|
||||
// ARRANGE
|
||||
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
updateJson(tree, 'package.json', (json) => ({
|
||||
...json,
|
||||
dependencies: { ...json.dependencies, '@angular/core': '^15.2.0' },
|
||||
}));
|
||||
|
||||
await generateTestApplication(tree, {
|
||||
name: 'app1',
|
||||
standalone: false,
|
||||
skipFormat: true,
|
||||
});
|
||||
|
||||
// ACT
|
||||
await setupSsr(tree, { project: 'app1', skipFormat: true });
|
||||
|
||||
// ASSERT
|
||||
expect(tree.read('app1/src/app/app.module.ts', 'utf-8'))
|
||||
.toMatchInlineSnapshot(`
|
||||
"import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { AppComponent } from './app.component';
|
||||
import { appRoutes } from './app.routes';
|
||||
import { NxWelcomeComponent } from './nx-welcome.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [AppComponent, NxWelcomeComponent],
|
||||
imports: [
|
||||
BrowserModule.withServerTransition({ appId: 'serverApp' }),
|
||||
RouterModule.forRoot(appRoutes, { initialNavigation: 'enabledBlocking' }),
|
||||
],
|
||||
providers: [],
|
||||
bootstrap: [AppComponent],
|
||||
})
|
||||
export class AppModule {}
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should set "initialNavigation: enabledBlocking" in "RouterModule.forRoot" options', async () => {
|
||||
// ARRANGE
|
||||
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
updateJson(tree, 'package.json', (json) => ({
|
||||
...json,
|
||||
dependencies: { ...json.dependencies, '@angular/core': '^15.2.0' },
|
||||
dependencies: { ...json.dependencies, '@angular/core': '^16.2.0' },
|
||||
}));
|
||||
|
||||
await generateTestApplication(tree, {
|
||||
@ -659,7 +622,7 @@ describe('setupSSR', () => {
|
||||
@NgModule({
|
||||
declarations: [AppComponent, NxWelcomeComponent],
|
||||
imports: [
|
||||
BrowserModule.withServerTransition({ appId: 'serverApp' }),
|
||||
BrowserModule,
|
||||
RouterModule.forRoot(appRoutes, { initialNavigation: 'enabledBlocking' }),
|
||||
],
|
||||
providers: [],
|
||||
@ -675,7 +638,7 @@ describe('setupSSR', () => {
|
||||
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
updateJson(tree, 'package.json', (json) => ({
|
||||
...json,
|
||||
dependencies: { ...json.dependencies, '@angular/core': '^15.2.0' },
|
||||
dependencies: { ...json.dependencies, '@angular/core': '^16.2.0' },
|
||||
}));
|
||||
|
||||
await generateTestApplication(tree, { name: 'app1', skipFormat: true });
|
||||
@ -686,7 +649,7 @@ describe('setupSSR', () => {
|
||||
// ASSERT
|
||||
expect(tree.read('app1/src/app/app.config.ts', 'utf-8'))
|
||||
.toMatchInlineSnapshot(`
|
||||
"import { ApplicationConfig } from '@angular/platform-browser';
|
||||
"import { ApplicationConfig } from '@angular/core';
|
||||
import { provideRouter, withEnabledBlockingInitialNavigation } from '@angular/router';
|
||||
import { appRoutes } from './app.routes';
|
||||
|
||||
@ -697,48 +660,12 @@ describe('setupSSR', () => {
|
||||
`);
|
||||
});
|
||||
|
||||
it('should wrap bootstrap call for Angular versions lower than 15.2', async () => {
|
||||
// ARRANGE
|
||||
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
await generateTestApplication(tree, {
|
||||
name: 'app1',
|
||||
standalone: false,
|
||||
skipFormat: true,
|
||||
});
|
||||
updateJson(tree, 'package.json', (json) => ({
|
||||
...json,
|
||||
dependencies: { '@angular/core': '15.1.0' },
|
||||
}));
|
||||
|
||||
// ACT
|
||||
await setupSsr(tree, { project: 'app1', skipFormat: true });
|
||||
|
||||
// ASSERT
|
||||
expect(tree.read('app1/src/main.ts', 'utf-8')).toMatchInlineSnapshot(`
|
||||
"import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
import { AppModule } from './app/app.module';
|
||||
|
||||
function bootstrap() {
|
||||
platformBrowserDynamic()
|
||||
.bootstrapModule(AppModule)
|
||||
.catch((err) => console.error(err));
|
||||
}
|
||||
|
||||
if (document.readyState !== 'loading') {
|
||||
bootstrap();
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', bootstrap);
|
||||
}
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should generate a correct server.ts', async () => {
|
||||
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
updateJson(tree, 'package.json', (json) => ({
|
||||
...json,
|
||||
dependencies: {
|
||||
'@angular/core': '15.2.0',
|
||||
'@angular/core': '16.2.0',
|
||||
},
|
||||
}));
|
||||
await generateTestApplication(tree, {
|
||||
@ -757,7 +684,7 @@ describe('setupSSR', () => {
|
||||
updateJson(tree, 'package.json', (json) => ({
|
||||
...json,
|
||||
dependencies: {
|
||||
'@angular/core': '15.2.0',
|
||||
'@angular/core': '16.2.0',
|
||||
},
|
||||
}));
|
||||
await generateTestApplication(tree, { name: 'app1', skipFormat: true });
|
||||
@ -766,7 +693,7 @@ describe('setupSSR', () => {
|
||||
|
||||
expect(tree.read('app1/src/app/app.config.ts', 'utf-8'))
|
||||
.toMatchInlineSnapshot(`
|
||||
"import { ApplicationConfig } from '@angular/platform-browser';
|
||||
"import { ApplicationConfig } from '@angular/core';
|
||||
import { provideRouter, withEnabledBlockingInitialNavigation } from '@angular/router';
|
||||
import { appRoutes } from './app.routes';
|
||||
|
||||
|
||||
@ -14,7 +14,6 @@ import {
|
||||
normalizeOptions,
|
||||
setRouterInitialNavigation,
|
||||
setServerTsConfigOptionsForApplicationBuilder,
|
||||
updateAppModule,
|
||||
updateProjectConfigForApplicationBuilder,
|
||||
updateProjectConfigForBrowserBuilder,
|
||||
validateOptions,
|
||||
@ -35,9 +34,6 @@ export async function setupSsr(tree: Tree, schema: Schema) {
|
||||
}
|
||||
generateSSRFiles(tree, options, isUsingApplicationBuilder);
|
||||
|
||||
if (!options.standalone) {
|
||||
updateAppModule(tree, options);
|
||||
}
|
||||
if (options.hydration) {
|
||||
addHydration(tree, options);
|
||||
}
|
||||
|
||||
@ -10,9 +10,9 @@ export async function generateStories(
|
||||
) {
|
||||
const project = readProjectConfiguration(tree, options.project);
|
||||
ensurePackage('@nx/cypress', nxVersion);
|
||||
const { getE2eProjectName } = await import(
|
||||
'@nx/cypress/src/utils/project-name'
|
||||
);
|
||||
const { getE2eProjectName } = <
|
||||
typeof import('@nx/cypress/src/utils/project-name')
|
||||
>require('@nx/cypress/src/utils/project-name');
|
||||
const e2eProjectName = getE2eProjectName(
|
||||
options.project,
|
||||
project.root,
|
||||
|
||||
@ -60,10 +60,10 @@ export function versions(
|
||||
): PackageLatestVersions | PackageCompatVersions {
|
||||
const majorAngularVersion = getInstalledAngularMajorVersion(tree);
|
||||
switch (majorAngularVersion) {
|
||||
case 15:
|
||||
return backwardCompatibleVersions.angularV15;
|
||||
case 16:
|
||||
return backwardCompatibleVersions.angularV16;
|
||||
case 17:
|
||||
return backwardCompatibleVersions.angularV17;
|
||||
default:
|
||||
return latestVersions;
|
||||
}
|
||||
|
||||
@ -0,0 +1,42 @@
|
||||
import { readJson, Tree, writeJson } from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import updateAngularCli, { angularCliVersion } from './update-angular-cli';
|
||||
|
||||
describe('update-angular-cli migration', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
});
|
||||
|
||||
it('should update @angular/cli version when defined as a dev dependency', async () => {
|
||||
writeJson(tree, 'package.json', {
|
||||
devDependencies: { '@angular/cli': '~13.3.0' },
|
||||
});
|
||||
|
||||
await updateAngularCli(tree);
|
||||
|
||||
const { devDependencies } = readJson(tree, 'package.json');
|
||||
expect(devDependencies['@angular/cli']).toBe(angularCliVersion);
|
||||
});
|
||||
|
||||
it('should update @angular/cli version when defined as a dependency', async () => {
|
||||
writeJson(tree, 'package.json', {
|
||||
dependencies: { '@angular/cli': '~13.3.0' },
|
||||
});
|
||||
|
||||
await updateAngularCli(tree);
|
||||
|
||||
const { dependencies } = readJson(tree, 'package.json');
|
||||
expect(dependencies['@angular/cli']).toBe(angularCliVersion);
|
||||
});
|
||||
|
||||
it('should not add @angular/cli to package.json when it is not set', async () => {
|
||||
const initialPackageJson = readJson(tree, 'package.json');
|
||||
|
||||
await updateAngularCli(tree);
|
||||
|
||||
const packageJson = readJson(tree, 'package.json');
|
||||
expect(packageJson).toStrictEqual(initialPackageJson);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,23 @@
|
||||
import { formatFiles, Tree, updateJson } from '@nx/devkit';
|
||||
|
||||
export const angularCliVersion = '~18.0.0';
|
||||
|
||||
export default async function (tree: Tree) {
|
||||
let shouldFormat = false;
|
||||
|
||||
updateJson(tree, 'package.json', (json) => {
|
||||
if (json.devDependencies?.['@angular/cli']) {
|
||||
json.devDependencies['@angular/cli'] = angularCliVersion;
|
||||
shouldFormat = true;
|
||||
} else if (json.dependencies?.['@angular/cli']) {
|
||||
json.dependencies['@angular/cli'] = angularCliVersion;
|
||||
shouldFormat = true;
|
||||
}
|
||||
|
||||
return json;
|
||||
});
|
||||
|
||||
if (shouldFormat) {
|
||||
await formatFiles(tree);
|
||||
}
|
||||
}
|
||||
@ -1,50 +1,32 @@
|
||||
import * as latestVersions from './versions';
|
||||
|
||||
type SupportedVersions = 'angularV15' | 'angularV16';
|
||||
type SupportedVersions = 'angularV16' | 'angularV17';
|
||||
|
||||
export type LatestPackageVersionNames = Exclude<
|
||||
type LatestPackageVersionNames = Exclude<
|
||||
keyof typeof latestVersions,
|
||||
'nxVersion'
|
||||
>;
|
||||
export type CompatPackageVersionNames =
|
||||
type CompatPackageVersionNames =
|
||||
| LatestPackageVersionNames
|
||||
// Can be removed when dropping support for Angular v16
|
||||
| 'ngUniversalVersion';
|
||||
|
||||
export type PackageLatestVersions = Record<LatestPackageVersionNames, string>;
|
||||
export type PackageCompatVersions = Record<CompatPackageVersionNames, string>;
|
||||
export type PackageVersionNames =
|
||||
| LatestPackageVersionNames
|
||||
| CompatPackageVersionNames;
|
||||
|
||||
export const backwardCompatibleVersions: Record<
|
||||
SupportedVersions,
|
||||
PackageCompatVersions
|
||||
> = {
|
||||
angularV15: {
|
||||
angularVersion: '~15.2.0',
|
||||
angularDevkitVersion: '~15.2.0',
|
||||
ngPackagrVersion: '~15.2.2',
|
||||
ngrxVersion: '~15.3.0',
|
||||
rxjsVersion: '~7.8.0',
|
||||
zoneJsVersion: '~0.12.0',
|
||||
angularJsVersion: '1.7.9',
|
||||
tsLibVersion: '^2.3.0',
|
||||
ngUniversalVersion: '~15.1.0',
|
||||
corsVersion: '~2.8.5',
|
||||
typesCorsVersion: '~2.8.5',
|
||||
expressVersion: '~4.18.2',
|
||||
typesExpressVersion: '4.17.14',
|
||||
browserSyncVersion: '^3.0.0',
|
||||
moduleFederationNodeVersion: '~1.0.5',
|
||||
angularEslintVersion: '~15.0.0',
|
||||
tailwindVersion: '^3.0.2',
|
||||
postcssVersion: '^8.4.5',
|
||||
postcssUrlVersion: '~10.1.3',
|
||||
autoprefixerVersion: '^10.4.0',
|
||||
tsNodeVersion: '10.9.1',
|
||||
jestPresetAngularVersion: '~13.0.0',
|
||||
typesNodeVersion: '16.11.7',
|
||||
jasmineMarblesVersion: '^0.9.2',
|
||||
jsoncEslintParserVersion: '^2.1.0',
|
||||
},
|
||||
export type VersionMap = {
|
||||
angularV16: Record<CompatPackageVersionNames, string>;
|
||||
angularV17: Record<
|
||||
Exclude<CompatPackageVersionNames, 'ngUniversalVersion'>,
|
||||
string
|
||||
>;
|
||||
};
|
||||
|
||||
export type PackageLatestVersions = Record<LatestPackageVersionNames, string>;
|
||||
export type PackageCompatVersions = VersionMap[SupportedVersions];
|
||||
|
||||
export const backwardCompatibleVersions: VersionMap = {
|
||||
angularV16: {
|
||||
angularVersion: '~16.2.0',
|
||||
angularDevkitVersion: '~16.2.0',
|
||||
@ -72,4 +54,30 @@ export const backwardCompatibleVersions: Record<
|
||||
jasmineMarblesVersion: '^0.9.2',
|
||||
jsoncEslintParserVersion: '^2.1.0',
|
||||
},
|
||||
angularV17: {
|
||||
angularVersion: '~17.3.0',
|
||||
angularDevkitVersion: '~17.3.0',
|
||||
ngPackagrVersion: '~17.3.0',
|
||||
ngrxVersion: '~17.0.0',
|
||||
rxjsVersion: '~7.8.0',
|
||||
zoneJsVersion: '~0.14.3',
|
||||
angularJsVersion: '1.7.9',
|
||||
tsLibVersion: '^2.3.0',
|
||||
corsVersion: '~2.8.5',
|
||||
typesCorsVersion: '~2.8.5',
|
||||
expressVersion: '~4.18.2',
|
||||
typesExpressVersion: '4.17.14',
|
||||
browserSyncVersion: '^3.0.0',
|
||||
moduleFederationNodeVersion: '~1.0.5',
|
||||
angularEslintVersion: '~17.3.0',
|
||||
tailwindVersion: '^3.0.2',
|
||||
postcssVersion: '^8.4.5',
|
||||
postcssUrlVersion: '~10.1.3',
|
||||
autoprefixerVersion: '^10.4.0',
|
||||
tsNodeVersion: '10.9.1',
|
||||
jestPresetAngularVersion: '~14.0.3',
|
||||
typesNodeVersion: '18.16.9',
|
||||
jasmineMarblesVersion: '^0.9.2',
|
||||
jsoncEslintParserVersion: '^2.1.0',
|
||||
},
|
||||
};
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { ModuleFederationConfig } from '@nx/webpack/src/utils/module-federation';
|
||||
import { getModuleFederationConfig } from './utils';
|
||||
import ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
|
||||
import ModuleFederationPlugin from 'webpack/lib/container/ModuleFederationPlugin';
|
||||
|
||||
export async function withModuleFederation(options: ModuleFederationConfig) {
|
||||
if (global.NX_GRAPH_CREATION) {
|
||||
|
||||
@ -1,16 +1,15 @@
|
||||
import { coerce, major } from 'semver';
|
||||
import type {
|
||||
CompatPackageVersionNames,
|
||||
LatestPackageVersionNames,
|
||||
PackageCompatVersions,
|
||||
PackageLatestVersions,
|
||||
PackageVersionNames,
|
||||
} from './backward-compatible-versions';
|
||||
import { backwardCompatibleVersions } from './backward-compatible-versions';
|
||||
import * as versions from './versions';
|
||||
import { angularVersion } from './versions';
|
||||
|
||||
export function getPkgVersionForAngularMajorVersion(
|
||||
pkgVersionName: LatestPackageVersionNames | CompatPackageVersionNames,
|
||||
pkgVersionName: PackageVersionNames,
|
||||
angularMajorVersion: number
|
||||
): string {
|
||||
return angularMajorVersion < major(coerce(angularVersion))
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
export const nxVersion = require('../../package.json').version;
|
||||
|
||||
export const angularVersion = '~17.3.0';
|
||||
export const angularDevkitVersion = '~17.3.0';
|
||||
export const ngPackagrVersion = '~17.3.0';
|
||||
export const angularVersion = '~18.0.0';
|
||||
export const angularDevkitVersion = '~18.0.0';
|
||||
export const ngPackagrVersion = '~18.0.0';
|
||||
export const ngrxVersion = '~17.0.0';
|
||||
export const rxjsVersion = '~7.8.0';
|
||||
export const zoneJsVersion = '~0.14.3';
|
||||
@ -23,7 +23,7 @@ export const postcssUrlVersion = '~10.1.3';
|
||||
export const autoprefixerVersion = '^10.4.0';
|
||||
export const tsNodeVersion = '10.9.1';
|
||||
|
||||
export const jestPresetAngularVersion = '~14.0.3';
|
||||
export const jestPresetAngularVersion = '~14.1.0';
|
||||
export const typesNodeVersion = '18.16.9';
|
||||
export const jasmineMarblesVersion = '^0.9.2';
|
||||
|
||||
|
||||
@ -10,6 +10,9 @@
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.lib.runtime.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node16",
|
||||
"module": "node16",
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"declaration": true,
|
||||
"types": ["node"]
|
||||
@ -12,7 +13,11 @@
|
||||
"**/*_spec.ts",
|
||||
"**/*_test.ts",
|
||||
"jest.config.ts",
|
||||
"test-setup.ts"
|
||||
"test-setup.ts",
|
||||
"index.ts",
|
||||
"src/runtime/**/*",
|
||||
"mf/**/*",
|
||||
"testing/**/*"
|
||||
],
|
||||
"include": ["**/*.ts"]
|
||||
}
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
{
|
||||
"extends": "./tsconfig.lib.json",
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"module": "esnext"
|
||||
"module": "esnext",
|
||||
"declaration": true
|
||||
},
|
||||
"include": ["index.ts", "src/runtime/**/*", "mf/**/*", "testing/**/*"],
|
||||
"angularCompilerOptions": {
|
||||
"compilationMode": "partial"
|
||||
}
|
||||
|
||||
@ -19,9 +19,10 @@ import { connectExistingRepoToNxCloudPrompt } from '../../../connect/connect-to-
|
||||
// key is major Angular version and value is Nx version to use
|
||||
const nxAngularLegacyVersionMap: Record<number, string> = {
|
||||
14: '~17.0.0',
|
||||
15: '~19.0.0',
|
||||
};
|
||||
// min major angular version supported in latest Nx
|
||||
const minMajorAngularVersionSupported = 15;
|
||||
const minMajorAngularVersionSupported = 16;
|
||||
// version when the Nx CLI changed from @nrwl/tao & @nrwl/cli to nx
|
||||
const versionWithConsolidatedPackages = '13.9.0';
|
||||
// version when packages were rescoped from @nrwl/* to @nx/*
|
||||
|
||||
@ -138,8 +138,6 @@ function createNxJson(
|
||||
inputs: ['default', '^production'],
|
||||
};
|
||||
}
|
||||
// Angular 15 workspaces still support defaultProject. Support was removed in Angular 16.
|
||||
nxJson.defaultProject = angularJson.defaultProject;
|
||||
writeJsonFile(join(repoRoot, 'nx.json'), nxJson);
|
||||
}
|
||||
|
||||
|
||||
@ -7,6 +7,7 @@ exports[`preset should create files (preset = angular-monorepo) 1`] = `
|
||||
"tsconfig.app.json",
|
||||
"tsconfig.editor.json",
|
||||
"tsconfig.json",
|
||||
"public",
|
||||
".eslintrc.json",
|
||||
"jest.config.ts",
|
||||
"tsconfig.spec.json",
|
||||
@ -15,8 +16,6 @@ exports[`preset should create files (preset = angular-monorepo) 1`] = `
|
||||
|
||||
exports[`preset should create files (preset = angular-monorepo) 2`] = `
|
||||
[
|
||||
"assets",
|
||||
"favicon.ico",
|
||||
"index.html",
|
||||
"styles.css",
|
||||
"app",
|
||||
|
||||
@ -4,4 +4,4 @@ export const typescriptVersion = '~5.4.2';
|
||||
|
||||
// TODO: remove when preset generation is reworked and
|
||||
// deps are not installed from workspace
|
||||
export const angularCliVersion = '~17.3.0';
|
||||
export const angularCliVersion = '~18.0.0';
|
||||
|
||||
3624
pnpm-lock.yaml
generated
3624
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user