Skip to content

Commit

Permalink
feat(react): add --directory option for React components
Browse files Browse the repository at this point in the history
Also adds alias support to tao cli.

Closes #1702
  • Loading branch information
jaysoo committed Aug 16, 2019
1 parent 01f1c81 commit f2fa8a8
Show file tree
Hide file tree
Showing 11 changed files with 127 additions and 76 deletions.
40 changes: 24 additions & 16 deletions packages/react/src/schematics/application/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,22 +76,8 @@ export default function(schema: Schema): Rule {
createApplicationFiles(options),
updateNxJson(options),
addProject(options),
options.e2eTestRunner === 'cypress'
? externalSchematic('@nrwl/cypress', 'cypress-project', {
...options,
name: options.name + '-e2e',
directory: options.directory,
project: options.projectName
})
: noop(),
options.unitTestRunner === 'jest'
? externalSchematic('@nrwl/jest', 'jest-project', {
project: options.projectName,
supportTsx: true,
skipSerializers: true,
setupFile: 'none'
})
: noop(),
addCypress(options),
addJest(options),
addStyledModuleDependencies(options),
addRouting(options, context),
addBabel(options),
Expand Down Expand Up @@ -215,6 +201,28 @@ function addProject(options: NormalizedSchema): Rule {
});
}

function addCypress(options: NormalizedSchema): Rule {
return options.e2eTestRunner === 'cypress'
? externalSchematic('@nrwl/cypress', 'cypress-project', {
...options,
name: options.name + '-e2e',
directory: options.directory,
project: options.projectName
})
: noop();
}

function addJest(options: NormalizedSchema): Rule {
return options.unitTestRunner === 'jest'
? externalSchematic('@nrwl/jest', 'jest-project', {
project: options.projectName,
supportTsx: true,
skipSerializers: true,
setupFile: 'none'
})
: noop();
}

function addStyledModuleDependencies(options: NormalizedSchema): Rule {
const extraDependencies = CSS_IN_JS_DEPENDENCIES[options.styledModule];

Expand Down
70 changes: 40 additions & 30 deletions packages/react/src/schematics/component/component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,9 @@ describe('component', () => {
appTree
);

expect(tree.exists('libs/my-lib/src/lib/hello/hello.tsx')).toBeTruthy();
expect(
tree.exists('libs/my-lib/src/lib/hello/hello.spec.tsx')
).toBeTruthy();
expect(tree.exists('libs/my-lib/src/lib/hello/hello.css')).toBeTruthy();
expect(tree.exists('libs/my-lib/src/lib/hello.tsx')).toBeTruthy();
expect(tree.exists('libs/my-lib/src/lib/hello.spec.tsx')).toBeTruthy();
expect(tree.exists('libs/my-lib/src/lib/hello.css')).toBeTruthy();
});

it('should generate files for an app', async () => {
Expand All @@ -36,11 +34,9 @@ describe('component', () => {
appTree
);

expect(tree.exists('apps/my-app/src/app/hello/hello.tsx')).toBeTruthy();
expect(
tree.exists('apps/my-app/src/app/hello/hello.spec.tsx')
).toBeTruthy();
expect(tree.exists('apps/my-app/src/app/hello/hello.css')).toBeTruthy();
expect(tree.exists('apps/my-app/src/app/hello.tsx')).toBeTruthy();
expect(tree.exists('apps/my-app/src/app/hello.spec.tsx')).toBeTruthy();
expect(tree.exists('apps/my-app/src/app/hello.css')).toBeTruthy();
});

describe('--export', () => {
Expand All @@ -53,7 +49,7 @@ describe('component', () => {

const indexContent = tree.read('libs/my-lib/src/index.ts').toString();

expect(indexContent).toMatch(/lib\/hello\/hello/);
expect(indexContent).toMatch(/lib\/hello/);
});

it('should not export from an app', async () => {
Expand All @@ -65,7 +61,7 @@ describe('component', () => {

const indexContent = tree.read('libs/my-lib/src/index.ts').toString();

expect(indexContent).not.toMatch(/lib\/hello\/hello/);
expect(indexContent).not.toMatch(/lib\/hello/);
});
});

Expand All @@ -76,11 +72,9 @@ describe('component', () => {
{ name: 'hello', project: projectName, pascalCaseFiles: true },
appTree
);
expect(tree.exists('libs/my-lib/src/lib/hello/Hello.tsx')).toBeTruthy();
expect(
tree.exists('libs/my-lib/src/lib/hello/Hello.spec.tsx')
).toBeTruthy();
expect(tree.exists('libs/my-lib/src/lib/hello/Hello.css')).toBeTruthy();
expect(tree.exists('libs/my-lib/src/lib/Hello.tsx')).toBeTruthy();
expect(tree.exists('libs/my-lib/src/lib/Hello.spec.tsx')).toBeTruthy();
expect(tree.exists('libs/my-lib/src/lib/Hello.css')).toBeTruthy();
});
});

