feat(nx): generating apps works without ng-add (#1287)

This commit is contained in:
Jason Jean 2019-04-26 15:19:50 -04:00 committed by GitHub
parent 422fef7c1c
commit a3be21ca78
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 305 additions and 129 deletions

View File

@ -1,19 +1,15 @@
import {
apply,
chain,
externalSchematic,
mergeWith,
move,
Rule,
SchematicContext,
template,
Tree,
url
Tree
} from '@angular-devkit/schematics';
import { join, normalize, Path } from '@angular-devkit/core';
import { Schema } from './schema';
import { updateJsonInTree } from '@nrwl/workspace';
import { toFileName } from '@nrwl/workspace';
import ngAdd from '../ng-add/ng-add';
interface NormalizedSchema extends Schema {
appProjectRoot: Path;
@ -60,6 +56,7 @@ export default function(schema: Schema): Rule {
return (host: Tree, context: SchematicContext) => {
const options = normalizeOptions(schema);
return chain([
ngAdd(),
externalSchematic('@nrwl/node', 'application', schema),
addMainFile(options),
addTypes(options)

View File

@ -1,5 +1,16 @@
import { Rule, chain, externalSchematic } from '@angular-devkit/schematics';
import { addDepsToPackageJson, updateJsonInTree } from '@nrwl/workspace';
import {
Rule,
chain,
externalSchematic,
Tree,
noop
} from '@angular-devkit/schematics';
import {
addDepsToPackageJson,
updateJsonInTree,
readJsonInTree,
addPackageWithNgAdd
} from '@nrwl/workspace';
import {
expressTypingsVersion,
expressVersion,
@ -29,7 +40,8 @@ function moveDependency(): Rule {
export default function() {
return chain([
externalSchematic('@nrwl/node', 'ng-add', {}),
addPackageWithNgAdd('@nrwl/node'),
addPackageWithNgAdd('@nrwl/jest'),
addDependencies(),
moveDependency()
]);

View File

@ -13,6 +13,7 @@ import {
import { join, normalize, Path } from '@angular-devkit/core';
import { Schema } from './schema';
import { toFileName } from '@nrwl/workspace';
import ngAdd from '../ng-add/ng-add';
interface NormalizedSchema extends Schema {
appProjectRoot: Path;
@ -64,6 +65,7 @@ export default function(schema: Schema): Rule {
return (host: Tree, context: SchematicContext) => {
const options = normalizeOptions(schema);
return chain([
ngAdd(),
externalSchematic('@nrwl/node', 'application', schema),
addMainFile(options),
addAppFiles(options)

View File

@ -1,12 +1,16 @@
import { Rule, chain, externalSchematic } from '@angular-devkit/schematics';
import { addDepsToPackageJson, updateJsonInTree } from '@nrwl/workspace';
import { Rule, chain } from '@angular-devkit/schematics';
import {
addPackageWithNgAdd,
addDepsToPackageJson,
updateJsonInTree
} from '@nrwl/workspace';
import {
nestJsSchematicsVersion,
nestJsVersion,
nxVersion
} from '../../utils/versions';
function addDependencies(): Rule {
export function addDependencies(): Rule {
return addDepsToPackageJson(
{
'@nestjs/common': nestJsVersion,
@ -31,7 +35,8 @@ function moveDependency(): Rule {
export default function() {
return chain([
externalSchematic('@nrwl/node', 'ng-add', {}),
addPackageWithNgAdd('@nrwl/node'),
addPackageWithNgAdd('@nrwl/jest'),
addDependencies(),
moveDependency()
]);

View File

@ -14,7 +14,7 @@ import { map } from 'rxjs/operators';
import { getNodeWebpackConfig } from '../../utils/node.config';
import { OUT_FILENAME } from '../../utils/config';
import { BuildBuilderOptions } from '../../utils/types';
import { normalizeBuildOptions } from '../../../../web/src/utils/normalize';
import { normalizeBuildOptions } from '../../utils/normalize';
try {
require('dotenv').config();

View File

@ -17,6 +17,7 @@ import { updateJsonInTree } from '@nrwl/workspace';
import { toFileName } from '@nrwl/workspace';
import { getProjectConfig } from '@nrwl/workspace';
import { offsetFromRoot } from '@nrwl/workspace';
import ngAdd from '../ng-add/ng-add';
interface NormalizedSchema extends Schema {
appProjectRoot: Path;
@ -145,6 +146,7 @@ export default function(schema: Schema): Rule {
return (host: Tree, context: SchematicContext) => {
const options = normalizeOptions(schema);
return chain([
ngAdd(),
addAppFiles(options),
updateAngularJson(options),
updateNxJson(options),

View File

@ -1,5 +1,9 @@
import { Rule, chain } from '@angular-devkit/schematics';
import { addDepsToPackageJson, updateJsonInTree } from '@nrwl/workspace';
import {
addDepsToPackageJson,
updateJsonInTree,
addPackageWithNgAdd
} from '@nrwl/workspace';
import { nxVersion } from '../../utils/versions';
function addDependencies(): Rule {
@ -21,5 +25,9 @@ function moveDependency(): Rule {
}
export default function() {
return chain([addDependencies(), moveDependency()]);
return chain([
addPackageWithNgAdd('@nrwl/jest'),
addDependencies(),
moveDependency()
]);
}

View File

@ -0,0 +1,102 @@
import { normalizeBuildOptions } from './normalize';
import { BuildBuilderOptions } from './types';
import { Path, normalize } from '@angular-devkit/core';
import * as fs from 'fs';
describe('normalizeBuildOptions', () => {
let testOptions: BuildBuilderOptions;
let root: string;
let sourceRoot: Path;
beforeEach(() => {
testOptions = {
main: 'apps/nodeapp/src/main.ts',
tsConfig: 'apps/nodeapp/tsconfig.app.json',
outputPath: 'dist/apps/nodeapp',
fileReplacements: [
{
replace: 'apps/environment/environment.ts',
with: 'apps/environment/environment.prod.ts'
},
{
replace: 'module1.ts',
with: 'module2.ts'
}
],
assets: [],
statsJson: false
};
root = '/root';
sourceRoot = normalize('apps/nodeapp/src');
});
it('should add the root', () => {
const result = normalizeBuildOptions(testOptions, root, sourceRoot);
expect(result.root).toEqual('/root');
});
it('should resolve main from root', () => {
const result = normalizeBuildOptions(testOptions, root, sourceRoot);
expect(result.main).toEqual('/root/apps/nodeapp/src/main.ts');
});
it('should resolve the output path', () => {
const result = normalizeBuildOptions(testOptions, root, sourceRoot);
expect(result.outputPath).toEqual('/root/dist/apps/nodeapp');
});
it('should resolve the tsConfig path', () => {
const result = normalizeBuildOptions(testOptions, root, sourceRoot);
expect(result.tsConfig).toEqual('/root/apps/nodeapp/tsconfig.app.json');
});
it('should normalize asset patterns', () => {
spyOn(fs, 'statSync').and.returnValue({
isDirectory: () => true
});
const result = normalizeBuildOptions(
<BuildBuilderOptions>{
...testOptions,
root,
assets: [
'apps/nodeapp/src/assets',
{
input: '/outsideroot',
output: 'output',
glob: '**/*',
ignore: ['**/*.json']
}
]
},
root,
sourceRoot
);
expect(result.assets).toEqual([
{
input: '/root/apps/nodeapp/src/assets',
output: 'assets',
glob: '**/*'
},
{
input: '/outsideroot',
output: 'output',
glob: '**/*',
ignore: ['**/*.json']
}
]);
});
it('should resolve the file replacement paths', () => {
const result = normalizeBuildOptions(testOptions, root, sourceRoot);
expect(result.fileReplacements).toEqual([
{
replace: '/root/apps/environment/environment.ts',
with: '/root/apps/environment/environment.prod.ts'
},
{
replace: '/root/module1.ts',
with: '/root/module2.ts'
}
]);
});
});

View File

@ -0,0 +1,86 @@
import { Path, normalize } from '@angular-devkit/core';
import { resolve, dirname, relative, basename } from 'path';
import {
AssetPattern,
AssetPatternObject
} from '@angular-devkit/build-angular';
import { BuildBuilderOptions } from './types';
import { statSync } from 'fs';
export interface FileReplacement {
replace: string;
with: string;
}
export function normalizeBuildOptions<T extends BuildBuilderOptions>(
options: T,
root: string,
sourceRoot: Path
): T {
return {
...options,
root: root,
sourceRoot: sourceRoot,
main: resolve(root, options.main),
outputPath: resolve(root, options.outputPath),
tsConfig: resolve(root, options.tsConfig),
fileReplacements: normalizeFileReplacements(root, options.fileReplacements),
assets: normalizeAssets(options.assets, root, sourceRoot),
webpackConfig: options.webpackConfig
? resolve(root, options.webpackConfig)
: options.webpackConfig
};
}
function normalizeAssets(
assets: AssetPattern[],
root: string,
sourceRoot: Path
): AssetPatternObject[] {
return assets.map(asset => {
if (typeof asset === 'string') {
const assetPath = normalize(asset);
const resolvedAssetPath = resolve(root, assetPath);
const resolvedSourceRoot = resolve(root, sourceRoot);
if (!resolvedAssetPath.startsWith(resolvedSourceRoot)) {
throw new Error(
`The ${resolvedAssetPath} asset path must start with the project source root: ${sourceRoot}`
);
}
const isDirectory = statSync(resolvedAssetPath).isDirectory();
const input = isDirectory
? resolvedAssetPath
: dirname(resolvedAssetPath);
const output = relative(resolvedSourceRoot, resolve(root, input));
const glob = isDirectory ? '**/*' : basename(resolvedAssetPath);
return {
input,
output,
glob
};
} else {
if (asset.output.startsWith('..')) {
throw new Error(
'An asset cannot be written to a location outside of the output path.'
);
}
return {
...asset,
// Now we remove starting slash to make Webpack place it from the output root.
output: asset.output.replace(/^\//, '')
};
}
});
}
function normalizeFileReplacements(
root: string,
fileReplacements: FileReplacement[]
): FileReplacement[] {
return fileReplacements.map(fileReplacement => ({
replace: resolve(root, fileReplacement.replace),
with: resolve(root, fileReplacement.with)
}));
}

View File

@ -14,11 +14,16 @@ import {
filter
} from '@angular-devkit/schematics';
import { Schema } from './schema';
import { updateJsonInTree, NxJson } from '@nrwl/workspace';
import { toFileName, names } from '@nrwl/workspace';
import { offsetFromRoot } from '@nrwl/workspace';
import { getNpmScope } from '@nrwl/workspace';
import { formatFiles } from '@nrwl/workspace';
import {
updateJsonInTree,
NxJson,
toFileName,
names,
offsetFromRoot,
getNpmScope,
formatFiles
} from '@nrwl/workspace';
import ngAdd from '../ng-add/ng-add';
interface NormalizedSchema extends Schema {
projectName: string;
@ -139,10 +144,11 @@ function addProject(options: NormalizedSchema): Rule {
}
export default function(schema: Schema): Rule {
return (host: Tree, context: SchematicContext) => {
return (host: Tree) => {
const options = normalizeOptions(host, schema);
return chain([
ngAdd(),
createApplicationFiles(options),
updateNxJson(options),
addProject(options),
@ -163,7 +169,7 @@ export default function(schema: Schema): Rule {
})
: noop(),
formatFiles(options)
])(host, context);
]);
};
}

View File

@ -8,7 +8,8 @@ import {
import {
addDepsToPackageJson,
updateJsonInTree,
readJsonInTree
readJsonInTree,
addPackageWithNgAdd
} from '@nrwl/workspace';
import {
frameworkVersion,
@ -18,7 +19,7 @@ import {
nxVersion
} from '../../utils/versions';
function addDependencies(): Rule {
export function addDependencies(): Rule {
return addDepsToPackageJson(
{
react: frameworkVersion,
@ -42,59 +43,11 @@ function moveDependency(): Rule {
});
}
function addJest(): Rule {
return (host: Tree) => {
const packageJson = readJsonInTree(host, 'package.json');
return !packageJson.devDependencies['@nrwl/jest']
? externalSchematic(
'@nrwl/jest',
'ng-add',
{},
{
interactive: false
}
)
: noop();
};
}
function addWeb(): Rule {
return (host: Tree) => {
const packageJson = readJsonInTree(host, 'package.json');
return !packageJson.devDependencies['@nrwl/web']
? externalSchematic(
'@nrwl/web',
'ng-add',
{},
{
interactive: false
}
)
: noop();
};
}
function addCypress(): Rule {
return (host: Tree) => {
const packageJson = readJsonInTree(host, 'package.json');
return !packageJson.devDependencies['@nrwl/cypress']
? externalSchematic(
'@nrwl/cypress',
'ng-add',
{},
{
interactive: false
}
)
: noop();
};
}
export default function() {
return chain([
addJest(),
addCypress(),
addWeb(),
addPackageWithNgAdd('@nrwl/jest'),
addPackageWithNgAdd('@nrwl/cypress'),
addPackageWithNgAdd('@nrwl/web'),
addDependencies(),
moveDependency()
]);

View File

@ -14,11 +14,16 @@ import {
filter
} from '@angular-devkit/schematics';
import { Schema } from './schema';
import { updateJsonInTree, NxJson } from '@nrwl/workspace';
import { toFileName, names } from '@nrwl/workspace';
import { offsetFromRoot } from '@nrwl/workspace';
import { getNpmScope } from '@nrwl/workspace';
import { formatFiles } from '@nrwl/workspace';
import {
updateJsonInTree,
NxJson,
toFileName,
names,
offsetFromRoot,
getNpmScope,
formatFiles
} from '@nrwl/workspace';
import ngAdd from '../ng-add/ng-add';
interface NormalizedSchema extends Schema {
projectName: string;
@ -143,6 +148,7 @@ export default function(schema: Schema): Rule {
const options = normalizeOptions(host, schema);
return chain([
ngAdd(),
createApplicationFiles(options),
updateNxJson(options),
addProject(options),

View File

@ -1,11 +1,5 @@
import {
Rule,
chain,
externalSchematic,
noop,
Tree
} from '@angular-devkit/schematics';
import { updateJsonInTree, readJsonInTree } from '@nrwl/workspace';
import { Rule, chain } from '@angular-devkit/schematics';
import { updateJsonInTree, addPackageWithNgAdd } from '@nrwl/workspace';
import { addDepsToPackageJson } from '@nrwl/workspace';
import {
nxVersion,
@ -23,38 +17,6 @@ function addDependencies(): Rule {
);
}
function addJest(): Rule {
return (host: Tree) => {
const packageJson = readJsonInTree(host, 'package.json');
return !packageJson.devDependencies['@nrwl/jest']
? externalSchematic(
'@nrwl/jest',
'ng-add',
{},
{
interactive: false
}
)
: noop();
};
}
function addCypress(): Rule {
return (host: Tree) => {
const packageJson = readJsonInTree(host, 'package.json');
return !packageJson.devDependencies['@nrwl/cypress']
? externalSchematic(
'@nrwl/cypress',
'ng-add',
{},
{
interactive: false
}
)
: noop();
};
}
function moveDependency(): Rule {
return updateJsonInTree('package.json', json => {
json.dependencies = json.dependencies || {};
@ -65,5 +27,10 @@ function moveDependency(): Rule {
}
export default function() {
return chain([addJest(), addCypress(), addDependencies(), moveDependency()]);
return chain([
addPackageWithNgAdd('@nrwl/jest'),
addPackageWithNgAdd('@nrwl/cypress'),
addDependencies(),
moveDependency()
]);
}

View File

@ -1,14 +1,14 @@
import { normalize, Path } from '@angular-devkit/core';
import { basename, dirname, relative, resolve } from 'path';
import { WebBuildBuilderOptions } from '../builders/build/build.builder';
import { Path, normalize } from '@angular-devkit/core';
import { resolve, dirname, relative, basename } from 'path';
import { BuildOptions } from '@angular-devkit/build-angular/src/angular-cli-files/models/build-options';
import {
NormalizedBrowserBuilderSchema,
AssetPattern,
AssetPatternObject,
NormalizedBrowserBuilderSchema
AssetPatternObject
} from '@angular-devkit/build-angular';
import { WebBuildBuilderOptions } from '../builders/build/build.builder';
import { statSync } from 'fs';
import { BuildBuilderOptions } from './types';
import { statSync } from 'fs';
export interface FileReplacement {
replace: string;

View File

@ -57,6 +57,7 @@ export {
export { formatFiles } from './src/utils/rules/format-files';
export { deleteFile } from './src/utils/rules/deleteFile';
export * from './src/utils/rules/ng-add';
export { updateKarmaConf } from './src/utils/rules/update-karma-conf';
import * as strings from './src/utils/strings';

View File

@ -609,6 +609,8 @@ export function updateJsonInTree<T = any, O = T>(
};
}
let installAdded = false;
export function addDepsToPackageJson(deps: any, devDeps: any): Rule {
return updateJsonInTree('package.json', (json, context: SchematicContext) => {
json.dependencies = {
@ -619,7 +621,10 @@ export function addDepsToPackageJson(deps: any, devDeps: any): Rule {
...devDeps,
...(json.devDependencies || {})
};
context.addTask(new NodePackageInstallTask());
if (!installAdded) {
context.addTask(new NodePackageInstallTask());
installAdded = true;
}
return json;
});
}

View File

@ -0,0 +1,23 @@
import {
Rule,
Tree,
externalSchematic,
noop
} from '@angular-devkit/schematics';
import { readJsonInTree } from '../ast-utils';
/**
* Calls ng-add _if_ the package does not already exist
*/
export function addPackageWithNgAdd(packageName: string): Rule {
return (host: Tree) => {
const { dependencies, devDependencies } = readJsonInTree(
host,
'package.json'
);
return dependencies[packageName] || devDependencies[packageName]
? noop()
: externalSchematic(packageName, 'ng-add', {});
};
}

View File

@ -13,6 +13,7 @@
"baseUrl": ".",
"paths": {
"@nrwl/workspace": ["./packages/workspace"],
"@nrwl/workspace/*": ["./packages/workspace/*"],
"@nrwl/workspace/testing": ["./packages/workspace/testing"]
}
},