feat(angular): support angular v16 (#15592)
This commit is contained in:
parent
9c3698982b
commit
f45aa076da
@ -993,8 +993,8 @@ Use this to expose a compatible Angular Builder
|
||||
#### Parameters
|
||||
|
||||
| Name | Type |
|
||||
| :--------- | :-------------------------------------------------------------- |
|
||||
| `executor` | [`Executor`](../../devkit/documents/nx_devkit#executor)<`any`\> |
|
||||
| :--------- | :------------------------------------------------------ |
|
||||
| `executor` | [`Executor`](../../devkit/documents/nx_devkit#executor) |
|
||||
|
||||
#### Returns
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
"outputCapture": "direct-nodejs",
|
||||
"$schema": "http://json-schema.org/schema",
|
||||
"title": "ng-packagr Target",
|
||||
"description": "Builds a 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 `@nrwl/angular:package` executor but with some key differences:\n- It doesn't run `ngcc` automatically (`ngcc` needs to be run separately beforehand if needed, this can be done in a `postinstall` hook on `package.json`).\n- It only produces ESM2020 bundles.\n- It doesn't generate package exports in the `package.json`.",
|
||||
"description": "Builds a 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).",
|
||||
"cli": "nx",
|
||||
"type": "object",
|
||||
"presets": [
|
||||
|
||||
@ -247,6 +247,11 @@
|
||||
"description": "Extract all licenses in a separate file, in the case of production builds only.",
|
||||
"default": true
|
||||
},
|
||||
"buildOptimizer": {
|
||||
"type": "boolean",
|
||||
"description": "Enables advanced build optimizations. _Note: only supported in Angular versions >= 16.0.0_.",
|
||||
"default": true
|
||||
},
|
||||
"namedChunks": {
|
||||
"type": "boolean",
|
||||
"description": "Use file name for lazy loaded chunks.",
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
"appId": {
|
||||
"type": "string",
|
||||
"format": "html-selector",
|
||||
"description": "The `appId` to use with `withServerTransition`.",
|
||||
"description": "The `appId` to use with `withServerTransition`. _Note: This is only used in Angular versions <16.0.0. It's deprecated since Angular 16._",
|
||||
"default": "serverApp"
|
||||
},
|
||||
"main": {
|
||||
@ -49,6 +49,10 @@
|
||||
"description": "The name of the root module class.",
|
||||
"default": "AppServerModule"
|
||||
},
|
||||
"standalone": {
|
||||
"type": "boolean",
|
||||
"description": "Use Standalone Components to bootstrap SSR. _Note: This is only supported in Angular versions >= 14.1.0_."
|
||||
},
|
||||
"skipFormat": {
|
||||
"type": "boolean",
|
||||
"description": "Skip formatting the workspace after the generator completes.",
|
||||
|
||||
@ -993,8 +993,8 @@ Use this to expose a compatible Angular Builder
|
||||
#### Parameters
|
||||
|
||||
| Name | Type |
|
||||
| :--------- | :-------------------------------------------------------------- |
|
||||
| `executor` | [`Executor`](../../devkit/documents/nx_devkit#executor)<`any`\> |
|
||||
| :--------- | :------------------------------------------------------ |
|
||||
| `executor` | [`Executor`](../../devkit/documents/nx_devkit#executor) |
|
||||
|
||||
#### Returns
|
||||
|
||||
|
||||
@ -177,7 +177,7 @@ describe('Tailwind support', () => {
|
||||
libSpacing: typeof spacing['root']
|
||||
) => {
|
||||
const builtComponentContent = readFile(
|
||||
`dist/libs/${lib}/esm2020/lib/foo.component.mjs`
|
||||
`dist/libs/${lib}/esm2022/lib/foo.component.mjs`
|
||||
);
|
||||
let expectedStylesRegex = new RegExp(
|
||||
`styles: \\[\\"\\.custom\\-btn(\\[_ngcontent\\-%COMP%\\])?{margin:${libSpacing.md};padding:${libSpacing.sm}}(\\\\n)?\\"\\]`
|
||||
|
||||
@ -601,9 +601,10 @@ function updateGeneratedRuleImplementation(
|
||||
true
|
||||
);
|
||||
|
||||
const transformer =
|
||||
<T extends ts.SourceFile>(context: ts.TransformationContext) =>
|
||||
(rootNode: T) => {
|
||||
const transformer = <T extends ts.SourceFile>(
|
||||
context: ts.TransformationContext
|
||||
) =>
|
||||
((rootNode: T) => {
|
||||
function visit(node: ts.Node): ts.Node {
|
||||
/**
|
||||
* Add an ESLint messageId which will show the knownLintErrorMessage
|
||||
@ -752,7 +753,7 @@ function updateGeneratedRuleImplementation(
|
||||
...rootNode.statements,
|
||||
]);
|
||||
return ts.visitNode(importAdded, visit);
|
||||
};
|
||||
}) as ts.Transformer<T>;
|
||||
|
||||
const result: ts.TransformationResult<ts.SourceFile> =
|
||||
ts.transform<ts.SourceFile>(newRuleSourceFile, [transformer]);
|
||||
|
||||
@ -493,7 +493,7 @@ describe('nest libraries', function () {
|
||||
const nestlib = uniq('nestlib');
|
||||
runCLI(`generate @nx/nest:lib ${nestlib} --buildable`);
|
||||
|
||||
packageInstall('@nestjs/swagger', undefined, '~6.0.0');
|
||||
packageInstall('@nestjs/swagger', undefined, '~6.3.0');
|
||||
|
||||
updateProjectConfig(nestlib, (config) => {
|
||||
config.targets.build.options.transformers = [
|
||||
|
||||
@ -219,10 +219,10 @@ export async function h() { return 'c'; }
|
||||
const json = JSON.parse(content);
|
||||
|
||||
/**
|
||||
* Set target as es3!!
|
||||
* Set target as es5!!
|
||||
*/
|
||||
|
||||
json.compilerOptions.target = 'es3';
|
||||
json.compilerOptions.target = 'es5';
|
||||
return JSON.stringify(json, null, 2);
|
||||
});
|
||||
// What we're testing
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"sourceMap": false,
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"allowJs": true,
|
||||
"types": ["cypress", "node"],
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"sourceMap": false
|
||||
},
|
||||
"files": [],
|
||||
"include": [
|
||||
"cypress.config.ts",
|
||||
|
||||
53
package.json
53
package.json
@ -26,21 +26,22 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@actions/core": "^1.10.0",
|
||||
"@angular-devkit/architect": "~0.1502.0",
|
||||
"@angular-devkit/build-angular": "~15.2.0",
|
||||
"@angular-devkit/core": "~15.2.0",
|
||||
"@angular-devkit/schematics": "~15.2.0",
|
||||
"@angular-eslint/eslint-plugin": "~15.0.0",
|
||||
"@angular-eslint/eslint-plugin-template": "~15.0.0",
|
||||
"@angular-eslint/template-parser": "~15.0.0",
|
||||
"@angular/cli": "~15.2.0",
|
||||
"@angular/common": "~15.2.0",
|
||||
"@angular/compiler": "~15.2.0",
|
||||
"@angular/compiler-cli": "~15.2.0",
|
||||
"@angular/core": "~15.2.0",
|
||||
"@angular/router": "~15.2.0",
|
||||
"@angular-devkit/architect": "~0.1600.0-rc.4",
|
||||
"@angular-devkit/build-angular": "~16.0.0-rc.4",
|
||||
"@angular-devkit/core": "~16.0.0-rc.4",
|
||||
"@angular-devkit/schematics": "~16.0.0-rc.4",
|
||||
"@angular-eslint/eslint-plugin": "~16.0.0-alpha.1",
|
||||
"@angular-eslint/eslint-plugin-template": "~16.0.0-alpha.1",
|
||||
"@angular-eslint/template-parser": "~16.0.0-alpha.1",
|
||||
"@angular/cli": "~16.0.0-rc.4",
|
||||
"@angular/common": "~16.0.0-rc.4",
|
||||
"@angular/compiler": "~16.0.0-rc.4",
|
||||
"@angular/compiler-cli": "~16.0.0-rc.4",
|
||||
"@angular/core": "~16.0.0-rc.4",
|
||||
"@angular/router": "~16.0.0-rc.4",
|
||||
"@babel/core": "^7.15.0",
|
||||
"@babel/helper-create-regexp-features-plugin": "^7.14.5",
|
||||
"@babel/plugin-transform-runtime": "^7.15.0",
|
||||
"@babel/preset-react": "^7.14.5",
|
||||
"@babel/preset-typescript": "^7.15.0",
|
||||
"@floating-ui/react": "0.19.2",
|
||||
@ -61,7 +62,7 @@
|
||||
"@ngrx/effects": "~15.3.0",
|
||||
"@ngrx/router-store": "~15.3.0",
|
||||
"@ngrx/store": "~15.3.0",
|
||||
"@nguniversal/builders": "~15.2.0",
|
||||
"@nguniversal/builders": "~16.0.0-rc.2",
|
||||
"nx-cloud": "16.0.5",
|
||||
"@nx/webpack": "16.0.0-rc.0",
|
||||
"@parcel/watcher": "2.0.4",
|
||||
@ -75,13 +76,13 @@
|
||||
"@rollup/plugin-json": "^4.1.0",
|
||||
"@rollup/plugin-node-resolve": "^13.0.4",
|
||||
"@rollup/plugin-url": "^7.0.0",
|
||||
"@schematics/angular": "~15.2.0",
|
||||
"@storybook/addon-essentials": "^7.0.2",
|
||||
"@storybook/angular": "^7.0.2",
|
||||
"@storybook/core-server": "^7.0.2",
|
||||
"@storybook/react": "^7.0.2",
|
||||
"@storybook/react-webpack5": "^7.0.2",
|
||||
"@storybook/types": "^7.0.2",
|
||||
"@schematics/angular": "~16.0.0-rc.4",
|
||||
"@storybook/addon-essentials": "^7.0.8",
|
||||
"@storybook/angular": "^7.0.8",
|
||||
"@storybook/core-server": "^7.0.8",
|
||||
"@storybook/react": "^7.0.8",
|
||||
"@storybook/react-webpack5": "^7.0.8",
|
||||
"@storybook/types": "^7.0.8",
|
||||
"@svgr/rollup": "^6.1.2",
|
||||
"@svgr/webpack": "^6.1.2",
|
||||
"@swc-node/register": "^1.4.2",
|
||||
@ -198,7 +199,7 @@
|
||||
"mini-css-extract-plugin": "~2.4.7",
|
||||
"minimatch": "3.0.5",
|
||||
"next-sitemap": "^3.1.10",
|
||||
"ng-packagr": "~15.2.2",
|
||||
"ng-packagr": "~16.0.0-rc.1",
|
||||
"node-fetch": "^2.6.7",
|
||||
"nx": "16.0.0-rc.0",
|
||||
"octokit": "^2.0.14",
|
||||
@ -225,7 +226,7 @@
|
||||
"rollup-plugin-peer-deps-external": "^2.2.4",
|
||||
"rollup-plugin-postcss": "^4.0.1",
|
||||
"rollup-plugin-typescript2": "0.34.1",
|
||||
"rxjs": "6.6.7",
|
||||
"rxjs": "^7.8.0",
|
||||
"sass": "1.55.0",
|
||||
"sass-loader": "^12.2.0",
|
||||
"semver": "7.3.4",
|
||||
@ -251,13 +252,13 @@
|
||||
"tslint-to-eslint-config": "^2.13.0",
|
||||
"typedoc": "0.23.28",
|
||||
"typedoc-plugin-markdown": "3.14.0",
|
||||
"typescript": "~4.9.5",
|
||||
"typescript": "~5.0.2",
|
||||
"unzipper": "^0.10.11",
|
||||
"url-loader": "^4.1.1",
|
||||
"use-sync-external-store": "^1.2.0",
|
||||
"verdaccio": "^5.0.4",
|
||||
"vite": "^4.0.1",
|
||||
"webpack": "^5.75.0",
|
||||
"webpack": "^5.80.0",
|
||||
"webpack-dev-server": "^4.9.3",
|
||||
"webpack-merge": "^5.8.0",
|
||||
"webpack-node-externals": "^3.0.0",
|
||||
@ -275,7 +276,6 @@
|
||||
"@nx/react": "16.0.0-rc.0",
|
||||
"@nx/jest": "16.0.0-rc.0",
|
||||
"@nx/next": "16.0.0-rc.0",
|
||||
"@nx/angular": "16.0.0-rc.0",
|
||||
"@nx/eslint-plugin": "16.0.0-rc.0"
|
||||
},
|
||||
"author": "Victor Savkin",
|
||||
@ -347,4 +347,3 @@
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -200,6 +200,42 @@
|
||||
},
|
||||
"description": "Remove exported `@angular/platform-server` `renderModule` method. The `renderModule` method is now exported by the Angular CLI.",
|
||||
"factory": "./src/migrations/update-16-1-0/remove-render-module-platform-server-exports"
|
||||
},
|
||||
"update-angular-cli-version-16-0-0-rc-4": {
|
||||
"cli": "nx",
|
||||
"version": "16.1.0-beta.0",
|
||||
"requires": {
|
||||
"@angular/core": ">=16.0.0-rc.4"
|
||||
},
|
||||
"description": "Update the @angular/cli package version to ~16.0.0-rc.4.",
|
||||
"factory": "./src/migrations/update-16-1-0/update-angular-cli"
|
||||
},
|
||||
"remove-ngcc-invocation": {
|
||||
"cli": "nx",
|
||||
"version": "16.1.0-beta.0",
|
||||
"requires": {
|
||||
"@angular/core": ">=16.0.0-rc.4"
|
||||
},
|
||||
"description": "Remove 'ngcc' invocation if exists from the 'postinstall' script in package.json.",
|
||||
"factory": "./src/migrations/update-16-1-0/remove-ngcc-invocation"
|
||||
},
|
||||
"extract-app-config-for-standalone": {
|
||||
"cli": "nx",
|
||||
"version": "16.1.0-beta.0",
|
||||
"requires": {
|
||||
"@angular/core": ">=16.0.0-rc.4"
|
||||
},
|
||||
"description": "Extract the app config for standalone apps",
|
||||
"factory": "./src/migrations/update-16-1-0/extract-standalone-config-from-bootstrap"
|
||||
},
|
||||
"update-server-executor-config": {
|
||||
"cli": "nx",
|
||||
"version": "16.1.0-beta.0",
|
||||
"requires": {
|
||||
"@angular/core": ">=16.0.0-rc.4"
|
||||
},
|
||||
"description": "Update server executors' configuration to disable 'buildOptimizer' for non optimized builds.",
|
||||
"factory": "./src/migrations/update-16-1-0/update-server-executor-config"
|
||||
}
|
||||
},
|
||||
"packageJsonUpdates": {
|
||||
@ -984,6 +1020,96 @@
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"16.1.0": {
|
||||
"version": "16.1.0-beta.0",
|
||||
"x-prompt": "Do you want to update the Angular version to v16?",
|
||||
"requires": {
|
||||
"@angular/core": "^15.2.0",
|
||||
"typescript": ">=4.9.3 <5.1"
|
||||
},
|
||||
"packages": {
|
||||
"@angular/core": {
|
||||
"version": "~16.0.0-rc.4",
|
||||
"alwaysAddToPackageJson": true
|
||||
},
|
||||
"zone.js": {
|
||||
"version": "~0.13.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@angular-devkit/architect": {
|
||||
"version": "~0.1600.0-rc.4",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@angular-devkit/build-angular": {
|
||||
"version": "~16.0.0-rc.4",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@angular-devkit/build-webpack": {
|
||||
"version": "~0.1600.0-rc.4",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@angular-devkit/core": {
|
||||
"version": "~16.0.0-rc.4",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@angular-devkit/schematics": {
|
||||
"version": "~16.0.0-rc.4",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@schematics/angular": {
|
||||
"version": "~16.0.0-rc.4",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"ng-packagr": {
|
||||
"version": "~16.0.0-rc.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@nguniversal/build-angular": {
|
||||
"version": "~16.0.0-rc.2",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@nguniversal/builders": {
|
||||
"version": "~16.0.0-rc.2",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@nguniversal/common": {
|
||||
"version": "~16.0.0-rc.2",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@nguniversal/express-engine": {
|
||||
"version": "~16.0.0-rc.2",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@angular/material": {
|
||||
"version": "~16.0.0-rc.2",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@angular/cdk": {
|
||||
"version": "~16.0.0-rc.2",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"16.1.0-angular-eslint": {
|
||||
"version": "16.1.0-beta.0",
|
||||
"requires": {
|
||||
"eslint": "^7.20.0 || ^8.0.0"
|
||||
},
|
||||
"packages": {
|
||||
"@angular-eslint/eslint-plugin": {
|
||||
"version": "~16.0.0-alpha.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@angular-eslint/eslint-plugin-template": {
|
||||
"version": "~16.0.0-alpha.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@angular-eslint/template-parser": {
|
||||
"version": "~16.0.0-alpha.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,6 +46,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@phenomnomnominal/tsquery": "~5.0.1",
|
||||
"@typescript-eslint/type-utils": "^5.36.1",
|
||||
"chalk": "^4.1.0",
|
||||
"chokidar": "^3.5.1",
|
||||
"http-server": "^14.1.0",
|
||||
@ -55,7 +56,7 @@
|
||||
"semver": "7.3.4",
|
||||
"ts-node": "10.9.1",
|
||||
"tsconfig-paths": "^4.1.2",
|
||||
"webpack": "^5.75.0",
|
||||
"webpack": "^5.80.0",
|
||||
"webpack-merge": "5.7.3",
|
||||
"enquirer": "^2.3.6",
|
||||
"@nx/cypress": "file:../cypress",
|
||||
@ -67,11 +68,11 @@
|
||||
"@nx/workspace": "file:../workspace"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular-devkit/build-angular": ">= 14.0.0 < 16.0.0",
|
||||
"@angular-devkit/schematics": ">= 14.0.0 < 16.0.0",
|
||||
"@schematics/angular": ">= 14.0.0 < 16.0.0",
|
||||
"@angular-devkit/core": ">= 14.0.0 < 16.0.0",
|
||||
"@nguniversal/builders": ">= 14.0.0 < 16.0.0",
|
||||
"@angular-devkit/build-angular": "^16.0.0-rc.4",
|
||||
"@angular-devkit/schematics": "^16.0.0-rc.4",
|
||||
"@schematics/angular": "^16.0.0-rc.4",
|
||||
"@angular-devkit/core": "^16.0.0-rc.4",
|
||||
"@nguniversal/builders": "^16.0.0-rc.2",
|
||||
"rxjs": "^6.5.3 || ^7.5.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { ServerBuilderOptions } from '@angular-devkit/build-angular';
|
||||
import type { ServerBuilderOptions } from '@angular-devkit/build-angular';
|
||||
|
||||
export interface Schema extends ServerBuilderOptions {
|
||||
customWebpackConfig?: {
|
||||
|
||||
@ -191,6 +191,11 @@
|
||||
"description": "Extract all licenses in a separate file, in the case of production builds only.",
|
||||
"default": true
|
||||
},
|
||||
"buildOptimizer": {
|
||||
"type": "boolean",
|
||||
"description": "Enables advanced build optimizations. _Note: only supported in Angular versions >= 16.0.0_.",
|
||||
"default": true
|
||||
},
|
||||
"namedChunks": {
|
||||
"type": "boolean",
|
||||
"description": "Use file name for lazy loaded chunks.",
|
||||
|
||||
@ -7,6 +7,7 @@ import type { Schema } from './schema';
|
||||
export function validateOptions(options: Schema): void {
|
||||
const angularVersionInfo = getInstalledAngularVersionInfo();
|
||||
validateAssets(options, angularVersionInfo);
|
||||
validateBuildOptimizer(options, angularVersionInfo);
|
||||
validateBundleDependencies(options, angularVersionInfo);
|
||||
validateVendorChunk(options, angularVersionInfo);
|
||||
}
|
||||
@ -22,6 +23,16 @@ function validateAssets(options: Schema, { version }: VersionInfo): void {
|
||||
}
|
||||
}
|
||||
|
||||
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 validateBundleDependencies(
|
||||
options: Schema,
|
||||
{ major, version }: VersionInfo
|
||||
|
||||
@ -3,7 +3,8 @@
|
||||
*
|
||||
* Changes made:
|
||||
* - Use our own StylesheetProcessor files instead of the ones provide by ng-packagr.
|
||||
* - Excludes the ngcc compilation for faster builds.
|
||||
* - Excludes the ngcc compilation for faster builds (angular < v16)
|
||||
* - Support ESM2020 for Angular < 16.
|
||||
*/
|
||||
|
||||
import type { Transform } from 'ng-packagr/lib/graph/transform';
|
||||
@ -15,6 +16,7 @@ import {
|
||||
import { setDependenciesTsConfigPaths } from 'ng-packagr/lib/ts/tsconfig';
|
||||
import * as path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
import { getInstalledAngularVersionInfo } from '../../../../utilities/angular-version-utils';
|
||||
import { compileSourceFiles } from '../../ngc/compile-source-files';
|
||||
import { StylesheetProcessor as StylesheetProcessorClass } from '../../styles/stylesheet-processor';
|
||||
import { NgPackagrOptions } from '../options.di';
|
||||
@ -33,8 +35,14 @@ export const nxCompileNgcTransformFactory = (
|
||||
entryPoints
|
||||
);
|
||||
|
||||
const angularVersion = getInstalledAngularVersionInfo();
|
||||
|
||||
// Compile TypeScript sources
|
||||
const { esm2020, declarations } = entryPoint.data.destinationFiles;
|
||||
const { declarations } = entryPoint.data.destinationFiles;
|
||||
const esmModulePath =
|
||||
angularVersion.major < 16
|
||||
? (entryPoint.data.destinationFiles as any).esm2020
|
||||
: entryPoint.data.destinationFiles.esm2022;
|
||||
const { basePath, cssUrl, styleIncludePaths } =
|
||||
entryPoint.data.entryPoint;
|
||||
const { moduleResolutionCache } = entryPoint.cache;
|
||||
@ -53,13 +61,15 @@ export const nxCompileNgcTransformFactory = (
|
||||
tsConfig,
|
||||
moduleResolutionCache,
|
||||
{
|
||||
outDir: path.dirname(esm2020),
|
||||
outDir: path.dirname(esmModulePath),
|
||||
declarationDir: path.dirname(declarations),
|
||||
declaration: true,
|
||||
target: ts.ScriptTarget.ES2020,
|
||||
target:
|
||||
angularVersion.major >= 16
|
||||
? ts.ScriptTarget.ES2022
|
||||
: ts.ScriptTarget.ES2020,
|
||||
},
|
||||
entryPoint.cache.stylesheetProcessor as any,
|
||||
null,
|
||||
options.watch
|
||||
);
|
||||
} catch (error) {
|
||||
|
||||
@ -32,8 +32,8 @@ import { pipe } from 'rxjs';
|
||||
* The transformation pipeline is pluggable through the dependency injection system.
|
||||
* Sub-transformations are passed to this factory function as arguments.
|
||||
*
|
||||
* @param compileTs Transformation compiling typescript sources to ES2020 modules.
|
||||
* @param writePackage Transformation writing a distribution-ready `package.json` (for publishing to npm registry).
|
||||
* @param compileTs Transformation compiling typescript sources to ES2022 modules.
|
||||
* @param writeBundles Transformation flattening ES2022 modules to ESM2022, UMD, and minified UMD.
|
||||
*/
|
||||
export const nxEntryPointTransformFactory = (
|
||||
compileTs: Transform,
|
||||
|
||||
@ -2,8 +2,8 @@
|
||||
* Adapted from the original ng-packagr.
|
||||
*
|
||||
* Changes made:
|
||||
* - Change the package.json metadata to only use the ESM2020 output as it's the only one generated.
|
||||
* - Remove package exports.
|
||||
* - Change the package.json metadata to only use the ESM2022 output.
|
||||
* - Change the package.json metadata to only use the ESM2020 output (Angular < 16).
|
||||
*/
|
||||
|
||||
import { logger } from '@nx/devkit';
|
||||
@ -33,6 +33,10 @@ import { globFiles } from 'ng-packagr/lib/utils/glob';
|
||||
import { ensureUnixPath } from 'ng-packagr/lib/utils/path';
|
||||
import { AssetPattern } from 'ng-packagr/ng-package.schema';
|
||||
import * as path from 'path';
|
||||
import {
|
||||
getInstalledAngularVersionInfo,
|
||||
VersionInfo,
|
||||
} from '../../../../utilities/angular-version-utils';
|
||||
|
||||
export const nxWritePackageTransform = (options: NgPackagrOptions) =>
|
||||
transformFromPromise(async (graph) => {
|
||||
@ -42,11 +46,13 @@ export const nxWritePackageTransform = (options: NgPackagrOptions) =>
|
||||
const ngPackage = ngPackageNode.data;
|
||||
const { destinationFiles } = entryPoint.data;
|
||||
|
||||
const angularVersion = getInstalledAngularVersionInfo();
|
||||
|
||||
if (!ngEntryPoint.isSecondaryEntryPoint) {
|
||||
logger.log('Copying assets');
|
||||
|
||||
try {
|
||||
await copyAssets(graph, entryPoint, ngPackageNode);
|
||||
await copyAssets(graph, entryPoint, ngPackageNode, angularVersion);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
@ -64,11 +70,28 @@ export const nxWritePackageTransform = (options: NgPackagrOptions) =>
|
||||
ngEntryPoint,
|
||||
ngPackage,
|
||||
{
|
||||
module: relativeUnixFromDestPath(destinationFiles.esm2020),
|
||||
es2020: relativeUnixFromDestPath(destinationFiles.esm2020),
|
||||
esm2020: relativeUnixFromDestPath(destinationFiles.esm2020),
|
||||
// backward compat for Angular < 16
|
||||
...(angularVersion.major < 16
|
||||
? {
|
||||
module: relativeUnixFromDestPath(
|
||||
(destinationFiles as any).esm2020
|
||||
),
|
||||
es2020: relativeUnixFromDestPath(
|
||||
(destinationFiles as any).esm2020
|
||||
),
|
||||
esm2020: relativeUnixFromDestPath(
|
||||
(destinationFiles as any).esm2020
|
||||
),
|
||||
}
|
||||
: {
|
||||
module: relativeUnixFromDestPath(destinationFiles.esm2022),
|
||||
}),
|
||||
typings: relativeUnixFromDestPath(destinationFiles.declarations),
|
||||
exports: generatePackageExports(ngEntryPoint, graph),
|
||||
exports: generatePackageExports(
|
||||
ngEntryPoint,
|
||||
graph,
|
||||
angularVersion
|
||||
),
|
||||
// webpack v4+ specific flag to enable advanced optimizations and code splitting
|
||||
sideEffects: ngEntryPoint.packageJson.sideEffects ?? false,
|
||||
},
|
||||
@ -109,7 +132,8 @@ type AssetEntry = Exclude<AssetPattern, string>;
|
||||
async function copyAssets(
|
||||
graph: BuildGraph,
|
||||
entryPointNode: EntryPointNode,
|
||||
ngPackageNode: PackageNode
|
||||
ngPackageNode: PackageNode,
|
||||
angularVersion: VersionInfo
|
||||
): Promise<void> {
|
||||
const ngPackage = ngPackageNode.data;
|
||||
|
||||
@ -164,14 +188,24 @@ async function copyAssets(
|
||||
}
|
||||
|
||||
for (const asset of assets) {
|
||||
const filePaths = await globFiles(asset.glob, {
|
||||
const globOptions: Parameters<typeof globFiles>[1] = {
|
||||
cwd: asset.input,
|
||||
ignore: [...(asset.ignore ?? []), ...globsForceIgnored],
|
||||
cache: ngPackageNode.cache.globCache,
|
||||
dot: true,
|
||||
nodir: true,
|
||||
follow: asset.followSymlinks,
|
||||
});
|
||||
};
|
||||
|
||||
if (angularVersion.major < 16) {
|
||||
// versions lower than v16 support these properties
|
||||
(globOptions as any).cache = (ngPackageNode.cache as any).globCache;
|
||||
(globOptions as any).nodir = true;
|
||||
(globOptions as any).follow = asset.followSymlinks;
|
||||
} else {
|
||||
// starting in v16 these properties are supported
|
||||
globOptions.onlyFiles = true;
|
||||
globOptions.followSymbolicLinks = asset.followSymlinks;
|
||||
}
|
||||
|
||||
const filePaths = await globFiles(asset.glob, globOptions);
|
||||
for (const filePath of filePaths) {
|
||||
const fileSrcFullPath = path.join(asset.input, filePath);
|
||||
const fileDestFullPath = path.join(asset.output, filePath);
|
||||
@ -334,12 +368,16 @@ type PackageExports = Record<string, ConditionalExport>;
|
||||
* https://nodejs.org/api/packages.html#packages_conditional_exports
|
||||
*/
|
||||
type ConditionalExport = {
|
||||
node?: string;
|
||||
types?: string;
|
||||
esm2022?: string;
|
||||
esm?: string;
|
||||
default?: string;
|
||||
|
||||
// backward compat for Angular < 16
|
||||
node?: string;
|
||||
esm2020?: string;
|
||||
es2020?: string;
|
||||
es2015?: string;
|
||||
default?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -348,7 +386,8 @@ type ConditionalExport = {
|
||||
*/
|
||||
function generatePackageExports(
|
||||
{ destinationPath, packageJson }: NgEntryPoint,
|
||||
graph: BuildGraph
|
||||
graph: BuildGraph,
|
||||
angularVersion: VersionInfo
|
||||
): PackageExports {
|
||||
const exports: PackageExports = packageJson.exports
|
||||
? JSON.parse(JSON.stringify(packageJson.exports))
|
||||
@ -396,12 +435,26 @@ function generatePackageExports(
|
||||
? `./${destinationFiles.directory}`
|
||||
: '.';
|
||||
|
||||
insertMappingOrError(subpath, {
|
||||
// backward compat for Angular < 16
|
||||
const mapping =
|
||||
angularVersion.major < 16
|
||||
? {
|
||||
types: relativeUnixFromDestPath(destinationFiles.declarations),
|
||||
es2020: relativeUnixFromDestPath(destinationFiles.esm2020),
|
||||
esm2020: relativeUnixFromDestPath(destinationFiles.esm2020),
|
||||
default: relativeUnixFromDestPath(destinationFiles.esm2020),
|
||||
});
|
||||
es2020: relativeUnixFromDestPath((destinationFiles as any).esm2020),
|
||||
esm2020: relativeUnixFromDestPath(
|
||||
(destinationFiles as any).esm2020
|
||||
),
|
||||
default: relativeUnixFromDestPath(
|
||||
(destinationFiles as any).esm2020
|
||||
),
|
||||
}
|
||||
: {
|
||||
esm2022: relativeUnixFromDestPath(destinationFiles.esm2022),
|
||||
esm: relativeUnixFromDestPath(destinationFiles.esm2022),
|
||||
default: relativeUnixFromDestPath(destinationFiles.esm2022),
|
||||
};
|
||||
|
||||
insertMappingOrError(subpath, mapping);
|
||||
}
|
||||
|
||||
return exports;
|
||||
|
||||
@ -2,8 +2,9 @@
|
||||
* Adapted from the original ng-packagr source.
|
||||
*
|
||||
* Changes made:
|
||||
* - Made sure ngccProcessor is optional.
|
||||
* - Remove ngccProcessor (Angular < 16)
|
||||
* - Use custom cacheCompilerHost instead of the one provided by ng-packagr.
|
||||
* - Support Angular Compiler `incrementalDriver` for Angular < 16.
|
||||
*/
|
||||
|
||||
import type {
|
||||
@ -17,12 +18,11 @@ import {
|
||||
isPackage,
|
||||
PackageNode,
|
||||
} from 'ng-packagr/lib/ng-package/nodes';
|
||||
import { NgccProcessor } from 'ng-packagr/lib/ngc/ngcc-processor';
|
||||
import { augmentProgramWithVersioning } from 'ng-packagr/lib/ts/cache-compiler-host';
|
||||
import { ngccTransformCompilerHost } from 'ng-packagr/lib/ts/ngcc-transform-compiler-host';
|
||||
import * as log from 'ng-packagr/lib/utils/log';
|
||||
import { ngCompilerCli } from 'ng-packagr/lib/utils/ng-compiler-cli';
|
||||
import * as ts from 'typescript';
|
||||
import { getInstalledAngularVersionInfo } from '../../../utilities/angular-version-utils';
|
||||
import { StylesheetProcessor } from '../styles/stylesheet-processor';
|
||||
import { cacheCompilerHost } from '../ts/cache-compiler-host';
|
||||
|
||||
@ -32,7 +32,6 @@ export async function compileSourceFiles(
|
||||
moduleResolutionCache: ts.ModuleResolutionCache,
|
||||
extraOptions?: Partial<CompilerOptions>,
|
||||
stylesheetProcessor?: StylesheetProcessor,
|
||||
ngccProcessor?: NgccProcessor,
|
||||
watch?: boolean
|
||||
) {
|
||||
const { NgtscProgram, formatDiagnostics } = await ngCompilerCli();
|
||||
@ -45,7 +44,7 @@ export async function compileSourceFiles(
|
||||
const ngPackageNode: PackageNode = graph.find(isPackage);
|
||||
const inlineStyleLanguage = ngPackageNode.data.inlineStyleLanguage;
|
||||
|
||||
let tsCompilerHost = cacheCompilerHost(
|
||||
const tsCompilerHost = cacheCompilerHost(
|
||||
graph,
|
||||
entryPoint,
|
||||
tsConfigOptions,
|
||||
@ -54,15 +53,6 @@ export async function compileSourceFiles(
|
||||
inlineStyleLanguage
|
||||
);
|
||||
|
||||
if (ngccProcessor) {
|
||||
tsCompilerHost = ngccTransformCompilerHost(
|
||||
tsCompilerHost,
|
||||
tsConfigOptions,
|
||||
ngccProcessor,
|
||||
moduleResolutionCache
|
||||
);
|
||||
}
|
||||
|
||||
const cache = entryPoint.cache;
|
||||
const sourceFileCache = cache.sourcesFileCache;
|
||||
|
||||
@ -165,9 +155,15 @@ export async function compileSourceFiles(
|
||||
}
|
||||
|
||||
// Collect sources that are required to be emitted
|
||||
const angularVersion = getInstalledAngularVersionInfo();
|
||||
const incrementalCompilation: typeof angularCompiler.incrementalCompilation =
|
||||
angularVersion.major < 16
|
||||
? (angularCompiler as any).incrementalDriver
|
||||
: angularCompiler.incrementalCompilation;
|
||||
|
||||
if (
|
||||
!ignoreForEmit.has(sourceFile) &&
|
||||
!angularCompiler.incrementalDriver.safeToSkipEmit(sourceFile)
|
||||
!incrementalCompilation.safeToSkipEmit(sourceFile)
|
||||
) {
|
||||
// If required to emit, diagnostics may have also changed
|
||||
if (!ignoreForDiagnostics.has(sourceFile)) {
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
"outputCapture": "direct-nodejs",
|
||||
"$schema": "http://json-schema.org/schema",
|
||||
"title": "ng-packagr Target",
|
||||
"description": "Builds a 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 `@nrwl/angular:package` executor but with some key differences:\n- It doesn't run `ngcc` automatically (`ngcc` needs to be run separately beforehand if needed, this can be done in a `postinstall` hook on `package.json`).\n- It only produces ESM2020 bundles.\n- It doesn't generate package exports in the `package.json`.",
|
||||
"description": "Builds a 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).",
|
||||
"cli": "nx",
|
||||
"type": "object",
|
||||
"presets": [
|
||||
|
||||
@ -3,6 +3,8 @@
|
||||
*
|
||||
* Changes made:
|
||||
* - Use our own StylesheetProcessor files instead of the ones provide by ng-packagr.
|
||||
* - Support ngcc for Angular < 16.
|
||||
* - Support ESM2020 for Angular < 16.
|
||||
*/
|
||||
|
||||
import {
|
||||
@ -14,14 +16,14 @@ import {
|
||||
isEntryPoint,
|
||||
isEntryPointInProgress,
|
||||
} from 'ng-packagr/lib/ng-package/nodes';
|
||||
import { NgccProcessor } from 'ng-packagr/lib/ngc/ngcc-processor';
|
||||
import { setDependenciesTsConfigPaths } from 'ng-packagr/lib/ts/tsconfig';
|
||||
import { ngccCompilerCli } from 'ng-packagr/lib/utils/ng-compiler-cli';
|
||||
import * as ora from 'ora';
|
||||
import * as path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
import { getInstalledAngularVersionInfo } from '../../../../utilities/angular-version-utils';
|
||||
import { compileSourceFiles } from '../../ngc/compile-source-files';
|
||||
import { StylesheetProcessor as StylesheetProcessorClass } from '../../styles/stylesheet-processor';
|
||||
import { ngccCompilerCli } from '../../utils/ng-compiler-cli';
|
||||
import { NgPackagrOptions } from '../options.di';
|
||||
|
||||
export const compileNgcTransformFactory = (
|
||||
@ -43,20 +45,29 @@ export const compileNgcTransformFactory = (
|
||||
entryPoints
|
||||
);
|
||||
|
||||
const angularVersion = getInstalledAngularVersionInfo();
|
||||
|
||||
// Compile TypeScript sources
|
||||
const { esm2020, declarations } = entryPoint.data.destinationFiles;
|
||||
const { declarations } = entryPoint.data.destinationFiles;
|
||||
const esmModulePath =
|
||||
angularVersion.major < 16
|
||||
? (entryPoint.data.destinationFiles as any).esm2020
|
||||
: entryPoint.data.destinationFiles.esm2022;
|
||||
const { basePath, cssUrl, styleIncludePaths } =
|
||||
entryPoint.data.entryPoint;
|
||||
const { moduleResolutionCache, ngccProcessingCache } = entryPoint.cache;
|
||||
const { moduleResolutionCache } = entryPoint.cache;
|
||||
|
||||
spinner.start(
|
||||
`Compiling with Angular sources in Ivy ${
|
||||
tsConfig.options.compilationMode || 'full'
|
||||
} compilation mode.`
|
||||
);
|
||||
const ngccProcessor = new NgccProcessor(
|
||||
let ngccProcessor: any;
|
||||
if (angularVersion && angularVersion.major < 16) {
|
||||
ngccProcessor =
|
||||
new (require('ng-packagr/lib/ngc/ngcc-processor').NgccProcessor)(
|
||||
await ngccCompilerCli(),
|
||||
ngccProcessingCache,
|
||||
(entryPoint.cache as any).ngccProcessingCache,
|
||||
tsConfig.project,
|
||||
tsConfig.options,
|
||||
entryPoints
|
||||
@ -65,6 +76,7 @@ export const compileNgcTransformFactory = (
|
||||
// Only run the async version of NGCC during the primary entrypoint processing.
|
||||
await ngccProcessor.process();
|
||||
}
|
||||
}
|
||||
|
||||
entryPoint.cache.stylesheetProcessor ??= new StylesheetProcessor(
|
||||
basePath,
|
||||
@ -80,10 +92,13 @@ export const compileNgcTransformFactory = (
|
||||
tsConfig,
|
||||
moduleResolutionCache,
|
||||
{
|
||||
outDir: path.dirname(esm2020),
|
||||
outDir: path.dirname(esmModulePath),
|
||||
declarationDir: path.dirname(declarations),
|
||||
declaration: true,
|
||||
target: ts.ScriptTarget.ES2020,
|
||||
target:
|
||||
angularVersion.major >= 16
|
||||
? ts.ScriptTarget.ES2022
|
||||
: ts.ScriptTarget.ES2020,
|
||||
},
|
||||
entryPoint.cache.stylesheetProcessor as any,
|
||||
ngccProcessor,
|
||||
|
||||
@ -3,6 +3,8 @@
|
||||
*
|
||||
* Changes made:
|
||||
* - Use custom cacheCompilerHost instead of the one provided by ng-packagr.
|
||||
* - Support ngcc for Angular < 16.
|
||||
* - Support Angular Compiler `incrementalDriver` for Angular < 16.
|
||||
*/
|
||||
|
||||
import type {
|
||||
@ -16,11 +18,10 @@ import {
|
||||
isPackage,
|
||||
PackageNode,
|
||||
} from 'ng-packagr/lib/ng-package/nodes';
|
||||
import { NgccProcessor } from 'ng-packagr/lib/ngc/ngcc-processor';
|
||||
import { ngccTransformCompilerHost } from 'ng-packagr/lib/ts/ngcc-transform-compiler-host';
|
||||
import * as log from 'ng-packagr/lib/utils/log';
|
||||
import { ngCompilerCli } from 'ng-packagr/lib/utils/ng-compiler-cli';
|
||||
import * as ts from 'typescript';
|
||||
import { getInstalledAngularVersionInfo } from '../../../utilities/angular-version-utils';
|
||||
import { StylesheetProcessor } from '../styles/stylesheet-processor';
|
||||
import {
|
||||
augmentProgramWithVersioning,
|
||||
@ -33,7 +34,7 @@ export async function compileSourceFiles(
|
||||
moduleResolutionCache: ts.ModuleResolutionCache,
|
||||
extraOptions?: Partial<CompilerOptions>,
|
||||
stylesheetProcessor?: StylesheetProcessor,
|
||||
ngccProcessor?: NgccProcessor,
|
||||
ngccProcessor?: any,
|
||||
watch?: boolean
|
||||
) {
|
||||
const { NgtscProgram, formatDiagnostics } = await ngCompilerCli();
|
||||
@ -46,19 +47,24 @@ export async function compileSourceFiles(
|
||||
const ngPackageNode: PackageNode = graph.find(isPackage);
|
||||
const inlineStyleLanguage = ngPackageNode.data.inlineStyleLanguage;
|
||||
|
||||
const tsCompilerHost = ngccTransformCompilerHost(
|
||||
cacheCompilerHost(
|
||||
let tsCompilerHost = cacheCompilerHost(
|
||||
graph,
|
||||
entryPoint,
|
||||
tsConfigOptions,
|
||||
moduleResolutionCache,
|
||||
stylesheetProcessor,
|
||||
inlineStyleLanguage
|
||||
),
|
||||
);
|
||||
|
||||
if (ngccProcessor) {
|
||||
tsCompilerHost =
|
||||
require('ng-packagr/lib/ts/ngcc-transform-compiler-host').ngccTransformCompilerHost(
|
||||
tsCompilerHost,
|
||||
tsConfigOptions,
|
||||
ngccProcessor,
|
||||
moduleResolutionCache
|
||||
);
|
||||
}
|
||||
|
||||
const cache = entryPoint.cache;
|
||||
const sourceFileCache = cache.sourcesFileCache;
|
||||
@ -162,9 +168,15 @@ export async function compileSourceFiles(
|
||||
}
|
||||
|
||||
// Collect sources that are required to be emitted
|
||||
const angularVersion = getInstalledAngularVersionInfo();
|
||||
const incrementalCompilation: typeof angularCompiler.incrementalCompilation =
|
||||
angularVersion.major < 16
|
||||
? (angularCompiler as any).incrementalDriver
|
||||
: angularCompiler.incrementalCompilation;
|
||||
|
||||
if (
|
||||
!ignoreForEmit.has(sourceFile) &&
|
||||
!angularCompiler.incrementalDriver.safeToSkipEmit(sourceFile)
|
||||
!incrementalCompilation.safeToSkipEmit(sourceFile)
|
||||
) {
|
||||
// If required to emit, diagnostics may have also changed
|
||||
if (!ignoreForDiagnostics.has(sourceFile)) {
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
export async function ngccCompilerCli(): Promise<any> {
|
||||
const compilerCliModule = await new Function(
|
||||
`return import('@angular/compiler-cli/ngcc');`
|
||||
)();
|
||||
|
||||
return compilerCliModule.process
|
||||
? compilerCliModule
|
||||
: compilerCliModule.default;
|
||||
}
|
||||
@ -61,8 +61,13 @@ function getTailwindConfigPath(
|
||||
projectRoot: string,
|
||||
workspaceRoot: string
|
||||
): string | undefined {
|
||||
// valid tailwind config files https://github.com/tailwindlabs/tailwindcss/blob/master/src/util/resolveConfigPath.js#L46
|
||||
const tailwindConfigFiles = ['tailwind.config.js', 'tailwind.config.cjs'];
|
||||
// valid tailwind config files https://github.com/tailwindlabs/tailwindcss/blob/master/src/util/resolveConfigPath.js#L4
|
||||
const tailwindConfigFiles = [
|
||||
'tailwind.config.js',
|
||||
'tailwind.config.cjs',
|
||||
'tailwind.config.mjs',
|
||||
'tailwind.config.ts',
|
||||
];
|
||||
|
||||
for (const basePath of [projectRoot, workspaceRoot]) {
|
||||
for (const configFile of tailwindConfigFiles) {
|
||||
|
||||
@ -208,27 +208,37 @@ exports[`app --minimal should skip "nx-welcome.component.ts" file and references
|
||||
|
||||
exports[`app --standalone should generate a standalone app correctly with routing 1`] = `
|
||||
"import { bootstrapApplication } from '@angular/platform-browser';
|
||||
import {
|
||||
provideRouter,
|
||||
withEnabledBlockingInitialNavigation,
|
||||
} from '@angular/router';
|
||||
import { appRoutes } from './app/app.routes';
|
||||
import { appConfig } from './app/app.config';
|
||||
import { AppComponent } from './app/app.component';
|
||||
|
||||
bootstrapApplication(AppComponent, {
|
||||
providers: [provideRouter(appRoutes, withEnabledBlockingInitialNavigation())],
|
||||
}).catch((err) => console.error(err));
|
||||
bootstrapApplication(AppComponent, appConfig).catch((err) =>
|
||||
console.error(err)
|
||||
);
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`app --standalone should generate a standalone app correctly with routing 2`] = `
|
||||
"import { ApplicationConfig } from '@angular/core';
|
||||
import {
|
||||
provideRouter,
|
||||
withEnabledBlockingInitialNavigation,
|
||||
} from '@angular/router';
|
||||
import { appRoutes } from './app.routes';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [provideRouter(appRoutes, withEnabledBlockingInitialNavigation())],
|
||||
};
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`app --standalone should generate a standalone app correctly with routing 3`] = `
|
||||
"import { Route } from '@angular/router';
|
||||
|
||||
export const appRoutes: Route[] = [];
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`app --standalone should generate a standalone app correctly with routing 3`] = `
|
||||
exports[`app --standalone should generate a standalone app correctly with routing 4`] = `
|
||||
"import { Component } from '@angular/core';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { NxWelcomeComponent } from './nx-welcome.component';
|
||||
@ -246,7 +256,7 @@ export class AppComponent {
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`app --standalone should generate a standalone app correctly with routing 4`] = `
|
||||
exports[`app --standalone should generate a standalone app correctly with routing 5`] = `
|
||||
"import { TestBed } from '@angular/core/testing';
|
||||
import { AppComponent } from './app.component';
|
||||
import { NxWelcomeComponent } from './nx-welcome.component';
|
||||
@ -279,15 +289,25 @@ describe('AppComponent', () => {
|
||||
|
||||
exports[`app --standalone should generate a standalone app correctly without routing 1`] = `
|
||||
"import { bootstrapApplication } from '@angular/platform-browser';
|
||||
import { appConfig } from './app/app.config';
|
||||
import { AppComponent } from './app/app.component';
|
||||
|
||||
bootstrapApplication(AppComponent, {
|
||||
providers: [],
|
||||
}).catch((err) => console.error(err));
|
||||
bootstrapApplication(AppComponent, appConfig).catch((err) =>
|
||||
console.error(err)
|
||||
);
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`app --standalone should generate a standalone app correctly without routing 2`] = `
|
||||
"import { ApplicationConfig } from '@angular/core';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [],
|
||||
};
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`app --standalone should generate a standalone app correctly without routing 3`] = `
|
||||
"import { Component } from '@angular/core';
|
||||
import { NxWelcomeComponent } from './nx-welcome.component';
|
||||
|
||||
@ -304,7 +324,7 @@ export class AppComponent {
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`app --standalone should generate a standalone app correctly without routing 3`] = `
|
||||
exports[`app --standalone should generate a standalone app correctly without routing 4`] = `
|
||||
"import { TestBed } from '@angular/core/testing';
|
||||
import { AppComponent } from './app.component';
|
||||
import { NxWelcomeComponent } from './nx-welcome.component';
|
||||
|
||||
@ -747,6 +747,9 @@ describe('app', () => {
|
||||
expect(
|
||||
appTree.read('apps/standalone/src/main.ts', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
appTree.read('apps/standalone/src/app/app.config.ts', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
appTree.read('apps/standalone/src/app/app.routes.ts', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
@ -775,6 +778,9 @@ describe('app', () => {
|
||||
expect(
|
||||
appTree.read('apps/standalone/src/main.ts', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
appTree.read('apps/standalone/src/app/app.config.ts', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
appTree.read('apps/standalone/src/app/app.component.ts', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
|
||||
@ -0,0 +1,7 @@
|
||||
import { ApplicationConfig } from '@angular/core';<% if (routing) { %>
|
||||
import { provideRouter, withEnabledBlockingInitialNavigation } from '@angular/router';
|
||||
import { appRoutes } from './app.routes';<% } %>
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [<% if (routing) { %>provideRouter(appRoutes, withEnabledBlockingInitialNavigation()) <% } %>]
|
||||
};
|
||||
@ -1,11 +1,5 @@
|
||||
import { bootstrapApplication } from '@angular/platform-browser';<% if(routing) { %>
|
||||
import {
|
||||
provideRouter,
|
||||
withEnabledBlockingInitialNavigation,
|
||||
} from '@angular/router';
|
||||
import { appRoutes } from './app/app.routes';<% } %>
|
||||
import { bootstrapApplication } from '@angular/platform-browser';
|
||||
import { appConfig } from './app/app.config';
|
||||
import { AppComponent } from './app/app.component';
|
||||
|
||||
bootstrapApplication(AppComponent, {
|
||||
providers: [<% if(routing) { %>provideRouter(appRoutes, withEnabledBlockingInitialNavigation())<% } %>],
|
||||
}).catch((err) => console.error(err));
|
||||
bootstrapApplication(AppComponent, appConfig).catch((err) => console.error(err));
|
||||
|
||||
@ -551,9 +551,9 @@ exports[`convert-tslint-to-eslint should work for Angular applications 1`] = `
|
||||
{
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@angular-eslint/eslint-plugin": "~15.0.0",
|
||||
"@angular-eslint/eslint-plugin-template": "~15.0.0",
|
||||
"@angular-eslint/template-parser": "~15.0.0",
|
||||
"@angular-eslint/eslint-plugin": "~16.0.0-alpha.1",
|
||||
"@angular-eslint/eslint-plugin-template": "~16.0.0-alpha.1",
|
||||
"@angular-eslint/template-parser": "~16.0.0-alpha.1",
|
||||
"@nx/eslint-plugin": "0.0.1",
|
||||
"@nx/linter": "0.0.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.58.0",
|
||||
@ -903,9 +903,9 @@ exports[`convert-tslint-to-eslint should work for Angular libraries 1`] = `
|
||||
{
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@angular-eslint/eslint-plugin": "~15.0.0",
|
||||
"@angular-eslint/eslint-plugin-template": "~15.0.0",
|
||||
"@angular-eslint/template-parser": "~15.0.0",
|
||||
"@angular-eslint/eslint-plugin": "~16.0.0-alpha.1",
|
||||
"@angular-eslint/eslint-plugin-template": "~16.0.0-alpha.1",
|
||||
"@angular-eslint/template-parser": "~16.0.0-alpha.1",
|
||||
"@nx/eslint-plugin": "0.0.1",
|
||||
"@nx/linter": "0.0.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.58.0",
|
||||
|
||||
@ -11,7 +11,7 @@ import { NxWelcomeComponent } from './nx-welcome.component';
|
||||
@NgModule({
|
||||
declarations: [AppComponent, NxWelcomeComponent],
|
||||
imports: [
|
||||
BrowserModule.withServerTransition({ appId: 'serverApp' }),
|
||||
BrowserModule,
|
||||
RouterModule.forRoot(appRoutes, { initialNavigation: 'enabledBlocking' }),
|
||||
],
|
||||
providers: [],
|
||||
@ -22,7 +22,12 @@ export class AppModule {}
|
||||
`;
|
||||
|
||||
exports[`Host App Generator --ssr should generate the correct files 2`] = `
|
||||
"import('./bootstrap').catch((err) => console.error(err));
|
||||
"import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
import { AppModule } from './app/app.module';
|
||||
|
||||
platformBrowserDynamic()
|
||||
.bootstrapModule(AppModule)
|
||||
.catch((err) => console.error(err));
|
||||
"
|
||||
`;
|
||||
|
||||
@ -138,9 +143,11 @@ exports[`Host App Generator --ssr should generate the correct files 9`] = `
|
||||
{
|
||||
"configurations": {
|
||||
"development": {
|
||||
"buildOptimizer": false,
|
||||
"extractLicenses": false,
|
||||
"optimization": false,
|
||||
"sourceMap": true,
|
||||
"vendorChunk": true,
|
||||
},
|
||||
"production": {
|
||||
"outputHashing": "media",
|
||||
@ -179,6 +186,205 @@ exports[`Host App Generator --ssr should generate the correct files 10`] = `
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Host App Generator --ssr should generate the correct files for standalone 1`] = `
|
||||
"import { bootstrapApplication } from '@angular/platform-browser';
|
||||
import { appConfig } from './app/app.config';
|
||||
import { AppComponent } from './app/app.component';
|
||||
|
||||
bootstrapApplication(AppComponent, appConfig).catch((err) =>
|
||||
console.error(err)
|
||||
);
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Host App Generator --ssr should generate the correct files for standalone 2`] = `
|
||||
"import { bootstrapApplication } from '@angular/platform-browser';
|
||||
import { AppComponent } from './app/app.component';
|
||||
import { config } from './app/app.config.server';
|
||||
|
||||
const bootstrap = () => bootstrapApplication(AppComponent, config);
|
||||
|
||||
export default bootstrap;
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Host App Generator --ssr should generate the correct files for standalone 3`] = `
|
||||
"import 'zone.js/dist/zone-node';
|
||||
|
||||
import { APP_BASE_HREF } from '@angular/common';
|
||||
import { ngExpressEngine } from '@nguniversal/express-engine';
|
||||
import * as express from 'express';
|
||||
import * as cors from 'cors';
|
||||
import { existsSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
|
||||
import bootstrap from './bootstrap.server';
|
||||
|
||||
// The Express app is exported so that it can be used by serverless Functions.
|
||||
export function app(): express.Express {
|
||||
const server = express();
|
||||
const browserBundles = join(process.cwd(), 'dist/apps/test/browser');
|
||||
|
||||
server.use(cors());
|
||||
const indexHtml = existsSync(join(browserBundles, 'index.original.html'))
|
||||
? 'index.original.html'
|
||||
: 'index';
|
||||
|
||||
// Our Universal express-engine (found @ https://github.com/angular/universal/tree/main/modules/express-engine)
|
||||
server.engine(
|
||||
'html',
|
||||
ngExpressEngine({
|
||||
bootstrap,
|
||||
})
|
||||
);
|
||||
|
||||
server.set('view engine', 'html');
|
||||
server.set('views', browserBundles);
|
||||
|
||||
// Serve static files from /browser
|
||||
server.get(
|
||||
'*.*',
|
||||
express.static(browserBundles, {
|
||||
maxAge: '1y',
|
||||
})
|
||||
);
|
||||
|
||||
// All regular routes use the Universal engine
|
||||
server.get('*', (req, res) => {
|
||||
// keep it async to avoid blocking the server thread
|
||||
|
||||
res.render(indexHtml, {
|
||||
providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }],
|
||||
req,
|
||||
});
|
||||
});
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
function run(): void {
|
||||
const port = process.env['PORT'] || 4000;
|
||||
|
||||
// Start up the Node server
|
||||
const server = app();
|
||||
server.listen(port, () => {
|
||||
console.log(\`Node Express server listening on http://localhost:\${port}\`);
|
||||
});
|
||||
}
|
||||
|
||||
run();
|
||||
|
||||
export default bootstrap;
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Host App Generator --ssr should generate the correct files for standalone 4`] = `
|
||||
"import('./src/main.server');
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Host App Generator --ssr should generate the correct files for standalone 5`] = `
|
||||
"module.exports = {
|
||||
name: 'test',
|
||||
remotes: [],
|
||||
};
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Host App Generator --ssr should generate the correct files for standalone 6`] = `
|
||||
"const { withModuleFederationForSSR } = require('@nx/angular/module-federation');
|
||||
const config = require('./module-federation.config');
|
||||
module.exports = withModuleFederationForSSR(config);
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Host App Generator --ssr should generate the correct files for standalone 7`] = `
|
||||
"import { NxWelcomeComponent } from './nx-welcome.component';
|
||||
import { Route } from '@angular/router';
|
||||
|
||||
export const appRoutes: Route[] = [
|
||||
{
|
||||
path: '',
|
||||
component: NxWelcomeComponent,
|
||||
},
|
||||
];
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Host App Generator --ssr should generate the correct files for standalone 8`] = `
|
||||
"import { ApplicationConfig } from '@angular/core';
|
||||
import {
|
||||
provideRouter,
|
||||
withEnabledBlockingInitialNavigation,
|
||||
} from '@angular/router';
|
||||
import { appRoutes } from './app.routes';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [provideRouter(appRoutes, withEnabledBlockingInitialNavigation())],
|
||||
};
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Host App Generator --ssr should generate the correct files for standalone 9`] = `
|
||||
"import { mergeApplicationConfig, ApplicationConfig } from '@angular/core';
|
||||
import { provideServerRendering } from '@angular/platform-server';
|
||||
import { appConfig } from './app.config';
|
||||
|
||||
const serverConfig: ApplicationConfig = {
|
||||
providers: [provideServerRendering()],
|
||||
};
|
||||
|
||||
export const config = mergeApplicationConfig(appConfig, serverConfig);
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Host App Generator --ssr should generate the correct files for standalone 10`] = `
|
||||
{
|
||||
"configurations": {
|
||||
"development": {
|
||||
"buildOptimizer": false,
|
||||
"extractLicenses": false,
|
||||
"optimization": false,
|
||||
"sourceMap": true,
|
||||
"vendorChunk": true,
|
||||
},
|
||||
"production": {
|
||||
"outputHashing": "media",
|
||||
},
|
||||
},
|
||||
"defaultConfiguration": "production",
|
||||
"dependsOn": [
|
||||
"build",
|
||||
],
|
||||
"executor": "@nx/angular:webpack-server",
|
||||
"options": {
|
||||
"customWebpackConfig": {
|
||||
"path": "apps/test/webpack.server.config.js",
|
||||
},
|
||||
"main": "apps/test/server.ts",
|
||||
"outputPath": "dist/apps/test/server",
|
||||
"tsConfig": "apps/test/tsconfig.server.json",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Host App Generator --ssr should generate the correct files for standalone 11`] = `
|
||||
{
|
||||
"configurations": {
|
||||
"development": {
|
||||
"browserTarget": "test:build:development",
|
||||
"serverTarget": "test:server:development",
|
||||
},
|
||||
"production": {
|
||||
"browserTarget": "test:build:production",
|
||||
"serverTarget": "test:server:production",
|
||||
},
|
||||
},
|
||||
"defaultConfiguration": "development",
|
||||
"executor": "@nx/angular:module-federation-dev-ssr",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Host App Generator should generate a host app with a remote 1`] = `
|
||||
"const { withModuleFederation } = require('@nx/angular/module-federation');
|
||||
const config = require('./module-federation.config');
|
||||
@ -202,16 +408,12 @@ module.exports = withModuleFederation(config);
|
||||
|
||||
exports[`Host App Generator should generate a host with remotes using standalone components 1`] = `
|
||||
"import { bootstrapApplication } from '@angular/platform-browser';
|
||||
import {
|
||||
provideRouter,
|
||||
withEnabledBlockingInitialNavigation,
|
||||
} from '@angular/router';
|
||||
import { appRoutes } from './app/app.routes';
|
||||
import { appConfig } from './app/app.config';
|
||||
import { AppComponent } from './app/app.component';
|
||||
|
||||
bootstrapApplication(AppComponent, {
|
||||
providers: [provideRouter(appRoutes, withEnabledBlockingInitialNavigation())],
|
||||
}).catch((err) => console.error(err));
|
||||
bootstrapApplication(AppComponent, appConfig).catch((err) =>
|
||||
console.error(err)
|
||||
);
|
||||
"
|
||||
`;
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ import * as cors from 'cors';
|
||||
import { existsSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
|
||||
import { AppServerModule } from './bootstrap.server';
|
||||
import<% if(standalone) { %> bootstrap <% } else { %> { AppServerModule } <% } %>from './bootstrap.server';
|
||||
|
||||
// The Express app is exported so that it can be used by serverless Functions.
|
||||
export function app(): express.Express {
|
||||
@ -23,7 +23,7 @@ export function app(): express.Express {
|
||||
server.engine(
|
||||
'html',
|
||||
ngExpressEngine({
|
||||
bootstrap: AppServerModule,
|
||||
<% if(standalone) { %>bootstrap<% } else { %>bootstrap: AppServerModule,<% } %>
|
||||
})
|
||||
);
|
||||
|
||||
@ -63,4 +63,4 @@ function run(): void {
|
||||
|
||||
run();
|
||||
|
||||
export * from './bootstrap.server';
|
||||
<% if(standalone) { %>export default bootstrap;<% } else { %>export * from './bootstrap.server';<% } %>
|
||||
|
||||
@ -229,6 +229,49 @@ describe('Host App Generator', () => {
|
||||
expect(project.targets.server).toMatchSnapshot();
|
||||
expect(project.targets['serve-ssr']).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should generate the correct files for standalone', async () => {
|
||||
// ARRANGE
|
||||
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
|
||||
// ACT
|
||||
await generateTestHostApplication(tree, {
|
||||
name: 'test',
|
||||
standalone: true,
|
||||
ssr: true,
|
||||
});
|
||||
|
||||
// ASSERT
|
||||
const project = readProjectConfiguration(tree, 'test');
|
||||
expect(tree.exists(`apps/test/src/app/app.module.ts`)).toBeFalsy();
|
||||
expect(
|
||||
tree.read(`apps/test/src/bootstrap.ts`, 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
tree.read(`apps/test/src/bootstrap.server.ts`, 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
tree.read(`apps/test/src/main.server.ts`, 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
expect(tree.read(`apps/test/server.ts`, 'utf-8')).toMatchSnapshot();
|
||||
expect(
|
||||
tree.read(`apps/test/module-federation.config.js`, 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
tree.read(`apps/test/webpack.server.config.js`, 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
tree.read(`apps/test/src/app/app.routes.ts`, 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
tree.read(`apps/test/src/app/app.config.ts`, 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
tree.read(`apps/test/src/app/app.config.server.ts`, 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
expect(project.targets.server).toMatchSnapshot();
|
||||
expect(project.targets['serve-ssr']).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
it('should error correctly when Angular version does not support standalone', async () => {
|
||||
|
||||
@ -22,6 +22,7 @@ export async function addSsr(tree: Tree, options: Schema, appName: string) {
|
||||
|
||||
await setupSsr(tree, {
|
||||
project: appName,
|
||||
standalone: options.standalone,
|
||||
});
|
||||
|
||||
tree.rename(
|
||||
@ -33,17 +34,9 @@ export async function addSsr(tree: Tree, options: Schema, appName: string) {
|
||||
"import('./src/main.server');"
|
||||
);
|
||||
|
||||
tree.rename(
|
||||
joinPathFragments(project.sourceRoot, 'main.ts'),
|
||||
joinPathFragments(project.sourceRoot, 'bootstrap.ts')
|
||||
);
|
||||
tree.write(
|
||||
joinPathFragments(project.sourceRoot, 'main.ts'),
|
||||
`import("./bootstrap")`
|
||||
);
|
||||
|
||||
generateFiles(tree, joinPathFragments(__dirname, '../files'), project.root, {
|
||||
appName,
|
||||
standalone: options.standalone,
|
||||
tmpl: '',
|
||||
});
|
||||
|
||||
|
||||
@ -173,6 +173,8 @@ async function addUnitTestRunner(
|
||||
switch (options.unitTestRunner) {
|
||||
case UnitTestRunner.Jest:
|
||||
if (!options.skipPackageJson) {
|
||||
process.env.npm_config_legacy_peer_deps ??= 'true';
|
||||
|
||||
addDependenciesToPackageJsonIfDontExist(
|
||||
tree,
|
||||
{},
|
||||
|
||||
@ -2,8 +2,8 @@
|
||||
"name": "<% if(npmScope) { %><%= npmScope %>/<% } %><%= libFileName %>",
|
||||
"version": "0.0.1",
|
||||
"peerDependencies": {
|
||||
"@angular/common": "^15.2.0",
|
||||
"@angular/core": "^15.2.0"
|
||||
"@angular/common": "<%= angularPeerDepVersion %>",
|
||||
"@angular/core": "<%= angularPeerDepVersion %>"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
|
||||
@ -7,8 +7,10 @@ import {
|
||||
offsetFromRoot,
|
||||
} from '@nx/devkit';
|
||||
import { getRootTsConfigFileName } from '@nx/js';
|
||||
import { parse } from 'semver';
|
||||
import { UnitTestRunner } from '../../../utils/test-runners';
|
||||
import type { AngularProjectConfiguration } from '../../../utils/types';
|
||||
import { getInstalledAngularVersion } from '../../utils/version-utils';
|
||||
import type { NormalizedSchema } from './normalized-schema';
|
||||
|
||||
export function createFiles(
|
||||
@ -26,6 +28,9 @@ export function createFiles(
|
||||
options.libraryOptions.fileName
|
||||
);
|
||||
|
||||
const version = getInstalledAngularVersion(tree);
|
||||
const { major, minor } = parse(version);
|
||||
|
||||
const substitutions = {
|
||||
libName: options.libraryOptions.name,
|
||||
libFileName: options.libraryOptions.fileName,
|
||||
@ -39,6 +44,7 @@ export function createFiles(
|
||||
pathToComponent,
|
||||
npmScope,
|
||||
rootOffset,
|
||||
angularPeerDepVersion: `^${major}.${minor}.0`,
|
||||
tpl: '',
|
||||
};
|
||||
|
||||
|
||||
@ -1654,6 +1654,31 @@ describe('lib', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should create a local package.json', async () => {
|
||||
// ACT
|
||||
await runLibraryGeneratorWithOpts({
|
||||
publishable: true,
|
||||
importPath: '@myorg/lib',
|
||||
});
|
||||
|
||||
// ASSERT
|
||||
const tsconfigJson = readJson(tree, 'libs/my-lib/package.json');
|
||||
expect(tsconfigJson).toMatchInlineSnapshot(`
|
||||
{
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0",
|
||||
},
|
||||
"name": "@myorg/lib",
|
||||
"peerDependencies": {
|
||||
"@angular/common": "^14.1.0",
|
||||
"@angular/core": "^14.1.0",
|
||||
},
|
||||
"sideEffects": false,
|
||||
"version": "0.0.1",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should generate a library with a standalone component as entry point with angular 14.1.0', async () => {
|
||||
await runLibraryGeneratorWithOpts({ standalone: true });
|
||||
|
||||
|
||||
@ -658,18 +658,28 @@ export const appRoutes: Routes = [
|
||||
|
||||
exports[`ngrx Standalone APIs should add a root module with feature module when minimal is set to false 1`] = `
|
||||
"import { bootstrapApplication } from '@angular/platform-browser';
|
||||
import { appConfig } from './app/app.config';
|
||||
import { AppComponent } from './app/app.component';
|
||||
|
||||
bootstrapApplication(AppComponent, appConfig).catch((err) =>
|
||||
console.error(err)
|
||||
);
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ngrx Standalone APIs should add a root module with feature module when minimal is set to false 2`] = `
|
||||
"import { ApplicationConfig } from '@angular/core';
|
||||
import {
|
||||
provideRouter,
|
||||
withEnabledBlockingInitialNavigation,
|
||||
} from '@angular/router';
|
||||
import { appRoutes } from './app/app.routes';
|
||||
import { AppComponent } from './app/app.component';
|
||||
import { appRoutes } from './app.routes';
|
||||
import { provideStore, provideState } from '@ngrx/store';
|
||||
import { provideEffects } from '@ngrx/effects';
|
||||
import * as fromUsers from './+state/users.reducer';
|
||||
import { UsersEffects } from './+state/users.effects';
|
||||
|
||||
bootstrapApplication(AppComponent, {
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [
|
||||
provideEffects(UsersEffects),
|
||||
provideState(fromUsers.USERS_FEATURE_KEY, fromUsers.usersReducer),
|
||||
@ -677,46 +687,66 @@ bootstrapApplication(AppComponent, {
|
||||
provideStore(),
|
||||
provideRouter(appRoutes, withEnabledBlockingInitialNavigation()),
|
||||
],
|
||||
}).catch((err) => console.error(err));
|
||||
};
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ngrx Standalone APIs should add an empty provideStore when minimal and root are set to true 1`] = `
|
||||
"import { bootstrapApplication } from '@angular/platform-browser';
|
||||
import { appConfig } from './app/app.config';
|
||||
import { AppComponent } from './app/app.component';
|
||||
|
||||
bootstrapApplication(AppComponent, appConfig).catch((err) =>
|
||||
console.error(err)
|
||||
);
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ngrx Standalone APIs should add an empty provideStore when minimal and root are set to true 2`] = `
|
||||
"import { ApplicationConfig } from '@angular/core';
|
||||
import {
|
||||
provideRouter,
|
||||
withEnabledBlockingInitialNavigation,
|
||||
} from '@angular/router';
|
||||
import { appRoutes } from './app/app.routes';
|
||||
import { AppComponent } from './app/app.component';
|
||||
import { appRoutes } from './app.routes';
|
||||
import { provideStore, provideState } from '@ngrx/store';
|
||||
import { provideEffects } from '@ngrx/effects';
|
||||
|
||||
bootstrapApplication(AppComponent, {
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [
|
||||
provideEffects(),
|
||||
provideStore(),
|
||||
provideRouter(appRoutes, withEnabledBlockingInitialNavigation()),
|
||||
],
|
||||
}).catch((err) => console.error(err));
|
||||
};
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ngrx Standalone APIs should add facade provider when facade is true 1`] = `
|
||||
"import { bootstrapApplication } from '@angular/platform-browser';
|
||||
import { appConfig } from './app/app.config';
|
||||
import { AppComponent } from './app/app.component';
|
||||
|
||||
bootstrapApplication(AppComponent, appConfig).catch((err) =>
|
||||
console.error(err)
|
||||
);
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ngrx Standalone APIs should add facade provider when facade is true 2`] = `
|
||||
"import { ApplicationConfig } from '@angular/core';
|
||||
import {
|
||||
provideRouter,
|
||||
withEnabledBlockingInitialNavigation,
|
||||
} from '@angular/router';
|
||||
import { appRoutes } from './app/app.routes';
|
||||
import { AppComponent } from './app/app.component';
|
||||
import { appRoutes } from './app.routes';
|
||||
import { provideStore, provideState } from '@ngrx/store';
|
||||
import { provideEffects } from '@ngrx/effects';
|
||||
import * as fromUsers from './+state/users.reducer';
|
||||
import { UsersEffects } from './+state/users.effects';
|
||||
import { UsersFacade } from './+state/users.facade';
|
||||
|
||||
bootstrapApplication(AppComponent, {
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [
|
||||
provideEffects(UsersEffects),
|
||||
provideState(fromUsers.USERS_FEATURE_KEY, fromUsers.usersReducer),
|
||||
@ -725,7 +755,7 @@ bootstrapApplication(AppComponent, {
|
||||
UsersFacade,
|
||||
provideRouter(appRoutes, withEnabledBlockingInitialNavigation()),
|
||||
],
|
||||
}).catch((err) => console.error(err));
|
||||
};
|
||||
"
|
||||
`;
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@ import { ensureTypescript } from '@nx/js/src/utils/typescript/ensure-typescript'
|
||||
import type { SourceFile } from 'typescript';
|
||||
import {
|
||||
addImportToModule,
|
||||
addProviderToBootstrapApplication,
|
||||
addProviderToAppConfig,
|
||||
addProviderToModule,
|
||||
} from '../../../utils/nx-devkit/ast-utils';
|
||||
import type { NormalizedNgRxGeneratorOptions } from './normalize-options';
|
||||
@ -23,8 +23,8 @@ function addRootStoreImport(
|
||||
storeForRoot: string
|
||||
) {
|
||||
if (isParentStandalone) {
|
||||
if (tree.read(parentPath, 'utf-8').includes('bootstrapApplication')) {
|
||||
addProviderToBootstrapApplication(tree, parentPath, provideRootStore);
|
||||
if (tree.read(parentPath, 'utf-8').includes('ApplicationConfig')) {
|
||||
addProviderToAppConfig(tree, parentPath, provideRootStore);
|
||||
} else {
|
||||
addProviderToRoute(tree, parentPath, route, provideRootStore);
|
||||
}
|
||||
@ -44,8 +44,8 @@ function addRootEffectsImport(
|
||||
effectsForEmptyRoot: string
|
||||
) {
|
||||
if (isParentStandalone) {
|
||||
if (tree.read(parentPath, 'utf-8').includes('bootstrapApplication')) {
|
||||
addProviderToBootstrapApplication(tree, parentPath, provideRootEffects);
|
||||
if (tree.read(parentPath, 'utf-8').includes('ApplicationConfig')) {
|
||||
addProviderToAppConfig(tree, parentPath, provideRootEffects);
|
||||
} else {
|
||||
addProviderToRoute(tree, parentPath, route, provideRootEffects);
|
||||
}
|
||||
@ -91,12 +91,8 @@ function addStoreForFeatureImport(
|
||||
storeForFeature: string
|
||||
) {
|
||||
if (isParentStandalone) {
|
||||
if (tree.read(parentPath, 'utf-8').includes('bootstrapApplication')) {
|
||||
addProviderToBootstrapApplication(
|
||||
tree,
|
||||
parentPath,
|
||||
provideStoreForFeature
|
||||
);
|
||||
if (tree.read(parentPath, 'utf-8').includes('ApplicationConfig')) {
|
||||
addProviderToAppConfig(tree, parentPath, provideStoreForFeature);
|
||||
} else {
|
||||
addProviderToRoute(tree, parentPath, route, provideStoreForFeature);
|
||||
}
|
||||
@ -121,12 +117,8 @@ function addEffectsForFeatureImport(
|
||||
effectsForFeature: string
|
||||
) {
|
||||
if (isParentStandalone) {
|
||||
if (tree.read(parentPath, 'utf-8').includes('bootstrapApplication')) {
|
||||
addProviderToBootstrapApplication(
|
||||
tree,
|
||||
parentPath,
|
||||
provideEffectsForFeature
|
||||
);
|
||||
if (tree.read(parentPath, 'utf-8').includes('ApplicationConfig')) {
|
||||
addProviderToAppConfig(tree, parentPath, provideEffectsForFeature);
|
||||
} else {
|
||||
addProviderToRoute(tree, parentPath, route, provideEffectsForFeature);
|
||||
}
|
||||
@ -257,8 +249,8 @@ export function addImportsToModule(
|
||||
if (options.facade) {
|
||||
sourceFile = addImport(sourceFile, facadeName, facadePath);
|
||||
if (isParentStandalone) {
|
||||
if (tree.read(parentPath, 'utf-8').includes('bootstrapApplication')) {
|
||||
addProviderToBootstrapApplication(tree, parentPath, facadeName);
|
||||
if (tree.read(parentPath, 'utf-8').includes('ApplicationConfig')) {
|
||||
addProviderToAppConfig(tree, parentPath, facadeName);
|
||||
} else {
|
||||
addProviderToRoute(tree, parentPath, options.route, facadeName);
|
||||
}
|
||||
|
||||
@ -13,6 +13,8 @@ export function addNgRxToPackageJson(
|
||||
: '~0.8.3';
|
||||
const ngrxVersion = versions(tree).ngrxVersion;
|
||||
|
||||
process.env.npm_config_legacy_peer_deps ??= 'true';
|
||||
|
||||
return addDependenciesToPackageJson(
|
||||
tree,
|
||||
{
|
||||
|
||||
@ -30,7 +30,7 @@ describe('ngrx', () => {
|
||||
const defaultStandaloneOptions: NgRxGeneratorOptions = {
|
||||
directory: '+state',
|
||||
minimal: true,
|
||||
parent: 'apps/my-app/src/main.ts',
|
||||
parent: 'apps/my-app/src/app/app.config.ts',
|
||||
name: 'users',
|
||||
};
|
||||
|
||||
@ -534,6 +534,9 @@ describe('ngrx', () => {
|
||||
});
|
||||
|
||||
expect(tree.read('/apps/my-app/src/main.ts', 'utf-8')).toMatchSnapshot();
|
||||
expect(
|
||||
tree.read('/apps/my-app/src/app/app.config.ts', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
expect(tree.exists('/apps/my-app/src/app/+state/users.actions.ts')).toBe(
|
||||
false
|
||||
);
|
||||
@ -562,6 +565,9 @@ describe('ngrx', () => {
|
||||
});
|
||||
|
||||
expect(tree.read('/apps/my-app/src/main.ts', 'utf-8')).toMatchSnapshot();
|
||||
expect(
|
||||
tree.read('/apps/my-app/src/app/app.config.ts', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should add a feature module when route is undefined', async () => {
|
||||
@ -619,6 +625,9 @@ describe('ngrx', () => {
|
||||
});
|
||||
|
||||
expect(tree.read('/apps/my-app/src/main.ts', 'utf-8')).toMatchSnapshot();
|
||||
expect(
|
||||
tree.read('/apps/my-app/src/app/app.config.ts', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should add facade provider when facade is true and --root is false', async () => {
|
||||
|
||||
@ -9,7 +9,7 @@ import { AppComponent } from './app.component';
|
||||
@NgModule({
|
||||
declarations: [AppComponent],
|
||||
imports: [
|
||||
BrowserModule.withServerTransition({ appId: 'serverApp' }),
|
||||
BrowserModule,
|
||||
RouterModule.forRoot(
|
||||
[
|
||||
{
|
||||
@ -181,9 +181,11 @@ exports[`MF Remote App Generator --ssr should generate the correct files 11`] =
|
||||
{
|
||||
"configurations": {
|
||||
"development": {
|
||||
"buildOptimizer": false,
|
||||
"extractLicenses": false,
|
||||
"optimization": false,
|
||||
"sourceMap": true,
|
||||
"vendorChunk": true,
|
||||
},
|
||||
"production": {
|
||||
"outputHashing": "media",
|
||||
|
||||
@ -7,7 +7,7 @@ import * as cors from 'cors';
|
||||
import { existsSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
|
||||
import { AppServerModule } from './bootstrap.server';
|
||||
import<% if(standalone) { %> bootstrap <% } else { %> { AppServerModule } <% } %>from './bootstrap.server';
|
||||
|
||||
// The Express app is exported so that it can be used by serverless Functions.
|
||||
export function app(): express.Express {
|
||||
@ -24,7 +24,7 @@ export function app(): express.Express {
|
||||
server.engine(
|
||||
'html',
|
||||
ngExpressEngine({
|
||||
bootstrap: AppServerModule,
|
||||
<% if(standalone) { %>bootstrap<% } else { %>bootstrap: AppServerModule,<% } %>
|
||||
})
|
||||
);
|
||||
|
||||
@ -71,4 +71,4 @@ function run(): void {
|
||||
|
||||
run();
|
||||
|
||||
export * from './bootstrap.server';
|
||||
<% if(standalone) { %>export default bootstrap;<% } else { %>export * from './bootstrap.server';<% } %>
|
||||
@ -0,0 +1,7 @@
|
||||
import {bootstrapApplication} from '@angular/platform-browser';
|
||||
import {RemoteEntryComponent} from './app/remote-entry/entry.component';
|
||||
import {config} from './app/app.config.server';
|
||||
|
||||
const bootstrap = () => bootstrapApplication(RemoteEntryComponent, config);
|
||||
|
||||
export default bootstrap;
|
||||
@ -18,27 +18,52 @@ import {
|
||||
|
||||
export async function addSsr(
|
||||
tree: Tree,
|
||||
{ appName, port }: { appName: string; port: number }
|
||||
{
|
||||
appName,
|
||||
port,
|
||||
standalone,
|
||||
}: { appName: string; port: number; standalone: boolean }
|
||||
) {
|
||||
let project = readProjectConfiguration(tree, appName);
|
||||
|
||||
await setupSsr(tree, {
|
||||
project: appName,
|
||||
standalone,
|
||||
});
|
||||
|
||||
tree.rename(
|
||||
joinPathFragments(project.sourceRoot, 'main.server.ts'),
|
||||
joinPathFragments(project.sourceRoot, 'bootstrap.server.ts')
|
||||
);
|
||||
|
||||
tree.write(
|
||||
joinPathFragments(project.root, 'server.ts'),
|
||||
"import('./src/main.server');"
|
||||
);
|
||||
|
||||
generateFiles(tree, joinPathFragments(__dirname, '../files'), project.root, {
|
||||
generateFiles(
|
||||
tree,
|
||||
joinPathFragments(__dirname, '../files/base'),
|
||||
project.root,
|
||||
{
|
||||
appName,
|
||||
standalone,
|
||||
tmpl: '',
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
if (standalone) {
|
||||
generateFiles(
|
||||
tree,
|
||||
joinPathFragments(__dirname, '../files/standalone'),
|
||||
project.root,
|
||||
{
|
||||
appName,
|
||||
standalone,
|
||||
tmpl: '',
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// update project.json
|
||||
project = readProjectConfiguration(tree, appName);
|
||||
|
||||
@ -61,7 +61,11 @@ export async function remote(tree: Tree, options: Schema) {
|
||||
|
||||
let installTasks = [appInstallTask];
|
||||
if (options.ssr) {
|
||||
let ssrInstallTask = await addSsr(tree, { appName, port });
|
||||
let ssrInstallTask = await addSsr(tree, {
|
||||
appName,
|
||||
port,
|
||||
standalone: options.standalone,
|
||||
});
|
||||
installTasks.push(ssrInstallTask);
|
||||
}
|
||||
|
||||
|
||||
@ -4,9 +4,11 @@ exports[`setupSSR should create the files correctly for ssr 1`] = `
|
||||
{
|
||||
"configurations": {
|
||||
"development": {
|
||||
"buildOptimizer": false,
|
||||
"extractLicenses": false,
|
||||
"optimization": false,
|
||||
"sourceMap": true,
|
||||
"vendorChunk": true,
|
||||
},
|
||||
"production": {
|
||||
"outputHashing": "media",
|
||||
@ -100,13 +102,117 @@ export * from './src/main.server';
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`setupSSR should create the files correctly for ssr when app is standalone 1`] = `
|
||||
{
|
||||
"configurations": {
|
||||
"development": {
|
||||
"buildOptimizer": false,
|
||||
"extractLicenses": false,
|
||||
"optimization": false,
|
||||
"sourceMap": true,
|
||||
"vendorChunk": true,
|
||||
},
|
||||
"production": {
|
||||
"outputHashing": "media",
|
||||
},
|
||||
},
|
||||
"defaultConfiguration": "production",
|
||||
"dependsOn": [
|
||||
"build",
|
||||
],
|
||||
"executor": "@angular-devkit/build-angular:server",
|
||||
"options": {
|
||||
"main": "apps/app1/server.ts",
|
||||
"outputPath": "dist/apps/app1/server",
|
||||
"tsConfig": "apps/app1/tsconfig.server.json",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`setupSSR should create the files correctly for ssr when app is standalone 2`] = `
|
||||
"import 'zone.js/dist/zone-node';
|
||||
|
||||
import { APP_BASE_HREF } from '@angular/common';
|
||||
import { ngExpressEngine } from '@nguniversal/express-engine';
|
||||
import * as express from 'express';
|
||||
import { existsSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
|
||||
import bootstrap from './src/main.server';
|
||||
|
||||
// The Express app is exported so that it can be used by serverless Functions.
|
||||
export function app(): express.Express {
|
||||
const server = express();
|
||||
const distFolder = join(process.cwd(), 'dist/apps/app1/browser');
|
||||
const indexHtml = existsSync(join(distFolder, 'index.original.html'))
|
||||
? 'index.original.html'
|
||||
: 'index';
|
||||
|
||||
// Our Universal express-engine (found @ https://github.com/angular/universal/tree/main/modules/express-engine)
|
||||
server.engine(
|
||||
'html',
|
||||
ngExpressEngine({
|
||||
bootstrap,
|
||||
})
|
||||
);
|
||||
|
||||
server.set('view engine', 'html');
|
||||
server.set('views', distFolder);
|
||||
|
||||
// Example Express Rest API endpoints
|
||||
// server.get('/api/**', (req, res) => { });
|
||||
// Serve static files from /browser
|
||||
server.get(
|
||||
'*.*',
|
||||
express.static(distFolder, {
|
||||
maxAge: '1y',
|
||||
})
|
||||
);
|
||||
|
||||
// All regular routes use the Universal engine
|
||||
server.get('*', (req, res) => {
|
||||
res.render(indexHtml, {
|
||||
req,
|
||||
providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }],
|
||||
});
|
||||
});
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
function run(): void {
|
||||
const port = process.env['PORT'] || 4000;
|
||||
|
||||
// Start up the Node server
|
||||
const server = app();
|
||||
server.listen(port, () => {
|
||||
console.log(\`Node Express server listening on http://localhost:\${port}\`);
|
||||
});
|
||||
}
|
||||
|
||||
// Webpack will replace 'require' with '__webpack_require__'
|
||||
// '__non_webpack_require__' is a proxy to Node 'require'
|
||||
// The below code is to ensure that the server is run only when not requiring the bundle.
|
||||
declare const __non_webpack_require__: NodeRequire;
|
||||
const mainModule = __non_webpack_require__.main;
|
||||
const moduleFilename = (mainModule && mainModule.filename) || '';
|
||||
if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
|
||||
run();
|
||||
}
|
||||
|
||||
export default bootstrap;
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`setupSSR should use fileReplacements if they already exist 1`] = `
|
||||
{
|
||||
"configurations": {
|
||||
"development": {
|
||||
"buildOptimizer": false,
|
||||
"extractLicenses": false,
|
||||
"optimization": false,
|
||||
"sourceMap": true,
|
||||
"vendorChunk": true,
|
||||
},
|
||||
"production": {
|
||||
"fileReplacements": [
|
||||
|
||||
@ -0,0 +1,60 @@
|
||||
import 'zone.js/dist/zone-node';
|
||||
|
||||
import {APP_BASE_HREF} from '@angular/common';
|
||||
import {ngExpressEngine} from '@nguniversal/express-engine';
|
||||
import * as express from 'express';
|
||||
import {existsSync} from 'fs';
|
||||
import {join} from 'path';
|
||||
|
||||
import bootstrap from './src/<%= main.slice(0, -3) %>';
|
||||
|
||||
// The Express app is exported so that it can be used by serverless Functions.
|
||||
export function app(): express.Express {
|
||||
const server = express();
|
||||
const distFolder = join(process.cwd(), 'dist/apps/<%= project %>/browser');
|
||||
const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index';
|
||||
|
||||
// Our Universal express-engine (found @ https://github.com/angular/universal/tree/main/modules/express-engine)
|
||||
server.engine('html', ngExpressEngine({
|
||||
bootstrap
|
||||
}));
|
||||
|
||||
server.set('view engine', 'html');
|
||||
server.set('views', distFolder);
|
||||
|
||||
// Example Express Rest API endpoints
|
||||
// server.get('/api/**', (req, res) => { });
|
||||
// Serve static files from /browser
|
||||
server.get('*.*', express.static(distFolder, {
|
||||
maxAge: '1y'
|
||||
}));
|
||||
|
||||
// All regular routes use the Universal engine
|
||||
server.get('*', (req, res) => {
|
||||
res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
|
||||
});
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
function run(): void {
|
||||
const port = process.env['PORT'] || <%= serverPort %>;
|
||||
|
||||
// Start up the Node server
|
||||
const server = app();
|
||||
server.listen(port, () => {
|
||||
console.log(`Node Express server listening on http://localhost:${port}`);
|
||||
});
|
||||
}
|
||||
|
||||
// Webpack will replace 'require' with '__webpack_require__'
|
||||
// '__non_webpack_require__' is a proxy to Node 'require'
|
||||
// The below code is to ensure that the server is run only when not requiring the bundle.
|
||||
declare const __non_webpack_require__: NodeRequire;
|
||||
const mainModule = __non_webpack_require__.main;
|
||||
const moduleFilename = mainModule && mainModule.filename || '';
|
||||
if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
|
||||
run();
|
||||
}
|
||||
|
||||
export default bootstrap;
|
||||
@ -0,0 +1,7 @@
|
||||
import { bootstrapApplication } from '@angular/platform-browser';
|
||||
import { AppComponent } from './app/app.component';
|
||||
import { config } from './app/app.config.server';
|
||||
|
||||
const bootstrap = () => bootstrapApplication(AppComponent, config);
|
||||
|
||||
export default bootstrap;
|
||||
@ -0,0 +1,11 @@
|
||||
import { mergeApplicationConfig, ApplicationConfig } from '@angular/core';
|
||||
import { provideServerRendering } from '@angular/platform-server';
|
||||
import { appConfig } from './app.config';
|
||||
|
||||
const serverConfig: ApplicationConfig = {
|
||||
providers: [
|
||||
provideServerRendering()
|
||||
]
|
||||
};
|
||||
|
||||
export const config = mergeApplicationConfig(appConfig, serverConfig);
|
||||
@ -4,40 +4,53 @@ import {
|
||||
joinPathFragments,
|
||||
readProjectConfiguration,
|
||||
} from '@nx/devkit';
|
||||
import {
|
||||
getInstalledAngularMajorVersion,
|
||||
getInstalledAngularVersionInfo,
|
||||
} from '../../utils/version-utils';
|
||||
import type { Schema } from '../schema';
|
||||
import { lt } from 'semver';
|
||||
import { getInstalledAngularVersionInfo } from '../../utils/version-utils';
|
||||
import type { Schema } from '../schema';
|
||||
|
||||
export function generateSSRFiles(tree: Tree, schema: Schema) {
|
||||
const projectRoot = readProjectConfiguration(tree, schema.project).root;
|
||||
|
||||
const pathToFiles = joinPathFragments(__dirname, '..', 'files');
|
||||
|
||||
generateFiles(tree, joinPathFragments(pathToFiles, 'base'), projectRoot, {
|
||||
...schema,
|
||||
tpl: '',
|
||||
});
|
||||
|
||||
if (schema.standalone) {
|
||||
generateFiles(
|
||||
tree,
|
||||
joinPathFragments(__dirname, '..', 'files', 'base'),
|
||||
joinPathFragments(pathToFiles, 'standalone'),
|
||||
projectRoot,
|
||||
{ ...schema, tpl: '' }
|
||||
);
|
||||
} else {
|
||||
generateFiles(
|
||||
tree,
|
||||
joinPathFragments(pathToFiles, 'ngmodule', 'base'),
|
||||
projectRoot,
|
||||
{ ...schema, tpl: '' }
|
||||
);
|
||||
|
||||
const { major: angularMajorVersion, version: angularVersion } =
|
||||
getInstalledAngularVersionInfo(tree);
|
||||
|
||||
if (angularMajorVersion < 15) {
|
||||
generateFiles(
|
||||
tree,
|
||||
joinPathFragments(__dirname, '..', 'files', 'v14'),
|
||||
joinPathFragments(pathToFiles, 'ngmodule', 'v14'),
|
||||
projectRoot,
|
||||
{ ...schema, tpl: '' }
|
||||
);
|
||||
}
|
||||
|
||||
if (lt(angularVersion, '15.2.0')) {
|
||||
generateFiles(
|
||||
tree,
|
||||
joinPathFragments(__dirname, '..', 'files', 'pre-v15-2'),
|
||||
joinPathFragments(pathToFiles, 'ngmodule', 'pre-v15-2'),
|
||||
projectRoot,
|
||||
{ ...schema, tpl: '' }
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,3 +2,4 @@ export * from './generate-files';
|
||||
export * from './normalize-options';
|
||||
export * from './update-app-module';
|
||||
export * from './update-project-config';
|
||||
export * from './validate-options';
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
import type { Tree } from '@nx/devkit';
|
||||
import { readNxJson } from '@nx/devkit';
|
||||
import { isNgStandaloneApp } from '../../../utils/nx-devkit/ast-utils';
|
||||
import type { Schema } from '../schema';
|
||||
|
||||
export function normalizeOptions(tree: Tree, options: Schema) {
|
||||
const isStandaloneApp = isNgStandaloneApp(tree, options.project);
|
||||
|
||||
return {
|
||||
project: options.project ?? readNxJson(tree).defaultProject,
|
||||
project: options.project,
|
||||
appId: options.appId ?? 'serverApp',
|
||||
main: options.main ?? 'main.server.ts',
|
||||
serverFileName: options.serverFileName ?? 'server.ts',
|
||||
@ -12,5 +14,6 @@ export function normalizeOptions(tree: Tree, options: Schema) {
|
||||
rootModuleFileName: options.rootModuleFileName ?? 'app.server.module.ts',
|
||||
rootModuleClassName: options.rootModuleClassName ?? 'AppServerModule',
|
||||
skipFormat: options.skipFormat ?? false,
|
||||
standalone: options.standalone ?? isStandaloneApp,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,9 +1,15 @@
|
||||
import type { Tree } from '@nx/devkit';
|
||||
import { joinPathFragments, readProjectConfiguration } from '@nx/devkit';
|
||||
import type { Schema } from '../schema';
|
||||
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
|
||||
|
||||
@ -1,3 +1,7 @@
|
||||
import type {
|
||||
BrowserBuilderOptions,
|
||||
ServerBuilderOptions,
|
||||
} from '@angular-devkit/build-angular';
|
||||
import type { Tree } from '@nx/devkit';
|
||||
import {
|
||||
joinPathFragments,
|
||||
@ -10,11 +14,17 @@ import type { Schema } from '../schema';
|
||||
|
||||
export function updateProjectConfig(tree: Tree, schema: Schema) {
|
||||
let projectConfig = readProjectConfiguration(tree, schema.project);
|
||||
const buildTarget = projectConfig.targets.build;
|
||||
|
||||
projectConfig.targets.build.options.outputPath = `dist/apps/${schema.project}/browser`;
|
||||
buildTarget.options.outputPath = `dist/apps/${schema.project}/browser`;
|
||||
|
||||
const buildTargetFileReplacements =
|
||||
projectConfig.targets.build.configurations?.production?.fileReplacements;
|
||||
const buildConfigurations = projectConfig.targets.build.configurations;
|
||||
const configurations: Record<string, {}> = {};
|
||||
if (buildConfigurations) {
|
||||
for (const [key, options] of Object.entries(buildConfigurations)) {
|
||||
configurations[key] = getServerOptions(options);
|
||||
}
|
||||
}
|
||||
|
||||
projectConfig.targets.server = {
|
||||
dependsOn: ['build'],
|
||||
@ -23,20 +33,9 @@ export function updateProjectConfig(tree: Tree, schema: Schema) {
|
||||
outputPath: `dist/${projectConfig.root}/server`,
|
||||
main: joinPathFragments(projectConfig.root, schema.serverFileName),
|
||||
tsConfig: joinPathFragments(projectConfig.root, 'tsconfig.server.json'),
|
||||
...(buildTarget.options ? getServerOptions(buildTarget.options) : {}),
|
||||
},
|
||||
configurations: {
|
||||
production: {
|
||||
outputHashing: 'media',
|
||||
...(buildTargetFileReplacements
|
||||
? { fileReplacements: buildTargetFileReplacements }
|
||||
: {}),
|
||||
},
|
||||
development: {
|
||||
optimization: false,
|
||||
sourceMap: true,
|
||||
extractLicenses: false,
|
||||
},
|
||||
},
|
||||
configurations,
|
||||
defaultConfiguration: 'production',
|
||||
};
|
||||
|
||||
@ -89,3 +88,27 @@ export function updateProjectConfig(tree: Tree, schema: Schema) {
|
||||
updateNxJson(tree, nxJson);
|
||||
}
|
||||
}
|
||||
|
||||
function getServerOptions(
|
||||
options: Partial<BrowserBuilderOptions> = {}
|
||||
): Partial<ServerBuilderOptions> {
|
||||
return {
|
||||
buildOptimizer: options?.buildOptimizer,
|
||||
outputHashing:
|
||||
options?.outputHashing === 'all'
|
||||
? ('media' as any)
|
||||
: options?.outputHashing,
|
||||
fileReplacements: options?.fileReplacements,
|
||||
optimization:
|
||||
options?.optimization === undefined ? undefined : !!options?.optimization,
|
||||
sourceMap: options?.sourceMap,
|
||||
stylePreprocessorOptions: options?.stylePreprocessorOptions,
|
||||
resourcesOutputPath: options?.resourcesOutputPath,
|
||||
deployUrl: options?.deployUrl,
|
||||
i18nMissingTranslation: options?.i18nMissingTranslation,
|
||||
preserveSymlinks: options?.preserveSymlinks,
|
||||
extractLicenses: options?.extractLicenses,
|
||||
inlineStyleLanguage: options?.inlineStyleLanguage,
|
||||
vendorChunk: options?.vendorChunk,
|
||||
};
|
||||
}
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
import type { Tree } from '@nx/devkit';
|
||||
import {
|
||||
validateProject,
|
||||
validateStandaloneOption,
|
||||
} from '../../utils/validations';
|
||||
import type { Schema } from '../schema';
|
||||
|
||||
export function validateOptions(tree: Tree, options: Schema): void {
|
||||
validateProject(tree, options.project);
|
||||
validateStandaloneOption(tree, options.standalone);
|
||||
}
|
||||
@ -6,5 +6,6 @@ export interface Schema {
|
||||
serverPort?: number;
|
||||
rootModuleFileName?: string;
|
||||
rootModuleClassName?: string;
|
||||
standalone?: boolean;
|
||||
skipFormat?: boolean;
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
"appId": {
|
||||
"type": "string",
|
||||
"format": "html-selector",
|
||||
"description": "The `appId` to use with `withServerTransition`.",
|
||||
"description": "The `appId` to use with `withServerTransition`. _Note: This is only used in Angular versions <16.0.0. It's deprecated since Angular 16._",
|
||||
"default": "serverApp"
|
||||
},
|
||||
"main": {
|
||||
@ -49,6 +49,10 @@
|
||||
"description": "The name of the root module class.",
|
||||
"default": "AppServerModule"
|
||||
},
|
||||
"standalone": {
|
||||
"type": "boolean",
|
||||
"description": "Use Standalone Components to bootstrap SSR. _Note: This is only supported in Angular versions >= 14.1.0_."
|
||||
},
|
||||
"skipFormat": {
|
||||
"type": "boolean",
|
||||
"description": "Skip formatting the workspace after the generator completes.",
|
||||
|
||||
@ -80,7 +80,7 @@ describe('setupSSR', () => {
|
||||
|
||||
@NgModule({
|
||||
declarations: [AppComponent, NxWelcomeComponent],
|
||||
imports: [BrowserModule.withServerTransition({ appId: 'serverApp' })],
|
||||
imports: [BrowserModule],
|
||||
providers: [],
|
||||
bootstrap: [AppComponent],
|
||||
})
|
||||
@ -148,6 +148,95 @@ describe('setupSSR', () => {
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should create the files correctly for ssr when app is standalone', async () => {
|
||||
// ARRANGE
|
||||
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
|
||||
await generateTestApplication(tree, {
|
||||
name: 'app1',
|
||||
standalone: true,
|
||||
});
|
||||
|
||||
// ACT
|
||||
await setupSsr(tree, { project: 'app1' });
|
||||
|
||||
// ASSERT
|
||||
expect(
|
||||
readProjectConfiguration(tree, 'app1').targets.server
|
||||
).toMatchSnapshot();
|
||||
expect(tree.read('apps/app1/server.ts', 'utf-8')).toMatchSnapshot();
|
||||
expect(tree.read('apps/app1/src/main.server.ts', 'utf-8'))
|
||||
.toMatchInlineSnapshot(`
|
||||
"import { bootstrapApplication } from '@angular/platform-browser';
|
||||
import { AppComponent } from './app/app.component';
|
||||
import { config } from './app/app.config.server';
|
||||
|
||||
const bootstrap = () => bootstrapApplication(AppComponent, config);
|
||||
|
||||
export default bootstrap;
|
||||
"
|
||||
`);
|
||||
expect(tree.read('apps/app1/tsconfig.server.json', 'utf-8'))
|
||||
.toMatchInlineSnapshot(`
|
||||
"/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "./tsconfig.app.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../out-tsc/server",
|
||||
"target": "es2019",
|
||||
"types": ["node"]
|
||||
},
|
||||
"files": ["src/main.server.ts", "server.ts"]
|
||||
}
|
||||
"
|
||||
`);
|
||||
expect(tree.read('apps/app1/src/app/app.config.server.ts', 'utf-8'))
|
||||
.toMatchInlineSnapshot(`
|
||||
"import { mergeApplicationConfig, ApplicationConfig } from '@angular/core';
|
||||
import { provideServerRendering } from '@angular/platform-server';
|
||||
import { appConfig } from './app.config';
|
||||
|
||||
const serverConfig: ApplicationConfig = {
|
||||
providers: [provideServerRendering()],
|
||||
};
|
||||
|
||||
export const config = mergeApplicationConfig(appConfig, serverConfig);
|
||||
"
|
||||
`);
|
||||
|
||||
const packageJson = readJson<PackageJson>(tree, 'package.json');
|
||||
const dependencies = {
|
||||
'@nguniversal/express-engine': ngUniversalVersion,
|
||||
'@angular/platform-server': angularVersion,
|
||||
};
|
||||
for (const [dep, version] of Object.entries(dependencies)) {
|
||||
expect(packageJson.dependencies[dep]).toEqual(version);
|
||||
}
|
||||
const devDeps = {
|
||||
'@nguniversal/builders': ngUniversalVersion,
|
||||
};
|
||||
for (const [dep, version] of Object.entries(devDeps)) {
|
||||
expect(packageJson.devDependencies[dep]).toEqual(version);
|
||||
}
|
||||
const nxJson = readJson<NxJsonConfiguration>(tree, 'nx.json');
|
||||
expect(nxJson.tasksRunnerOptions).toMatchInlineSnapshot(`
|
||||
{
|
||||
"default": {
|
||||
"options": {
|
||||
"cacheableOperations": [
|
||||
"build",
|
||||
"lint",
|
||||
"test",
|
||||
"e2e",
|
||||
"server",
|
||||
],
|
||||
},
|
||||
"runner": "nx/tasks-runners/default",
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
describe('compat', () => {
|
||||
it('should install the correct versions when using older versions of Angular', async () => {
|
||||
// ARRANGE
|
||||
@ -212,6 +301,40 @@ describe('setupSSR', () => {
|
||||
`);
|
||||
});
|
||||
|
||||
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',
|
||||
});
|
||||
|
||||
// ACT
|
||||
await setupSsr(tree, { project: 'app1' });
|
||||
|
||||
// ASSERT
|
||||
expect(tree.read('apps/app1/src/app/app.module.ts', 'utf-8'))
|
||||
.toMatchInlineSnapshot(`
|
||||
"import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { AppComponent } from './app.component';
|
||||
import { NxWelcomeComponent } from './nx-welcome.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [AppComponent, NxWelcomeComponent],
|
||||
imports: [BrowserModule.withServerTransition({ appId: 'serverApp' })],
|
||||
providers: [],
|
||||
bootstrap: [AppComponent],
|
||||
})
|
||||
export class AppModule {}
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should wrap bootstrap call for Angular versions lower than 15.2', async () => {
|
||||
// ARRANGE
|
||||
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
|
||||
@ -10,14 +10,20 @@ import {
|
||||
normalizeOptions,
|
||||
updateAppModule,
|
||||
updateProjectConfig,
|
||||
validateOptions,
|
||||
} from './lib';
|
||||
import type { Schema } from './schema';
|
||||
|
||||
export async function setupSsr(tree: Tree, schema: Schema) {
|
||||
validateOptions(tree, schema);
|
||||
const options = normalizeOptions(tree, schema);
|
||||
|
||||
generateSSRFiles(tree, options);
|
||||
|
||||
if (!options.standalone) {
|
||||
updateAppModule(tree, options);
|
||||
}
|
||||
|
||||
updateProjectConfig(tree, options);
|
||||
|
||||
const pkgVersions = versions(tree);
|
||||
|
||||
@ -3,7 +3,6 @@ import type {
|
||||
__String,
|
||||
CallExpression,
|
||||
ClassDeclaration,
|
||||
Decorator,
|
||||
ImportDeclaration,
|
||||
ObjectLiteralExpression,
|
||||
PropertyAssignment,
|
||||
@ -48,38 +47,15 @@ export function insertNgModuleProperty(
|
||||
|
||||
const ngModuleName = ngModuleNamedImport.name.escapedText;
|
||||
|
||||
/**
|
||||
* Ensure backwards compatibility with TS < 4.8 due to the API change in TS4.8.
|
||||
* The getDecorators util is only in TS 4.8, so we need the previous logic to handle TS < 4.8.
|
||||
*
|
||||
* TODO: clean this up using another util or when we don't need to support TS < 4.8 anymore.
|
||||
*/
|
||||
let ngModuleClassDeclaration: ClassDeclaration;
|
||||
let ngModuleDecorator: Decorator;
|
||||
try {
|
||||
ngModuleClassDeclaration = findDecoratedClass(sourceFile, ngModuleName);
|
||||
ngModuleDecorator = tsModule
|
||||
.getDecorators(ngModuleClassDeclaration)
|
||||
.find(
|
||||
const ngModuleClassDeclaration = findDecoratedClass(sourceFile, ngModuleName);
|
||||
|
||||
const { getDecorators } = getTsEsLintTypeUtils();
|
||||
const ngModuleDecorator = getDecorators(ngModuleClassDeclaration).find(
|
||||
(decorator) =>
|
||||
tsModule.isCallExpression(decorator.expression) &&
|
||||
tsModule.isIdentifier(decorator.expression.expression) &&
|
||||
decorator.expression.expression.escapedText === ngModuleName
|
||||
);
|
||||
} catch {
|
||||
// Support for TS < 4.8
|
||||
ngModuleClassDeclaration = findDecoratedClassLegacy(
|
||||
sourceFile,
|
||||
ngModuleName
|
||||
);
|
||||
// @ts-ignore
|
||||
ngModuleDecorator = ngModuleClassDeclaration.decorators.find(
|
||||
(decorator) =>
|
||||
tsModule.isCallExpression(decorator.expression) &&
|
||||
tsModule.isIdentifier(decorator.expression.expression) &&
|
||||
decorator.expression.expression.escapedText === ngModuleName
|
||||
);
|
||||
}
|
||||
|
||||
const ngModuleCall = ngModuleDecorator.expression as CallExpression;
|
||||
|
||||
@ -203,8 +179,10 @@ function findDecoratedClass(
|
||||
const classDeclarations = sourceFile.statements.filter(
|
||||
tsModule.isClassDeclaration
|
||||
);
|
||||
const { getDecorators } = getTsEsLintTypeUtils();
|
||||
|
||||
return classDeclarations.find((declaration) => {
|
||||
const decorators = tsModule.getDecorators(declaration);
|
||||
const decorators = getDecorators(declaration);
|
||||
if (decorators) {
|
||||
return decorators.some(
|
||||
(decorator) =>
|
||||
@ -217,29 +195,6 @@ function findDecoratedClass(
|
||||
});
|
||||
}
|
||||
|
||||
function findDecoratedClassLegacy(
|
||||
sourceFile: SourceFile,
|
||||
ngModuleName: __String
|
||||
) {
|
||||
if (!tsModule) {
|
||||
tsModule = ensureTypescript();
|
||||
}
|
||||
|
||||
const classDeclarations = sourceFile.statements.filter(
|
||||
tsModule.isClassDeclaration
|
||||
);
|
||||
return classDeclarations.find(
|
||||
(declaration) =>
|
||||
declaration.decorators &&
|
||||
(declaration.decorators as any[]).some(
|
||||
(decorator) =>
|
||||
tsModule.isCallExpression(decorator.expression) &&
|
||||
tsModule.isIdentifier(decorator.expression.expression) &&
|
||||
decorator.expression.expression.escapedText === ngModuleName
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function findPropertyAssignment(
|
||||
ngModuleOptions: ObjectLiteralExpression,
|
||||
propertyName: ngModuleDecoratorProperty
|
||||
@ -255,3 +210,8 @@ function findPropertyAssignment(
|
||||
property.name.escapedText === propertyName
|
||||
) as PropertyAssignment;
|
||||
}
|
||||
|
||||
let tsUtils: typeof import('@typescript-eslint/type-utils');
|
||||
function getTsEsLintTypeUtils(): typeof import('@typescript-eslint/type-utils') {
|
||||
return tsUtils ?? require('@typescript-eslint/type-utils');
|
||||
}
|
||||
|
||||
@ -1,33 +1,11 @@
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import { updateJson } from '@nx/devkit';
|
||||
import {
|
||||
getGeneratorDirectoryForInstalledAngularVersion,
|
||||
getInstalledAngularMajorVersion,
|
||||
getInstalledAngularVersion,
|
||||
} from './version-utils';
|
||||
|
||||
describe('angularVersionUtils', () => {
|
||||
test.each(['14.0.0', '~14.1.0', '^14.2.0', '~14.3.0-beta.0'])(
|
||||
'should return correct directory name for v14',
|
||||
(ngVersion) => {
|
||||
// ARRANGE
|
||||
const tree = createTreeWithEmptyWorkspace();
|
||||
updateJson(tree, 'package.json', (json) => ({
|
||||
...json,
|
||||
dependencies: {
|
||||
'@angular/core': ngVersion,
|
||||
},
|
||||
}));
|
||||
|
||||
// ACT
|
||||
const directoryName =
|
||||
getGeneratorDirectoryForInstalledAngularVersion(tree);
|
||||
|
||||
// ASSERT
|
||||
expect(directoryName).toEqual('angular-v14');
|
||||
}
|
||||
);
|
||||
|
||||
test.each(['14.0.0', '~14.1.0', '^14.2.0', '~14.3.0-beta.0'])(
|
||||
'should return correct major version',
|
||||
(ngVersion) => {
|
||||
@ -69,28 +47,4 @@ describe('angularVersionUtils', () => {
|
||||
// ASSERT
|
||||
expect(angularVersion).toEqual(expectedVersion);
|
||||
});
|
||||
|
||||
test.each([
|
||||
'15.0.0',
|
||||
'~15.1.0',
|
||||
'^13.2.0',
|
||||
'~15.3.0-beta.0',
|
||||
'latest',
|
||||
'next',
|
||||
])('should return null for anything other than v14', (ngVersion) => {
|
||||
// ARRANGE
|
||||
const tree = createTreeWithEmptyWorkspace();
|
||||
updateJson(tree, 'package.json', (json) => ({
|
||||
...json,
|
||||
dependencies: {
|
||||
'@angular/core': ngVersion,
|
||||
},
|
||||
}));
|
||||
|
||||
// ACT
|
||||
const directoryName = getGeneratorDirectoryForInstalledAngularVersion(tree);
|
||||
|
||||
// ASSERT
|
||||
expect(directoryName).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
@ -5,18 +5,6 @@ import * as latestVersions from '../../utils/versions';
|
||||
import { angularVersion } from '../../utils/versions';
|
||||
import { backwardCompatibleVersions } from '../../utils/backward-compatible-versions';
|
||||
|
||||
export function getGeneratorDirectoryForInstalledAngularVersion(
|
||||
tree: Tree
|
||||
): string | null {
|
||||
const majorAngularVersion = getInstalledAngularMajorVersion(tree);
|
||||
|
||||
const directoryDictionary = {
|
||||
14: 'angular-v14',
|
||||
};
|
||||
|
||||
return directoryDictionary[majorAngularVersion] ?? null;
|
||||
}
|
||||
|
||||
export function getInstalledAngularVersion(tree: Tree): string {
|
||||
const pkgJson = readJson(tree, 'package.json');
|
||||
const installedAngularVersion =
|
||||
@ -99,6 +87,8 @@ export function versions(tree: Tree) {
|
||||
switch (majorAngularVersion) {
|
||||
case 14:
|
||||
return backwardCompatibleVersions.angularV14;
|
||||
case 15:
|
||||
return backwardCompatibleVersions.angularV15;
|
||||
default:
|
||||
return latestVersions;
|
||||
}
|
||||
|
||||
@ -0,0 +1,123 @@
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import extractStandaloneConfig from './extract-standalone-config-from-bootstrap';
|
||||
import { addProjectConfiguration } from '@nx/devkit';
|
||||
|
||||
const TEST_MAIN_FILE = `import { bootstrapApplication } from '@angular/platform-browser';
|
||||
import {
|
||||
provideRouter,
|
||||
withEnabledBlockingInitialNavigation,
|
||||
} from '@angular/router';
|
||||
import { appRoutes } from './app/app.routes';
|
||||
import { AppComponent } from './app/app.component';
|
||||
|
||||
bootstrapApplication(AppComponent, {
|
||||
providers: [provideRouter(appRoutes, withEnabledBlockingInitialNavigation())],
|
||||
}).catch((err) => console.error(err));`;
|
||||
|
||||
describe('extractStandaloneConfigFromBootstrap', () => {
|
||||
it('should extract the config correctly from a standard main.ts file', async () => {
|
||||
// ARRANGE
|
||||
const tree = createTreeWithEmptyWorkspace();
|
||||
addProjectConfiguration(tree, 'app1', {
|
||||
name: 'app1',
|
||||
root: 'apps/app1',
|
||||
sourceRoot: 'apps/app1/src',
|
||||
projectType: 'application',
|
||||
targets: {
|
||||
build: {
|
||||
options: {
|
||||
main: 'apps/app1/src/main.ts',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
tree.write('apps/app1/src/main.ts', TEST_MAIN_FILE);
|
||||
|
||||
// ACT
|
||||
await extractStandaloneConfig(tree);
|
||||
|
||||
// ASSERT
|
||||
expect(tree.read('apps/app1/src/main.ts', 'utf-8')).toMatchInlineSnapshot(`
|
||||
"import { appConfig } from './app/app.config';
|
||||
import { bootstrapApplication } from '@angular/platform-browser';
|
||||
|
||||
import { AppComponent } from './app/app.component';
|
||||
|
||||
bootstrapApplication(AppComponent, appConfig).catch((err) =>
|
||||
console.error(err)
|
||||
);
|
||||
"
|
||||
`);
|
||||
expect(tree.exists('apps/app1/src/app/app.config.ts')).toBeTruthy();
|
||||
expect(tree.read('apps/app1/src/app/app.config.ts', 'utf-8'))
|
||||
.toMatchInlineSnapshot(`
|
||||
"import { ApplicationConfig } from '@angular/core';
|
||||
import {
|
||||
provideRouter,
|
||||
withEnabledBlockingInitialNavigation,
|
||||
} from '@angular/router';
|
||||
import { appRoutes } from './app/app.routes';
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [provideRouter(appRoutes, withEnabledBlockingInitialNavigation())],
|
||||
};
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should extract the config correctly when the main.ts imports bootstrap from bootstrap.ts file', async () => {
|
||||
// ARRANGE
|
||||
const tree = createTreeWithEmptyWorkspace();
|
||||
addProjectConfiguration(tree, 'app1', {
|
||||
name: 'app1',
|
||||
root: 'apps/app1',
|
||||
sourceRoot: 'apps/app1/src',
|
||||
projectType: 'application',
|
||||
targets: {
|
||||
build: {
|
||||
options: {
|
||||
main: 'apps/app1/src/main.ts',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
tree.write('apps/app1/src/main.ts', `import('./bootstrap');`);
|
||||
tree.write('apps/app1/src/bootstrap.ts', TEST_MAIN_FILE);
|
||||
|
||||
// ACT
|
||||
await extractStandaloneConfig(tree);
|
||||
|
||||
// ASSERT
|
||||
expect(tree.read('apps/app1/src/main.ts', 'utf-8')).toMatchInlineSnapshot(`
|
||||
"import('./bootstrap');
|
||||
"
|
||||
`);
|
||||
expect(tree.read('apps/app1/src/bootstrap.ts', 'utf-8'))
|
||||
.toMatchInlineSnapshot(`
|
||||
"import { appConfig } from './app/app.config';
|
||||
import { bootstrapApplication } from '@angular/platform-browser';
|
||||
|
||||
import { AppComponent } from './app/app.component';
|
||||
|
||||
bootstrapApplication(AppComponent, appConfig).catch((err) =>
|
||||
console.error(err)
|
||||
);
|
||||
"
|
||||
`);
|
||||
expect(tree.exists('apps/app1/src/app/app.config.ts')).toBeTruthy();
|
||||
expect(tree.read('apps/app1/src/app/app.config.ts', 'utf-8'))
|
||||
.toMatchInlineSnapshot(`
|
||||
"import { ApplicationConfig } from '@angular/core';
|
||||
import {
|
||||
provideRouter,
|
||||
withEnabledBlockingInitialNavigation,
|
||||
} from '@angular/router';
|
||||
import { appRoutes } from './app/app.routes';
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [provideRouter(appRoutes, withEnabledBlockingInitialNavigation())],
|
||||
};
|
||||
"
|
||||
`);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,191 @@
|
||||
import type { ProjectConfiguration, Tree } from '@nx/devkit';
|
||||
import { formatFiles, getProjects, joinPathFragments } from '@nx/devkit';
|
||||
import type { Node, SourceFile } from 'typescript';
|
||||
import { ensureTypescript } from '@nx/js/src/utils/typescript/ensure-typescript';
|
||||
|
||||
let tsModule: typeof import('typescript');
|
||||
let tsquery: typeof import('@phenomnomnominal/tsquery').tsquery;
|
||||
|
||||
function getBootstrapCallFileInfo<T>(
|
||||
project: ProjectConfiguration,
|
||||
tree: Tree
|
||||
) {
|
||||
const IMPORT_BOOTSTRAP_FILE =
|
||||
'CallExpression:has(ImportKeyword) > StringLiteral';
|
||||
|
||||
let bootstrapCallFilePath = project.targets?.build?.options?.main;
|
||||
let bootstrapCallFileContents = tree.read(bootstrapCallFilePath, 'utf-8');
|
||||
|
||||
const ast = tsquery.ast(bootstrapCallFileContents);
|
||||
const importBootstrapNodes = tsquery(ast, IMPORT_BOOTSTRAP_FILE, {
|
||||
visitAllChildren: true,
|
||||
});
|
||||
|
||||
if (
|
||||
importBootstrapNodes.length > 0 &&
|
||||
importBootstrapNodes[0].getText().includes('./bootstrap')
|
||||
) {
|
||||
bootstrapCallFilePath = joinPathFragments(
|
||||
project.sourceRoot,
|
||||
'bootstrap.ts'
|
||||
);
|
||||
bootstrapCallFileContents = tree.read(bootstrapCallFilePath, 'utf-8');
|
||||
}
|
||||
return { bootstrapCallFilePath, bootstrapCallFileContents };
|
||||
}
|
||||
|
||||
function getImportTokenMap(bootstrapCallFileContentsAst: SourceFile) {
|
||||
const importTokenMap = new Map<string, string>();
|
||||
const importedTokensNodes = tsquery(
|
||||
bootstrapCallFileContentsAst,
|
||||
'ImportDeclaration > ImportClause',
|
||||
{ visitAllChildren: true }
|
||||
);
|
||||
|
||||
for (const node of importedTokensNodes) {
|
||||
importTokenMap.set(node.getText(), node.parent.getText());
|
||||
}
|
||||
return importTokenMap;
|
||||
}
|
||||
|
||||
function getImportsRequiredForAppConfig(
|
||||
importTokenMap: Map<string, string>,
|
||||
appConfigNode: Node
|
||||
) {
|
||||
const importsRequiredForAppConfig = new Set<string>();
|
||||
const checkImportsForTokens = (nodeText: string) => {
|
||||
const keys = importTokenMap.keys();
|
||||
for (const key of keys) {
|
||||
if (key.includes(nodeText))
|
||||
importsRequiredForAppConfig.add(importTokenMap.get(key));
|
||||
}
|
||||
};
|
||||
const visitEachChild = (node: Node) => {
|
||||
node.forEachChild((node) => {
|
||||
const nodeText = node.getText();
|
||||
checkImportsForTokens(nodeText);
|
||||
visitEachChild(node);
|
||||
});
|
||||
};
|
||||
visitEachChild(appConfigNode);
|
||||
return importsRequiredForAppConfig;
|
||||
}
|
||||
|
||||
function getAppConfigFileContents(
|
||||
importsRequiredForAppConfig: Set<string>,
|
||||
appConfigText: string
|
||||
) {
|
||||
const buildAppConfigFileContents = (
|
||||
importStatements: string[],
|
||||
appConfig: string
|
||||
) => `import { ApplicationConfig } from '@angular/core';${importStatements.join(
|
||||
'\n'
|
||||
)}
|
||||
export const appConfig: ApplicationConfig = ${appConfig}`;
|
||||
|
||||
const appConfigFileContents = buildAppConfigFileContents(
|
||||
Array.from(importsRequiredForAppConfig),
|
||||
appConfigText
|
||||
);
|
||||
return appConfigFileContents;
|
||||
}
|
||||
|
||||
function getBootstrapCallFileContents(
|
||||
bootstrapCallFileContents: string,
|
||||
appConfigNode: Node,
|
||||
importsRequiredForAppConfig: Set<string>
|
||||
) {
|
||||
let newBootstrapCallFileContents = `import { appConfig } from './app/app.config';
|
||||
${bootstrapCallFileContents.slice(
|
||||
0,
|
||||
appConfigNode.getStart()
|
||||
)}appConfig${bootstrapCallFileContents.slice(appConfigNode.getEnd())}`;
|
||||
|
||||
for (const importStatement of importsRequiredForAppConfig) {
|
||||
newBootstrapCallFileContents = newBootstrapCallFileContents.replace(
|
||||
importStatement,
|
||||
''
|
||||
);
|
||||
}
|
||||
return newBootstrapCallFileContents;
|
||||
}
|
||||
|
||||
export default async function extractStandaloneConfig(tree: Tree) {
|
||||
if (!tsModule) {
|
||||
tsModule = ensureTypescript();
|
||||
}
|
||||
if (!tsquery) {
|
||||
tsquery = require('@phenomnomnominal/tsquery').tsquery;
|
||||
}
|
||||
|
||||
const projects = getProjects(tree);
|
||||
|
||||
const BOOTSTRAP_APPLICATION_CALL_SELECTOR =
|
||||
'CallExpression:has(Identifier[name=bootstrapApplication])';
|
||||
const BOOTSTRAP_APPLICATION_CALL_CONFIG_SELECTOR =
|
||||
'CallExpression:has(Identifier[name=bootstrapApplication]) > ObjectLiteralExpression';
|
||||
|
||||
for (const [projectName, project] of projects.entries()) {
|
||||
if (project.projectType !== 'application') {
|
||||
continue;
|
||||
}
|
||||
if (project.targets?.build?.options?.main === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const { bootstrapCallFilePath, bootstrapCallFileContents } =
|
||||
getBootstrapCallFileInfo(project, tree);
|
||||
|
||||
const bootstrapCallFileContentsAst = tsquery.ast(bootstrapCallFileContents);
|
||||
const nodes: Node[] = tsquery(
|
||||
bootstrapCallFileContentsAst,
|
||||
BOOTSTRAP_APPLICATION_CALL_SELECTOR,
|
||||
{ visitAllChildren: true }
|
||||
);
|
||||
if (nodes.length === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const importTokenMap = getImportTokenMap(bootstrapCallFileContentsAst);
|
||||
|
||||
const bootstrapCallNode = nodes[0];
|
||||
const appConfigNodes = tsquery(
|
||||
bootstrapCallNode,
|
||||
BOOTSTRAP_APPLICATION_CALL_CONFIG_SELECTOR,
|
||||
{ visitAllChildren: true }
|
||||
);
|
||||
if (appConfigNodes.length === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const appConfigNode = appConfigNodes[0];
|
||||
const appConfigText = appConfigNode.getText();
|
||||
|
||||
const importsRequiredForAppConfig = getImportsRequiredForAppConfig(
|
||||
importTokenMap,
|
||||
appConfigNode
|
||||
);
|
||||
|
||||
const appConfigFilePath = joinPathFragments(
|
||||
project.sourceRoot,
|
||||
'app/app.config.ts'
|
||||
);
|
||||
|
||||
const appConfigFileContents = getAppConfigFileContents(
|
||||
importsRequiredForAppConfig,
|
||||
appConfigText
|
||||
);
|
||||
|
||||
tree.write(appConfigFilePath, appConfigFileContents);
|
||||
|
||||
let newBootstrapCallFileContents = getBootstrapCallFileContents(
|
||||
bootstrapCallFileContents,
|
||||
appConfigNode,
|
||||
importsRequiredForAppConfig
|
||||
);
|
||||
|
||||
tree.write(bootstrapCallFilePath, newBootstrapCallFileContents);
|
||||
}
|
||||
|
||||
await formatFiles(tree);
|
||||
}
|
||||
@ -0,0 +1,125 @@
|
||||
import type { Tree } from '@nx/devkit';
|
||||
import { readJson, updateJson } from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import removeNgccInvocation from './remove-ngcc-invocation';
|
||||
|
||||
describe('remove-ngcc-invocation migration', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace();
|
||||
});
|
||||
|
||||
it('should not throw when there is no scripts entry', async () => {
|
||||
updateJson(tree, 'package.json', (json) => {
|
||||
delete json.scripts;
|
||||
return json;
|
||||
});
|
||||
|
||||
await expect(removeNgccInvocation(tree)).resolves.not.toThrow();
|
||||
});
|
||||
|
||||
it('should not throw when there is no postinstall script', async () => {
|
||||
updateJson(tree, 'package.json', (json) => {
|
||||
json.scripts = {};
|
||||
return json;
|
||||
});
|
||||
|
||||
await expect(removeNgccInvocation(tree)).resolves.not.toThrow();
|
||||
});
|
||||
|
||||
it('should handle postinstall script without ngcc invocation', async () => {
|
||||
updateJson(tree, 'package.json', (json) => ({
|
||||
...json,
|
||||
scripts: {
|
||||
postinstall:
|
||||
'node ./some-awesome-script.js && node ./another-awesome-script.js',
|
||||
},
|
||||
}));
|
||||
|
||||
await removeNgccInvocation(tree);
|
||||
|
||||
const { scripts } = readJson(tree, 'package.json');
|
||||
expect(scripts.postinstall).toBe(
|
||||
'node ./some-awesome-script.js && node ./another-awesome-script.js'
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle postinstall script with only ngcc invocation', async () => {
|
||||
updateJson(tree, 'package.json', (json) => ({
|
||||
...json,
|
||||
scripts: {
|
||||
postinstall: 'ngcc --properties es2020 browser module main',
|
||||
},
|
||||
}));
|
||||
|
||||
await removeNgccInvocation(tree);
|
||||
|
||||
const { scripts } = readJson(tree, 'package.json');
|
||||
expect(scripts.postinstall).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should handle postinstall script with extra leading command', async () => {
|
||||
updateJson(tree, 'package.json', (json) => ({
|
||||
...json,
|
||||
scripts: {
|
||||
postinstall:
|
||||
'node ./some-awesome-script.js && ngcc --properties es2020 browser module main',
|
||||
},
|
||||
}));
|
||||
|
||||
await removeNgccInvocation(tree);
|
||||
|
||||
const { scripts } = readJson(tree, 'package.json');
|
||||
expect(scripts.postinstall).toBe('node ./some-awesome-script.js');
|
||||
});
|
||||
|
||||
it('should handle postinstall script with extra trailing command', async () => {
|
||||
updateJson(tree, 'package.json', (json) => ({
|
||||
...json,
|
||||
scripts: {
|
||||
postinstall:
|
||||
'ngcc --properties es2020 browser module main && node ./some-awesome-script.js',
|
||||
},
|
||||
}));
|
||||
|
||||
await removeNgccInvocation(tree);
|
||||
|
||||
const { scripts } = readJson(tree, 'package.json');
|
||||
expect(scripts.postinstall).toBe('node ./some-awesome-script.js');
|
||||
});
|
||||
|
||||
it('should handle postinstall script with extra leading and trailing commands', async () => {
|
||||
updateJson(tree, 'package.json', (json) => ({
|
||||
...json,
|
||||
scripts: {
|
||||
postinstall:
|
||||
'node ./some-awesome-script.js && ngcc --properties es2020 browser module main && node ./another-awesome-script.js',
|
||||
},
|
||||
}));
|
||||
|
||||
await removeNgccInvocation(tree);
|
||||
|
||||
const { scripts } = readJson(tree, 'package.json');
|
||||
expect(scripts.postinstall).toBe(
|
||||
'node ./some-awesome-script.js && node ./another-awesome-script.js'
|
||||
);
|
||||
});
|
||||
|
||||
it('should remove ngcc invocation with an arbitrary amount of spaces around "&&"', async () => {
|
||||
updateJson(tree, 'package.json', (json) => ({
|
||||
...json,
|
||||
scripts: {
|
||||
postinstall:
|
||||
'node ./some-awesome-script.js && ngcc --properties es2020 browser module main &&node ./another-awesome-script.js',
|
||||
},
|
||||
}));
|
||||
|
||||
await removeNgccInvocation(tree);
|
||||
|
||||
const { scripts } = readJson(tree, 'package.json');
|
||||
expect(scripts.postinstall).toBe(
|
||||
'node ./some-awesome-script.js &&node ./another-awesome-script.js'
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,25 @@
|
||||
import type { Tree } from '@nx/devkit';
|
||||
import { formatFiles, updateJson } from '@nx/devkit';
|
||||
|
||||
export default async function (tree: Tree) {
|
||||
updateJson(tree, 'package.json', (json) => {
|
||||
if (!json.scripts?.postinstall?.includes('ngcc ')) {
|
||||
return json;
|
||||
}
|
||||
|
||||
json.scripts.postinstall = json.scripts.postinstall
|
||||
// special case when ngcc is at the start so we remove the && as well
|
||||
.replace(/^(ngcc.*?&& *)(.*)/, '$2')
|
||||
// everything else
|
||||
.replace(/(.*?)((&& *)?ngcc.*?)((?=&)|$)(.*)/, '$1$5')
|
||||
.trim();
|
||||
|
||||
if (json.scripts.postinstall === '') {
|
||||
json.scripts.postinstall = undefined;
|
||||
}
|
||||
|
||||
return json;
|
||||
});
|
||||
|
||||
await formatFiles(tree);
|
||||
}
|
||||
@ -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']).toEqual(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']).toEqual(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 = '~16.0.0-rc.4';
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,108 @@
|
||||
import type { Tree } from '@nx/devkit';
|
||||
import { addProjectConfiguration, readProjectConfiguration } from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import migration from './update-server-executor-config';
|
||||
|
||||
describe.each([
|
||||
'@angular-devkit/build-angular:server',
|
||||
'@nx/angular:server',
|
||||
'@nrwl/angular:server',
|
||||
])('update-server-executor-config migration', (executor) => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace();
|
||||
});
|
||||
|
||||
it(`should add 'buildOptimizer: false' to config with 'optimization: false' (${executor})`, async () => {
|
||||
addProjectConfiguration(tree, 'app1', {
|
||||
root: 'apps/app1',
|
||||
targets: {
|
||||
server: {
|
||||
executor,
|
||||
configurations: {
|
||||
development: { optimization: false },
|
||||
production: { optimization: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await migration(tree);
|
||||
|
||||
const project = readProjectConfiguration(tree, 'app1');
|
||||
expect(
|
||||
project.targets.server.configurations.development.buildOptimizer
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it(`should not add 'buildOptimizer' option to config when 'optimization' is not defined (${executor})`, async () => {
|
||||
addProjectConfiguration(tree, 'app1', {
|
||||
root: 'apps/app1',
|
||||
targets: {
|
||||
server: {
|
||||
executor,
|
||||
options: {},
|
||||
configurations: {
|
||||
development: { optimization: false },
|
||||
production: { optimization: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await migration(tree);
|
||||
|
||||
const project = readProjectConfiguration(tree, 'app1');
|
||||
expect(project.targets.server.options.buildOptimizer).toBeUndefined();
|
||||
});
|
||||
|
||||
it(`should add 'buildOptimizer: true' to config with 'optimization: true' (${executor})`, async () => {
|
||||
addProjectConfiguration(tree, 'app1', {
|
||||
root: 'apps/app1',
|
||||
targets: {
|
||||
server: {
|
||||
executor,
|
||||
options: {},
|
||||
configurations: {
|
||||
development: { optimization: false },
|
||||
production: { optimization: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await migration(tree);
|
||||
|
||||
const project = readProjectConfiguration(tree, 'app1');
|
||||
expect(
|
||||
project.targets.server.configurations.production.buildOptimizer
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it(`should not change 'buildOptimizer' if already set (${executor})`, async () => {
|
||||
addProjectConfiguration(tree, 'app1', {
|
||||
root: 'apps/app1',
|
||||
targets: {
|
||||
server: {
|
||||
executor,
|
||||
options: {},
|
||||
configurations: {
|
||||
development: {
|
||||
optimization: false,
|
||||
buildOptimizer: true,
|
||||
},
|
||||
production: { optimization: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await migration(tree);
|
||||
|
||||
const project = readProjectConfiguration(tree, 'app1');
|
||||
expect(
|
||||
project.targets.server.configurations.development.buildOptimizer
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,45 @@
|
||||
import type { ServerBuilderOptions } from '@angular-devkit/build-angular';
|
||||
import type { Tree } from '@nx/devkit';
|
||||
import {
|
||||
formatFiles,
|
||||
readProjectConfiguration,
|
||||
updateProjectConfiguration,
|
||||
} from '@nx/devkit';
|
||||
import { forEachExecutorOptions } from '@nx/devkit/src/generators/executor-options-utils';
|
||||
|
||||
const executors = [
|
||||
'@angular-devkit/build-angular:server',
|
||||
'@nx/angular:server',
|
||||
'@nrwl/angular:server',
|
||||
];
|
||||
|
||||
export default async function (tree: Tree) {
|
||||
executors.forEach((executor) => {
|
||||
forEachExecutorOptions<ServerBuilderOptions>(
|
||||
tree,
|
||||
executor,
|
||||
(_options, projectName, targetName, configurationName) => {
|
||||
const projectConfiguration = readProjectConfiguration(
|
||||
tree,
|
||||
projectName
|
||||
);
|
||||
const configToUpdate: ServerBuilderOptions = configurationName
|
||||
? projectConfiguration.targets[targetName].configurations[
|
||||
configurationName
|
||||
]
|
||||
: projectConfiguration.targets[targetName].options;
|
||||
|
||||
if (
|
||||
configToUpdate.buildOptimizer === undefined &&
|
||||
configToUpdate.optimization !== undefined
|
||||
) {
|
||||
configToUpdate.buildOptimizer = !!configToUpdate.optimization;
|
||||
}
|
||||
|
||||
updateProjectConfiguration(tree, projectName, projectConfiguration);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
await formatFiles(tree);
|
||||
}
|
||||
30
packages/angular/src/migrations/utils/projects.ts
Normal file
30
packages/angular/src/migrations/utils/projects.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import type {
|
||||
ProjectConfiguration,
|
||||
ProjectGraphProjectNode,
|
||||
Tree,
|
||||
} from '@nx/devkit';
|
||||
import { createProjectGraphAsync, readProjectConfiguration } from '@nx/devkit';
|
||||
|
||||
export async function getProjectsFilteredByDependencies(
|
||||
tree: Tree,
|
||||
dependencies: string[]
|
||||
): Promise<
|
||||
Array<{
|
||||
project: ProjectConfiguration;
|
||||
graphNode: ProjectGraphProjectNode;
|
||||
}>
|
||||
> {
|
||||
const projectGraph = await createProjectGraphAsync();
|
||||
|
||||
return Object.entries(projectGraph.dependencies)
|
||||
.filter(([node, dep]) =>
|
||||
dep.some(
|
||||
({ target }) =>
|
||||
!projectGraph.externalNodes?.[node] && dependencies.includes(target)
|
||||
)
|
||||
)
|
||||
.map(([projectName]) => ({
|
||||
project: readProjectConfiguration(tree, projectName),
|
||||
graphNode: projectGraph.nodes[projectName],
|
||||
}));
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
import * as latestVersions from './versions';
|
||||
|
||||
type SupportedVersions = 'angularV14';
|
||||
type SupportedVersions = 'angularV14' | 'angularV15';
|
||||
export type PackageVersionNames = Exclude<
|
||||
keyof typeof latestVersions,
|
||||
'nxVersion'
|
||||
@ -16,7 +16,7 @@ export const backwardCompatibleVersions: Record<
|
||||
angularDevkitVersion: '~14.2.0',
|
||||
ngPackagrVersion: '~14.2.0',
|
||||
ngrxVersion: '~14.0.0',
|
||||
rxjsVersion: '~7.5.0',
|
||||
rxjsVersion: '~7.8.0',
|
||||
zoneJsVersion: '~0.11.4',
|
||||
angularJsVersion: '1.7.9',
|
||||
tsLibVersion: '^2.3.0',
|
||||
@ -38,4 +38,31 @@ export const backwardCompatibleVersions: Record<
|
||||
typesNodeVersion: '16.11.7',
|
||||
jasmineMarblesVersion: '^0.9.2',
|
||||
},
|
||||
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',
|
||||
moduleFederationNodeVersion: '~0.10.1',
|
||||
angularEslintVersion: '~15.0.0',
|
||||
tailwindVersion: '^3.0.2',
|
||||
postcssVersion: '^8.4.5',
|
||||
postcssImportVersion: '~14.1.0',
|
||||
postcssPresetEnvVersion: '~7.5.0',
|
||||
postcssUrlVersion: '~10.1.3',
|
||||
autoprefixerVersion: '^10.4.0',
|
||||
tsNodeVersion: '10.9.1',
|
||||
jestPresetAngularVersion: '~13.0.0',
|
||||
typesNodeVersion: '16.11.7',
|
||||
jasmineMarblesVersion: '^0.9.2',
|
||||
},
|
||||
};
|
||||
|
||||
@ -21,6 +21,10 @@ export class FileChangeRecorder {
|
||||
this.tree.write(this.filePath, this.mutableContent.toString());
|
||||
}
|
||||
|
||||
hasChanged(): boolean {
|
||||
return this.mutableContent.hasChanged();
|
||||
}
|
||||
|
||||
insertLeft(index: number, content: string): void {
|
||||
this.mutableContent.appendLeft(index, content);
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ import {
|
||||
addImportToDirective,
|
||||
addImportToModule,
|
||||
addImportToPipe,
|
||||
addProviderToAppConfig,
|
||||
addProviderToBootstrapApplication,
|
||||
isStandalone,
|
||||
} from './ast-utils';
|
||||
@ -302,4 +303,35 @@ bootstrapApplication(AppComponent, {
|
||||
}).catch((err) => console.error(err));"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should add a provider to the appConfig', () => {
|
||||
// ARRANGE
|
||||
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
tree.write(
|
||||
'app.config.ts',
|
||||
`import { ApplicationConfig } from '@angular/core';
|
||||
import { provideRouter } from '@angular/router';
|
||||
|
||||
import { routes } from './app.routes';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [provideRouter(routes) ]
|
||||
};`
|
||||
);
|
||||
|
||||
// ACT
|
||||
addProviderToAppConfig(tree, 'app.config.ts', 'provideStore()');
|
||||
|
||||
// ASSERT
|
||||
expect(tree.read('app.config.ts', 'utf-8')).toMatchInlineSnapshot(`
|
||||
"import { ApplicationConfig } from '@angular/core';
|
||||
import { provideRouter } from '@angular/router';
|
||||
|
||||
import { routes } from './app.routes';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [provideStore(),provideRouter(routes) ]
|
||||
};"
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import type * as ts from 'typescript';
|
||||
import { findNodes } from '@nx/js';
|
||||
import {
|
||||
findNodes,
|
||||
getImport,
|
||||
getSourceNodes,
|
||||
insertChange,
|
||||
@ -676,6 +676,28 @@ function getListOfRoutes(
|
||||
return null;
|
||||
}
|
||||
|
||||
export function isNgStandaloneApp(tree: Tree, projectName: string) {
|
||||
const project = readProjectConfiguration(tree, projectName);
|
||||
const mainFile = project.targets?.build?.options?.main;
|
||||
|
||||
if (project.projectType !== 'application' || !mainFile) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ensureTypescript();
|
||||
const { tsquery } = require('@phenomnomnominal/tsquery');
|
||||
|
||||
const mainFileContents = tree.read(mainFile, 'utf-8');
|
||||
|
||||
const BOOTSTRAP_APPLICATION_SELECTOR =
|
||||
'CallExpression:has(Identifier[name=bootstrapApplication])';
|
||||
const ast = tsquery.ast(mainFileContents);
|
||||
const nodes = tsquery(ast, BOOTSTRAP_APPLICATION_SELECTOR, {
|
||||
visitAllChildren: true,
|
||||
});
|
||||
return nodes.length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a provider to bootstrapApplication call for Standalone Applications
|
||||
* @param tree Virtual Tree
|
||||
@ -716,6 +738,47 @@ export function addProviderToBootstrapApplication(
|
||||
tree.write(filePath, newFileContents);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a provider to appConfig for Standalone Applications
|
||||
* NOTE: The appConfig must be marked with type ApplicationConfig and the providers must be declared as an array in the config
|
||||
* @param tree Virtual Tree
|
||||
* @param filePath Path to the file containing the bootstrapApplication call
|
||||
* @param providerToAdd Provider to add
|
||||
*/
|
||||
export function addProviderToAppConfig(
|
||||
tree: Tree,
|
||||
filePath: string,
|
||||
providerToAdd: string
|
||||
) {
|
||||
ensureTypescript();
|
||||
const { tsquery } = require('@phenomnomnominal/tsquery');
|
||||
const PROVIDERS_ARRAY_SELECTOR =
|
||||
'VariableDeclaration:has(TypeReference > Identifier[name=ApplicationConfig]) > ObjectLiteralExpression PropertyAssignment:has(Identifier[name=providers]) > ArrayLiteralExpression';
|
||||
|
||||
const fileContents = tree.read(filePath, 'utf-8');
|
||||
const ast = tsquery.ast(fileContents);
|
||||
const providersArrayNodes = tsquery(ast, PROVIDERS_ARRAY_SELECTOR, {
|
||||
visitAllChildren: true,
|
||||
});
|
||||
if (providersArrayNodes.length === 0) {
|
||||
throw new Error(
|
||||
`'providers' does not exist in the application configuration at '${filePath}'.`
|
||||
);
|
||||
}
|
||||
|
||||
const arrayNode = providersArrayNodes[0];
|
||||
|
||||
const newFileContents = `${fileContents.slice(
|
||||
0,
|
||||
arrayNode.getStart() + 1
|
||||
)}${providerToAdd},${fileContents.slice(
|
||||
arrayNode.getStart() + 1,
|
||||
fileContents.length
|
||||
)}`;
|
||||
|
||||
tree.write(filePath, newFileContents);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a provider to an NgModule
|
||||
* @param host Virtual Tree
|
||||
|
||||
@ -5,6 +5,7 @@ export {
|
||||
addImportToPipe,
|
||||
addImportToModule,
|
||||
addProviderToBootstrapApplication,
|
||||
addProviderToAppConfig,
|
||||
addProviderToComponent,
|
||||
addProviderToModule,
|
||||
} from './nx-devkit/ast-utils';
|
||||
|
||||
@ -1,22 +1,22 @@
|
||||
export const nxVersion = require('../../package.json').version;
|
||||
|
||||
export const angularVersion = '~15.2.0';
|
||||
export const angularDevkitVersion = '~15.2.0';
|
||||
export const ngPackagrVersion = '~15.2.2';
|
||||
export const angularVersion = '~16.0.0-rc.4';
|
||||
export const angularDevkitVersion = '~16.0.0-rc.4';
|
||||
export const ngPackagrVersion = '~16.0.0-rc.1';
|
||||
export const ngrxVersion = '~15.3.0';
|
||||
export const rxjsVersion = '~7.8.0';
|
||||
export const zoneJsVersion = '~0.12.0';
|
||||
export const angularJsVersion = '1.7.9';
|
||||
export const tsLibVersion = '^2.3.0';
|
||||
|
||||
export const ngUniversalVersion = '~15.1.0';
|
||||
export const ngUniversalVersion = '~16.0.0-rc.2';
|
||||
export const corsVersion = '~2.8.5';
|
||||
export const typesCorsVersion = '~2.8.5';
|
||||
export const expressVersion = '~4.18.2';
|
||||
export const typesExpressVersion = '4.17.14';
|
||||
export const moduleFederationNodeVersion = '~0.10.1';
|
||||
|
||||
export const angularEslintVersion = '~15.0.0';
|
||||
export const angularEslintVersion = '~16.0.0-alpha.1';
|
||||
export const tailwindVersion = '^3.0.2';
|
||||
export const postcssVersion = '^8.4.5';
|
||||
export const postcssImportVersion = '~14.1.0';
|
||||
|
||||
@ -32,6 +32,8 @@ import { cypressInitGenerator } from '../init/init';
|
||||
// app
|
||||
import { Schema } from './schema';
|
||||
import { addLinterToCyProject } from '../../utils/add-linter';
|
||||
import { checkAndCleanWithSemver } from '@nx/devkit/src/utils/semver';
|
||||
import { major } from 'semver';
|
||||
|
||||
export interface CypressProjectSchema extends Schema {
|
||||
projectName: string;
|
||||
@ -84,7 +86,9 @@ function createFiles(tree: Tree, options: CypressProjectSchema) {
|
||||
function addProject(tree: Tree, options: CypressProjectSchema) {
|
||||
let e2eProjectConfig: ProjectConfiguration;
|
||||
|
||||
const detectedCypressVersion = installedCypressVersion() ?? cypressVersion;
|
||||
const detectedCypressVersion =
|
||||
installedCypressVersion() ??
|
||||
major(checkAndCleanWithSemver('cypress', cypressVersion));
|
||||
|
||||
const cypressConfig =
|
||||
detectedCypressVersion < 10 ? 'cypress.json' : 'cypress.config.ts';
|
||||
|
||||
@ -1,11 +1,20 @@
|
||||
import * as fs from 'fs';
|
||||
import * as ts from 'typescript';
|
||||
import { readTsPathMappings } from './typescript';
|
||||
|
||||
let readConfigFileResult: any;
|
||||
let parseJsonConfigFileContentResult: any;
|
||||
jest.mock('typescript', () => ({
|
||||
...jest.requireActual('typescript'),
|
||||
readConfigFile: jest.fn().mockImplementation(() => readConfigFileResult),
|
||||
parseJsonConfigFileContent: jest
|
||||
.fn()
|
||||
.mockImplementation(() => parseJsonConfigFileContentResult),
|
||||
}));
|
||||
|
||||
describe('readTsPathMappings', () => {
|
||||
it('should normalize paths', () => {
|
||||
jest.spyOn(fs, 'existsSync').mockReturnValue(true);
|
||||
jest.spyOn(ts, 'readConfigFile').mockReturnValue({
|
||||
readConfigFileResult = {
|
||||
config: {
|
||||
options: {
|
||||
paths: {
|
||||
@ -14,9 +23,8 @@ describe('readTsPathMappings', () => {
|
||||
},
|
||||
},
|
||||
},
|
||||
} as any);
|
||||
|
||||
jest.spyOn(ts, 'parseJsonConfigFileContent').mockReturnValue({
|
||||
};
|
||||
parseJsonConfigFileContentResult = {
|
||||
options: {
|
||||
paths: {
|
||||
'@myorg/lib1': ['./libs/lib1/src/index.ts'],
|
||||
@ -25,7 +33,7 @@ describe('readTsPathMappings', () => {
|
||||
},
|
||||
fileNames: [],
|
||||
errors: [],
|
||||
});
|
||||
};
|
||||
|
||||
const paths = readTsPathMappings('/path/to/tsconfig.json');
|
||||
|
||||
|
||||
@ -1 +1 @@
|
||||
export const typescriptVersion = '~4.9.5';
|
||||
export const typescriptVersion = '~5.0.2';
|
||||
|
||||
@ -35,6 +35,7 @@
|
||||
"dependencies": {
|
||||
"@nx/devkit": "file:../devkit",
|
||||
"@nx/js": "file:../js",
|
||||
"@typescript-eslint/type-utils": "^5.58.0",
|
||||
"@typescript-eslint/utils": "^5.58.0",
|
||||
"chalk": "^4.1.0",
|
||||
"confusing-browser-globals": "^1.0.9",
|
||||
|
||||
@ -1,13 +1,15 @@
|
||||
import {
|
||||
joinPathFragments,
|
||||
logger,
|
||||
ProjectGraphProjectNode,
|
||||
readJsonFile,
|
||||
workspaceRoot,
|
||||
} from '@nx/devkit';
|
||||
import { findNodes } from '@nx/js';
|
||||
import { getModifiers } from '@typescript-eslint/type-utils';
|
||||
import { existsSync, readFileSync } from 'fs';
|
||||
import { dirname } from 'path';
|
||||
import ts = require('typescript');
|
||||
import { logger, workspaceRoot } from '@nx/devkit';
|
||||
|
||||
function tryReadBaseJson() {
|
||||
try {
|
||||
@ -126,8 +128,7 @@ export function getRelativeImportPath(exportedMember, filePath, basePath) {
|
||||
}
|
||||
|
||||
if (
|
||||
parent.modifiers &&
|
||||
parent.modifiers.find(
|
||||
getModifiers(parent)?.find(
|
||||
(modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword
|
||||
)
|
||||
) {
|
||||
|
||||
@ -68,6 +68,18 @@
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"16.1.0": {
|
||||
"version": "16.1.0-beta.0",
|
||||
"x-prompt": "Do you want to update to TypeScript v5.0?",
|
||||
"requires": {
|
||||
"typescript": ">=4.9.5 <5.0.0"
|
||||
},
|
||||
"packages": {
|
||||
"typescript": {
|
||||
"version": "~5.0.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,4 +8,4 @@ export const swcHelpersVersion = '~0.5.0';
|
||||
export const swcNodeVersion = '~1.4.2';
|
||||
export const tsLibVersion = '^2.3.0';
|
||||
export const typesNodeVersion = '18.7.1';
|
||||
export const typescriptVersion = '~4.9.5';
|
||||
export const typescriptVersion = '~5.0.2';
|
||||
|
||||
@ -42,6 +42,35 @@
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"16.1.0": {
|
||||
"version": "16.1.0-beta.0",
|
||||
"packages": {
|
||||
"@nestjs/common": {
|
||||
"version": "^9.1.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@nestjs/core": {
|
||||
"version": "^9.1.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@nestjs/platform-express": {
|
||||
"version": "^9.1.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@nestjs/schematics": {
|
||||
"version": "^9.1.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@nestjs/swagger": {
|
||||
"version": "^6.3.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@nestjs/testing": {
|
||||
"version": "^9.1.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
export const nxVersion = require('../../package.json').version;
|
||||
|
||||
export const nestJsVersion = '^9.0.0';
|
||||
export const rxjsVersion = '^7.0.0';
|
||||
export const nestJsVersion = '^9.1.0';
|
||||
export const rxjsVersion = '^7.8.0';
|
||||
export const reflectMetadataVersion = '^0.1.13';
|
||||
export const tsLibVersion = '^2.3.0';
|
||||
|
||||
@ -1 +1 @@
|
||||
export const typescriptVersion = '~4.9.5';
|
||||
export const typescriptVersion = '~5.0.2';
|
||||
|
||||
@ -44,7 +44,7 @@
|
||||
"rollup-plugin-peer-deps-external": "^2.2.4",
|
||||
"rollup-plugin-postcss": "^4.0.1",
|
||||
"rollup-plugin-typescript2": "0.34.1",
|
||||
"rxjs": "^6.5.4",
|
||||
"rxjs": "^7.8.0",
|
||||
"tslib": "^2.3.0",
|
||||
"@nx/devkit": "file:../devkit",
|
||||
"@nx/js": "file:../js"
|
||||
|
||||
@ -142,7 +142,7 @@ export async function* rollupExecutor(
|
||||
})
|
||||
)
|
||||
),
|
||||
scan<RollupExecutorEvent>(
|
||||
scan<RollupExecutorEvent, RollupExecutorEvent>(
|
||||
(acc, result) => {
|
||||
if (!acc.success) return acc;
|
||||
return result;
|
||||
|
||||
@ -56,6 +56,114 @@
|
||||
}
|
||||
},
|
||||
"packageJsonUpdates": {
|
||||
"16.1.0": {
|
||||
"version": "16.1.0-beta.0",
|
||||
"requires": {
|
||||
"@storybook/core-server": ">=7.0.0 <7.0.8"
|
||||
},
|
||||
"packages": {
|
||||
"@storybook/core-server": {
|
||||
"version": "^7.0.8",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@storybook/angular": {
|
||||
"version": "^7.0.8",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@storybook/react": {
|
||||
"version": "^7.0.8",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@storybook/web-components-vite": {
|
||||
"version": "^7.0.8",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@storybook/web-components-webpack5": {
|
||||
"version": "^7.0.8",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@storybook/builder-vite": {
|
||||
"version": "^7.0.8",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@storybook/builder-webpack5": {
|
||||
"version": "^7.0.8",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@storybook/addon-a11y": {
|
||||
"version": "^7.0.8",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@storybook/addon-actions": {
|
||||
"version": "^7.0.8",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@storybook/addon-backgrounds": {
|
||||
"version": "^7.0.8",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@storybook/addon-controls": {
|
||||
"version": "^7.0.8",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@storybook/addon-docs": {
|
||||
"version": "^7.0.8",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@storybook/addon-essentials": {
|
||||
"version": "^7.0.8",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@storybook/addon-mdx-gfm": {
|
||||
"version": "^7.0.8",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@storybook/addon-highlight": {
|
||||
"version": "^7.0.8",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@storybook/addon-interactions": {
|
||||
"version": "^7.0.8",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@storybook/addon-jest": {
|
||||
"version": "^7.0.8",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@storybook/addon-links": {
|
||||
"version": "^7.0.8",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@storybook/addon-measure": {
|
||||
"version": "^7.0.8",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@storybook/addon-outline": {
|
||||
"version": "^7.0.8",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@storybook/addon-storyshots": {
|
||||
"version": "^7.0.8",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@storybook/addon-storyshots-puppeteer": {
|
||||
"version": "^7.0.8",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@storybook/addon-storysource": {
|
||||
"version": "^7.0.8",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@storybook/addon-toolbars": {
|
||||
"version": "^7.0.8",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@storybook/addon-viewport": {
|
||||
"version": "^7.0.8",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"16.0.0": {
|
||||
"version": "16.0.0-beta.1",
|
||||
"packages": {
|
||||
|
||||
@ -17,7 +17,7 @@ exports[`@nx/storybook:init dependencies for package.json should add angular rel
|
||||
"existing": "1.0.0",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"prettier": "^2.6.2",
|
||||
"typescript": "~4.9.5",
|
||||
"typescript": "~5.0.2",
|
||||
"webpack": "^5.64.0",
|
||||
},
|
||||
"name": "test-name",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user