Expand All @@ -93,13 +87,11 @@ describe('component', () => {
);

expect(
tree.exists('libs/my-lib/src/lib/hello/hello.styled-components')
tree.exists('libs/my-lib/src/lib/hello.styled-components')
).toBeFalsy();
expect(tree.exists('libs/my-lib/src/lib/hello/hello.tsx')).toBeTruthy();
expect(tree.exists('libs/my-lib/src/lib/hello.tsx')).toBeTruthy();

const content = tree
.read('libs/my-lib/src/lib/hello/hello.tsx')
.toString();
const content = tree.read('libs/my-lib/src/lib/hello.tsx').toString();
expect(content).toContain('styled-components');
expect(content).toContain('<StyledHello>');
});
Expand All @@ -125,13 +117,11 @@ describe('component', () => {
);

expect(
tree.exists('libs/my-lib/src/lib/hello/hello.@emotion/styled')
tree.exists('libs/my-lib/src/lib/hello.@emotion/styled')
).toBeFalsy();
expect(tree.exists('libs/my-lib/src/lib/hello/hello.tsx')).toBeTruthy();
expect(tree.exists('libs/my-lib/src/lib/hello.tsx')).toBeTruthy();

const content = tree
.read('libs/my-lib/src/lib/hello/hello.tsx')
.toString();
const content = tree.read('libs/my-lib/src/lib/hello.tsx').toString();
expect(content).toContain('@emotion/styled');
expect(content).toContain('<StyledHello>');
});
Expand All @@ -157,9 +147,7 @@ describe('component', () => {
appTree
);

const content = tree
.read('libs/my-lib/src/lib/hello/hello.tsx')
.toString();
const content = tree.read('libs/my-lib/src/lib/hello.tsx').toString();
expect(content).toContain('react-router-dom');
expect(content).toMatch(/<Route\s*path="\/"/);
expect(content).toMatch(/<Link\s*to="\/"/);
Expand All @@ -168,4 +156,26 @@ describe('component', () => {
expect(packageJSON.dependencies['react-router-dom']).toBeDefined();
});
});

describe('--directory', () => {
it('should create component under the directory', async () => {
const tree = await runSchematic(
'component',
{ name: 'hello', project: projectName, directory: 'components' },
appTree
);

expect(tree.exists('/libs/my-lib/src/lib/components/hello.tsx'));
});

it('should create with nested directories', async () => {
const tree = await runSchematic(
'component',
{ name: 'helloWorld', project: projectName, directory: 'foo' },
appTree
);

expect(tree.exists('/libs/my-lib/src/lib/foo/bar/faz/hello-world.tsx'));
});
});
});
22 changes: 19 additions & 3 deletions packages/react/src/schematics/component/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
url
} from '@angular-devkit/schematics';
import { Schema } from './schema';
import { getWorkspace, names, formatFiles } from '@nrwl/workspace';
import { formatFiles, getWorkspace, names } from '@nrwl/workspace';
import {
addDepsToPackageJson,
addGlobal,
Expand Down Expand Up @@ -108,7 +108,11 @@ function addExportsToBarrel(options: NormalizedSchema): Rule {
addGlobal(
indexSourceFile,
indexFilePath,
`export * from './lib/${options.name}/${options.fileName}';`
options.directory
? `export * from './lib/${options.directory}/${
options.fileName
}';`
: `export * from './lib/${options.fileName}';`
)
);
}
Expand Down Expand Up @@ -143,11 +147,23 @@ function normalizeOptions(
);
}

const slashes = ['/', '\\'];
slashes.forEach(s => {
if (componentFileName.indexOf(s) !== -1) {
const [name, ...rest] = componentFileName.split(s).reverse();
throw new Error(
`Found "${s}" in the component name. Did you mean to use the --directory option (e.g. \`nx g c ${name} --directory ${rest.join(
s
)}\`)?`
);
}
});

