feat(react): use helper to determine project name and root directory in project generators (#18615)
This commit is contained in:
parent
71d2994be9
commit
eb9caa1ade
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "application",
|
"name": "application",
|
||||||
"factory": "./src/generators/application/application#applicationGenerator",
|
"factory": "./src/generators/application/application#applicationGeneratorInternal",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$schema": "http://json-schema.org/schema",
|
"$schema": "http://json-schema.org/schema",
|
||||||
"cli": "nx",
|
"cli": "nx",
|
||||||
@ -28,7 +28,7 @@
|
|||||||
"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.",
|
||||||
@ -36,6 +36,11 @@
|
|||||||
"alias": "dir",
|
"alias": "dir",
|
||||||
"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",
|
||||||
@ -192,7 +197,7 @@
|
|||||||
"aliases": ["app"],
|
"aliases": ["app"],
|
||||||
"x-type": "application",
|
"x-type": "application",
|
||||||
"description": "Create a React application.",
|
"description": "Create a React application.",
|
||||||
"implementation": "/packages/react/src/generators/application/application#applicationGenerator.ts",
|
"implementation": "/packages/react/src/generators/application/application#applicationGeneratorInternal.ts",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"path": "/packages/react/src/generators/application/schema.json",
|
"path": "/packages/react/src/generators/application/schema.json",
|
||||||
"type": "generator"
|
"type": "generator"
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "host",
|
"name": "host",
|
||||||
"factory": "./src/generators/host/host#hostGenerator",
|
"factory": "./src/generators/host/host#hostGeneratorInternal",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$schema": "http://json-schema.org/schema",
|
"$schema": "http://json-schema.org/schema",
|
||||||
"$id": "GeneratorReactHost",
|
"$id": "GeneratorReactHost",
|
||||||
@ -14,7 +14,7 @@
|
|||||||
"description": "The name of the host application to generate the Module Federation configuration",
|
"description": "The name of the host application to generate the Module Federation configuration",
|
||||||
"$default": { "$source": "argv", "index": 0 },
|
"$default": { "$source": "argv", "index": 0 },
|
||||||
"x-prompt": "What name would you like to use as the host application?",
|
"x-prompt": "What name would you like to use as the host application?",
|
||||||
"pattern": "^[a-zA-Z].*$",
|
"pattern": "^[a-zA-Z][^:]*$",
|
||||||
"x-priority": "important"
|
"x-priority": "important"
|
||||||
},
|
},
|
||||||
"directory": {
|
"directory": {
|
||||||
@ -23,6 +23,11 @@
|
|||||||
"alias": "dir",
|
"alias": "dir",
|
||||||
"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",
|
||||||
@ -163,7 +168,7 @@
|
|||||||
},
|
},
|
||||||
"x-type": "application",
|
"x-type": "application",
|
||||||
"description": "Generate a host react application",
|
"description": "Generate a host react application",
|
||||||
"implementation": "/packages/react/src/generators/host/host#hostGenerator.ts",
|
"implementation": "/packages/react/src/generators/host/host#hostGeneratorInternal.ts",
|
||||||
"aliases": [],
|
"aliases": [],
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"path": "/packages/react/src/generators/host/schema.json",
|
"path": "/packages/react/src/generators/host/schema.json",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "library",
|
"name": "library",
|
||||||
"factory": "./src/generators/library/library#libraryGenerator",
|
"factory": "./src/generators/library/library#libraryGeneratorInternal",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$schema": "http://json-schema.org/schema",
|
"$schema": "http://json-schema.org/schema",
|
||||||
"cli": "nx",
|
"cli": "nx",
|
||||||
@ -24,7 +24,7 @@
|
|||||||
"description": "Library name",
|
"description": "Library name",
|
||||||
"$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][^:]*)$",
|
||||||
"x-priority": "important"
|
"x-priority": "important"
|
||||||
},
|
},
|
||||||
"directory": {
|
"directory": {
|
||||||
@ -33,6 +33,11 @@
|
|||||||
"alias": "dir",
|
"alias": "dir",
|
||||||
"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",
|
||||||
@ -196,7 +201,7 @@
|
|||||||
"aliases": ["lib"],
|
"aliases": ["lib"],
|
||||||
"x-type": "library",
|
"x-type": "library",
|
||||||
"description": "Create a React library.",
|
"description": "Create a React library.",
|
||||||
"implementation": "/packages/react/src/generators/library/library#libraryGenerator.ts",
|
"implementation": "/packages/react/src/generators/library/library#libraryGeneratorInternal.ts",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"path": "/packages/react/src/generators/library/schema.json",
|
"path": "/packages/react/src/generators/library/schema.json",
|
||||||
"type": "generator"
|
"type": "generator"
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "remote",
|
"name": "remote",
|
||||||
"factory": "./src/generators/remote/remote#remoteGenerator",
|
"factory": "./src/generators/remote/remote#remoteGeneratorInternal",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$schema": "http://json-schema.org/schema",
|
"$schema": "http://json-schema.org/schema",
|
||||||
"$id": "GeneratorReactRemote",
|
"$id": "GeneratorReactRemote",
|
||||||
@ -14,7 +14,7 @@
|
|||||||
"description": "The name of the remote application to generate the Module Federation configuration",
|
"description": "The name of the remote application to generate the Module Federation configuration",
|
||||||
"$default": { "$source": "argv", "index": 0 },
|
"$default": { "$source": "argv", "index": 0 },
|
||||||
"x-prompt": "What name would you like to use as the remote application?",
|
"x-prompt": "What name would you like to use as the remote application?",
|
||||||
"pattern": "^[a-zA-Z].*$",
|
"pattern": "^[a-zA-Z][^:]*$",
|
||||||
"x-priority": "important"
|
"x-priority": "important"
|
||||||
},
|
},
|
||||||
"directory": {
|
"directory": {
|
||||||
@ -23,6 +23,11 @@
|
|||||||
"alias": "dir",
|
"alias": "dir",
|
||||||
"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",
|
||||||
@ -162,7 +167,7 @@
|
|||||||
},
|
},
|
||||||
"x-type": "application",
|
"x-type": "application",
|
||||||
"description": "Generate a remote react application",
|
"description": "Generate a remote react application",
|
||||||
"implementation": "/packages/react/src/generators/remote/remote#remoteGenerator.ts",
|
"implementation": "/packages/react/src/generators/remote/remote#remoteGeneratorInternal.ts",
|
||||||
"aliases": [],
|
"aliases": [],
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"path": "/packages/react/src/generators/remote/schema.json",
|
"path": "/packages/react/src/generators/remote/schema.json",
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import { stripIndents } from '@nx/devkit';
|
|||||||
import {
|
import {
|
||||||
checkFilesExist,
|
checkFilesExist,
|
||||||
cleanupProject,
|
cleanupProject,
|
||||||
killPort,
|
|
||||||
newProject,
|
newProject,
|
||||||
readProjectConfig,
|
readProjectConfig,
|
||||||
runCLI,
|
runCLI,
|
||||||
@ -114,12 +113,29 @@ describe('React Module Federation', () => {
|
|||||||
// }
|
// }
|
||||||
}, 500_000);
|
}, 500_000);
|
||||||
|
|
||||||
|
it('should should support generating host and remote apps with the new name and root format', async () => {
|
||||||
|
const shell = uniq('shell');
|
||||||
|
const remote = uniq('remote');
|
||||||
|
|
||||||
|
runCLI(
|
||||||
|
`generate @nx/react:host ${shell} --project-name-and-root-format=as-provided --no-interactive`
|
||||||
|
);
|
||||||
|
runCLI(
|
||||||
|
`generate @nx/react:remote ${remote} --host=${shell} --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(`${shell}/module-federation.config.js`);
|
||||||
|
checkFilesExist(`${remote}/module-federation.config.js`);
|
||||||
|
|
||||||
|
// check default generated host is built successfully
|
||||||
|
const buildOutput = runCLI(`run ${shell}:build:development`);
|
||||||
|
expect(buildOutput).toContain('Successfully ran target build');
|
||||||
|
}, 500_000);
|
||||||
|
|
||||||
async function readPort(appName: string): Promise<number> {
|
async function readPort(appName: string): Promise<number> {
|
||||||
const config = await readProjectConfig(appName);
|
const config = await readProjectConfig(appName);
|
||||||
return config.targets.serve.options.port;
|
return config.targets.serve.options.port;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function killPorts(ports: number[]): Promise<boolean[]> {
|
|
||||||
return Promise.all(ports.map((p) => killPort(p)));
|
|
||||||
}
|
|
||||||
|
|||||||
@ -227,6 +227,52 @@ describe('React Applications', () => {
|
|||||||
);
|
);
|
||||||
}, 250_000);
|
}, 250_000);
|
||||||
|
|
||||||
|
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/react:app ${appName} --bundler=webpack --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/main.tsx`);
|
||||||
|
// 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/react:lib ${libName} --unit-test-runner=jest --buildable --project-name-and-root-format=derived --no-interactive`
|
||||||
|
)
|
||||||
|
).toThrow();
|
||||||
|
|
||||||
|
runCLI(
|
||||||
|
`generate @nx/react:lib ${libName} --unit-test-runner=jest --buildable --project-name-and-root-format=as-provided --no-interactive`
|
||||||
|
);
|
||||||
|
|
||||||
|
// 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`);
|
||||||
|
// 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);
|
||||||
|
|
||||||
describe('React Applications: --style option', () => {
|
describe('React Applications: --style option', () => {
|
||||||
it.each`
|
it.each`
|
||||||
style
|
style
|
||||||
|
|||||||
@ -56,6 +56,27 @@ describe('determineProjectNameAndRootOptions', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it(`should handle window's style paths correctly when format is "as-provided"`, async () => {
|
||||||
|
const result = await determineProjectNameAndRootOptions(tree, {
|
||||||
|
name: 'libName',
|
||||||
|
directory: 'shared\\libName',
|
||||||
|
projectType: 'library',
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
callingGenerator: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toStrictEqual({
|
||||||
|
projectName: 'lib-name',
|
||||||
|
names: {
|
||||||
|
projectSimpleName: 'lib-name',
|
||||||
|
projectFileName: 'lib-name',
|
||||||
|
},
|
||||||
|
importPath: '@proj/lib-name',
|
||||||
|
projectRoot: 'shared/lib-name',
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should use a scoped package name as the project name and import path when format is "as-provided"', async () => {
|
it('should use a scoped package name as the project name and import path when format is "as-provided"', async () => {
|
||||||
const result = await determineProjectNameAndRootOptions(tree, {
|
const result = await determineProjectNameAndRootOptions(tree, {
|
||||||
name: '@scope/libName',
|
name: '@scope/libName',
|
||||||
@ -253,6 +274,27 @@ describe('determineProjectNameAndRootOptions', () => {
|
|||||||
expect(result.importPath).toBe('@proj/lib-name');
|
expect(result.importPath).toBe('@proj/lib-name');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it(`should handle window's style paths correctly when format is "derived"`, async () => {
|
||||||
|
const result = await determineProjectNameAndRootOptions(tree, {
|
||||||
|
name: 'libName',
|
||||||
|
directory: 'shared\\sub-dir',
|
||||||
|
projectType: 'library',
|
||||||
|
projectNameAndRootFormat: 'derived',
|
||||||
|
callingGenerator: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toStrictEqual({
|
||||||
|
projectName: 'shared-sub-dir-lib-name',
|
||||||
|
names: {
|
||||||
|
projectSimpleName: 'lib-name',
|
||||||
|
projectFileName: 'shared-sub-dir-lib-name',
|
||||||
|
},
|
||||||
|
importPath: '@proj/shared/sub-dir/lib-name',
|
||||||
|
projectRoot: 'shared/sub-dir/lib-name',
|
||||||
|
projectNameAndRootFormat: 'derived',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should prompt for the project name and root format', async () => {
|
it('should prompt for the project name and root format', async () => {
|
||||||
// simulate interactive mode
|
// simulate interactive mode
|
||||||
ensureInteractiveMode();
|
ensureInteractiveMode();
|
||||||
@ -370,6 +412,27 @@ describe('determineProjectNameAndRootOptions', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it(`should handle window's style paths correctly when format is "as-provided"`, async () => {
|
||||||
|
const result = await determineProjectNameAndRootOptions(tree, {
|
||||||
|
name: 'libName',
|
||||||
|
directory: 'shared\\libName',
|
||||||
|
projectType: 'library',
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
callingGenerator: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toStrictEqual({
|
||||||
|
projectName: 'lib-name',
|
||||||
|
names: {
|
||||||
|
projectSimpleName: 'lib-name',
|
||||||
|
projectFileName: 'lib-name',
|
||||||
|
},
|
||||||
|
importPath: '@proj/lib-name',
|
||||||
|
projectRoot: 'shared/lib-name',
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should use a scoped package name as the project name and import path when format is "as-provided"', async () => {
|
it('should use a scoped package name as the project name and import path when format is "as-provided"', async () => {
|
||||||
const result = await determineProjectNameAndRootOptions(tree, {
|
const result = await determineProjectNameAndRootOptions(tree, {
|
||||||
name: '@scope/libName',
|
name: '@scope/libName',
|
||||||
@ -514,6 +577,27 @@ describe('determineProjectNameAndRootOptions', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it(`should handle window's style paths correctly when format is "derived"`, async () => {
|
||||||
|
const result = await determineProjectNameAndRootOptions(tree, {
|
||||||
|
name: 'libName',
|
||||||
|
directory: 'shared\\sub-dir',
|
||||||
|
projectType: 'library',
|
||||||
|
projectNameAndRootFormat: 'derived',
|
||||||
|
callingGenerator: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toStrictEqual({
|
||||||
|
projectName: 'shared-sub-dir-lib-name',
|
||||||
|
names: {
|
||||||
|
projectSimpleName: 'lib-name',
|
||||||
|
projectFileName: 'shared-sub-dir-lib-name',
|
||||||
|
},
|
||||||
|
importPath: '@proj/shared/sub-dir/lib-name',
|
||||||
|
projectRoot: 'libs/shared/sub-dir/lib-name',
|
||||||
|
projectNameAndRootFormat: 'derived',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should throw when using a scoped package name as the project name and format is derived', async () => {
|
it('should throw when using a scoped package name as the project name and format is derived', async () => {
|
||||||
await expect(
|
await expect(
|
||||||
determineProjectNameAndRootOptions(tree, {
|
determineProjectNameAndRootOptions(tree, {
|
||||||
|
|||||||
@ -8,7 +8,8 @@ import {
|
|||||||
} from '../utils/get-workspace-layout';
|
} from '../utils/get-workspace-layout';
|
||||||
import { names } from '../utils/names';
|
import { names } from '../utils/names';
|
||||||
|
|
||||||
const { joinPathFragments, readJson, readNxJson, updateNxJson } = requireNx();
|
const { joinPathFragments, normalizePath, readJson, readNxJson, updateNxJson } =
|
||||||
|
requireNx();
|
||||||
|
|
||||||
export type ProjectNameAndRootFormat = 'as-provided' | 'derived';
|
export type ProjectNameAndRootFormat = 'as-provided' | 'derived';
|
||||||
export type ProjectGenerationOptions = {
|
export type ProjectGenerationOptions = {
|
||||||
@ -169,7 +170,9 @@ function getProjectNameAndRootFormats(
|
|||||||
options: ProjectGenerationOptions
|
options: ProjectGenerationOptions
|
||||||
): ProjectNameAndRootFormats {
|
): ProjectNameAndRootFormats {
|
||||||
const name = names(options.name).fileName;
|
const name = names(options.name).fileName;
|
||||||
const directory = options.directory?.replace(/^\.?\//, '');
|
const directory = options.directory
|
||||||
|
? normalizePath(options.directory.replace(/^\.?\//, ''))
|
||||||
|
: undefined;
|
||||||
|
|
||||||
const asProvidedProjectName = name;
|
const asProvidedProjectName = name;
|
||||||
const asProvidedProjectDirectory = directory
|
const asProvidedProjectDirectory = directory
|
||||||
|
|||||||
@ -114,7 +114,7 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
"application": {
|
"application": {
|
||||||
"factory": "./src/generators/application/application#applicationGenerator",
|
"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",
|
||||||
@ -122,7 +122,7 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
"library": {
|
"library": {
|
||||||
"factory": "./src/generators/library/library#libraryGenerator",
|
"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",
|
||||||
@ -179,14 +179,14 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
"host": {
|
"host": {
|
||||||
"factory": "./src/generators/host/host#hostGenerator",
|
"factory": "./src/generators/host/host#hostGeneratorInternal",
|
||||||
"schema": "./src/generators/host/schema.json",
|
"schema": "./src/generators/host/schema.json",
|
||||||
"x-type": "application",
|
"x-type": "application",
|
||||||
"description": "Generate a host react application"
|
"description": "Generate a host react application"
|
||||||
},
|
},
|
||||||
|
|
||||||
"remote": {
|
"remote": {
|
||||||
"factory": "./src/generators/remote/remote#remoteGenerator",
|
"factory": "./src/generators/remote/remote#remoteGeneratorInternal",
|
||||||
"schema": "./src/generators/remote/schema.json",
|
"schema": "./src/generators/remote/schema.json",
|
||||||
"x-type": "application",
|
"x-type": "application",
|
||||||
"description": "Generate a remote react application"
|
"description": "Generate a remote react application"
|
||||||
|
|||||||
@ -284,7 +284,8 @@ describe('app', () => {
|
|||||||
appTree.exists('apps/my-dir/my-app-e2e/src/example.spec.ts')
|
appTree.exists('apps/my-dir/my-app-e2e/src/example.spec.ts')
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
expect(
|
expect(
|
||||||
readProjectConfiguration(appTree, 'my-app-e2e')?.targets?.e2e?.executor
|
readProjectConfiguration(appTree, 'my-dir-my-app-e2e')?.targets?.e2e
|
||||||
|
?.executor
|
||||||
).toEqual('@nx/playwright:playwright');
|
).toEqual('@nx/playwright:playwright');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -85,10 +85,20 @@ async function addLinting(host: Tree, options: NormalizedSchema) {
|
|||||||
export async function applicationGenerator(
|
export async function applicationGenerator(
|
||||||
host: Tree,
|
host: Tree,
|
||||||
schema: Schema
|
schema: Schema
|
||||||
|
): Promise<GeneratorCallback> {
|
||||||
|
return await applicationGeneratorInternal(host, {
|
||||||
|
projectNameAndRootFormat: 'derived',
|
||||||
|
...schema,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function applicationGeneratorInternal(
|
||||||
|
host: Tree,
|
||||||
|
schema: Schema
|
||||||
): Promise<GeneratorCallback> {
|
): Promise<GeneratorCallback> {
|
||||||
const tasks = [];
|
const tasks = [];
|
||||||
|
|
||||||
const options = normalizeOptions(host, schema);
|
const options = await normalizeOptions(host, schema);
|
||||||
showPossibleWarnings(host, options);
|
showPossibleWarnings(host, options);
|
||||||
|
|
||||||
const initTask = await reactInitGenerator(host, {
|
const initTask = await reactInitGenerator(host, {
|
||||||
|
|||||||
@ -29,7 +29,9 @@ export async function addE2e(
|
|||||||
return await cypressProjectGenerator(tree, {
|
return await cypressProjectGenerator(tree, {
|
||||||
...options,
|
...options,
|
||||||
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.projectName,
|
project: options.projectName,
|
||||||
bundler: options.bundler === 'rspack' ? 'webpack' : options.bundler,
|
bundler: options.bundler === 'rspack' ? 'webpack' : options.bundler,
|
||||||
skipFormat: true,
|
skipFormat: true,
|
||||||
|
|||||||
@ -1,13 +1,7 @@
|
|||||||
import { NormalizedSchema, Schema } from '../schema';
|
import { Tree, extractLayoutDirectory, names } from '@nx/devkit';
|
||||||
|
import { determineProjectNameAndRootOptions } from '@nx/devkit/src/generators/project-name-and-root-utils';
|
||||||
import { assertValidStyle } from '../../../utils/assertion';
|
import { assertValidStyle } from '../../../utils/assertion';
|
||||||
import {
|
import { NormalizedSchema, Schema } from '../schema';
|
||||||
extractLayoutDirectory,
|
|
||||||
getWorkspaceLayout,
|
|
||||||
joinPathFragments,
|
|
||||||
names,
|
|
||||||
normalizePath,
|
|
||||||
Tree,
|
|
||||||
} from '@nx/devkit';
|
|
||||||
import { findFreePort } from './find-free-port';
|
import { findFreePort } from './find-free-port';
|
||||||
|
|
||||||
export function normalizeDirectory(options: Schema) {
|
export function normalizeDirectory(options: Schema) {
|
||||||
@ -22,24 +16,40 @@ export function normalizeProjectName(options: Schema) {
|
|||||||
return normalizeDirectory(options).replace(new RegExp('/', 'g'), '-');
|
return normalizeDirectory(options).replace(new RegExp('/', 'g'), '-');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function normalizeOptions<T extends Schema = Schema>(
|
export async function normalizeOptions<T extends Schema = Schema>(
|
||||||
host: Tree,
|
host: Tree,
|
||||||
options: Schema
|
options: Schema,
|
||||||
): NormalizedSchema<T> {
|
callingGenerator = '@nx/react:application'
|
||||||
const appDirectory = normalizeDirectory(options);
|
): Promise<NormalizedSchema<T>> {
|
||||||
const appProjectName = normalizeProjectName(options);
|
const {
|
||||||
const e2eProjectName = options.rootProject
|
projectName: appProjectName,
|
||||||
? 'e2e'
|
projectRoot: appProjectRoot,
|
||||||
: `${names(options.name).fileName}-e2e`;
|
projectNameAndRootFormat,
|
||||||
|
} = await determineProjectNameAndRootOptions(host, {
|
||||||
|
name: options.name,
|
||||||
|
projectType: 'application',
|
||||||
|
directory: options.directory,
|
||||||
|
projectNameAndRootFormat: options.projectNameAndRootFormat,
|
||||||
|
rootProject: options.rootProject,
|
||||||
|
callingGenerator,
|
||||||
|
});
|
||||||
|
options.rootProject = appProjectRoot === '.';
|
||||||
|
options.projectNameAndRootFormat = projectNameAndRootFormat;
|
||||||
|
|
||||||
const { layoutDirectory } = extractLayoutDirectory(options.directory);
|
let e2eProjectName = 'e2e';
|
||||||
const appsDir = layoutDirectory ?? getWorkspaceLayout(host).appsDir;
|
let e2eProjectRoot = 'e2e';
|
||||||
const appProjectRoot = options.rootProject
|
if (!options.rootProject) {
|
||||||
? '.'
|
const projectNameAndRoot = await determineProjectNameAndRootOptions(host, {
|
||||||
: normalizePath(`${appsDir}/${appDirectory}`);
|
name: `${options.name}-e2e`,
|
||||||
const e2eProjectRoot = options.rootProject
|
projectType: 'application',
|
||||||
? 'e2e'
|
directory: options.directory,
|
||||||
: joinPathFragments(appsDir, `${appDirectory}-e2e`);
|
projectNameAndRootFormat: options.projectNameAndRootFormat,
|
||||||
|
rootProject: options.rootProject,
|
||||||
|
callingGenerator,
|
||||||
|
});
|
||||||
|
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())
|
||||||
|
|||||||
@ -1,11 +1,13 @@
|
|||||||
import { Linter } from '@nx/linter';
|
import type { ProjectNameAndRootFormat } from '@nx/devkit/src/generators/project-name-and-root-utils';
|
||||||
import { SupportedStyles } from '../../../typings/style';
|
import type { Linter } from '@nx/linter';
|
||||||
|
import type { SupportedStyles } from '../../../typings/style';
|
||||||
|
|
||||||
export interface Schema {
|
export interface Schema {
|
||||||
name: string;
|
name: string;
|
||||||
style: SupportedStyles;
|
style: SupportedStyles;
|
||||||
skipFormat?: boolean;
|
skipFormat?: boolean;
|
||||||
directory?: string;
|
directory?: string;
|
||||||
|
projectNameAndRootFormat?: ProjectNameAndRootFormat;
|
||||||
tags?: string;
|
tags?: string;
|
||||||
unitTestRunner?: 'jest' | 'vitest' | 'none';
|
unitTestRunner?: 'jest' | 'vitest' | 'none';
|
||||||
inSourceTests?: boolean;
|
inSourceTests?: boolean;
|
||||||
|
|||||||
@ -28,7 +28,7 @@
|
|||||||
"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.",
|
||||||
@ -36,6 +36,11 @@
|
|||||||
"alias": "dir",
|
"alias": "dir",
|
||||||
"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",
|
||||||
|
|||||||
@ -21,8 +21,19 @@ import setupSsrGenerator from '../setup-ssr/setup-ssr';
|
|||||||
import { setupSsrForHost } from './lib/setup-ssr-for-host';
|
import { setupSsrForHost } from './lib/setup-ssr-for-host';
|
||||||
|
|
||||||
export async function hostGenerator(host: Tree, schema: Schema) {
|
export async function hostGenerator(host: Tree, schema: Schema) {
|
||||||
|
return hostGeneratorInternal(host, {
|
||||||
|
projectNameAndRootFormat: 'derived',
|
||||||
|
...schema,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function hostGeneratorInternal(host: Tree, schema: Schema) {
|
||||||
const tasks: GeneratorCallback[] = [];
|
const tasks: GeneratorCallback[] = [];
|
||||||
const options = normalizeOptions<Schema>(host, schema);
|
const options = await normalizeOptions<Schema>(
|
||||||
|
host,
|
||||||
|
schema,
|
||||||
|
'@nx/react:host'
|
||||||
|
);
|
||||||
|
|
||||||
const initTask = await applicationGenerator(host, {
|
const initTask = await applicationGenerator(host, {
|
||||||
...options,
|
...options,
|
||||||
|
|||||||
@ -1,11 +1,13 @@
|
|||||||
import { Linter } from '@nx/linter';
|
import type { ProjectNameAndRootFormat } from '@nx/devkit/src/generators/project-name-and-root-utils';
|
||||||
import { SupportedStyles } from '../../../typings';
|
import type { Linter } from '@nx/linter';
|
||||||
|
import type { SupportedStyles } from '../../../typings';
|
||||||
|
|
||||||
export interface Schema {
|
export interface Schema {
|
||||||
classComponent?: boolean;
|
classComponent?: boolean;
|
||||||
compiler?: 'babel' | 'swc';
|
compiler?: 'babel' | 'swc';
|
||||||
devServerPort?: number;
|
devServerPort?: number;
|
||||||
directory?: string;
|
directory?: string;
|
||||||
|
projectNameAndRootFormat?: ProjectNameAndRootFormat;
|
||||||
e2eTestRunner: 'cypress' | 'none';
|
e2eTestRunner: 'cypress' | 'none';
|
||||||
globalCss?: boolean;
|
globalCss?: boolean;
|
||||||
js?: boolean;
|
js?: boolean;
|
||||||
|
|||||||
@ -14,7 +14,7 @@
|
|||||||
"index": 0
|
"index": 0
|
||||||
},
|
},
|
||||||
"x-prompt": "What name would you like to use as the host application?",
|
"x-prompt": "What name would you like to use as the host application?",
|
||||||
"pattern": "^[a-zA-Z].*$",
|
"pattern": "^[a-zA-Z][^:]*$",
|
||||||
"x-priority": "important"
|
"x-priority": "important"
|
||||||
},
|
},
|
||||||
"directory": {
|
"directory": {
|
||||||
@ -23,6 +23,11 @@
|
|||||||
"alias": "dir",
|
"alias": "dir",
|
||||||
"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",
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import { Tree } from 'nx/src/generators/tree';
|
|||||||
import {
|
import {
|
||||||
addDependenciesToPackageJson,
|
addDependenciesToPackageJson,
|
||||||
ensurePackage,
|
ensurePackage,
|
||||||
getWorkspaceLayout,
|
|
||||||
joinPathFragments,
|
joinPathFragments,
|
||||||
readProjectConfiguration,
|
readProjectConfiguration,
|
||||||
updateProjectConfiguration,
|
updateProjectConfiguration,
|
||||||
@ -37,7 +36,6 @@ export async function addRollupBuildTarget(
|
|||||||
|
|
||||||
const { targets } = readProjectConfiguration(host, options.name);
|
const { targets } = readProjectConfiguration(host, options.name);
|
||||||
|
|
||||||
const { libsDir } = getWorkspaceLayout(host);
|
|
||||||
const external: string[] = ['react', 'react-dom'];
|
const external: string[] = ['react', 'react-dom'];
|
||||||
|
|
||||||
if (options.style === '@emotion/styled') {
|
if (options.style === '@emotion/styled') {
|
||||||
@ -50,10 +48,7 @@ export async function addRollupBuildTarget(
|
|||||||
executor: '@nx/rollup:rollup',
|
executor: '@nx/rollup:rollup',
|
||||||
outputs: ['{options.outputPath}'],
|
outputs: ['{options.outputPath}'],
|
||||||
options: {
|
options: {
|
||||||
outputPath:
|
outputPath: joinPathFragments('dist', options.projectRoot),
|
||||||
libsDir !== '.'
|
|
||||||
? `dist/${libsDir}/${options.projectDirectory}`
|
|
||||||
: `dist/${options.projectDirectory}`,
|
|
||||||
tsConfig: `${options.projectRoot}/tsconfig.lib.json`,
|
tsConfig: `${options.projectRoot}/tsconfig.lib.json`,
|
||||||
project: `${options.projectRoot}/package.json`,
|
project: `${options.projectRoot}/package.json`,
|
||||||
entryFile: maybeJs(options, `${options.projectRoot}/src/index.ts`),
|
entryFile: maybeJs(options, `${options.projectRoot}/src/index.ts`),
|
||||||
|
|||||||
@ -11,7 +11,7 @@ describe('normalizeOptions', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should set unitTestRunner=jest and bundler=none by default', async () => {
|
it('should set unitTestRunner=jest and bundler=none by default', async () => {
|
||||||
const options = normalizeOptions(tree, {
|
const options = await normalizeOptions(tree, {
|
||||||
name: 'test',
|
name: 'test',
|
||||||
style: 'css',
|
style: 'css',
|
||||||
linter: Linter.None,
|
linter: Linter.None,
|
||||||
@ -27,7 +27,7 @@ describe('normalizeOptions', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should set buildable to true when bundler is not "none"', async () => {
|
it('should set buildable to true when bundler is not "none"', async () => {
|
||||||
let options = normalizeOptions(tree, {
|
let options = await normalizeOptions(tree, {
|
||||||
name: 'test',
|
name: 'test',
|
||||||
style: 'css',
|
style: 'css',
|
||||||
linter: Linter.None,
|
linter: Linter.None,
|
||||||
@ -39,7 +39,7 @@ describe('normalizeOptions', () => {
|
|||||||
bundler: 'rollup',
|
bundler: 'rollup',
|
||||||
});
|
});
|
||||||
|
|
||||||
options = normalizeOptions(tree, {
|
options = await normalizeOptions(tree, {
|
||||||
name: 'test',
|
name: 'test',
|
||||||
style: 'css',
|
style: 'css',
|
||||||
linter: Linter.None,
|
linter: Linter.None,
|
||||||
@ -53,7 +53,7 @@ describe('normalizeOptions', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should set unitTestRunner=vitest by default when bundler is vite', async () => {
|
it('should set unitTestRunner=vitest by default when bundler is vite', async () => {
|
||||||
const options = normalizeOptions(tree, {
|
const options = await normalizeOptions(tree, {
|
||||||
name: 'test',
|
name: 'test',
|
||||||
style: 'css',
|
style: 'css',
|
||||||
linter: Linter.None,
|
linter: Linter.None,
|
||||||
@ -70,7 +70,7 @@ describe('normalizeOptions', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should set maintain unitTestRunner when bundler is vite', async () => {
|
it('should set maintain unitTestRunner when bundler is vite', async () => {
|
||||||
const options = normalizeOptions(tree, {
|
const options = await normalizeOptions(tree, {
|
||||||
name: 'test',
|
name: 'test',
|
||||||
style: 'css',
|
style: 'css',
|
||||||
linter: Linter.None,
|
linter: Linter.None,
|
||||||
@ -86,8 +86,8 @@ describe('normalizeOptions', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set bundler to rollup if buildable is true not no bundler is passed', () => {
|
it('should set bundler to rollup if buildable is true not no bundler is passed', async () => {
|
||||||
const options = normalizeOptions(tree, {
|
const options = await normalizeOptions(tree, {
|
||||||
name: 'test',
|
name: 'test',
|
||||||
style: 'css',
|
style: 'css',
|
||||||
linter: Linter.None,
|
linter: Linter.None,
|
||||||
@ -102,8 +102,8 @@ describe('normalizeOptions', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set bundler to rollup if buildable is true and bundler is none ', () => {
|
it('should set bundler to rollup if buildable is true and bundler is none ', async () => {
|
||||||
const options = normalizeOptions(tree, {
|
const options = await normalizeOptions(tree, {
|
||||||
name: 'test',
|
name: 'test',
|
||||||
style: 'css',
|
style: 'css',
|
||||||
linter: Linter.None,
|
linter: Linter.None,
|
||||||
|
|||||||
@ -1,43 +1,34 @@
|
|||||||
import {
|
import { getProjects, logger, normalizePath, Tree } from '@nx/devkit';
|
||||||
extractLayoutDirectory,
|
import { determineProjectNameAndRootOptions } from '@nx/devkit/src/generators/project-name-and-root-utils';
|
||||||
getProjects,
|
|
||||||
getWorkspaceLayout,
|
|
||||||
joinPathFragments,
|
|
||||||
logger,
|
|
||||||
names,
|
|
||||||
normalizePath,
|
|
||||||
Tree,
|
|
||||||
} from '@nx/devkit';
|
|
||||||
import { getImportPath } from '@nx/js/src/utils/get-import-path';
|
|
||||||
|
|
||||||
import { assertValidStyle } from '../../../utils/assertion';
|
import { assertValidStyle } from '../../../utils/assertion';
|
||||||
import { NormalizedSchema, Schema } from '../schema';
|
import { NormalizedSchema, Schema } from '../schema';
|
||||||
|
|
||||||
export function normalizeOptions(
|
export async function normalizeOptions(
|
||||||
host: Tree,
|
host: Tree,
|
||||||
options: Schema
|
options: Schema
|
||||||
): NormalizedSchema {
|
): Promise<NormalizedSchema> {
|
||||||
const name = names(options.name).fileName;
|
const {
|
||||||
const { projectDirectory, layoutDirectory } = extractLayoutDirectory(
|
projectName,
|
||||||
options.directory
|
names: projectNames,
|
||||||
);
|
projectRoot,
|
||||||
const fullProjectDirectory = projectDirectory
|
importPath,
|
||||||
? `${names(projectDirectory).fileName}/${name}`
|
} = await determineProjectNameAndRootOptions(host, {
|
||||||
: name;
|
name: options.name,
|
||||||
|
projectType: 'library',
|
||||||
|
directory: options.directory,
|
||||||
|
importPath: options.importPath,
|
||||||
|
projectNameAndRootFormat: options.projectNameAndRootFormat,
|
||||||
|
callingGenerator: '@nx/react:library',
|
||||||
|
});
|
||||||
|
|
||||||
const projectName = fullProjectDirectory.replace(new RegExp('/', 'g'), '-');
|
const fileName = options.simpleName
|
||||||
const fileName = options.simpleName ? name : projectName;
|
? projectNames.projectSimpleName
|
||||||
const { libsDir: defaultLibsDir } = getWorkspaceLayout(host);
|
: projectNames.projectFileName;
|
||||||
const libsDir = layoutDirectory ?? defaultLibsDir;
|
|
||||||
const projectRoot = joinPathFragments(libsDir, fullProjectDirectory);
|
|
||||||
|
|
||||||
const parsedTags = options.tags
|
const parsedTags = options.tags
|
||||||
? options.tags.split(',').map((s) => s.trim())
|
? options.tags.split(',').map((s) => s.trim())
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
const importPath =
|
|
||||||
options.importPath || getImportPath(host, fullProjectDirectory);
|
|
||||||
|
|
||||||
let bundler = options.bundler ?? 'none';
|
let bundler = options.bundler ?? 'none';
|
||||||
|
|
||||||
if (bundler === 'none') {
|
if (bundler === 'none') {
|
||||||
@ -60,13 +51,11 @@ export function normalizeOptions(
|
|||||||
compiler: options.compiler ?? 'babel',
|
compiler: options.compiler ?? 'babel',
|
||||||
bundler,
|
bundler,
|
||||||
fileName,
|
fileName,
|
||||||
routePath: `/${name}`,
|
routePath: `/${projectNames.projectSimpleName}`,
|
||||||
name: projectName,
|
name: projectName,
|
||||||
projectRoot,
|
projectRoot,
|
||||||
projectDirectory: fullProjectDirectory,
|
|
||||||
parsedTags,
|
parsedTags,
|
||||||
importPath,
|
importPath,
|
||||||
libsDir,
|
|
||||||
} as NormalizedSchema;
|
} as NormalizedSchema;
|
||||||
|
|
||||||
// Libraries with a bundler or is publishable must also be buildable.
|
// Libraries with a bundler or is publishable must also be buildable.
|
||||||
|
|||||||
@ -17,7 +17,6 @@ import { addInitialRoutes } from '../../../utils/ast-utils';
|
|||||||
import { maybeJs } from './maybe-js';
|
import { maybeJs } from './maybe-js';
|
||||||
import { reactRouterDomVersion } from '../../../utils/versions';
|
import { reactRouterDomVersion } from '../../../utils/versions';
|
||||||
import { ensureTypescript } from '@nx/js/src/utils/typescript/ensure-typescript';
|
import { ensureTypescript } from '@nx/js/src/utils/typescript/ensure-typescript';
|
||||||
import { getImportPath } from '@nx/js/src/utils/get-import-path';
|
|
||||||
|
|
||||||
let tsModule: typeof import('typescript');
|
let tsModule: typeof import('typescript');
|
||||||
|
|
||||||
@ -82,7 +81,7 @@ export function updateAppRoutes(host: Tree, options: NormalizedSchema) {
|
|||||||
addRoute(appComponentPath, componentSource, {
|
addRoute(appComponentPath, componentSource, {
|
||||||
routePath: options.routePath,
|
routePath: options.routePath,
|
||||||
componentName: names(options.name).className,
|
componentName: names(options.name).className,
|
||||||
moduleName: getImportPath(host, options.projectDirectory),
|
moduleName: options.importPath,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
host.write(appComponentPath, changes);
|
host.write(appComponentPath, changes);
|
||||||
|
|||||||
@ -27,9 +27,16 @@ import { installCommonDependencies } from './lib/install-common-dependencies';
|
|||||||
import { setDefaults } from './lib/set-defaults';
|
import { setDefaults } from './lib/set-defaults';
|
||||||
|
|
||||||
export async function libraryGenerator(host: Tree, schema: Schema) {
|
export async function libraryGenerator(host: Tree, schema: Schema) {
|
||||||
|
return await libraryGeneratorInternal(host, {
|
||||||
|
projectNameAndRootFormat: 'derived',
|
||||||
|
...schema,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function libraryGeneratorInternal(host: Tree, schema: Schema) {
|
||||||
const tasks: GeneratorCallback[] = [];
|
const tasks: GeneratorCallback[] = [];
|
||||||
|
|
||||||
const options = normalizeOptions(host, schema);
|
const options = await normalizeOptions(host, schema);
|
||||||
if (options.publishable === true && !schema.importPath) {
|
if (options.publishable === true && !schema.importPath) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`For publishable libs you have to provide a proper "--importPath" which needs to be a valid npm package name (e.g. my-awesome-lib or @myorg/my-lib)`
|
`For publishable libs you have to provide a proper "--importPath" which needs to be a valid npm package name (e.g. my-awesome-lib or @myorg/my-lib)`
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
|
import type { ProjectNameAndRootFormat } from '@nx/devkit/src/generators/project-name-and-root-utils';
|
||||||
import type { Linter } from '@nx/linter';
|
import type { Linter } from '@nx/linter';
|
||||||
import { SupportedStyles } from '../../../typings/style';
|
import type { SupportedStyles } from '../../../typings/style';
|
||||||
|
|
||||||
export interface Schema {
|
export interface Schema {
|
||||||
appProject?: string;
|
appProject?: string;
|
||||||
@ -8,6 +9,7 @@ export interface Schema {
|
|||||||
compiler?: 'babel' | 'swc';
|
compiler?: 'babel' | 'swc';
|
||||||
component?: boolean;
|
component?: boolean;
|
||||||
directory?: string;
|
directory?: string;
|
||||||
|
projectNameAndRootFormat?: ProjectNameAndRootFormat;
|
||||||
globalCss?: boolean;
|
globalCss?: boolean;
|
||||||
importPath?: string;
|
importPath?: string;
|
||||||
inSourceTests?: boolean;
|
inSourceTests?: boolean;
|
||||||
@ -35,10 +37,8 @@ export interface NormalizedSchema extends Schema {
|
|||||||
fileName: string;
|
fileName: string;
|
||||||
projectRoot: string;
|
projectRoot: string;
|
||||||
routePath: string;
|
routePath: string;
|
||||||
projectDirectory: string;
|
|
||||||
parsedTags: string[];
|
parsedTags: string[];
|
||||||
appMain?: string;
|
appMain?: string;
|
||||||
appSourceRoot?: string;
|
appSourceRoot?: string;
|
||||||
libsDir?: string;
|
|
||||||
unitTestRunner: 'jest' | 'vitest' | 'none';
|
unitTestRunner: 'jest' | 'vitest' | 'none';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,7 +24,7 @@
|
|||||||
"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][^:]*)$",
|
||||||
"x-priority": "important"
|
"x-priority": "important"
|
||||||
},
|
},
|
||||||
"directory": {
|
"directory": {
|
||||||
@ -33,6 +33,11 @@
|
|||||||
"alias": "dir",
|
"alias": "dir",
|
||||||
"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",
|
||||||
|
|||||||
@ -39,8 +39,19 @@ export function addModuleFederationFiles(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function remoteGenerator(host: Tree, schema: Schema) {
|
export async function remoteGenerator(host: Tree, schema: Schema) {
|
||||||
|
return await remoteGeneratorInternal(host, {
|
||||||
|
projectNameAndRootFormat: 'derived',
|
||||||
|
...schema,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function remoteGeneratorInternal(host: Tree, schema: Schema) {
|
||||||
const tasks: GeneratorCallback[] = [];
|
const tasks: GeneratorCallback[] = [];
|
||||||
const options = normalizeOptions<Schema>(host, schema);
|
const options = await normalizeOptions<Schema>(
|
||||||
|
host,
|
||||||
|
schema,
|
||||||
|
'@nx/react:remote'
|
||||||
|
);
|
||||||
const initAppTask = await applicationGenerator(host, {
|
const initAppTask = await applicationGenerator(host, {
|
||||||
...options,
|
...options,
|
||||||
// Only webpack works with module federation for now.
|
// Only webpack works with module federation for now.
|
||||||
|
|||||||
@ -1,12 +1,13 @@
|
|||||||
import { Linter } from '@nx/linter';
|
import type { ProjectNameAndRootFormat } from '@nx/devkit/src/generators/project-name-and-root-utils';
|
||||||
|
import type { Linter } from '@nx/linter';
|
||||||
import { SupportedStyles } from '../../../typings';
|
import type { SupportedStyles } from '../../../typings';
|
||||||
|
|
||||||
export interface Schema {
|
export interface Schema {
|
||||||
classComponent?: boolean;
|
classComponent?: boolean;
|
||||||
compiler?: 'babel' | 'swc';
|
compiler?: 'babel' | 'swc';
|
||||||
devServerPort?: number;
|
devServerPort?: number;
|
||||||
directory?: string;
|
directory?: string;
|
||||||
|
projectNameAndRootFormat?: ProjectNameAndRootFormat;
|
||||||
e2eTestRunner: 'cypress' | 'none';
|
e2eTestRunner: 'cypress' | 'none';
|
||||||
globalCss?: boolean;
|
globalCss?: boolean;
|
||||||
host?: string;
|
host?: string;
|
||||||
|
|||||||
@ -14,7 +14,7 @@
|
|||||||
"index": 0
|
"index": 0
|
||||||
},
|
},
|
||||||
"x-prompt": "What name would you like to use as the remote application?",
|
"x-prompt": "What name would you like to use as the remote application?",
|
||||||
"pattern": "^[a-zA-Z].*$",
|
"pattern": "^[a-zA-Z][^:]*$",
|
||||||
"x-priority": "important"
|
"x-priority": "important"
|
||||||
},
|
},
|
||||||
"directory": {
|
"directory": {
|
||||||
@ -23,6 +23,11 @@
|
|||||||
"alias": "dir",
|
"alias": "dir",
|
||||||
"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",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user