fix(frontend): fix react component for apps

This commit is contained in:
Jason Jean 2019-05-26 12:09:10 -04:00 committed by Victor Savkin
parent c3fc1fd95e
commit 2958f503f7
4 changed files with 141 additions and 76 deletions

View File

@ -27,7 +27,8 @@
"component": { "component": {
"factory": "./src/schematics/component/component", "factory": "./src/schematics/component/component",
"schema": "./src/schematics/component/schema.json", "schema": "./src/schematics/component/schema.json",
"description": "Create a component" "description": "Create a component",
"aliases": "c"
} }
} }
} }

View File

@ -1,18 +1,18 @@
import { Tree } from '@angular-devkit/schematics'; import { Tree } from '@angular-devkit/schematics';
import { createEmptyWorkspace } from '@nrwl/workspace/testing'; import { createEmptyWorkspace } from '@nrwl/workspace/testing';
import { runSchematic } from '../../utils/testing'; import { runSchematic, createApp, createLib } from '../../utils/testing';
import { names } from '@nrwl/workspace/src/utils/name-utils';
import { readJsonInTree } from '@nrwl/workspace/src/utils/ast-utils'; import { readJsonInTree } from '@nrwl/workspace/src/utils/ast-utils';
describe('component', () => { describe('component', () => {
let appTree: Tree; let appTree: Tree;
let projectName: string; let projectName: string;
beforeEach(() => { beforeEach(async () => {
projectName = 'my-lib'; projectName = 'my-lib';
appTree = Tree.empty(); appTree = Tree.empty();
appTree = createEmptyWorkspace(appTree); appTree = createEmptyWorkspace(appTree);
appTree = createLib(appTree, projectName); appTree = await createApp(appTree, 'my-app');
appTree = await createLib(appTree, projectName);
}); });
it('should generate files', async () => { it('should generate files', async () => {
@ -29,6 +29,20 @@ describe('component', () => {
expect(tree.exists('libs/my-lib/src/lib/hello/hello.css')).toBeTruthy(); expect(tree.exists('libs/my-lib/src/lib/hello/hello.css')).toBeTruthy();
}); });
it('should generate files for an app', async () => {
const tree = await runSchematic(
'component',
{ name: 'hello', project: 'my-app' },
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();
});
describe('--export', () => { describe('--export', () => {
it('should add to index.ts barrel', async () => { it('should add to index.ts barrel', async () => {
const tree = await runSchematic( const tree = await runSchematic(
@ -41,6 +55,18 @@ describe('component', () => {
expect(indexContent).toMatch(/lib\/hello\/hello/); expect(indexContent).toMatch(/lib\/hello\/hello/);
}); });
it('should not export from an app', async () => {
const tree = await runSchematic(
'component',
{ name: 'hello', project: 'my-app', export: true },
appTree
);
const indexContent = tree.read('libs/my-lib/src/index.ts').toString();
expect(indexContent).not.toMatch(/lib\/hello\/hello/);
});
}); });
describe('--pascalCaseFiles', () => { describe('--pascalCaseFiles', () => {
@ -123,27 +149,3 @@ describe('component', () => {
}); });
}); });
}); });
export function createLib(tree: Tree, libName: string): Tree {
const { fileName } = names(libName);
tree.create(`/libs/${fileName}/src/index.ts`, `\n`);
tree.overwrite(
'/angular.json',
`
{
"projects": {
"${libName}": {
"root": "libs/${fileName}",
"sourceRoot": "libs/${fileName}/src",
"projectType": "library",
"schematics": {}
}
}
}
`
);
return tree;
}

View File

@ -8,12 +8,13 @@ import {
move, move,
noop, noop,
Rule, Rule,
SchematicContext,
template, template,
Tree, Tree,
url url
} from '@angular-devkit/schematics'; } from '@angular-devkit/schematics';
import { Schema } from './schema'; import { Schema } from './schema';
import { names } from '@nrwl/workspace'; import { getWorkspace, names, formatFiles } from '@nrwl/workspace';
import { import {
addDepsToPackageJson, addDepsToPackageJson,
addGlobal, addGlobal,
@ -21,7 +22,6 @@ import {
insert insert
} from '@nrwl/workspace/src/utils/ast-utils'; } from '@nrwl/workspace/src/utils/ast-utils';
import { CSS_IN_JS_DEPENDENCIES } from '../../utils/styled'; import { CSS_IN_JS_DEPENDENCIES } from '../../utils/styled';
import { formatFiles } from '@nrwl/workspace';
interface NormalizedSchema extends Schema { interface NormalizedSchema extends Schema {
projectSourceRoot: Path; projectSourceRoot: Path;
@ -31,8 +31,8 @@ interface NormalizedSchema extends Schema {
} }
export default function(schema: Schema): Rule { export default function(schema: Schema): Rule {
return (host: Tree) => { return (host: Tree, context: SchematicContext) => {
const options = normalizeOptions(host, schema); const options = normalizeOptions(host, schema, context);
return chain([ return chain([
createComponentFiles(options), createComponentFiles(options),
addStyledModuleDependencies(options), addStyledModuleDependencies(options),
@ -43,19 +43,29 @@ export default function(schema: Schema): Rule {
} }
function createComponentFiles(options: NormalizedSchema): Rule { function createComponentFiles(options: NormalizedSchema): Rule {
return mergeWith( return async (host: Tree, context: SchematicContext) => {
apply(url(`./files`), [ const workspace = await getWorkspace(host);
template({ const directory = join(
...options, options.projectSourceRoot,
tmpl: '' workspace.projects.get(options.project).extensions.projectType ===
}), 'application'
options.skipTests ? filter(file => !/.*spec.tsx/.test(file)) : noop(), ? 'app'
options.styledModule : 'lib'
? filter(file => !file.endsWith(`.${options.style}`)) );
: noop(), return mergeWith(
move(join(options.projectSourceRoot, 'lib')) apply(url(`./files`), [
]) template({
); ...options,
tmpl: ''
}),
options.skipTests ? filter(file => !/.*spec.tsx/.test(file)) : noop(),
options.styledModule
? filter(file => !file.endsWith(`.${options.style}`))
: noop(),
move(directory)
])
);
};
} }
function addStyledModuleDependencies(options: NormalizedSchema): Rule { function addStyledModuleDependencies(options: NormalizedSchema): Rule {
@ -70,48 +80,62 @@ function addStyledModuleDependencies(options: NormalizedSchema): Rule {
} }
function addExportsToBarrel(options: NormalizedSchema): Rule { function addExportsToBarrel(options: NormalizedSchema): Rule {
return options.export return async (host: Tree) => {
? (host: Tree) => { const workspace = await getWorkspace(host);
const indexFilePath = join(options.projectSourceRoot, 'index.ts'); const isApp =
const buffer = host.read(indexFilePath); workspace.projects.get(options.project).extensions.type === 'application';
return options.export && !isApp
if (!!buffer) { ? (host: Tree) => {
const indexSource = buffer!.toString('utf-8'); const indexFilePath = join(options.projectSourceRoot, 'index.ts');
const indexSourceFile = ts.createSourceFile( const buffer = host.read(indexFilePath);
indexFilePath, if (!!buffer) {
indexSource, const indexSource = buffer!.toString('utf-8');
ts.ScriptTarget.Latest, const indexSourceFile = ts.createSourceFile(
true
);
insert(
host,
indexFilePath,
addGlobal(
indexSourceFile,
indexFilePath, indexFilePath,
`export { default as ${options.className}, ${ indexSource,
options.className ts.ScriptTarget.Latest,
}Props } from './lib/${options.name}/${options.fileName}';` true
) );
);
}
return host; insert(
} host,
: noop(); indexFilePath,
addGlobal(
indexSourceFile,
indexFilePath,
`export { default as ${options.className}, ${
options.className
}Props } from './lib/${options.name}/${options.fileName}';`
)
);
}
return host;
}
: noop();
};
} }
function normalizeOptions(host: Tree, options: Schema): NormalizedSchema { function normalizeOptions(
host: Tree,
options: Schema,
context: SchematicContext
): NormalizedSchema {
const { className, fileName } = names(options.name); const { className, fileName } = names(options.name);
const componentFileName = options.pascalCaseFiles ? className : fileName; const componentFileName = options.pascalCaseFiles ? className : fileName;
const { sourceRoot: projectSourceRoot } = getProjectConfig( const { sourceRoot: projectSourceRoot, projectType } = getProjectConfig(
host, host,
options.project options.project
); );
if (options.export && projectType === 'application') {
context.logger.warn(
`The "--export" option should not be used with applications and will do nothing.`
);
}
const styledModule = /^(css|scss|less|styl)$/.test(options.style) const styledModule = /^(css|scss|less|styl)$/.test(options.style)
? null ? null
: options.style; : options.style;

View File

@ -1,6 +1,8 @@
import { join } from 'path'; import { join } from 'path';
import { SchematicTestRunner } from '@angular-devkit/schematics/testing'; import { SchematicTestRunner } from '@angular-devkit/schematics/testing';
import { Rule, Tree } from '@angular-devkit/schematics'; import { Rule, Tree } from '@angular-devkit/schematics';
import { names } from '@nrwl/workspace/src/utils/name-utils';
import { updateWorkspace } from '@nrwl/workspace/src/utils/workspace';
const testRunner = new SchematicTestRunner( const testRunner = new SchematicTestRunner(
'@nrwl/react', '@nrwl/react',
@ -14,3 +16,39 @@ export function runSchematic(schematicName: string, options: any, tree: Tree) {
export function callRule(rule: Rule, tree: Tree) { export function callRule(rule: Rule, tree: Tree) {
return testRunner.callRule(rule, tree).toPromise(); return testRunner.callRule(rule, tree).toPromise();
} }
export function createApp(tree: Tree, appName: string): Promise<Tree> {
const { fileName } = names(appName);
return callRule(
updateWorkspace(workspace => {
workspace.projects.add({
name: fileName,
root: `apps/${fileName}`,
projectType: 'application',
sourceRoot: `apps/${fileName}/src`,
targets: {}
});
}),
tree
);
}
export function createLib(tree: Tree, libName: string): Promise<Tree> {
const { fileName } = names(libName);
tree.create(`/libs/${fileName}/src/index.ts`, `\n`);
return callRule(
updateWorkspace(workspace => {
workspace.projects.add({
name: fileName,
root: `libs/${fileName}`,
projectType: 'library',
sourceRoot: `libs/${fileName}/src`,
targets: {}
});
}),
tree
);
}