Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(expo): use helper to determine project name and root in project generators #18678

Merged
merged 7 commits into from
Aug 22, 2023
12 changes: 9 additions & 3 deletions docs/generated/packages/expo/generators/application.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "application",
"factory": "./src/generators/application/application#expoApplicationGenerator",
"factory": "./src/generators/application/application#expoApplicationGeneratorInternal",
"schema": {
"cli": "nx",
"$id": "NxExpoApplication",
Expand All @@ -22,7 +22,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 @@
"type": "string",
"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"]
},
"skipFormat": {
"description": "Skip formatting files",
"type": "boolean",
Expand Down Expand Up @@ -90,7 +96,7 @@
"aliases": ["app"],
"x-type": "application",
"description": "Create an application",
"implementation": "/packages/expo/src/generators/application/application#expoApplicationGenerator.ts",
"implementation": "/packages/expo/src/generators/application/application#expoApplicationGeneratorInternal.ts",
"hidden": false,
"path": "/packages/expo/src/generators/application/schema.json",
"type": "generator"
Expand Down
11 changes: 8 additions & 3 deletions docs/generated/packages/expo/generators/library.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "library",
"factory": "./src/generators/library/library#expoLibraryGenerator",
"factory": "./src/generators/library/library#expoLibraryGeneratorInternal",
"schema": {
"cli": "nx",
"$id": "NxExpoLibrary",
Expand All @@ -19,13 +19,18 @@
"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.",
"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 @@ -101,7 +106,7 @@
"aliases": ["lib"],
"x-type": "library",
"description": "Create a library",
"implementation": "/packages/expo/src/generators/library/library#expoLibraryGenerator.ts",
"implementation": "/packages/expo/src/generators/library/library#expoLibraryGeneratorInternal.ts",
"hidden": false,
"path": "/packages/expo/src/generators/library/schema.json",
"type": "generator"
Expand Down
38 changes: 38 additions & 0 deletions e2e/expo/src/expo.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,4 +155,42 @@ describe('expo', () => {
);
}).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/expo: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/expo:library ${libName} --buildable --project-name-and-root-format=derived`
)
).toThrow();

runCLI(
`generate @nx/expo: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/expo/generators.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,14 @@
"hidden": true
},
"application": {
"factory": "./src/generators/application/application#expoApplicationGenerator",
"factory": "./src/generators/application/application#expoApplicationGeneratorInternal",
"schema": "./src/generators/application/schema.json",
"aliases": ["app"],
"x-type": "application",
"description": "Create an application"
},
"library": {
"factory": "./src/generators/library/library#expoLibraryGenerator",
"factory": "./src/generators/library/library#expoLibraryGeneratorInternal",
"schema": "./src/generators/library/schema.json",
"aliases": ["lib"],
"x-type": "library",
Expand Down
12 changes: 11 additions & 1 deletion packages/expo/src/generators/application/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,17 @@ export async function expoApplicationGenerator(
host: Tree,
schema: Schema
): Promise<GeneratorCallback> {
const options = normalizeOptions(host, schema);
return await expoApplicationGeneratorInternal(host, {
projectNameAndRootFormat: 'derived',
...schema,
});
}

export async function expoApplicationGeneratorInternal(
host: Tree,
schema: Schema
): Promise<GeneratorCallback> {
const options = await normalizeOptions(host, schema);

createApplicationFiles(host, options);
addProject(host, options);
Expand Down
5 changes: 3 additions & 2 deletions packages/expo/src/generators/application/lib/add-detox.ts
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 @@ -38,7 +38,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,7 +11,7 @@ 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,
Expand All @@ -20,7 +20,7 @@ describe('Normalize Options', () => {
js: true,
unitTestRunner: 'jest',
};
const options = normalizeOptions(appTree, schema);
const options = await normalizeOptions(appTree, schema);
expect(options).toEqual({
appProjectRoot: 'apps/my-app',
className: 'MyApp',
Expand All @@ -29,6 +29,7 @@ describe('Normalize Options', () => {
name: 'my-app',
parsedTags: [],
projectName: 'my-app',
projectNameAndRootFormat: 'derived',
linter: Linter.EsLint,
e2eTestRunner: 'none',
unitTestRunner: 'jest',
Expand All @@ -37,7 +38,7 @@ 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',
linter: Linter.EsLint,
Expand All @@ -46,7 +47,7 @@ describe('Normalize Options', () => {
js: true,
unitTestRunner: 'jest',
};
const options = normalizeOptions(appTree, schema);
const options = await normalizeOptions(appTree, schema);
expect(options).toEqual({
appProjectRoot: 'apps/my-app',
className: 'MyApp',
Expand All @@ -55,6 +56,7 @@ describe('Normalize Options', () => {
name: 'my-app',
parsedTags: [],
projectName: 'my-app',
projectNameAndRootFormat: 'derived',
linter: Linter.EsLint,
e2eTestRunner: 'none',
skipFormat: false,
Expand All @@ -63,7 +65,7 @@ describe('Normalize Options', () => {
});
});

it('should normalize options with directory', () => {
it('should normalize options with directory', async () => {
const schema: Schema = {
name: 'my-app',
directory: 'directory',
Expand All @@ -73,7 +75,7 @@ describe('Normalize Options', () => {
js: true,
unitTestRunner: 'jest',
};
const options = normalizeOptions(appTree, schema);
const options = await normalizeOptions(appTree, schema);
expect(options).toEqual({
appProjectRoot: 'apps/directory/my-app',
className: 'MyApp',
Expand All @@ -83,6 +85,7 @@ describe('Normalize Options', () => {
directory: 'directory',
parsedTags: [],
projectName: 'directory-my-app',
projectNameAndRootFormat: 'derived',
e2eTestRunner: 'none',
unitTestRunner: 'jest',
linter: Linter.EsLint,
Expand All @@ -91,7 +94,7 @@ 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 = {
name: 'directory/my-app',
linter: Linter.EsLint,
Expand All @@ -100,7 +103,7 @@ describe('Normalize Options', () => {
js: true,
unitTestRunner: 'jest',
};
const options = normalizeOptions(appTree, schema);
const options = await normalizeOptions(appTree, schema);
expect(options).toEqual({
appProjectRoot: 'apps/directory/my-app',
className: 'DirectoryMyApp',
Expand All @@ -109,6 +112,7 @@ describe('Normalize Options', () => {
name: 'directory/my-app',
parsedTags: [],
projectName: 'directory-my-app',
projectNameAndRootFormat: 'derived',
e2eTestRunner: 'none',
unitTestRunner: 'jest',
linter: Linter.EsLint,
Expand All @@ -117,7 +121,7 @@ describe('Normalize Options', () => {
});
});

it('should normalize options with display name', () => {
it('should normalize options with display name', async () => {
const schema: Schema = {
name: 'my-app',
displayName: 'My App',
Expand All @@ -127,7 +131,7 @@ describe('Normalize Options', () => {
js: true,
unitTestRunner: 'jest',
};
const options = normalizeOptions(appTree, schema);
const options = await normalizeOptions(appTree, schema);
expect(options).toEqual({
appProjectRoot: 'apps/my-app',
className: 'MyApp',
Expand All @@ -136,6 +140,7 @@ describe('Normalize Options', () => {
name: 'my-app',
parsedTags: [],
projectName: 'my-app',
projectNameAndRootFormat: 'derived',
e2eTestRunner: 'none',
unitTestRunner: 'jest',
linter: Linter.EsLint,
Expand Down
42 changes: 19 additions & 23 deletions packages/expo/src/generators/application/lib/normalize-options.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { getWorkspaceLayout, joinPathFragments, names, Tree } from '@nx/devkit';
import { 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 @@ -9,39 +10,34 @@ export interface NormalizedSchema extends Schema {
parsedTags: 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(new RegExp('/', '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/expo:application',
});
options.projectNameAndRootFormat = projectNameAndRootFormat;

const { className } = names(options.name);
const parsedTags = options.tags
? options.tags.split(',').map((s) => s.trim())
: [];

/**
* 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/expo/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; // default is false
directory?: string;
projectNameAndRootFormat?: ProjectNameAndRootFormat;
tags?: string;
unitTestRunner: 'jest' | 'none'; // default is jest
pascalCaseFiles?: boolean;
Expand Down
Loading