return {
...options,
directory: options.directory || '',
styledModule,
className,
name: fileName,
fileName: componentFileName,
projectSourceRoot
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export class <%= className %> extends Component<<%= className %>Props> {
render() {
return (
<<%= wrapper %>>
<h1>Welcome to <%= name %> component!</h1>
<p>Welcome to <%= name %> component!</p>
<% if (routing) { %>
<ul>
<li><Link to="/"><%= name %> root</Link></li>
Expand Down
1 change: 1 addition & 0 deletions packages/react/src/schematics/component/schema.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export interface Schema {
project: string;
style?: string;
skipTests?: boolean;
directory?: string;
export?: boolean;
pascalCaseFiles?: boolean;
classComponent?: boolean;
Expand Down
17 changes: 13 additions & 4 deletions packages/react/src/schematics/component/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"style": {
"description": "The file extension to be used for style files.",
"type": "string",
"alias": "s",
"default": "css",
"x-prompt": {
"message": "Which stylesheet format would you like to use?",
Expand Down Expand Up @@ -58,25 +59,33 @@
"description": "When true, does not create \"spec.ts\" test files for the new component.",
"default": false
},
"directory": {
"type": "string",
"description": "Create the component under this directory (can be nested).",
"alias": "d"
},
"export": {
"type": "boolean",
"default": false,
"description": "When true, the component is exported from the project index.ts (if it exists).",
"alias": "e",
"default": false,
"x-prompt": "Should this component be exported in the project?"
},
"pascalCaseFiles": {
"type": "boolean",
"description": "Use pascal case component file name (e.g. App.tsx)",
"description": "Use pascal case component file name (e.g. App.tsx).",
"alias": "p",
"default": false
},
"classComponent": {
"type": "boolean",
"description": "Use class components instead of functional component",
"alias": "c",
"description": "Use class components instead of functional component.",
"default": false
},
"routing": {
"type": "boolean",
"description": "Generate library with routes"
"description": "Generate a library with routes."
}
},
"required": ["name", "project"]
Expand Down
22 changes: 7 additions & 15 deletions packages/react/src/schematics/library/library.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,9 @@ describe('lib', () => {
const tree = await runSchematic('lib', { name: 'myLib' }, appTree);
expect(tree.exists(`libs/my-lib/jest.config.js`)).toBeTruthy();
expect(tree.exists('libs/my-lib/src/index.ts')).toBeTruthy();
expect(tree.exists('libs/my-lib/src/lib/my-lib/my-lib.tsx')).toBeTruthy();
expect(tree.exists('libs/my-lib/src/lib/my-lib/my-lib.css')).toBeTruthy();
expect(
tree.exists('libs/my-lib/src/lib/my-lib/my-lib.spec.tsx')
).toBeTruthy();
expect(tree.exists('libs/my-lib/src/lib/my-lib.tsx')).toBeTruthy();
expect(tree.exists('libs/my-lib/src/lib/my-lib.css')).toBeTruthy();
expect(tree.exists('libs/my-lib/src/lib/my-lib.spec.tsx')).toBeTruthy();
});
});

Expand Down Expand Up @@ -154,19 +152,13 @@ describe('lib', () => {
expect(tree.exists(`libs/my-dir/my-lib/jest.config.js`)).toBeTruthy();
expect(tree.exists('libs/my-dir/my-lib/src/index.ts')).toBeTruthy();
expect(
tree.exists(
'libs/my-dir/my-lib/src/lib/my-dir-my-lib/my-dir-my-lib.tsx'
)
tree.exists('libs/my-dir/my-lib/src/lib/my-dir-my-lib.tsx')
).toBeTruthy();
expect(
tree.exists(
'libs/my-dir/my-lib/src/lib/my-dir-my-lib/my-dir-my-lib.css'
)
tree.exists('libs/my-dir/my-lib/src/lib/my-dir-my-lib.css')
).toBeTruthy();
expect(
tree.exists(
'libs/my-dir/my-lib/src/lib/my-dir-my-lib/my-dir-my-lib.spec.tsx'
)
tree.exists('libs/my-dir/my-lib/src/lib/my-dir-my-lib.spec.tsx')
).toBeTruthy();
});

Expand Down Expand Up @@ -241,7 +233,7 @@ describe('lib', () => {
appTree
);

expect(result.exists('libs/my-lib/src/lib/my-lib/my-lib.scss'));
expect(result.exists('libs/my-lib/src/lib/my-lib.scss')).toBeTruthy();
});
});

Expand Down
15 changes: 8 additions & 7 deletions packages/tao/src/commands/generate.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
import {
coerceTypes,
convertAliases,
convertToCamelCase,
handleErrors,
Schema,
coerceTypes
Schema
} from '../shared/params';
import {
experimental,
JsonObject,
logging,
normalize,
schema,
tags,
terminal,
virtualFs,
experimental
virtualFs
} from '@angular-devkit/core';
import { DryRunEvent, HostTree, Schematic } from '@angular-devkit/schematics';
import { NodeJsSyncHost } from '@angular-devkit/core/node';
import { NodeWorkflow } from '@angular-devkit/schematics/tools';
import * as inquirer from 'inquirer';
import { logger } from '../shared/logger';
import { printHelp, commandName } from '../shared/print-help';
import { commandName, printHelp } from '../shared/print-help';
import * as fs from 'fs';
import minimist = require('minimist');

Expand Down Expand Up @@ -281,8 +282,8 @@ async function runSchematic(
);
const record = { loggingQueue: [] as string[], error: false };
workflow.reporter.subscribe(createRecorder(record, logger));
const schematicOptions = coerceTypes(
opts.schematicOptions,
const schematicOptions = convertAliases(
coerceTypes(opts.schematicOptions, flattenedSchema as any),
flattenedSchema as any
);
await workflow
Expand Down
Loading

0 comments on commit f2fa8a8

Please sign in to comment.