Skip to content

Commit

Permalink
feat(react-native): use helper to determine project name and root in …
Browse files Browse the repository at this point in the history
…project generators (nrwl#18734)
  • Loading branch information
leosvelperez committed Aug 22, 2023
1 parent b3a0c0e commit a2f7613
Show file tree
Hide file tree
Showing 15 changed files with 157 additions and 73 deletions.
12 changes: 9 additions & 3 deletions docs/generated/packages/react-native/generators/application.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "application",
"factory": "./src/generators/application/application#reactNativeApplicationGenerator",
"factory": "./src/generators/application/application#reactNativeApplicationGeneratorInternal",
"schema": {
"cli": "nx",
"$id": "NxReactNativeApplication",
Expand All @@ -23,7 +23,8 @@
"description": "The name of the application.",
"type": "string",
"$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": {
"description": "The display name to show in the application. Defaults to name.",
Expand All @@ -33,6 +34,11 @@
"description": "The directory of the new application.",
"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": {
"description": "Skip formatting files",
"type": "boolean",
Expand Down Expand Up @@ -91,7 +97,7 @@
"aliases": ["app"],
"x-type": "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,
"path": "/packages/react-native/src/generators/application/schema.json",
"type": "generator"
Expand Down
11 changes: 8 additions & 3 deletions docs/generated/packages/react-native/generators/library.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "library",
"factory": "./src/generators/library/library#reactNativeLibraryGenerator",
"factory": "./src/generators/library/library#reactNativeLibraryGeneratorInternal",
"schema": {
"cli": "nx",
"$id": "NxReactNativeLibrary",
Expand All @@ -20,14 +20,19 @@
"description": "Library name.",
"$default": { "$source": "argv", "index": 0 },
"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": {
"type": "string",
"description": "A directory where the lib is placed.",
"alias": "dir",
"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": {
"description": "The tool to use for running lint checks.",
"type": "string",
Expand Down Expand Up @@ -104,7 +109,7 @@
"aliases": ["lib"],
"x-type": "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,
"path": "/packages/react-native/src/generators/library/schema.json",
"type": "generator"
Expand Down
38 changes: 38 additions & 0 deletions e2e/react-native/src/react-native.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,4 +222,42 @@ describe('react native', () => {
);
}).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}`
);
});
});
4 changes: 2 additions & 2 deletions packages/react-native/generators.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,14 @@
"hidden": true
},
"application": {
"factory": "./src/generators/application/application#reactNativeApplicationGenerator",
"factory": "./src/generators/application/application#reactNativeApplicationGeneratorInternal",
"schema": "./src/generators/application/schema.json",
"aliases": ["app"],
"x-type": "application",
"description": "Create a React Native application."
},
"library": {
"factory": "./src/generators/library/library#reactNativeLibraryGenerator",
"factory": "./src/generators/library/library#reactNativeLibraryGeneratorInternal",
"schema": "./src/generators/library/schema.json",
"aliases": ["lib"],
"x-type": "library",
Expand Down
12 changes: 11 additions & 1 deletion packages/react-native/src/generators/application/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,17 @@ export async function reactNativeApplicationGenerator(
host: Tree,
schema: Schema
): 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);
addProject(host, options);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ export async function addDetox(host: Tree, options: NormalizedSchema) {
return detoxApplicationGenerator(host, {
...options,
linter: Linter.EsLint,
e2eName: `${options.name}-e2e`,
e2eDirectory: options.directory,
e2eName: `${options.projectName}-e2e`,
e2eDirectory: `${options.appProjectRoot}-e2e`,
projectNameAndRootFormat: 'as-provided',
appProject: options.projectName,
appDisplayName: options.displayName,
appName: options.name,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ function getTargets(options: NormalizedSchema) {
architect.serve = {
executor: 'nx:run-commands',
options: {
command: `nx start ${options.name}`,
command: `nx start ${options.projectName}`,
},
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ describe('Normalize Options', () => {
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 = {
name: 'my-app',
linter: Linter.EsLint,
e2eTestRunner: 'none',
install: false,
};
const options = normalizeOptions(appTree, schema);
const options = await normalizeOptions(appTree, schema);
expect(options).toEqual({
androidProjectRoot: 'apps/my-app/android',
appProjectRoot: 'apps/my-app',
Expand All @@ -29,6 +29,7 @@ describe('Normalize Options', () => {
name: 'my-app',
parsedTags: [],
projectName: 'my-app',
projectNameAndRootFormat: 'derived',
linter: Linter.EsLint,
entryFile: 'src/main.tsx',
e2eTestRunner: 'none',
Expand All @@ -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 = {
name: 'myApp',
e2eTestRunner: 'none',
install: false,
};
const options = normalizeOptions(appTree, schema);
const options = await normalizeOptions(appTree, schema);
expect(options).toEqual({
androidProjectRoot: 'apps/my-app/android',
appProjectRoot: 'apps/my-app',
Expand All @@ -54,21 +55,22 @@ describe('Normalize Options', () => {
name: 'my-app',
parsedTags: [],
projectName: 'my-app',
projectNameAndRootFormat: 'derived',
entryFile: 'src/main.tsx',
e2eTestRunner: 'none',
unitTestRunner: 'jest',
install: false,
});
});

it('should normalize options with directory', () => {
it('should normalize options with directory', async () => {
const schema: Schema = {
name: 'my-app',
directory: 'directory',
e2eTestRunner: 'none',
install: false,
};
const options = normalizeOptions(appTree, schema);
const options = await normalizeOptions(appTree, schema);
expect(options).toEqual({
androidProjectRoot: 'apps/directory/my-app/android',
appProjectRoot: 'apps/directory/my-app',
Expand All @@ -80,20 +82,21 @@ describe('Normalize Options', () => {
directory: 'directory',
parsedTags: [],
projectName: 'directory-my-app',
projectNameAndRootFormat: 'derived',
entryFile: 'src/main.tsx',
e2eTestRunner: 'none',
unitTestRunner: 'jest',
install: false,
});
});

it('should normalize options that has directory in its name', () => {
it('should normalize options that has directory in its name', async () => {
const schema: Schema = {
name: 'directory/my-app',
e2eTestRunner: 'none',
install: false,
};
const options = normalizeOptions(appTree, schema);
const options = await normalizeOptions(appTree, schema);
expect(options).toEqual({
androidProjectRoot: 'apps/directory/my-app/android',
appProjectRoot: 'apps/directory/my-app',
Expand All @@ -104,21 +107,22 @@ describe('Normalize Options', () => {
name: 'directory/my-app',
parsedTags: [],
projectName: 'directory-my-app',
projectNameAndRootFormat: 'derived',
entryFile: 'src/main.tsx',
e2eTestRunner: 'none',
unitTestRunner: 'jest',
install: false,
});
});

it('should normalize options with display name', () => {
it('should normalize options with display name', async () => {
const schema: Schema = {
name: 'my-app',
displayName: 'My App',
e2eTestRunner: 'none',
install: false,
};
const options = normalizeOptions(appTree, schema);
const options = await normalizeOptions(appTree, schema);
expect(options).toEqual({
androidProjectRoot: 'apps/my-app/android',
appProjectRoot: 'apps/my-app',
Expand All @@ -129,6 +133,7 @@ describe('Normalize Options', () => {
name: 'my-app',
parsedTags: [],
projectName: 'my-app',
projectNameAndRootFormat: 'derived',
entryFile: 'src/main.tsx',
e2eTestRunner: 'none',
unitTestRunner: 'jest',
Expand Down
Original file line number Diff line number Diff line change
@@ -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';

export interface NormalizedSchema extends Schema {
Expand All @@ -12,23 +13,25 @@ export interface NormalizedSchema extends Schema {
entryFile: string;
}

export function normalizeOptions(
export async function normalizeOptions(
host: Tree,
options: Schema
): NormalizedSchema {
const { fileName, className } = names(options.name);
const { appsDir } = getWorkspaceLayout(host);

const directoryName = options.directory
? names(options.directory).fileName
: '';
const projectDirectory = directoryName
? `${directoryName}/${fileName}`
: fileName;

const appProjectName = projectDirectory.replace(/\//g, '-');

const appProjectRoot = joinPathFragments(appsDir, projectDirectory);
): Promise<NormalizedSchema> {
const {
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 { className } = names(options.name);
const iosProjectRoot = joinPathFragments(appProjectRoot, 'ios');
const androidProjectRoot = joinPathFragments(appProjectRoot, 'android');

Expand All @@ -38,17 +41,11 @@ export function normalizeOptions(

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 {
...options,
unitTestRunner: options.unitTestRunner || 'jest',
e2eTestRunner: options.e2eTestRunner || 'detox',
name: fileName,
name: projectNames.projectSimpleName,
className,
lowerCaseName: className.toLowerCase(),
displayName: options.displayName || className,
Expand Down
4 changes: 3 additions & 1 deletion packages/react-native/src/generators/application/schema.d.ts
Original file line number Diff line number Diff line change
@@ -1,11 +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';

export interface Schema {
name: string;
displayName?: string;
style?: string;
skipFormat?: boolean;
directory?: string;
projectNameAndRootFormat?: ProjectNameAndRootFormat;
tags?: string;
unitTestRunner?: 'jest' | 'none';
pascalCaseFiles?: boolean;
Expand Down
Loading

0 comments on commit a2f7613

Please sign in to comment.