feat(react-native): use helper to determine project name and root in project generators (#18734)
This commit is contained in:
parent
b3a0c0efed
commit
a2f761352b
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "application",
|
"name": "application",
|
||||||
"factory": "./src/generators/application/application#reactNativeApplicationGenerator",
|
"factory": "./src/generators/application/application#reactNativeApplicationGeneratorInternal",
|
||||||
"schema": {
|
"schema": {
|
||||||
"cli": "nx",
|
"cli": "nx",
|
||||||
"$id": "NxReactNativeApplication",
|
"$id": "NxReactNativeApplication",
|
||||||
@ -23,7 +23,8 @@
|
|||||||
"description": "The name of the application.",
|
"description": "The name of the application.",
|
||||||
"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][^:]*$"
|
||||||
},
|
},
|
||||||
"displayName": {
|
"displayName": {
|
||||||
"description": "The display name to show in the application. Defaults to name.",
|
"description": "The display name to show in the application. Defaults to name.",
|
||||||
@ -33,6 +34,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"]
|
||||||
|
},
|
||||||
"skipFormat": {
|
"skipFormat": {
|
||||||
"description": "Skip formatting files",
|
"description": "Skip formatting files",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
@ -91,7 +97,7 @@
|
|||||||
"aliases": ["app"],
|
"aliases": ["app"],
|
||||||
"x-type": "application",
|
"x-type": "application",
|
||||||
"description": "Create a React Native application.",
|
"description": "Create a React Native application.",
|
||||||
"implementation": "/packages/react-native/src/generators/application/application#reactNativeApplicationGenerator.ts",
|
"implementation": "/packages/react-native/src/generators/application/application#reactNativeApplicationGeneratorInternal.ts",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"path": "/packages/react-native/src/generators/application/schema.json",
|
"path": "/packages/react-native/src/generators/application/schema.json",
|
||||||
"type": "generator"
|
"type": "generator"
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "library",
|
"name": "library",
|
||||||
"factory": "./src/generators/library/library#reactNativeLibraryGenerator",
|
"factory": "./src/generators/library/library#reactNativeLibraryGeneratorInternal",
|
||||||
"schema": {
|
"schema": {
|
||||||
"cli": "nx",
|
"cli": "nx",
|
||||||
"$id": "NxReactNativeLibrary",
|
"$id": "NxReactNativeLibrary",
|
||||||
@ -20,7 +20,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][^:]*)$"
|
||||||
},
|
},
|
||||||
"directory": {
|
"directory": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@ -28,6 +28,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"]
|
||||||
|
},
|
||||||
"linter": {
|
"linter": {
|
||||||
"description": "The tool to use for running lint checks.",
|
"description": "The tool to use for running lint checks.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@ -104,7 +109,7 @@
|
|||||||
"aliases": ["lib"],
|
"aliases": ["lib"],
|
||||||
"x-type": "library",
|
"x-type": "library",
|
||||||
"description": "Create a React Native library.",
|
"description": "Create a React Native library.",
|
||||||
"implementation": "/packages/react-native/src/generators/library/library#reactNativeLibraryGenerator.ts",
|
"implementation": "/packages/react-native/src/generators/library/library#reactNativeLibraryGeneratorInternal.ts",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"path": "/packages/react-native/src/generators/library/schema.json",
|
"path": "/packages/react-native/src/generators/library/schema.json",
|
||||||
"type": "generator"
|
"type": "generator"
|
||||||
|
|||||||
@ -222,4 +222,42 @@ describe('react native', () => {
|
|||||||
);
|
);
|
||||||
}).not.toThrow();
|
}).not.toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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-native:application ${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.tsx`);
|
||||||
|
// 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-native:library ${libName} --buildable --project-name-and-root-format=derived`
|
||||||
|
)
|
||||||
|
).toThrow();
|
||||||
|
|
||||||
|
runCLI(
|
||||||
|
`generate @nx/react-native:library ${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`);
|
||||||
|
// check tests pass
|
||||||
|
const libTestResult = runCLI(`test ${libName}`);
|
||||||
|
expect(libTestResult).toContain(
|
||||||
|
`Successfully ran target test for project ${libName}`
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -62,14 +62,14 @@
|
|||||||
"hidden": true
|
"hidden": true
|
||||||
},
|
},
|
||||||
"application": {
|
"application": {
|
||||||
"factory": "./src/generators/application/application#reactNativeApplicationGenerator",
|
"factory": "./src/generators/application/application#reactNativeApplicationGeneratorInternal",
|
||||||
"schema": "./src/generators/application/schema.json",
|
"schema": "./src/generators/application/schema.json",
|
||||||
"aliases": ["app"],
|
"aliases": ["app"],
|
||||||
"x-type": "application",
|
"x-type": "application",
|
||||||
"description": "Create a React Native application."
|
"description": "Create a React Native application."
|
||||||
},
|
},
|
||||||
"library": {
|
"library": {
|
||||||
"factory": "./src/generators/library/library#reactNativeLibraryGenerator",
|
"factory": "./src/generators/library/library#reactNativeLibraryGeneratorInternal",
|
||||||
"schema": "./src/generators/library/schema.json",
|
"schema": "./src/generators/library/schema.json",
|
||||||
"aliases": ["lib"],
|
"aliases": ["lib"],
|
||||||
"x-type": "library",
|
"x-type": "library",
|
||||||
|
|||||||
@ -23,7 +23,17 @@ export async function reactNativeApplicationGenerator(
|
|||||||
host: Tree,
|
host: Tree,
|
||||||
schema: Schema
|
schema: Schema
|
||||||
): Promise<GeneratorCallback> {
|
): Promise<GeneratorCallback> {
|
||||||
const options = normalizeOptions(host, schema);
|
return await reactNativeApplicationGeneratorInternal(host, {
|
||||||
|
projectNameAndRootFormat: 'derived',
|
||||||
|
...schema,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function reactNativeApplicationGeneratorInternal(
|
||||||
|
host: Tree,
|
||||||
|
schema: Schema
|
||||||
|
): Promise<GeneratorCallback> {
|
||||||
|
const options = await normalizeOptions(host, schema);
|
||||||
|
|
||||||
createApplicationFiles(host, options);
|
createApplicationFiles(host, options);
|
||||||
addProject(host, options);
|
addProject(host, options);
|
||||||
|
|||||||
@ -11,8 +11,9 @@ export async function addDetox(host: Tree, options: NormalizedSchema) {
|
|||||||
return detoxApplicationGenerator(host, {
|
return detoxApplicationGenerator(host, {
|
||||||
...options,
|
...options,
|
||||||
linter: Linter.EsLint,
|
linter: Linter.EsLint,
|
||||||
e2eName: `${options.name}-e2e`,
|
e2eName: `${options.projectName}-e2e`,
|
||||||
e2eDirectory: options.directory,
|
e2eDirectory: `${options.appProjectRoot}-e2e`,
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
appProject: options.projectName,
|
appProject: options.projectName,
|
||||||
appDisplayName: options.displayName,
|
appDisplayName: options.displayName,
|
||||||
appName: options.name,
|
appName: options.name,
|
||||||
|
|||||||
@ -34,7 +34,7 @@ function getTargets(options: NormalizedSchema) {
|
|||||||
architect.serve = {
|
architect.serve = {
|
||||||
executor: 'nx:run-commands',
|
executor: 'nx:run-commands',
|
||||||
options: {
|
options: {
|
||||||
command: `nx start ${options.name}`,
|
command: `nx start ${options.projectName}`,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -11,14 +11,14 @@ describe('Normalize Options', () => {
|
|||||||
appTree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
appTree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should normalize options with name in kebab case', () => {
|
it('should normalize options with name in kebab case', async () => {
|
||||||
const schema: Schema = {
|
const schema: Schema = {
|
||||||
name: 'my-app',
|
name: 'my-app',
|
||||||
linter: Linter.EsLint,
|
linter: Linter.EsLint,
|
||||||
e2eTestRunner: 'none',
|
e2eTestRunner: 'none',
|
||||||
install: false,
|
install: false,
|
||||||
};
|
};
|
||||||
const options = normalizeOptions(appTree, schema);
|
const options = await normalizeOptions(appTree, schema);
|
||||||
expect(options).toEqual({
|
expect(options).toEqual({
|
||||||
androidProjectRoot: 'apps/my-app/android',
|
androidProjectRoot: 'apps/my-app/android',
|
||||||
appProjectRoot: 'apps/my-app',
|
appProjectRoot: 'apps/my-app',
|
||||||
@ -29,6 +29,7 @@ describe('Normalize Options', () => {
|
|||||||
name: 'my-app',
|
name: 'my-app',
|
||||||
parsedTags: [],
|
parsedTags: [],
|
||||||
projectName: 'my-app',
|
projectName: 'my-app',
|
||||||
|
projectNameAndRootFormat: 'derived',
|
||||||
linter: Linter.EsLint,
|
linter: Linter.EsLint,
|
||||||
entryFile: 'src/main.tsx',
|
entryFile: 'src/main.tsx',
|
||||||
e2eTestRunner: 'none',
|
e2eTestRunner: 'none',
|
||||||
@ -37,13 +38,13 @@ describe('Normalize Options', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should normalize options with name in camel case', () => {
|
it('should normalize options with name in camel case', async () => {
|
||||||
const schema: Schema = {
|
const schema: Schema = {
|
||||||
name: 'myApp',
|
name: 'myApp',
|
||||||
e2eTestRunner: 'none',
|
e2eTestRunner: 'none',
|
||||||
install: false,
|
install: false,
|
||||||
};
|
};
|
||||||
const options = normalizeOptions(appTree, schema);
|
const options = await normalizeOptions(appTree, schema);
|
||||||
expect(options).toEqual({
|
expect(options).toEqual({
|
||||||
androidProjectRoot: 'apps/my-app/android',
|
androidProjectRoot: 'apps/my-app/android',
|
||||||
appProjectRoot: 'apps/my-app',
|
appProjectRoot: 'apps/my-app',
|
||||||
@ -54,6 +55,7 @@ describe('Normalize Options', () => {
|
|||||||
name: 'my-app',
|
name: 'my-app',
|
||||||
parsedTags: [],
|
parsedTags: [],
|
||||||
projectName: 'my-app',
|
projectName: 'my-app',
|
||||||
|
projectNameAndRootFormat: 'derived',
|
||||||
entryFile: 'src/main.tsx',
|
entryFile: 'src/main.tsx',
|
||||||
e2eTestRunner: 'none',
|
e2eTestRunner: 'none',
|
||||||
unitTestRunner: 'jest',
|
unitTestRunner: 'jest',
|
||||||
@ -61,14 +63,14 @@ describe('Normalize Options', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should normalize options with directory', () => {
|
it('should normalize options with directory', async () => {
|
||||||
const schema: Schema = {
|
const schema: Schema = {
|
||||||
name: 'my-app',
|
name: 'my-app',
|
||||||
directory: 'directory',
|
directory: 'directory',
|
||||||
e2eTestRunner: 'none',
|
e2eTestRunner: 'none',
|
||||||
install: false,
|
install: false,
|
||||||
};
|
};
|
||||||
const options = normalizeOptions(appTree, schema);
|
const options = await normalizeOptions(appTree, schema);
|
||||||
expect(options).toEqual({
|
expect(options).toEqual({
|
||||||
androidProjectRoot: 'apps/directory/my-app/android',
|
androidProjectRoot: 'apps/directory/my-app/android',
|
||||||
appProjectRoot: 'apps/directory/my-app',
|
appProjectRoot: 'apps/directory/my-app',
|
||||||
@ -80,6 +82,7 @@ describe('Normalize Options', () => {
|
|||||||
directory: 'directory',
|
directory: 'directory',
|
||||||
parsedTags: [],
|
parsedTags: [],
|
||||||
projectName: 'directory-my-app',
|
projectName: 'directory-my-app',
|
||||||
|
projectNameAndRootFormat: 'derived',
|
||||||
entryFile: 'src/main.tsx',
|
entryFile: 'src/main.tsx',
|
||||||
e2eTestRunner: 'none',
|
e2eTestRunner: 'none',
|
||||||
unitTestRunner: 'jest',
|
unitTestRunner: 'jest',
|
||||||
@ -87,13 +90,13 @@ describe('Normalize Options', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should normalize options that has directory in its name', () => {
|
it('should normalize options that has directory in its name', async () => {
|
||||||
const schema: Schema = {
|
const schema: Schema = {
|
||||||
name: 'directory/my-app',
|
name: 'directory/my-app',
|
||||||
e2eTestRunner: 'none',
|
e2eTestRunner: 'none',
|
||||||
install: false,
|
install: false,
|
||||||
};
|
};
|
||||||
const options = normalizeOptions(appTree, schema);
|
const options = await normalizeOptions(appTree, schema);
|
||||||
expect(options).toEqual({
|
expect(options).toEqual({
|
||||||
androidProjectRoot: 'apps/directory/my-app/android',
|
androidProjectRoot: 'apps/directory/my-app/android',
|
||||||
appProjectRoot: 'apps/directory/my-app',
|
appProjectRoot: 'apps/directory/my-app',
|
||||||
@ -104,6 +107,7 @@ describe('Normalize Options', () => {
|
|||||||
name: 'directory/my-app',
|
name: 'directory/my-app',
|
||||||
parsedTags: [],
|
parsedTags: [],
|
||||||
projectName: 'directory-my-app',
|
projectName: 'directory-my-app',
|
||||||
|
projectNameAndRootFormat: 'derived',
|
||||||
entryFile: 'src/main.tsx',
|
entryFile: 'src/main.tsx',
|
||||||
e2eTestRunner: 'none',
|
e2eTestRunner: 'none',
|
||||||
unitTestRunner: 'jest',
|
unitTestRunner: 'jest',
|
||||||
@ -111,14 +115,14 @@ describe('Normalize Options', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should normalize options with display name', () => {
|
it('should normalize options with display name', async () => {
|
||||||
const schema: Schema = {
|
const schema: Schema = {
|
||||||
name: 'my-app',
|
name: 'my-app',
|
||||||
displayName: 'My App',
|
displayName: 'My App',
|
||||||
e2eTestRunner: 'none',
|
e2eTestRunner: 'none',
|
||||||
install: false,
|
install: false,
|
||||||
};
|
};
|
||||||
const options = normalizeOptions(appTree, schema);
|
const options = await normalizeOptions(appTree, schema);
|
||||||
expect(options).toEqual({
|
expect(options).toEqual({
|
||||||
androidProjectRoot: 'apps/my-app/android',
|
androidProjectRoot: 'apps/my-app/android',
|
||||||
appProjectRoot: 'apps/my-app',
|
appProjectRoot: 'apps/my-app',
|
||||||
@ -129,6 +133,7 @@ describe('Normalize Options', () => {
|
|||||||
name: 'my-app',
|
name: 'my-app',
|
||||||
parsedTags: [],
|
parsedTags: [],
|
||||||
projectName: 'my-app',
|
projectName: 'my-app',
|
||||||
|
projectNameAndRootFormat: 'derived',
|
||||||
entryFile: 'src/main.tsx',
|
entryFile: 'src/main.tsx',
|
||||||
e2eTestRunner: 'none',
|
e2eTestRunner: 'none',
|
||||||
unitTestRunner: 'jest',
|
unitTestRunner: 'jest',
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { getWorkspaceLayout, joinPathFragments, names, Tree } from '@nx/devkit';
|
import { joinPathFragments, names, Tree } from '@nx/devkit';
|
||||||
|
import { determineProjectNameAndRootOptions } from '@nx/devkit/src/generators/project-name-and-root-utils';
|
||||||
import { Schema } from '../schema';
|
import { Schema } from '../schema';
|
||||||
|
|
||||||
export interface NormalizedSchema extends Schema {
|
export interface NormalizedSchema extends Schema {
|
||||||
@ -12,23 +13,25 @@ export interface NormalizedSchema extends Schema {
|
|||||||
entryFile: string;
|
entryFile: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function normalizeOptions(
|
export async function normalizeOptions(
|
||||||
host: Tree,
|
host: Tree,
|
||||||
options: Schema
|
options: Schema
|
||||||
): NormalizedSchema {
|
): Promise<NormalizedSchema> {
|
||||||
const { fileName, className } = names(options.name);
|
const {
|
||||||
const { appsDir } = getWorkspaceLayout(host);
|
projectName: appProjectName,
|
||||||
|
names: projectNames,
|
||||||
|
projectRoot: appProjectRoot,
|
||||||
|
projectNameAndRootFormat,
|
||||||
|
} = await determineProjectNameAndRootOptions(host, {
|
||||||
|
name: options.name,
|
||||||
|
projectType: 'application',
|
||||||
|
directory: options.directory,
|
||||||
|
projectNameAndRootFormat: options.projectNameAndRootFormat,
|
||||||
|
callingGenerator: '@nx/react-native:application',
|
||||||
|
});
|
||||||
|
options.projectNameAndRootFormat = projectNameAndRootFormat;
|
||||||
|
|
||||||
const directoryName = options.directory
|
const { className } = names(options.name);
|
||||||
? names(options.directory).fileName
|
|
||||||
: '';
|
|
||||||
const projectDirectory = directoryName
|
|
||||||
? `${directoryName}/${fileName}`
|
|
||||||
: fileName;
|
|
||||||
|
|
||||||
const appProjectName = projectDirectory.replace(/\//g, '-');
|
|
||||||
|
|
||||||
const appProjectRoot = joinPathFragments(appsDir, projectDirectory);
|
|
||||||
const iosProjectRoot = joinPathFragments(appProjectRoot, 'ios');
|
const iosProjectRoot = joinPathFragments(appProjectRoot, 'ios');
|
||||||
const androidProjectRoot = joinPathFragments(appProjectRoot, 'android');
|
const androidProjectRoot = joinPathFragments(appProjectRoot, 'android');
|
||||||
|
|
||||||
@ -38,17 +41,11 @@ export function normalizeOptions(
|
|||||||
|
|
||||||
const entryFile = options.js ? 'src/main.js' : 'src/main.tsx';
|
const entryFile = options.js ? 'src/main.js' : 'src/main.tsx';
|
||||||
|
|
||||||
/**
|
|
||||||
* if options.name is "my-app"
|
|
||||||
* name: "my-app", className: 'MyApp', lowerCaseName: 'myapp', displayName: 'MyApp', projectName: 'my-app', appProjectRoot: 'apps/my-app', androidProjectRoot: 'apps/my-app/android', iosProjectRoot: 'apps/my-app/ios'
|
|
||||||
* if options.name is "myApp"
|
|
||||||
* name: "my-app", className: 'MyApp', lowerCaseName: 'myapp', displayName: 'MyApp', projectName: 'my-app', appProjectRoot: 'apps/my-app', androidProjectRoot: 'apps/my-app/android', iosProjectRoot: 'apps/my-app/ios'
|
|
||||||
*/
|
|
||||||
return {
|
return {
|
||||||
...options,
|
...options,
|
||||||
unitTestRunner: options.unitTestRunner || 'jest',
|
unitTestRunner: options.unitTestRunner || 'jest',
|
||||||
e2eTestRunner: options.e2eTestRunner || 'detox',
|
e2eTestRunner: options.e2eTestRunner || 'detox',
|
||||||
name: fileName,
|
name: projectNames.projectSimpleName,
|
||||||
className,
|
className,
|
||||||
lowerCaseName: className.toLowerCase(),
|
lowerCaseName: className.toLowerCase(),
|
||||||
displayName: options.displayName || className,
|
displayName: options.displayName || className,
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { Linter } from '@nx/linter';
|
import type { ProjectNameAndRootFormat } from '@nx/devkit/src/generators/project-name-and-root-utils';
|
||||||
|
import type { Linter } from '@nx/linter';
|
||||||
|
|
||||||
export interface Schema {
|
export interface Schema {
|
||||||
name: string;
|
name: string;
|
||||||
@ -6,6 +7,7 @@ export interface Schema {
|
|||||||
style?: string;
|
style?: string;
|
||||||
skipFormat?: boolean;
|
skipFormat?: boolean;
|
||||||
directory?: string;
|
directory?: string;
|
||||||
|
projectNameAndRootFormat?: ProjectNameAndRootFormat;
|
||||||
tags?: string;
|
tags?: string;
|
||||||
unitTestRunner?: 'jest' | 'none';
|
unitTestRunner?: 'jest' | 'none';
|
||||||
pascalCaseFiles?: boolean;
|
pascalCaseFiles?: boolean;
|
||||||
|
|||||||
@ -23,7 +23,8 @@
|
|||||||
"$source": "argv",
|
"$source": "argv",
|
||||||
"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][^:]*$"
|
||||||
},
|
},
|
||||||
"displayName": {
|
"displayName": {
|
||||||
"description": "The display name to show in the application. Defaults to name.",
|
"description": "The display name to show in the application. Defaults to name.",
|
||||||
@ -33,6 +34,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"]
|
||||||
|
},
|
||||||
"skipFormat": {
|
"skipFormat": {
|
||||||
"description": "Skip formatting files",
|
"description": "Skip formatting files",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { getWorkspaceLayout, joinPathFragments, names, Tree } from '@nx/devkit';
|
import { Tree } from '@nx/devkit';
|
||||||
import { getImportPath } from '@nx/js/src/utils/get-import-path';
|
import { determineProjectNameAndRootOptions } from '@nx/devkit/src/generators/project-name-and-root-utils';
|
||||||
import { Schema } from '../schema';
|
import { Schema } from '../schema';
|
||||||
|
|
||||||
export interface NormalizedSchema extends Schema {
|
export interface NormalizedSchema extends Schema {
|
||||||
@ -7,40 +7,39 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
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 = options.directory
|
projectName,
|
||||||
? `${names(options.directory).fileName}/${name}`
|
names: projectNames,
|
||||||
: name;
|
projectRoot,
|
||||||
|
importPath,
|
||||||
const projectName = projectDirectory.replace(new RegExp('/', 'g'), '-');
|
} = await determineProjectNameAndRootOptions(host, {
|
||||||
const fileName = projectName;
|
name: options.name,
|
||||||
const { libsDir } = getWorkspaceLayout(host);
|
projectType: 'library',
|
||||||
const projectRoot = joinPathFragments(libsDir, projectDirectory);
|
directory: options.directory,
|
||||||
|
importPath: options.importPath,
|
||||||
|
projectNameAndRootFormat: options.projectNameAndRootFormat,
|
||||||
|
callingGenerator: '@nx/react-native:library',
|
||||||
|
});
|
||||||
|
|
||||||
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, projectDirectory);
|
|
||||||
|
|
||||||
const normalized: NormalizedSchema = {
|
const normalized: NormalizedSchema = {
|
||||||
...options,
|
...options,
|
||||||
fileName,
|
fileName: projectName,
|
||||||
routePath: `/${name}`,
|
routePath: `/${projectNames.projectSimpleName}`,
|
||||||
name: projectName,
|
name: projectName,
|
||||||
projectRoot,
|
projectRoot,
|
||||||
projectDirectory,
|
|
||||||
parsedTags,
|
parsedTags,
|
||||||
importPath,
|
importPath,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import {
|
|||||||
formatFiles,
|
formatFiles,
|
||||||
generateFiles,
|
generateFiles,
|
||||||
GeneratorCallback,
|
GeneratorCallback,
|
||||||
getWorkspaceLayout,
|
|
||||||
joinPathFragments,
|
joinPathFragments,
|
||||||
names,
|
names,
|
||||||
offsetFromRoot,
|
offsetFromRoot,
|
||||||
@ -32,7 +31,17 @@ export async function reactNativeLibraryGenerator(
|
|||||||
host: Tree,
|
host: Tree,
|
||||||
schema: Schema
|
schema: Schema
|
||||||
): Promise<GeneratorCallback> {
|
): Promise<GeneratorCallback> {
|
||||||
const options = normalizeOptions(host, schema);
|
return await reactNativeLibraryGeneratorInternal(host, {
|
||||||
|
projectNameAndRootFormat: 'derived',
|
||||||
|
...schema,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function reactNativeLibraryGeneratorInternal(
|
||||||
|
host: Tree,
|
||||||
|
schema: Schema
|
||||||
|
): Promise<GeneratorCallback> {
|
||||||
|
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)`
|
||||||
@ -105,7 +114,6 @@ async function addProject(host: Tree, options: NormalizedSchema) {
|
|||||||
nxVersion
|
nxVersion
|
||||||
);
|
);
|
||||||
|
|
||||||
const { libsDir } = getWorkspaceLayout(host);
|
|
||||||
const external = [
|
const external = [
|
||||||
'react/jsx-runtime',
|
'react/jsx-runtime',
|
||||||
'react-native',
|
'react-native',
|
||||||
@ -117,7 +125,7 @@ async function addProject(host: Tree, options: NormalizedSchema) {
|
|||||||
executor: '@nx/rollup:rollup',
|
executor: '@nx/rollup:rollup',
|
||||||
outputs: ['{options.outputPath}'],
|
outputs: ['{options.outputPath}'],
|
||||||
options: {
|
options: {
|
||||||
outputPath: `dist/${libsDir}/${options.projectDirectory}`,
|
outputPath: `dist/${options.projectRoot}`,
|
||||||
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`),
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { Linter } from '@nx/linter';
|
import type { ProjectNameAndRootFormat } from '@nx/devkit/src/generators/project-name-and-root-utils';
|
||||||
|
import type { Linter } from '@nx/linter';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Same as the @nx/react library schema, except it removes keys: style, component, routing, appProject
|
* Same as the @nx/react library schema, except it removes keys: style, component, routing, appProject
|
||||||
@ -6,6 +7,7 @@ import { Linter } from '@nx/linter';
|
|||||||
export interface Schema {
|
export interface Schema {
|
||||||
name: string;
|
name: string;
|
||||||
directory?: string;
|
directory?: string;
|
||||||
|
projectNameAndRootFormat?: ProjectNameAndRootFormat;
|
||||||
skipTsConfig: boolean;
|
skipTsConfig: boolean;
|
||||||
skipFormat: boolean;
|
skipFormat: boolean;
|
||||||
tags?: string;
|
tags?: string;
|
||||||
|
|||||||
@ -20,7 +20,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][^:]*)$"
|
||||||
},
|
},
|
||||||
"directory": {
|
"directory": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@ -28,6 +28,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"]
|
||||||
|
},
|
||||||
"linter": {
|
"linter": {
|
||||||
"description": "The tool to use for running lint checks.",
|
"description": "The tool to use for running lint checks.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user