feat(angular): prompt users for standalone components in application (#14987)

This commit is contained in:
Colum Ferry 2023-02-17 20:44:10 +00:00 committed by GitHub
parent 2899b3abfb
commit 1e6a4f8e65
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 83 additions and 12 deletions

View File

@ -148,7 +148,7 @@
"standalone": { "standalone": {
"description": "Generate an application that is setup to use standalone components. _Note: This is only supported in Angular versions >= 14.1.0_", "description": "Generate an application that is setup to use standalone components. _Note: This is only supported in Angular versions >= 14.1.0_",
"type": "boolean", "type": "boolean",
"default": false "x-priority": "important"
}, },
"rootProject": { "rootProject": {
"description": "Create an application at the root of the workspace.", "description": "Create an application at the root of the workspace.",

View File

@ -14,18 +14,24 @@ describe('Angular Package', () => {
it('should support eslint and pass linting on the standard generated code', async () => { it('should support eslint and pass linting on the standard generated code', async () => {
const myapp = uniq('myapp'); const myapp = uniq('myapp');
runCLI(`generate @nrwl/angular:app ${myapp} --linter=eslint`); runCLI(
`generate @nrwl/angular:app ${myapp} --linter=eslint --no-interactive`
);
expect(runCLI(`lint ${myapp}`)).toContain('All files pass linting.'); expect(runCLI(`lint ${myapp}`)).toContain('All files pass linting.');
const mylib = uniq('mylib'); const mylib = uniq('mylib');
runCLI(`generate @nrwl/angular:lib ${mylib} --linter=eslint`); runCLI(
`generate @nrwl/angular:lib ${mylib} --linter=eslint --no-interactive`
);
expect(runCLI(`lint ${mylib}`)).toContain('All files pass linting.'); expect(runCLI(`lint ${mylib}`)).toContain('All files pass linting.');
}); });
it('should support eslint and successfully lint external HTML files and inline templates', async () => { it('should support eslint and successfully lint external HTML files and inline templates', async () => {
const myapp = uniq('myapp'); const myapp = uniq('myapp');
runCLI(`generate @nrwl/angular:app ${myapp} --linter=eslint`); runCLI(
`generate @nrwl/angular:app ${myapp} --linter=eslint --no-interactive`
);
const templateWhichFailsBananaInBoxLintCheck = `<div ([foo])="bar"></div>`; const templateWhichFailsBananaInBoxLintCheck = `<div ([foo])="bar"></div>`;
const wrappedAsInlineTemplate = ` const wrappedAsInlineTemplate = `

View File

@ -20,7 +20,7 @@ describe('Move Angular Project', () => {
app1 = uniq('app1'); app1 = uniq('app1');
app2 = uniq('app2'); app2 = uniq('app2');
newPath = `subfolder/${app2}`; newPath = `subfolder/${app2}`;
runCLI(`generate @nrwl/angular:app ${app1}`); runCLI(`generate @nrwl/angular:app ${app1} --no-interactive`);
}); });
afterAll(() => cleanupProject()); afterAll(() => cleanupProject());
@ -99,13 +99,13 @@ describe('Move Angular Project', () => {
it('should work for libraries', () => { it('should work for libraries', () => {
const lib1 = uniq('mylib'); const lib1 = uniq('mylib');
const lib2 = uniq('mylib'); const lib2 = uniq('mylib');
runCLI(`generate @nrwl/angular:lib ${lib1}`); runCLI(`generate @nrwl/angular:lib ${lib1} --no-interactive`);
/** /**
* Create a library which imports the module from the other lib * Create a library which imports the module from the other lib
*/ */
runCLI(`generate @nrwl/angular:lib ${lib2}`); runCLI(`generate @nrwl/angular:lib ${lib2} --no-interactive`);
updateFile( updateFile(
`libs/${lib2}/src/lib/${lib2}.module.ts`, `libs/${lib2}/src/lib/${lib2}.module.ts`,

View File

@ -22,7 +22,8 @@
"semver", "semver",
"webpack", "webpack",
"http-server", "http-server",
"magic-string" "magic-string",
"enquirer"
], ],
"keepLifecycleScripts": true "keepLifecycleScripts": true
} }

View File

@ -54,7 +54,8 @@
"ts-node": "10.9.1", "ts-node": "10.9.1",
"tsconfig-paths": "^4.1.2", "tsconfig-paths": "^4.1.2",
"webpack": "^5.75.0", "webpack": "^5.75.0",
"webpack-merge": "5.7.3" "webpack-merge": "5.7.3",
"enquirer": "^2.3.6"
}, },
"peerDependencies": { "peerDependencies": {
"@angular-devkit/build-angular": ">= 14.0.0 < 16.0.0", "@angular-devkit/build-angular": ">= 14.0.0 < 16.0.0",

View File

@ -20,17 +20,23 @@ import {
} from '../../utils/versions'; } from '../../utils/versions';
import { applicationGenerator } from './application'; import { applicationGenerator } from './application';
import type { Schema } from './schema'; import type { Schema } from './schema';
import * as enquirer from 'enquirer';
// need to mock cypress otherwise it'll use the nx installed version from package.json // need to mock cypress otherwise it'll use the nx installed version from package.json
// which is v9 while we are testing for the new v10 version // which is v9 while we are testing for the new v10 version
jest.mock('@nrwl/cypress/src/utils/cypress-version'); jest.mock('@nrwl/cypress/src/utils/cypress-version');
jest.mock('enquirer');
describe('app', () => { describe('app', () => {
let appTree: Tree; let appTree: Tree;
let mockedInstalledCypressVersion: jest.Mock< let mockedInstalledCypressVersion: jest.Mock<
ReturnType<typeof installedCypressVersion> ReturnType<typeof installedCypressVersion>
> = installedCypressVersion as never; > = installedCypressVersion as never;
beforeEach(() => { beforeEach(() => {
mockedInstalledCypressVersion.mockReturnValue(10); mockedInstalledCypressVersion.mockReturnValue(10);
// @ts-ignore
enquirer.prompt = jest
.fn()
.mockReturnValue(Promise.resolve({ 'standalone-components': true }));
appTree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' }); appTree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
}); });
@ -819,6 +825,48 @@ describe('app', () => {
appTree.read('apps/standalone/src/app/nx-welcome.component.ts', 'utf-8') appTree.read('apps/standalone/src/app/nx-welcome.component.ts', 'utf-8')
).toContain('standalone: true'); ).toContain('standalone: true');
}); });
it('should prompt for standalone components and not use them when the user selects false', async () => {
// ARRANGE
process.env.NX_INTERACTIVE = 'true';
// @ts-ignore
enquirer.prompt = jest
.fn()
.mockReturnValue(Promise.resolve({ 'standalone-components': false }));
// ACT
await generateApp(appTree, 'nostandalone');
// ASSERT
expect(
appTree.exists('apps/nostandalone/src/app/app.module.ts')
).toBeTruthy();
expect(enquirer.prompt).toHaveBeenCalled();
// CLEANUP
process.env.NX_INTERACTIVE = undefined;
});
it('should prompt for standalone components and use them when the user selects true', async () => {
// ARRANGE
process.env.NX_INTERACTIVE = 'true';
// @ts-ignore
enquirer.prompt = jest
.fn()
.mockReturnValue(Promise.resolve({ 'standalone-components': true }));
// ACT
await generateApp(appTree, 'nostandalone');
// ASSERT
expect(
appTree.exists('apps/nostandalone/src/app/app.module.ts')
).not.toBeTruthy();
expect(enquirer.prompt).toHaveBeenCalled();
// CLEANUP
process.env.NX_INTERACTIVE = undefined;
});
}); });
it('should generate correct main.ts', async () => { it('should generate correct main.ts', async () => {

View File

@ -34,7 +34,8 @@ import {
updateNxComponentTemplate, updateNxComponentTemplate,
} from './lib'; } from './lib';
import type { Schema } from './schema'; import type { Schema } from './schema';
import { lt } from 'semver'; import { gte, lt } from 'semver';
import { prompt } from 'enquirer';
export async function applicationGenerator( export async function applicationGenerator(
tree: Tree, tree: Tree,
@ -47,6 +48,18 @@ export async function applicationGenerator(
You can resolve this error by removing the "standalone" option or by migrating to Angular 14.1.0.`); You can resolve this error by removing the "standalone" option or by migrating to Angular 14.1.0.`);
} }
if (
gte(installedAngularVersionInfo.version, '14.1.0') &&
schema.standalone === undefined &&
process.env.NX_INTERACTIVE === 'true'
) {
schema.standalone = await prompt({
name: 'standalone-components',
message: 'Would you like to use Standalone Components?',
type: 'confirm',
}).then((a) => a['standalone-components']);
}
const generatorDirectory = const generatorDirectory =
getGeneratorDirectoryForInstalledAngularVersion(tree); getGeneratorDirectoryForInstalledAngularVersion(tree);
if (generatorDirectory) { if (generatorDirectory) {

View File

@ -151,7 +151,7 @@
"standalone": { "standalone": {
"description": "Generate an application that is setup to use standalone components. _Note: This is only supported in Angular versions >= 14.1.0_", "description": "Generate an application that is setup to use standalone components. _Note: This is only supported in Angular versions >= 14.1.0_",
"type": "boolean", "type": "boolean",
"default": false "x-priority": "important"
}, },
"rootProject": { "rootProject": {
"description": "Create an application at the root of the workspace.", "description": "Create an application at the root of the workspace.",

View File

@ -37,6 +37,7 @@ export async function host(tree: Tree, options: Schema) {
const appInstallTask = await applicationGenerator(tree, { const appInstallTask = await applicationGenerator(tree, {
...options, ...options,
standalone: options.standalone ?? false,
routing: true, routing: true,
port: 4200, port: 4200,
skipFormat: true, skipFormat: true,

View File

@ -29,6 +29,7 @@ export async function remote(tree: Tree, options: Schema) {
const appInstallTask = await applicationGenerator(tree, { const appInstallTask = await applicationGenerator(tree, {
...options, ...options,
standalone: options.standalone ?? false,
routing: true, routing: true,
port, port,
}); });