feat(angular): use helper to determine project name and root directory in project generators (#18607)
This commit is contained in:
parent
b9ca7ce09f
commit
47f8b7a8be
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "application",
|
"name": "application",
|
||||||
"factory": "./src/generators/application/application",
|
"factory": "./src/generators/application/application#applicationGeneratorInternal",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$schema": "http://json-schema.org/schema",
|
"$schema": "http://json-schema.org/schema",
|
||||||
"$id": "GeneratorNxApp",
|
"$id": "GeneratorNxApp",
|
||||||
@ -14,13 +14,18 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"$default": { "$source": "argv", "index": 0 },
|
"$default": { "$source": "argv", "index": 0 },
|
||||||
"x-prompt": "What name would you like to use for the application?",
|
"x-prompt": "What name would you like to use for the application?",
|
||||||
"pattern": "^[a-zA-Z].*$"
|
"pattern": "^[a-zA-Z][^:]*$"
|
||||||
},
|
},
|
||||||
"directory": {
|
"directory": {
|
||||||
"description": "The directory of the new application.",
|
"description": "The directory of the new application.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"x-priority": "important"
|
"x-priority": "important"
|
||||||
},
|
},
|
||||||
|
"projectNameAndRootFormat": {
|
||||||
|
"description": "Whether to generate the project name and root directory as provided (`as-provided`) or generate them composing their values and taking the configured layout into account (`derived`).",
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["as-provided", "derived"]
|
||||||
|
},
|
||||||
"style": {
|
"style": {
|
||||||
"description": "The file extension to be used for style files.",
|
"description": "The file extension to be used for style files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@ -178,7 +183,7 @@
|
|||||||
"aliases": ["app"],
|
"aliases": ["app"],
|
||||||
"x-type": "application",
|
"x-type": "application",
|
||||||
"description": "Creates an Angular application.",
|
"description": "Creates an Angular application.",
|
||||||
"implementation": "/packages/angular/src/generators/application/application.ts",
|
"implementation": "/packages/angular/src/generators/application/application#applicationGeneratorInternal.ts",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"path": "/packages/angular/src/generators/application/schema.json",
|
"path": "/packages/angular/src/generators/application/schema.json",
|
||||||
"type": "generator"
|
"type": "generator"
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "host",
|
"name": "host",
|
||||||
"factory": "./src/generators/host/host",
|
"factory": "./src/generators/host/host#hostInternal",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$schema": "http://json-schema.org/schema",
|
"$schema": "http://json-schema.org/schema",
|
||||||
"$id": "NxMFHost",
|
"$id": "NxMFHost",
|
||||||
@ -19,7 +19,7 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The name to give to the host Angular application.",
|
"description": "The name to give to the host Angular application.",
|
||||||
"$default": { "$source": "argv", "index": 0 },
|
"$default": { "$source": "argv", "index": 0 },
|
||||||
"pattern": "^[a-zA-Z].*$"
|
"pattern": "^[a-zA-Z][^:]*$"
|
||||||
},
|
},
|
||||||
"remotes": {
|
"remotes": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
@ -35,6 +35,11 @@
|
|||||||
"description": "The directory of the new application.",
|
"description": "The directory of the new application.",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"projectNameAndRootFormat": {
|
||||||
|
"description": "Whether to generate the project name and root directory as provided (`as-provided`) or generate them composing their values and taking the configured layout into account (`derived`).",
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["as-provided", "derived"]
|
||||||
|
},
|
||||||
"style": {
|
"style": {
|
||||||
"description": "The file extension to be used for style files.",
|
"description": "The file extension to be used for style files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@ -173,7 +178,7 @@
|
|||||||
},
|
},
|
||||||
"x-type": "application",
|
"x-type": "application",
|
||||||
"description": "Generate a Host Angular Module Federation Application.",
|
"description": "Generate a Host Angular Module Federation Application.",
|
||||||
"implementation": "/packages/angular/src/generators/host/host.ts",
|
"implementation": "/packages/angular/src/generators/host/host#hostInternal.ts",
|
||||||
"aliases": [],
|
"aliases": [],
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"path": "/packages/angular/src/generators/host/schema.json",
|
"path": "/packages/angular/src/generators/host/schema.json",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "library",
|
"name": "library",
|
||||||
"factory": "./src/generators/library/library",
|
"factory": "./src/generators/library/library#libraryGeneratorInternal",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$schema": "http://json-schema.org/schema",
|
"$schema": "http://json-schema.org/schema",
|
||||||
"$id": "GeneratorAngularLibrary",
|
"$id": "GeneratorAngularLibrary",
|
||||||
@ -14,13 +14,18 @@
|
|||||||
"description": "The name of the library.",
|
"description": "The name of the library.",
|
||||||
"$default": { "$source": "argv", "index": 0 },
|
"$default": { "$source": "argv", "index": 0 },
|
||||||
"x-prompt": "What name would you like to use for the library?",
|
"x-prompt": "What name would you like to use for the library?",
|
||||||
"pattern": "^[a-zA-Z].*$"
|
"pattern": "(?:^@[a-zA-Z0-9-*~][a-zA-Z0-9-*._~]*\\/[a-zA-Z0-9-~][a-zA-Z0-9-._~]*|^[a-zA-Z][^:]*)$"
|
||||||
},
|
},
|
||||||
"directory": {
|
"directory": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "A directory where the library is placed.",
|
"description": "A directory where the library is placed.",
|
||||||
"x-priority": "important"
|
"x-priority": "important"
|
||||||
},
|
},
|
||||||
|
"projectNameAndRootFormat": {
|
||||||
|
"description": "Whether to generate the project name and root directory as provided (`as-provided`) or generate them composing their values and taking the configured layout into account (`derived`).",
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["as-provided", "derived"]
|
||||||
|
},
|
||||||
"publishable": {
|
"publishable": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false,
|
"default": false,
|
||||||
@ -205,7 +210,7 @@
|
|||||||
"aliases": ["lib"],
|
"aliases": ["lib"],
|
||||||
"x-type": "library",
|
"x-type": "library",
|
||||||
"description": "Creates an Angular library.",
|
"description": "Creates an Angular library.",
|
||||||
"implementation": "/packages/angular/src/generators/library/library.ts",
|
"implementation": "/packages/angular/src/generators/library/library#libraryGeneratorInternal.ts",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"path": "/packages/angular/src/generators/library/schema.json",
|
"path": "/packages/angular/src/generators/library/schema.json",
|
||||||
"type": "generator"
|
"type": "generator"
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "remote",
|
"name": "remote",
|
||||||
"factory": "./src/generators/remote/remote",
|
"factory": "./src/generators/remote/remote#remoteInternal",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$schema": "http://json-schema.org/schema",
|
"$schema": "http://json-schema.org/schema",
|
||||||
"$id": "NxMFRemote",
|
"$id": "NxMFRemote",
|
||||||
@ -19,7 +19,7 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The name to give to the remote Angular app.",
|
"description": "The name to give to the remote Angular app.",
|
||||||
"$default": { "$source": "argv", "index": 0 },
|
"$default": { "$source": "argv", "index": 0 },
|
||||||
"pattern": "^[a-zA-Z].*$"
|
"pattern": "^[a-zA-Z][^:]*$"
|
||||||
},
|
},
|
||||||
"host": {
|
"host": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@ -35,6 +35,11 @@
|
|||||||
"description": "The directory of the new application.",
|
"description": "The directory of the new application.",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"projectNameAndRootFormat": {
|
||||||
|
"description": "Whether to generate the project name and root directory as provided (`as-provided`) or generate them composing their values and taking the configured layout into account (`derived`).",
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["as-provided", "derived"]
|
||||||
|
},
|
||||||
"style": {
|
"style": {
|
||||||
"description": "The file extension to be used for style files.",
|
"description": "The file extension to be used for style files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@ -166,7 +171,7 @@
|
|||||||
},
|
},
|
||||||
"x-type": "application",
|
"x-type": "application",
|
||||||
"description": "Generate a Remote Angular Module Federation Application.",
|
"description": "Generate a Remote Angular Module Federation Application.",
|
||||||
"implementation": "/packages/angular/src/generators/remote/remote.ts",
|
"implementation": "/packages/angular/src/generators/remote/remote#remoteInternal.ts",
|
||||||
"aliases": [],
|
"aliases": [],
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"path": "/packages/angular/src/generators/remote/schema.json",
|
"path": "/packages/angular/src/generators/remote/schema.json",
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { names } from '@nx/devkit';
|
import { names } from '@nx/devkit';
|
||||||
import {
|
import {
|
||||||
|
checkFilesExist,
|
||||||
cleanupProject,
|
cleanupProject,
|
||||||
killProcessAndPorts,
|
killProcessAndPorts,
|
||||||
newProject,
|
newProject,
|
||||||
@ -195,4 +196,39 @@ describe('Angular Module Federation', () => {
|
|||||||
// port and process cleanup
|
// port and process cleanup
|
||||||
await killProcessAndPorts(process.pid, hostPort, remote1Port, remote2Port);
|
await killProcessAndPorts(process.pid, hostPort, remote1Port, remote2Port);
|
||||||
}, 20_000_000);
|
}, 20_000_000);
|
||||||
|
|
||||||
|
it('should should support generating host and remote apps with the new name and root format', async () => {
|
||||||
|
const hostApp = uniq('host');
|
||||||
|
const remoteApp = uniq('remote');
|
||||||
|
const hostPort = 4800;
|
||||||
|
const remotePort = 4801;
|
||||||
|
|
||||||
|
// generate host app
|
||||||
|
runCLI(
|
||||||
|
`generate @nx/angular:host ${hostApp} --project-name-and-root-format=as-provided --no-interactive`
|
||||||
|
);
|
||||||
|
// generate remote app
|
||||||
|
runCLI(
|
||||||
|
`generate @nx/angular:remote ${remoteApp} --host=${hostApp} --port=${remotePort} --project-name-and-root-format=as-provided --no-interactive`
|
||||||
|
);
|
||||||
|
|
||||||
|
// check files are generated without the layout directory ("apps/") and
|
||||||
|
// using the project name as the directory when no directory is provided
|
||||||
|
checkFilesExist(`${hostApp}/src/app/app.module.ts`);
|
||||||
|
checkFilesExist(`${remoteApp}/src/app/app.module.ts`);
|
||||||
|
|
||||||
|
// check default generated host is built successfully
|
||||||
|
const buildOutput = runCLI(`build ${hostApp}`);
|
||||||
|
expect(buildOutput).toContain('Successfully ran target build');
|
||||||
|
|
||||||
|
const process = await runCommandUntil(
|
||||||
|
`serve ${hostApp} --port=${hostPort} --dev-remotes=${remoteApp}`,
|
||||||
|
(output) =>
|
||||||
|
output.includes(`listening on localhost:${remotePort}`) &&
|
||||||
|
output.includes(`listening on localhost:${hostPort}`)
|
||||||
|
);
|
||||||
|
|
||||||
|
// port and process cleanup
|
||||||
|
await killProcessAndPorts(process.pid, hostPort, remotePort);
|
||||||
|
}, 20_000_000);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -350,4 +350,53 @@ describe('Angular Projects', () => {
|
|||||||
);
|
);
|
||||||
expect(buildOutput).toContain('Successfully ran target build');
|
expect(buildOutput).toContain('Successfully ran target build');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should support generating projects with the new name and root format', () => {
|
||||||
|
const appName = uniq('app1');
|
||||||
|
const libName = uniq('@my-org/lib1');
|
||||||
|
|
||||||
|
runCLI(
|
||||||
|
`generate @nx/angular:app ${appName} --project-name-and-root-format=as-provided --no-interactive`
|
||||||
|
);
|
||||||
|
|
||||||
|
// check files are generated without the layout directory ("apps/") and
|
||||||
|
// using the project name as the directory when no directory is provided
|
||||||
|
checkFilesExist(`${appName}/src/app/app.module.ts`);
|
||||||
|
// check build works
|
||||||
|
expect(runCLI(`build ${appName}`)).toContain(
|
||||||
|
`Successfully ran target build for project ${appName}`
|
||||||
|
);
|
||||||
|
// check tests pass
|
||||||
|
const appTestResult = runCLI(`test ${appName}`);
|
||||||
|
expect(appTestResult).toContain(
|
||||||
|
`Successfully ran target test for project ${appName}`
|
||||||
|
);
|
||||||
|
|
||||||
|
// assert scoped project names are not supported when --project-name-and-root-format=derived
|
||||||
|
expect(() =>
|
||||||
|
runCLI(
|
||||||
|
`generate @nx/angular:lib ${libName} --buildable --project-name-and-root-format=derived`
|
||||||
|
)
|
||||||
|
).toThrow();
|
||||||
|
|
||||||
|
runCLI(
|
||||||
|
`generate @nx/angular:lib ${libName} --buildable --project-name-and-root-format=as-provided`
|
||||||
|
);
|
||||||
|
|
||||||
|
// check files are generated without the layout directory ("libs/") and
|
||||||
|
// using the project name as the directory when no directory is provided
|
||||||
|
checkFilesExist(
|
||||||
|
`${libName}/src/index.ts`,
|
||||||
|
`${libName}/src/lib/${libName.split('/')[1]}.module.ts`
|
||||||
|
);
|
||||||
|
// check build works
|
||||||
|
expect(runCLI(`build ${libName}`)).toContain(
|
||||||
|
`Successfully ran target build for project ${libName}`
|
||||||
|
);
|
||||||
|
// check tests pass
|
||||||
|
const libTestResult = runCLI(`test ${libName}`);
|
||||||
|
expect(libTestResult).toContain(
|
||||||
|
`Successfully ran target test for project ${libName}`
|
||||||
|
);
|
||||||
|
}, 500_000);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -163,7 +163,7 @@
|
|||||||
"hidden": true
|
"hidden": true
|
||||||
},
|
},
|
||||||
"application": {
|
"application": {
|
||||||
"factory": "./src/generators/application/application",
|
"factory": "./src/generators/application/application#applicationGeneratorInternal",
|
||||||
"schema": "./src/generators/application/schema.json",
|
"schema": "./src/generators/application/schema.json",
|
||||||
"aliases": ["app"],
|
"aliases": ["app"],
|
||||||
"x-type": "application",
|
"x-type": "application",
|
||||||
@ -211,7 +211,7 @@
|
|||||||
"hidden": true
|
"hidden": true
|
||||||
},
|
},
|
||||||
"library": {
|
"library": {
|
||||||
"factory": "./src/generators/library/library",
|
"factory": "./src/generators/library/library#libraryGeneratorInternal",
|
||||||
"schema": "./src/generators/library/schema.json",
|
"schema": "./src/generators/library/schema.json",
|
||||||
"aliases": ["lib"],
|
"aliases": ["lib"],
|
||||||
"x-type": "library",
|
"x-type": "library",
|
||||||
@ -224,7 +224,7 @@
|
|||||||
"description": "Creates a secondary entry point for an Angular publishable library."
|
"description": "Creates a secondary entry point for an Angular publishable library."
|
||||||
},
|
},
|
||||||
"remote": {
|
"remote": {
|
||||||
"factory": "./src/generators/remote/remote",
|
"factory": "./src/generators/remote/remote#remoteInternal",
|
||||||
"schema": "./src/generators/remote/schema.json",
|
"schema": "./src/generators/remote/schema.json",
|
||||||
"x-type": "application",
|
"x-type": "application",
|
||||||
"description": "Generate a Remote Angular Module Federation Application."
|
"description": "Generate a Remote Angular Module Federation Application."
|
||||||
@ -241,7 +241,7 @@
|
|||||||
"description": "Converts an old micro frontend configuration to use the new withModuleFederation helper. It will run successfully if the following conditions are met: \n - Is either a host or remote application \n - Shared npm package configurations have not been modified \n - Name used to identify the Micro Frontend application matches the project name \n\n{% callout type=\"warning\" title=\"Overrides\" %}This generator will overwrite your webpack config. If you have additional custom configuration in your config file, it will be lost!{% /callout %}"
|
"description": "Converts an old micro frontend configuration to use the new withModuleFederation helper. It will run successfully if the following conditions are met: \n - Is either a host or remote application \n - Shared npm package configurations have not been modified \n - Name used to identify the Micro Frontend application matches the project name \n\n{% callout type=\"warning\" title=\"Overrides\" %}This generator will overwrite your webpack config. If you have additional custom configuration in your config file, it will be lost!{% /callout %}"
|
||||||
},
|
},
|
||||||
"host": {
|
"host": {
|
||||||
"factory": "./src/generators/host/host",
|
"factory": "./src/generators/host/host#hostInternal",
|
||||||
"schema": "./src/generators/host/schema.json",
|
"schema": "./src/generators/host/schema.json",
|
||||||
"x-type": "application",
|
"x-type": "application",
|
||||||
"description": "Generate a Host Angular Module Federation Application."
|
"description": "Generate a Host Angular Module Federation Application."
|
||||||
|
|||||||
@ -30,6 +30,16 @@ import { prompt } from 'enquirer';
|
|||||||
export async function applicationGenerator(
|
export async function applicationGenerator(
|
||||||
tree: Tree,
|
tree: Tree,
|
||||||
schema: Partial<Schema>
|
schema: Partial<Schema>
|
||||||
|
): Promise<GeneratorCallback> {
|
||||||
|
return await applicationGeneratorInternal(tree, {
|
||||||
|
projectNameAndRootFormat: 'derived',
|
||||||
|
...schema,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function applicationGeneratorInternal(
|
||||||
|
tree: Tree,
|
||||||
|
schema: Partial<Schema>
|
||||||
): Promise<GeneratorCallback> {
|
): Promise<GeneratorCallback> {
|
||||||
const installedAngularVersionInfo = getInstalledAngularVersionInfo(tree);
|
const installedAngularVersionInfo = getInstalledAngularVersionInfo(tree);
|
||||||
|
|
||||||
@ -50,7 +60,7 @@ export async function applicationGenerator(
|
|||||||
}).then((a) => a['standalone-components']);
|
}).then((a) => a['standalone-components']);
|
||||||
}
|
}
|
||||||
|
|
||||||
const options = normalizeOptions(tree, schema);
|
const options = await normalizeOptions(tree, schema);
|
||||||
const rootOffset = offsetFromRoot(options.appProjectRoot);
|
const rootOffset = offsetFromRoot(options.appProjectRoot);
|
||||||
|
|
||||||
await angularInitGenerator(tree, {
|
await angularInitGenerator(tree, {
|
||||||
|
|||||||
@ -10,19 +10,18 @@ import {
|
|||||||
} from '@nx/devkit';
|
} from '@nx/devkit';
|
||||||
import { nxVersion } from '../../../utils/versions';
|
import { nxVersion } from '../../../utils/versions';
|
||||||
import type { NormalizedSchema } from './normalized-schema';
|
import type { NormalizedSchema } from './normalized-schema';
|
||||||
import { removeScaffoldedE2e } from './remove-scaffolded-e2e';
|
|
||||||
import { cypressProjectGenerator } from '@nx/cypress';
|
import { cypressProjectGenerator } from '@nx/cypress';
|
||||||
|
|
||||||
export async function addE2e(tree: Tree, options: NormalizedSchema) {
|
export async function addE2e(tree: Tree, options: NormalizedSchema) {
|
||||||
removeScaffoldedE2e(tree, options, options.ngCliSchematicE2ERoot);
|
|
||||||
|
|
||||||
if (options.e2eTestRunner === 'cypress') {
|
if (options.e2eTestRunner === 'cypress') {
|
||||||
// TODO: This can call `@nx/web:static-config` generator when ready
|
// TODO: This can call `@nx/web:static-config` generator when ready
|
||||||
addFileServerTarget(tree, options, 'serve-static');
|
addFileServerTarget(tree, options, 'serve-static');
|
||||||
|
|
||||||
await cypressProjectGenerator(tree, {
|
await cypressProjectGenerator(tree, {
|
||||||
name: options.e2eProjectName,
|
name: options.e2eProjectName,
|
||||||
directory: options.directory,
|
directory: options.e2eProjectRoot,
|
||||||
|
// the name and root are already normalized, instruct the generator to use them as is
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
project: options.name,
|
project: options.name,
|
||||||
linter: options.linter,
|
linter: options.linter,
|
||||||
standaloneConfig: options.standaloneConfig,
|
standaloneConfig: options.standaloneConfig,
|
||||||
|
|||||||
@ -7,7 +7,5 @@ export * from './create-project';
|
|||||||
export * from './enable-strict-type-checking';
|
export * from './enable-strict-type-checking';
|
||||||
export * from './normalize-options';
|
export * from './normalize-options';
|
||||||
export * from './normalized-schema';
|
export * from './normalized-schema';
|
||||||
export * from './remove-scaffolded-e2e';
|
|
||||||
export * from './set-app-strict-default';
|
export * from './set-app-strict-default';
|
||||||
export * from './update-e2e-project';
|
|
||||||
export * from './update-editor-tsconfig';
|
export * from './update-editor-tsconfig';
|
||||||
|
|||||||
@ -1,45 +1,43 @@
|
|||||||
import {
|
import { Tree } from '@nx/devkit';
|
||||||
extractLayoutDirectory,
|
import { determineProjectNameAndRootOptions } from '@nx/devkit/src/generators/project-name-and-root-utils';
|
||||||
getWorkspaceLayout,
|
|
||||||
joinPathFragments,
|
|
||||||
names,
|
|
||||||
Tree,
|
|
||||||
} from '@nx/devkit';
|
|
||||||
|
|
||||||
import { getNpmScope } from '@nx/js/src/utils/package-json/get-npm-scope';
|
import { getNpmScope } from '@nx/js/src/utils/package-json/get-npm-scope';
|
||||||
|
import { Linter } from '@nx/linter';
|
||||||
|
import { E2eTestRunner, UnitTestRunner } from '../../../utils/test-runners';
|
||||||
|
import { normalizeNewProjectPrefix } from '../../utils/project';
|
||||||
import type { Schema } from '../schema';
|
import type { Schema } from '../schema';
|
||||||
import type { NormalizedSchema } from './normalized-schema';
|
import type { NormalizedSchema } from './normalized-schema';
|
||||||
import { E2eTestRunner, UnitTestRunner } from '../../../utils/test-runners';
|
|
||||||
import { Linter } from '@nx/linter';
|
|
||||||
import {
|
|
||||||
normalizeDirectory,
|
|
||||||
normalizeNewProjectPrefix,
|
|
||||||
normalizeProjectName,
|
|
||||||
} from '../../utils/project';
|
|
||||||
|
|
||||||
export function normalizeOptions(
|
export async function normalizeOptions(
|
||||||
host: Tree,
|
host: Tree,
|
||||||
options: Partial<Schema>
|
options: Partial<Schema>
|
||||||
): NormalizedSchema {
|
): Promise<NormalizedSchema> {
|
||||||
const { layoutDirectory, projectDirectory } = extractLayoutDirectory(
|
const {
|
||||||
options.directory
|
projectName: appProjectName,
|
||||||
);
|
projectRoot: appProjectRoot,
|
||||||
const appDirectory = normalizeDirectory(options.name, projectDirectory);
|
projectNameAndRootFormat,
|
||||||
const appProjectName = normalizeProjectName(options.name, projectDirectory);
|
} = await determineProjectNameAndRootOptions(host, {
|
||||||
const e2eProjectName = options.rootProject
|
name: options.name,
|
||||||
? 'e2e'
|
projectType: 'application',
|
||||||
: `${names(options.name).fileName}-e2e`;
|
directory: options.directory,
|
||||||
|
projectNameAndRootFormat: options.projectNameAndRootFormat,
|
||||||
|
rootProject: options.rootProject,
|
||||||
|
});
|
||||||
|
options.rootProject = appProjectRoot === '.';
|
||||||
|
options.projectNameAndRootFormat = projectNameAndRootFormat;
|
||||||
|
|
||||||
const { appsDir: defaultAppsDir, standaloneAsDefault } =
|
let e2eProjectName = 'e2e';
|
||||||
getWorkspaceLayout(host);
|
let e2eProjectRoot = 'e2e';
|
||||||
const appsDir = layoutDirectory ?? defaultAppsDir;
|
if (!options.rootProject) {
|
||||||
const appProjectRoot = options.rootProject
|
const projectNameAndRoot = await determineProjectNameAndRootOptions(host, {
|
||||||
? '.'
|
name: `${options.name}-e2e`,
|
||||||
: joinPathFragments(appsDir, appDirectory);
|
projectType: 'application',
|
||||||
const e2eProjectRoot = options.rootProject
|
directory: options.directory,
|
||||||
? 'e2e'
|
projectNameAndRootFormat: options.projectNameAndRootFormat,
|
||||||
: joinPathFragments(appsDir, `${appDirectory}-e2e`);
|
rootProject: options.rootProject,
|
||||||
|
});
|
||||||
|
e2eProjectName = projectNameAndRoot.projectName;
|
||||||
|
e2eProjectRoot = projectNameAndRoot.projectRoot;
|
||||||
|
}
|
||||||
|
|
||||||
const parsedTags = options.tags
|
const parsedTags = options.tags
|
||||||
? options.tags.split(',').map((s) => s.trim())
|
? options.tags.split(',').map((s) => s.trim())
|
||||||
@ -51,11 +49,6 @@ export function normalizeOptions(
|
|||||||
'app'
|
'app'
|
||||||
);
|
);
|
||||||
|
|
||||||
options.standaloneConfig = options.standaloneConfig ?? standaloneAsDefault;
|
|
||||||
|
|
||||||
const ngCliSchematicAppRoot = appProjectName;
|
|
||||||
const ngCliSchematicE2ERoot = `${appProjectName}/e2e`;
|
|
||||||
|
|
||||||
// Set defaults and then overwrite with user options
|
// Set defaults and then overwrite with user options
|
||||||
return {
|
return {
|
||||||
style: 'css',
|
style: 'css',
|
||||||
@ -76,7 +69,5 @@ export function normalizeOptions(
|
|||||||
e2eProjectRoot,
|
e2eProjectRoot,
|
||||||
e2eProjectName,
|
e2eProjectName,
|
||||||
parsedTags,
|
parsedTags,
|
||||||
ngCliSchematicAppRoot,
|
|
||||||
ngCliSchematicE2ERoot,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,6 +11,4 @@ export interface NormalizedSchema extends Schema {
|
|||||||
e2eProjectName: string;
|
e2eProjectName: string;
|
||||||
e2eProjectRoot: string;
|
e2eProjectRoot: string;
|
||||||
parsedTags: string[];
|
parsedTags: string[];
|
||||||
ngCliSchematicAppRoot: string;
|
|
||||||
ngCliSchematicE2ERoot: string;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,31 +0,0 @@
|
|||||||
import type { Tree } from '@nx/devkit';
|
|
||||||
import type { NormalizedSchema } from './normalized-schema';
|
|
||||||
|
|
||||||
import {
|
|
||||||
updateProjectConfiguration,
|
|
||||||
readProjectConfiguration,
|
|
||||||
} from '@nx/devkit';
|
|
||||||
|
|
||||||
export function removeScaffoldedE2e(
|
|
||||||
host: Tree,
|
|
||||||
{ name }: NormalizedSchema,
|
|
||||||
e2eProjectRoot: string
|
|
||||||
) {
|
|
||||||
if (host.exists(`${e2eProjectRoot}/src/app.e2e-spec.ts`)) {
|
|
||||||
host.delete(`${e2eProjectRoot}/src/app.e2e-spec.ts`);
|
|
||||||
}
|
|
||||||
if (host.exists(`${e2eProjectRoot}/src/app.po.ts`)) {
|
|
||||||
host.delete(`${e2eProjectRoot}/src/app.po.ts`);
|
|
||||||
}
|
|
||||||
if (host.exists(`${e2eProjectRoot}/protractor.conf.js`)) {
|
|
||||||
host.delete(`${e2eProjectRoot}/protractor.conf.js`);
|
|
||||||
}
|
|
||||||
if (host.exists(`${e2eProjectRoot}/tsconfig.json`)) {
|
|
||||||
host.delete(`${e2eProjectRoot}/tsconfig.json`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const project = readProjectConfiguration(host, name);
|
|
||||||
delete project.targets['e2e'];
|
|
||||||
|
|
||||||
updateProjectConfiguration(host, name, project);
|
|
||||||
}
|
|
||||||
@ -1,66 +0,0 @@
|
|||||||
import type { ProjectConfiguration, Tree } from '@nx/devkit';
|
|
||||||
import {
|
|
||||||
addProjectConfiguration,
|
|
||||||
offsetFromRoot,
|
|
||||||
readProjectConfiguration,
|
|
||||||
updateJson,
|
|
||||||
updateProjectConfiguration,
|
|
||||||
} from '@nx/devkit';
|
|
||||||
import { getRelativePathToRootTsConfig } from '@nx/js';
|
|
||||||
import type { NormalizedSchema } from './normalized-schema';
|
|
||||||
|
|
||||||
export function updateE2eProject(tree: Tree, options: NormalizedSchema) {
|
|
||||||
const spec = `${options.e2eProjectRoot}/src/app.e2e-spec.ts`;
|
|
||||||
const content = tree.read(spec, 'utf-8');
|
|
||||||
tree.write(
|
|
||||||
spec,
|
|
||||||
content.replace(
|
|
||||||
`${options.name} app is running!`,
|
|
||||||
`Welcome ${options.name}`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
const page = `${options.e2eProjectRoot}/src/app.po.ts`;
|
|
||||||
const pageContent = tree.read(page, 'utf-8');
|
|
||||||
tree.write(page, pageContent.replace(`.content span`, `header h1`));
|
|
||||||
|
|
||||||
const proj = readProjectConfiguration(tree, options.name);
|
|
||||||
const project: ProjectConfiguration = {
|
|
||||||
root: options.e2eProjectRoot,
|
|
||||||
projectType: 'application',
|
|
||||||
targets: {
|
|
||||||
e2e: proj.targets.e2e,
|
|
||||||
},
|
|
||||||
implicitDependencies: [options.name],
|
|
||||||
tags: [],
|
|
||||||
};
|
|
||||||
project.targets.e2e.options.protractorConfig = `${options.e2eProjectRoot}/protractor.conf.js`;
|
|
||||||
addProjectConfiguration(tree, options.e2eProjectName, project);
|
|
||||||
|
|
||||||
delete proj.targets.e2e;
|
|
||||||
updateProjectConfiguration(tree, options.name, proj);
|
|
||||||
|
|
||||||
// update tsconfig e2e
|
|
||||||
if (!tree.exists(`${options.e2eProjectRoot}/tsconfig.e2e.json`)) {
|
|
||||||
tree.write(`${options.e2eProjectRoot}/tsconfig.e2e.json`, '{}');
|
|
||||||
}
|
|
||||||
|
|
||||||
updateJson(tree, `${options.e2eProjectRoot}/tsconfig.e2e.json`, (json) => {
|
|
||||||
return {
|
|
||||||
...json,
|
|
||||||
extends: `./tsconfig.json`,
|
|
||||||
compilerOptions: {
|
|
||||||
...json.compilerOptions,
|
|
||||||
outDir: `${offsetFromRoot(options.e2eProjectRoot)}dist/out-tsc`,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
// update tsconfig
|
|
||||||
updateJson(tree, `${options.e2eProjectRoot}/tsconfig.json`, (json) => {
|
|
||||||
return {
|
|
||||||
...json,
|
|
||||||
extends: getRelativePathToRootTsConfig(tree, options.e2eProjectRoot),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@ -1,5 +1,6 @@
|
|||||||
import { Linter } from '@nx/linter';
|
import type { ProjectNameAndRootFormat } from '@nx/devkit/src/generators/project-name-and-root-utils';
|
||||||
import { E2eTestRunner, UnitTestRunner } from '../../utils/test-runners';
|
import type { Linter } from '@nx/linter';
|
||||||
|
import type { E2eTestRunner, UnitTestRunner } from '../../utils/test-runners';
|
||||||
import type { Styles } from '../utils/types';
|
import type { Styles } from '../utils/types';
|
||||||
|
|
||||||
export interface Schema {
|
export interface Schema {
|
||||||
@ -14,6 +15,7 @@ export interface Schema {
|
|||||||
style?: Styles;
|
style?: Styles;
|
||||||
skipTests?: boolean;
|
skipTests?: boolean;
|
||||||
directory?: string;
|
directory?: string;
|
||||||
|
projectNameAndRootFormat?: ProjectNameAndRootFormat;
|
||||||
tags?: string;
|
tags?: string;
|
||||||
linter?: Linter;
|
linter?: Linter;
|
||||||
unitTestRunner?: UnitTestRunner;
|
unitTestRunner?: UnitTestRunner;
|
||||||
|
|||||||
@ -14,13 +14,18 @@
|
|||||||
"index": 0
|
"index": 0
|
||||||
},
|
},
|
||||||
"x-prompt": "What name would you like to use for the application?",
|
"x-prompt": "What name would you like to use for the application?",
|
||||||
"pattern": "^[a-zA-Z].*$"
|
"pattern": "^[a-zA-Z][^:]*$"
|
||||||
},
|
},
|
||||||
"directory": {
|
"directory": {
|
||||||
"description": "The directory of the new application.",
|
"description": "The directory of the new application.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"x-priority": "important"
|
"x-priority": "important"
|
||||||
},
|
},
|
||||||
|
"projectNameAndRootFormat": {
|
||||||
|
"description": "Whether to generate the project name and root directory as provided (`as-provided`) or generate them composing their values and taking the configured layout into account (`derived`).",
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["as-provided", "derived"]
|
||||||
|
},
|
||||||
"style": {
|
"style": {
|
||||||
"description": "The file extension to be used for style files.",
|
"description": "The file extension to be used for style files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
@ -1,23 +1,28 @@
|
|||||||
import {
|
import {
|
||||||
extractLayoutDirectory,
|
|
||||||
formatFiles,
|
formatFiles,
|
||||||
getProjects,
|
getProjects,
|
||||||
runTasksInSerial,
|
runTasksInSerial,
|
||||||
stripIndents,
|
stripIndents,
|
||||||
Tree,
|
Tree,
|
||||||
} from '@nx/devkit';
|
} from '@nx/devkit';
|
||||||
import type { Schema } from './schema';
|
import { determineProjectNameAndRootOptions } from '@nx/devkit/src/generators/project-name-and-root-utils';
|
||||||
|
import { lt } from 'semver';
|
||||||
|
import { E2eTestRunner } from '../../utils/test-runners';
|
||||||
import applicationGenerator from '../application/application';
|
import applicationGenerator from '../application/application';
|
||||||
import remoteGenerator from '../remote/remote';
|
import remoteGenerator from '../remote/remote';
|
||||||
import { normalizeProjectName } from '../utils/project';
|
|
||||||
import { setupMf } from '../setup-mf/setup-mf';
|
import { setupMf } from '../setup-mf/setup-mf';
|
||||||
import { E2eTestRunner } from '../../utils/test-runners';
|
|
||||||
import { addSsr } from './lib';
|
|
||||||
|
|
||||||
import { getInstalledAngularVersionInfo } from '../utils/version-utils';
|
import { getInstalledAngularVersionInfo } from '../utils/version-utils';
|
||||||
import { lt } from 'semver';
|
import { addSsr } from './lib';
|
||||||
|
import type { Schema } from './schema';
|
||||||
|
|
||||||
export async function host(tree: Tree, options: Schema) {
|
export async function host(tree: Tree, options: Schema) {
|
||||||
|
return await hostInternal(tree, {
|
||||||
|
projectNameAndRootFormat: 'derived',
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function hostInternal(tree: Tree, options: Schema) {
|
||||||
const installedAngularVersionInfo = getInstalledAngularVersionInfo(tree);
|
const installedAngularVersionInfo = getInstalledAngularVersionInfo(tree);
|
||||||
|
|
||||||
if (lt(installedAngularVersionInfo.version, '14.1.0') && options.standalone) {
|
if (lt(installedAngularVersionInfo.version, '14.1.0') && options.standalone) {
|
||||||
@ -40,8 +45,14 @@ export async function host(tree: Tree, options: Schema) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { projectDirectory } = extractLayoutDirectory(options.directory);
|
const { projectName: hostProjectName, projectNameAndRootFormat } =
|
||||||
const appName = normalizeProjectName(options.name, projectDirectory);
|
await determineProjectNameAndRootOptions(tree, {
|
||||||
|
name: options.name,
|
||||||
|
projectType: 'application',
|
||||||
|
directory: options.directory,
|
||||||
|
projectNameAndRootFormat: options.projectNameAndRootFormat,
|
||||||
|
});
|
||||||
|
options.projectNameAndRootFormat = projectNameAndRootFormat;
|
||||||
|
|
||||||
const appInstallTask = await applicationGenerator(tree, {
|
const appInstallTask = await applicationGenerator(tree, {
|
||||||
...options,
|
...options,
|
||||||
@ -54,7 +65,7 @@ export async function host(tree: Tree, options: Schema) {
|
|||||||
const skipE2E =
|
const skipE2E =
|
||||||
!options.e2eTestRunner || options.e2eTestRunner === E2eTestRunner.None;
|
!options.e2eTestRunner || options.e2eTestRunner === E2eTestRunner.None;
|
||||||
await setupMf(tree, {
|
await setupMf(tree, {
|
||||||
appName,
|
appName: hostProjectName,
|
||||||
mfType: 'host',
|
mfType: 'host',
|
||||||
routing: true,
|
routing: true,
|
||||||
port: 4200,
|
port: 4200,
|
||||||
@ -63,13 +74,13 @@ export async function host(tree: Tree, options: Schema) {
|
|||||||
skipPackageJson: options.skipPackageJson,
|
skipPackageJson: options.skipPackageJson,
|
||||||
skipFormat: true,
|
skipFormat: true,
|
||||||
skipE2E,
|
skipE2E,
|
||||||
e2eProjectName: skipE2E ? undefined : `${appName}-e2e`,
|
e2eProjectName: skipE2E ? undefined : `${hostProjectName}-e2e`,
|
||||||
prefix: options.prefix,
|
prefix: options.prefix,
|
||||||
});
|
});
|
||||||
|
|
||||||
let installTasks = [appInstallTask];
|
let installTasks = [appInstallTask];
|
||||||
if (options.ssr) {
|
if (options.ssr) {
|
||||||
let ssrInstallTask = await addSsr(tree, options, appName);
|
let ssrInstallTask = await addSsr(tree, options, hostProjectName);
|
||||||
installTasks.push(ssrInstallTask);
|
installTasks.push(ssrInstallTask);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,7 +88,7 @@ export async function host(tree: Tree, options: Schema) {
|
|||||||
await remoteGenerator(tree, {
|
await remoteGenerator(tree, {
|
||||||
...options,
|
...options,
|
||||||
name: remote,
|
name: remote,
|
||||||
host: appName,
|
host: hostProjectName,
|
||||||
skipFormat: true,
|
skipFormat: true,
|
||||||
standalone: options.standalone,
|
standalone: options.standalone,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { Linter } from '@nx/linter';
|
import type { ProjectNameAndRootFormat } from '@nx/devkit/src/generators/project-name-and-root-utils';
|
||||||
import { E2eTestRunner, UnitTestRunner } from '../../utils/test-runners';
|
import type { Linter } from '@nx/linter';
|
||||||
|
import type { E2eTestRunner, UnitTestRunner } from '../../utils/test-runners';
|
||||||
import type { Styles } from '../utils/types';
|
import type { Styles } from '../utils/types';
|
||||||
|
|
||||||
export interface Schema {
|
export interface Schema {
|
||||||
@ -14,6 +15,7 @@ export interface Schema {
|
|||||||
style?: Styles;
|
style?: Styles;
|
||||||
skipTests?: boolean;
|
skipTests?: boolean;
|
||||||
directory?: string;
|
directory?: string;
|
||||||
|
projectNameAndRootFormat?: ProjectNameAndRootFormat;
|
||||||
tags?: string;
|
tags?: string;
|
||||||
linter?: Linter;
|
linter?: Linter;
|
||||||
unitTestRunner?: UnitTestRunner;
|
unitTestRunner?: UnitTestRunner;
|
||||||
|
|||||||
@ -19,7 +19,7 @@
|
|||||||
"$source": "argv",
|
"$source": "argv",
|
||||||
"index": 0
|
"index": 0
|
||||||
},
|
},
|
||||||
"pattern": "^[a-zA-Z].*$"
|
"pattern": "^[a-zA-Z][^:]*$"
|
||||||
},
|
},
|
||||||
"remotes": {
|
"remotes": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
@ -35,6 +35,11 @@
|
|||||||
"description": "The directory of the new application.",
|
"description": "The directory of the new application.",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"projectNameAndRootFormat": {
|
||||||
|
"description": "Whether to generate the project name and root directory as provided (`as-provided`) or generate them composing their values and taking the configured layout into account (`derived`).",
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["as-provided", "derived"]
|
||||||
|
},
|
||||||
"style": {
|
"style": {
|
||||||
"description": "The file extension to be used for style files.",
|
"description": "The file extension to be used for style files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
@ -1,22 +1,16 @@
|
|||||||
import {
|
import { names, Tree } from '@nx/devkit';
|
||||||
extractLayoutDirectory,
|
import { determineProjectNameAndRootOptions } from '@nx/devkit/src/generators/project-name-and-root-utils';
|
||||||
getWorkspaceLayout,
|
|
||||||
joinPathFragments,
|
|
||||||
names,
|
|
||||||
Tree,
|
|
||||||
} from '@nx/devkit';
|
|
||||||
|
|
||||||
import { getImportPath } from '@nx/js/src/utils/get-import-path';
|
|
||||||
import { getNpmScope } from '@nx/js/src/utils/package-json/get-npm-scope';
|
import { getNpmScope } from '@nx/js/src/utils/package-json/get-npm-scope';
|
||||||
|
|
||||||
import { Linter } from '@nx/linter';
|
import { Linter } from '@nx/linter';
|
||||||
|
|
||||||
import { Schema } from '../schema';
|
|
||||||
import { NormalizedSchema } from './normalized-schema';
|
|
||||||
import { UnitTestRunner } from '../../../utils/test-runners';
|
import { UnitTestRunner } from '../../../utils/test-runners';
|
||||||
import { normalizeNewProjectPrefix } from '../../utils/project';
|
import { normalizeNewProjectPrefix } from '../../utils/project';
|
||||||
|
import { Schema } from '../schema';
|
||||||
|
import { NormalizedSchema } from './normalized-schema';
|
||||||
|
|
||||||
export function normalizeOptions(host: Tree, schema: Schema): NormalizedSchema {
|
export async function normalizeOptions(
|
||||||
|
host: Tree,
|
||||||
|
schema: Schema
|
||||||
|
): Promise<NormalizedSchema> {
|
||||||
// Create a schema with populated default values
|
// Create a schema with populated default values
|
||||||
const options: Schema = {
|
const options: Schema = {
|
||||||
buildable: false,
|
buildable: false,
|
||||||
@ -33,37 +27,32 @@ export function normalizeOptions(host: Tree, schema: Schema): NormalizedSchema {
|
|||||||
...schema,
|
...schema,
|
||||||
};
|
};
|
||||||
|
|
||||||
const name = names(options.name).fileName;
|
const {
|
||||||
const { layoutDirectory, projectDirectory } = extractLayoutDirectory(
|
projectName,
|
||||||
options.directory
|
names: projectNames,
|
||||||
);
|
projectRoot,
|
||||||
const fullProjectDirectory = projectDirectory
|
importPath,
|
||||||
? `${names(projectDirectory).fileName}/${name}`.replace(/\/+/g, '/')
|
} = await determineProjectNameAndRootOptions(host, {
|
||||||
: name;
|
name: options.name,
|
||||||
|
projectType: 'library',
|
||||||
|
directory: options.directory,
|
||||||
|
importPath: options.importPath,
|
||||||
|
projectNameAndRootFormat: options.projectNameAndRootFormat,
|
||||||
|
});
|
||||||
|
|
||||||
const { libsDir: defaultLibsDirectory, standaloneAsDefault } =
|
const fileName = options.simpleName
|
||||||
getWorkspaceLayout(host);
|
? projectNames.projectSimpleName
|
||||||
const npmScope = getNpmScope(host);
|
: projectNames.projectFileName;
|
||||||
const libsDir = layoutDirectory ?? defaultLibsDirectory;
|
|
||||||
|
|
||||||
const projectName = fullProjectDirectory
|
|
||||||
.replace(new RegExp('/', 'g'), '-')
|
|
||||||
.replace(/-\d+/g, '');
|
|
||||||
const fileName = options.simpleName ? name : projectName;
|
|
||||||
const projectRoot = joinPathFragments(libsDir, fullProjectDirectory);
|
|
||||||
|
|
||||||
const moduleName = `${names(fileName).className}Module`;
|
const moduleName = `${names(fileName).className}Module`;
|
||||||
const parsedTags = options.tags
|
const parsedTags = options.tags
|
||||||
? options.tags.split(',').map((s) => s.trim())
|
? options.tags.split(',').map((s) => s.trim())
|
||||||
: [];
|
: [];
|
||||||
const modulePath = `${projectRoot}/src/lib/${fileName}.module.ts`;
|
const modulePath = `${projectRoot}/src/lib/${fileName}.module.ts`;
|
||||||
|
|
||||||
|
const npmScope = getNpmScope(host);
|
||||||
const prefix = normalizeNewProjectPrefix(options.prefix, npmScope, 'lib');
|
const prefix = normalizeNewProjectPrefix(options.prefix, npmScope, 'lib');
|
||||||
|
|
||||||
options.standaloneConfig = options.standaloneConfig ?? standaloneAsDefault;
|
|
||||||
|
|
||||||
const importPath =
|
|
||||||
options.importPath || getImportPath(host, fullProjectDirectory);
|
|
||||||
|
|
||||||
const ngCliSchematicLibRoot = projectName;
|
const ngCliSchematicLibRoot = projectName;
|
||||||
const allNormalizedOptions = {
|
const allNormalizedOptions = {
|
||||||
...options,
|
...options,
|
||||||
@ -74,13 +63,14 @@ export function normalizeOptions(host: Tree, schema: Schema): NormalizedSchema {
|
|||||||
projectRoot,
|
projectRoot,
|
||||||
entryFile: 'index',
|
entryFile: 'index',
|
||||||
moduleName,
|
moduleName,
|
||||||
projectDirectory: fullProjectDirectory,
|
|
||||||
modulePath,
|
modulePath,
|
||||||
parsedTags,
|
parsedTags,
|
||||||
fileName,
|
fileName,
|
||||||
importPath,
|
importPath,
|
||||||
ngCliSchematicLibRoot,
|
ngCliSchematicLibRoot,
|
||||||
standaloneComponentName: `${names(name).className}Component`,
|
standaloneComponentName: `${
|
||||||
|
names(projectNames.projectSimpleName).className
|
||||||
|
}Component`,
|
||||||
};
|
};
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
|||||||
@ -35,7 +35,6 @@ export interface NormalizedSchema {
|
|||||||
entryFile: string;
|
entryFile: string;
|
||||||
modulePath: string;
|
modulePath: string;
|
||||||
moduleName: string;
|
moduleName: string;
|
||||||
projectDirectory: string;
|
|
||||||
parsedTags: string[];
|
parsedTags: string[];
|
||||||
ngCliSchematicLibRoot: string;
|
ngCliSchematicLibRoot: string;
|
||||||
standaloneComponentName: string;
|
standaloneComponentName: string;
|
||||||
|
|||||||
@ -571,7 +571,7 @@ describe('lib', () => {
|
|||||||
it('should accept numbers in the path', async () => {
|
it('should accept numbers in the path', async () => {
|
||||||
await runLibraryGeneratorWithOpts({ directory: 'src/1-api' });
|
await runLibraryGeneratorWithOpts({ directory: 'src/1-api' });
|
||||||
|
|
||||||
expect(readProjectConfiguration(tree, 'src-api-my-lib').root).toEqual(
|
expect(readProjectConfiguration(tree, 'src-1-api-my-lib').root).toEqual(
|
||||||
'src/1-api/my-lib'
|
'src/1-api/my-lib'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -36,6 +36,18 @@ import { addProject } from './lib/add-project';
|
|||||||
export async function libraryGenerator(
|
export async function libraryGenerator(
|
||||||
tree: Tree,
|
tree: Tree,
|
||||||
schema: Schema
|
schema: Schema
|
||||||
|
): Promise<GeneratorCallback> {
|
||||||
|
return await libraryGeneratorInternal(tree, {
|
||||||
|
// provide a default projectNameAndRootFormat to avoid breaking changes
|
||||||
|
// to external generators invoking this one
|
||||||
|
projectNameAndRootFormat: 'derived',
|
||||||
|
...schema,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function libraryGeneratorInternal(
|
||||||
|
tree: Tree,
|
||||||
|
schema: Schema
|
||||||
): Promise<GeneratorCallback> {
|
): Promise<GeneratorCallback> {
|
||||||
// Do some validation checks
|
// Do some validation checks
|
||||||
if (!schema.routing && schema.lazy) {
|
if (!schema.routing && schema.lazy) {
|
||||||
@ -61,7 +73,7 @@ export async function libraryGenerator(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const options = normalizeOptions(tree, schema);
|
const options = await normalizeOptions(tree, schema);
|
||||||
const { libraryOptions } = options;
|
const { libraryOptions } = options;
|
||||||
|
|
||||||
const pkgVersions = versions(tree);
|
const pkgVersions = versions(tree);
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { UnitTestRunner } from '../../utils/test-runners';
|
import type { ProjectNameAndRootFormat } from '@nx/devkit/src/generators/project-name-directory-utils';
|
||||||
import { Linter } from '@nx/linter';
|
import type { Linter } from '@nx/linter';
|
||||||
|
import type { UnitTestRunner } from '../../utils/test-runners';
|
||||||
|
|
||||||
export interface Schema {
|
export interface Schema {
|
||||||
name: string;
|
name: string;
|
||||||
@ -8,6 +9,7 @@ export interface Schema {
|
|||||||
simpleName?: boolean;
|
simpleName?: boolean;
|
||||||
addModuleSpec?: boolean;
|
addModuleSpec?: boolean;
|
||||||
directory?: string;
|
directory?: string;
|
||||||
|
projectNameAndRootFormat?: ProjectNameAndRootFormat;
|
||||||
sourceDir?: string;
|
sourceDir?: string;
|
||||||
buildable?: boolean;
|
buildable?: boolean;
|
||||||
publishable?: boolean;
|
publishable?: boolean;
|
||||||
|
|||||||
@ -14,13 +14,18 @@
|
|||||||
"index": 0
|
"index": 0
|
||||||
},
|
},
|
||||||
"x-prompt": "What name would you like to use for the library?",
|
"x-prompt": "What name would you like to use for the library?",
|
||||||
"pattern": "^[a-zA-Z].*$"
|
"pattern": "(?:^@[a-zA-Z0-9-*~][a-zA-Z0-9-*._~]*\\/[a-zA-Z0-9-~][a-zA-Z0-9-._~]*|^[a-zA-Z][^:]*)$"
|
||||||
},
|
},
|
||||||
"directory": {
|
"directory": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "A directory where the library is placed.",
|
"description": "A directory where the library is placed.",
|
||||||
"x-priority": "important"
|
"x-priority": "important"
|
||||||
},
|
},
|
||||||
|
"projectNameAndRootFormat": {
|
||||||
|
"description": "Whether to generate the project name and root directory as provided (`as-provided`) or generate them composing their values and taking the configured layout into account (`derived`).",
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["as-provided", "derived"]
|
||||||
|
},
|
||||||
"publishable": {
|
"publishable": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false,
|
"default": false,
|
||||||
|
|||||||
@ -1,22 +1,27 @@
|
|||||||
import {
|
import {
|
||||||
extractLayoutDirectory,
|
|
||||||
formatFiles,
|
formatFiles,
|
||||||
getProjects,
|
getProjects,
|
||||||
runTasksInSerial,
|
runTasksInSerial,
|
||||||
stripIndents,
|
stripIndents,
|
||||||
Tree,
|
Tree,
|
||||||
} from '@nx/devkit';
|
} from '@nx/devkit';
|
||||||
import type { Schema } from './schema';
|
import { determineProjectNameAndRootOptions } from '@nx/devkit/src/generators/project-name-and-root-utils';
|
||||||
import applicationGenerator from '../application/application';
|
|
||||||
import { normalizeProjectName } from '../utils/project';
|
|
||||||
import { setupMf } from '../setup-mf/setup-mf';
|
|
||||||
import { E2eTestRunner } from '../../utils/test-runners';
|
|
||||||
import { addSsr, findNextAvailablePort } from './lib';
|
|
||||||
|
|
||||||
import { getInstalledAngularVersionInfo } from '../utils/version-utils';
|
|
||||||
import { lt } from 'semver';
|
import { lt } from 'semver';
|
||||||
|
import { E2eTestRunner } from '../../utils/test-runners';
|
||||||
|
import { applicationGenerator } from '../application/application';
|
||||||
|
import { setupMf } from '../setup-mf/setup-mf';
|
||||||
|
import { getInstalledAngularVersionInfo } from '../utils/version-utils';
|
||||||
|
import { addSsr, findNextAvailablePort } from './lib';
|
||||||
|
import type { Schema } from './schema';
|
||||||
|
|
||||||
export async function remote(tree: Tree, options: Schema) {
|
export async function remote(tree: Tree, options: Schema) {
|
||||||
|
return await remoteInternal(tree, {
|
||||||
|
projectNameAndRootFormat: 'derived',
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function remoteInternal(tree: Tree, options: Schema) {
|
||||||
const installedAngularVersionInfo = getInstalledAngularVersionInfo(tree);
|
const installedAngularVersionInfo = getInstalledAngularVersionInfo(tree);
|
||||||
|
|
||||||
if (lt(installedAngularVersionInfo.version, '14.1.0') && options.standalone) {
|
if (lt(installedAngularVersionInfo.version, '14.1.0') && options.standalone) {
|
||||||
@ -31,8 +36,15 @@ export async function remote(tree: Tree, options: Schema) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { projectDirectory } = extractLayoutDirectory(options.directory);
|
const { projectName: remoteProjectName, projectNameAndRootFormat } =
|
||||||
const appName = normalizeProjectName(options.name, projectDirectory);
|
await determineProjectNameAndRootOptions(tree, {
|
||||||
|
name: options.name,
|
||||||
|
projectType: 'application',
|
||||||
|
directory: options.directory,
|
||||||
|
projectNameAndRootFormat: options.projectNameAndRootFormat,
|
||||||
|
});
|
||||||
|
options.projectNameAndRootFormat = projectNameAndRootFormat;
|
||||||
|
|
||||||
const port = options.port ?? findNextAvailablePort(tree);
|
const port = options.port ?? findNextAvailablePort(tree);
|
||||||
|
|
||||||
const appInstallTask = await applicationGenerator(tree, {
|
const appInstallTask = await applicationGenerator(tree, {
|
||||||
@ -47,7 +59,7 @@ export async function remote(tree: Tree, options: Schema) {
|
|||||||
!options.e2eTestRunner || options.e2eTestRunner === E2eTestRunner.None;
|
!options.e2eTestRunner || options.e2eTestRunner === E2eTestRunner.None;
|
||||||
|
|
||||||
await setupMf(tree, {
|
await setupMf(tree, {
|
||||||
appName,
|
appName: remoteProjectName,
|
||||||
mfType: 'remote',
|
mfType: 'remote',
|
||||||
routing: true,
|
routing: true,
|
||||||
host: options.host,
|
host: options.host,
|
||||||
@ -55,7 +67,7 @@ export async function remote(tree: Tree, options: Schema) {
|
|||||||
skipPackageJson: options.skipPackageJson,
|
skipPackageJson: options.skipPackageJson,
|
||||||
skipFormat: true,
|
skipFormat: true,
|
||||||
skipE2E,
|
skipE2E,
|
||||||
e2eProjectName: skipE2E ? undefined : `${appName}-e2e`,
|
e2eProjectName: skipE2E ? undefined : `${remoteProjectName}-e2e`,
|
||||||
standalone: options.standalone,
|
standalone: options.standalone,
|
||||||
prefix: options.prefix,
|
prefix: options.prefix,
|
||||||
});
|
});
|
||||||
@ -63,7 +75,7 @@ export async function remote(tree: Tree, options: Schema) {
|
|||||||
let installTasks = [appInstallTask];
|
let installTasks = [appInstallTask];
|
||||||
if (options.ssr) {
|
if (options.ssr) {
|
||||||
let ssrInstallTask = await addSsr(tree, {
|
let ssrInstallTask = await addSsr(tree, {
|
||||||
appName,
|
appName: remoteProjectName,
|
||||||
port,
|
port,
|
||||||
standalone: options.standalone,
|
standalone: options.standalone,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { Linter } from '@nx/linter';
|
import type { ProjectNameAndRootFormat } from '@nx/devkit/src/generators/project-name-and-root-utils';
|
||||||
import { E2eTestRunner, UnitTestRunner } from '../../utils/test-runners';
|
import type { Linter } from '@nx/linter';
|
||||||
|
import type { E2eTestRunner, UnitTestRunner } from '../../utils/test-runners';
|
||||||
import type { Styles } from '../utils/types';
|
import type { Styles } from '../utils/types';
|
||||||
|
|
||||||
export interface Schema {
|
export interface Schema {
|
||||||
@ -13,6 +14,7 @@ export interface Schema {
|
|||||||
style?: Styles;
|
style?: Styles;
|
||||||
skipTests?: boolean;
|
skipTests?: boolean;
|
||||||
directory?: string;
|
directory?: string;
|
||||||
|
projectNameAndRootFormat?: ProjectNameAndRootFormat;
|
||||||
tags?: string;
|
tags?: string;
|
||||||
linter?: Linter;
|
linter?: Linter;
|
||||||
unitTestRunner?: UnitTestRunner;
|
unitTestRunner?: UnitTestRunner;
|
||||||
|
|||||||
@ -19,7 +19,7 @@
|
|||||||
"$source": "argv",
|
"$source": "argv",
|
||||||
"index": 0
|
"index": 0
|
||||||
},
|
},
|
||||||
"pattern": "^[a-zA-Z].*$"
|
"pattern": "^[a-zA-Z][^:]*$"
|
||||||
},
|
},
|
||||||
"host": {
|
"host": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@ -35,6 +35,11 @@
|
|||||||
"description": "The directory of the new application.",
|
"description": "The directory of the new application.",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"projectNameAndRootFormat": {
|
||||||
|
"description": "Whether to generate the project name and root directory as provided (`as-provided`) or generate them composing their values and taking the configured layout into account (`derived`).",
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["as-provided", "derived"]
|
||||||
|
},
|
||||||
"style": {
|
"style": {
|
||||||
"description": "The file extension to be used for style files.",
|
"description": "The file extension to be used for style files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
@ -50,6 +50,7 @@ describe('determineProjectNameAndRootOptions', () => {
|
|||||||
},
|
},
|
||||||
importPath: '@proj/lib-name',
|
importPath: '@proj/lib-name',
|
||||||
projectRoot: 'shared',
|
projectRoot: 'shared',
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -69,6 +70,7 @@ describe('determineProjectNameAndRootOptions', () => {
|
|||||||
},
|
},
|
||||||
importPath: '@scope/lib-name',
|
importPath: '@scope/lib-name',
|
||||||
projectRoot: 'shared',
|
projectRoot: 'shared',
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -89,6 +91,7 @@ describe('determineProjectNameAndRootOptions', () => {
|
|||||||
},
|
},
|
||||||
importPath: '@custom-scope/lib-name',
|
importPath: '@custom-scope/lib-name',
|
||||||
projectRoot: 'shared',
|
projectRoot: 'shared',
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -111,6 +114,7 @@ describe('determineProjectNameAndRootOptions', () => {
|
|||||||
},
|
},
|
||||||
importPath: '@scope/lib-name',
|
importPath: '@scope/lib-name',
|
||||||
projectRoot: '@scope/lib-name',
|
projectRoot: '@scope/lib-name',
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -135,6 +139,7 @@ describe('determineProjectNameAndRootOptions', () => {
|
|||||||
},
|
},
|
||||||
importPath: 'lib-name',
|
importPath: 'lib-name',
|
||||||
projectRoot: '.',
|
projectRoot: '.',
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -181,6 +186,7 @@ describe('determineProjectNameAndRootOptions', () => {
|
|||||||
},
|
},
|
||||||
importPath: '@proj/shared/lib-name',
|
importPath: '@proj/shared/lib-name',
|
||||||
projectRoot: 'shared/lib-name',
|
projectRoot: 'shared/lib-name',
|
||||||
|
projectNameAndRootFormat: 'derived',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -215,6 +221,7 @@ describe('determineProjectNameAndRootOptions', () => {
|
|||||||
},
|
},
|
||||||
importPath: 'lib-name',
|
importPath: 'lib-name',
|
||||||
projectRoot: '.',
|
projectRoot: '.',
|
||||||
|
projectNameAndRootFormat: 'derived',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -288,6 +295,7 @@ describe('determineProjectNameAndRootOptions', () => {
|
|||||||
},
|
},
|
||||||
importPath: '@scope/lib-name',
|
importPath: '@scope/lib-name',
|
||||||
projectRoot: 'shared',
|
projectRoot: 'shared',
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
});
|
});
|
||||||
|
|
||||||
// restore original interactive mode
|
// restore original interactive mode
|
||||||
@ -317,6 +325,7 @@ describe('determineProjectNameAndRootOptions', () => {
|
|||||||
},
|
},
|
||||||
importPath: '@proj/lib-name',
|
importPath: '@proj/lib-name',
|
||||||
projectRoot: 'shared',
|
projectRoot: 'shared',
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -336,6 +345,7 @@ describe('determineProjectNameAndRootOptions', () => {
|
|||||||
},
|
},
|
||||||
importPath: '@scope/lib-name',
|
importPath: '@scope/lib-name',
|
||||||
projectRoot: 'shared',
|
projectRoot: 'shared',
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -356,6 +366,7 @@ describe('determineProjectNameAndRootOptions', () => {
|
|||||||
},
|
},
|
||||||
importPath: '@custom-scope/lib-name',
|
importPath: '@custom-scope/lib-name',
|
||||||
projectRoot: 'shared',
|
projectRoot: 'shared',
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -379,6 +390,7 @@ describe('determineProjectNameAndRootOptions', () => {
|
|||||||
},
|
},
|
||||||
importPath: '@scope/lib-name',
|
importPath: '@scope/lib-name',
|
||||||
projectRoot: '@scope/lib-name',
|
projectRoot: '@scope/lib-name',
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -403,6 +415,7 @@ describe('determineProjectNameAndRootOptions', () => {
|
|||||||
},
|
},
|
||||||
importPath: 'lib-name',
|
importPath: 'lib-name',
|
||||||
projectRoot: '.',
|
projectRoot: '.',
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -449,6 +462,7 @@ describe('determineProjectNameAndRootOptions', () => {
|
|||||||
},
|
},
|
||||||
importPath: '@proj/shared/lib-name',
|
importPath: '@proj/shared/lib-name',
|
||||||
projectRoot: 'libs/shared/lib-name',
|
projectRoot: 'libs/shared/lib-name',
|
||||||
|
projectNameAndRootFormat: 'derived',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -484,6 +498,7 @@ describe('determineProjectNameAndRootOptions', () => {
|
|||||||
},
|
},
|
||||||
importPath: 'lib-name',
|
importPath: 'lib-name',
|
||||||
projectRoot: '.',
|
projectRoot: '.',
|
||||||
|
projectNameAndRootFormat: 'derived',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -557,6 +572,7 @@ describe('determineProjectNameAndRootOptions', () => {
|
|||||||
},
|
},
|
||||||
importPath: '@scope/lib-name',
|
importPath: '@scope/lib-name',
|
||||||
projectRoot: 'shared',
|
projectRoot: 'shared',
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
});
|
});
|
||||||
|
|
||||||
// restore original interactive mode
|
// restore original interactive mode
|
||||||
|
|||||||
@ -57,13 +57,20 @@ type ProjectNameAndRootFormats = {
|
|||||||
export async function determineProjectNameAndRootOptions(
|
export async function determineProjectNameAndRootOptions(
|
||||||
tree: Tree,
|
tree: Tree,
|
||||||
options: ProjectGenerationOptions
|
options: ProjectGenerationOptions
|
||||||
): Promise<ProjectNameAndRootOptions> {
|
): Promise<
|
||||||
|
ProjectNameAndRootOptions & {
|
||||||
|
projectNameAndRootFormat: ProjectNameAndRootFormat;
|
||||||
|
}
|
||||||
|
> {
|
||||||
validateName(options.name, options.projectNameAndRootFormat);
|
validateName(options.name, options.projectNameAndRootFormat);
|
||||||
const formats = getProjectNameAndRootFormats(tree, options);
|
const formats = getProjectNameAndRootFormats(tree, options);
|
||||||
const format =
|
const format =
|
||||||
options.projectNameAndRootFormat ?? (await determineFormat(formats));
|
options.projectNameAndRootFormat ?? (await determineFormat(formats));
|
||||||
|
|
||||||
return formats[format];
|
return {
|
||||||
|
...formats[format],
|
||||||
|
projectNameAndRootFormat: format,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateName(
|
function validateName(
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user