feat(schematics): fail gracefully and warn when project is missing a format npm script
This commit is contained in:
parent
e18f94e566
commit
d2350c4e86
@ -75,7 +75,7 @@
|
||||
"jest": {
|
||||
"modulePathIgnorePatterns": [
|
||||
"tmp",
|
||||
"files"
|
||||
"collection/.*/files"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,7 +24,7 @@ import { addBootstrapToModule } from '@schematics/angular/utility/ast-utils';
|
||||
import { insertImport } from '@schematics/angular/utility/route-utils';
|
||||
import { addApp, readCliConfigFile } from '../../utils/fileutils';
|
||||
import { offsetFromRoot } from '../../utils/common';
|
||||
import { wrapIntoFormat } from '../../utils/tasks';
|
||||
import { formatFiles } from '../../utils/rules/format-files';
|
||||
|
||||
interface NormalizedSchema extends Schema {
|
||||
fullName: string;
|
||||
@ -236,57 +236,56 @@ ts_web_test(
|
||||
}
|
||||
|
||||
export default function(schema: Schema): Rule {
|
||||
return wrapIntoFormat(() => {
|
||||
let npmScope = schema.npmScope;
|
||||
if (!npmScope) {
|
||||
npmScope = readCliConfigFile().project.npmScope;
|
||||
}
|
||||
let npmScope = schema.npmScope;
|
||||
if (!npmScope) {
|
||||
npmScope = readCliConfigFile().project.npmScope;
|
||||
}
|
||||
|
||||
const options = normalizeOptions(schema);
|
||||
const templateSource = apply(url('./files'), [
|
||||
template({
|
||||
utils: strings,
|
||||
dot: '.',
|
||||
tmpl: '',
|
||||
offsetFromRoot: offsetFromRoot(options.fullPath),
|
||||
...(options as object),
|
||||
npmScope
|
||||
})
|
||||
]);
|
||||
const options = normalizeOptions(schema);
|
||||
const templateSource = apply(url('./files'), [
|
||||
template({
|
||||
utils: strings,
|
||||
dot: '.',
|
||||
tmpl: '',
|
||||
offsetFromRoot: offsetFromRoot(options.fullPath),
|
||||
...(options as object),
|
||||
npmScope
|
||||
})
|
||||
]);
|
||||
|
||||
const selector = `${options.prefix}-root`;
|
||||
const selector = `${options.prefix}-root`;
|
||||
|
||||
return chain([
|
||||
branchAndMerge(chain([mergeWith(templateSource)])),
|
||||
externalSchematic('@schematics/angular', 'module', {
|
||||
name: 'app',
|
||||
commonModule: false,
|
||||
flat: true,
|
||||
routing: false,
|
||||
sourceDir: options.fullPath,
|
||||
spec: false
|
||||
}),
|
||||
externalSchematic('@schematics/angular', 'component', {
|
||||
name: 'app',
|
||||
selector: selector,
|
||||
sourceDir: options.fullPath,
|
||||
flat: true,
|
||||
inlineStyle: options.inlineStyle,
|
||||
inlineTemplate: options.inlineTemplate,
|
||||
spec: !options.skipTests,
|
||||
styleext: options.style,
|
||||
viewEncapsulation: options.viewEncapsulation,
|
||||
changeDetection: options.changeDetection
|
||||
}),
|
||||
updateComponentTemplate(options),
|
||||
addBootstrap(options.fullPath),
|
||||
addNxModule(options.fullPath),
|
||||
addAppToAngularCliJson(options),
|
||||
addBazelBuildFile(options.fullPath),
|
||||
addAppToAngularCliJson(options),
|
||||
options.routing ? addRouterRootConfiguration(options.fullPath) : noop()
|
||||
]);
|
||||
});
|
||||
return chain([
|
||||
branchAndMerge(chain([mergeWith(templateSource)])),
|
||||
externalSchematic('@schematics/angular', 'module', {
|
||||
name: 'app',
|
||||
commonModule: false,
|
||||
flat: true,
|
||||
routing: false,
|
||||
sourceDir: options.fullPath,
|
||||
spec: false
|
||||
}),
|
||||
externalSchematic('@schematics/angular', 'component', {
|
||||
name: 'app',
|
||||
selector: selector,
|
||||
sourceDir: options.fullPath,
|
||||
flat: true,
|
||||
inlineStyle: options.inlineStyle,
|
||||
inlineTemplate: options.inlineTemplate,
|
||||
spec: !options.skipTests,
|
||||
styleext: options.style,
|
||||
viewEncapsulation: options.viewEncapsulation,
|
||||
changeDetection: options.changeDetection
|
||||
}),
|
||||
updateComponentTemplate(options),
|
||||
addBootstrap(options.fullPath),
|
||||
addNxModule(options.fullPath),
|
||||
addAppToAngularCliJson(options),
|
||||
addBazelBuildFile(options.fullPath),
|
||||
addAppToAngularCliJson(options),
|
||||
options.routing ? addRouterRootConfiguration(options.fullPath) : noop(),
|
||||
formatFiles(options)
|
||||
]);
|
||||
}
|
||||
|
||||
function normalizeOptions(options: Schema): NormalizedSchema {
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
export interface Schema {
|
||||
name: string;
|
||||
skipFormat: boolean;
|
||||
npmScope?: string;
|
||||
directory?: string;
|
||||
sourceDir?: string;
|
||||
|
||||
@ -43,6 +43,11 @@
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"skipFormat": {
|
||||
"description": "Skip formatting files",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"skipTests": {
|
||||
"description": "Skip creating spec files.",
|
||||
"type": "boolean",
|
||||
|
||||
@ -29,8 +29,8 @@ import {
|
||||
toFileName,
|
||||
toPropertyName
|
||||
} from '../../utils/name-utils';
|
||||
import { wrapIntoFormat } from '../../utils/tasks';
|
||||
import { Schema } from './schema';
|
||||
import { formatFiles } from '../../utils/rules/format-files';
|
||||
|
||||
interface NormalizedSchema extends Schema {
|
||||
name: string;
|
||||
@ -279,12 +279,11 @@ function validateLibSchema(schema) {
|
||||
}
|
||||
|
||||
export default function(schema: Schema): Rule {
|
||||
return wrapIntoFormat(() => {
|
||||
const { templateSource, routingRules } = validateLibSchema(schema);
|
||||
const { templateSource, routingRules } = validateLibSchema(schema);
|
||||
|
||||
return chain([
|
||||
branchAndMerge(chain([mergeWith(templateSource)])),
|
||||
...routingRules
|
||||
]);
|
||||
});
|
||||
return chain([
|
||||
branchAndMerge(chain([mergeWith(templateSource)])),
|
||||
...routingRules,
|
||||
formatFiles(schema)
|
||||
]);
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
export interface Schema {
|
||||
name: string;
|
||||
skipFormat: boolean;
|
||||
directory?: string;
|
||||
sourceDir?: string;
|
||||
nomodule: boolean;
|
||||
|
||||
@ -1,38 +1,43 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/schema",
|
||||
"id": "library",
|
||||
"title": "Create a library",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Library name"
|
||||
},
|
||||
"directory": {
|
||||
"type": "string",
|
||||
"description": "A directory where the app is placed"
|
||||
},
|
||||
"nomodule": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Generate a simple TS library when set to true."
|
||||
},
|
||||
"routing": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Add router configuration. See lazy for more information."
|
||||
},
|
||||
"lazy": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Add RouterModule.forChild when set to true, and a simple array of routes when set to false."
|
||||
},
|
||||
"parentModule": {
|
||||
"type": "string",
|
||||
"description": "Update the router configuration of the parent module using loadChildren or children, depending on what `lazy` is set to."
|
||||
}
|
||||
"$schema": "http://json-schema.org/schema",
|
||||
"id": "library",
|
||||
"title": "Create a library",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Library name"
|
||||
},
|
||||
"required": [
|
||||
"name"
|
||||
]
|
||||
}
|
||||
"directory": {
|
||||
"type": "string",
|
||||
"description": "A directory where the app is placed"
|
||||
},
|
||||
"nomodule": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Generate a simple TS library when set to true."
|
||||
},
|
||||
"routing": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Add router configuration. See lazy for more information."
|
||||
},
|
||||
"skipFormat": {
|
||||
"description": "Skip formatting files",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"lazy": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description":
|
||||
"Add RouterModule.forChild when set to true, and a simple array of routes when set to false."
|
||||
},
|
||||
"parentModule": {
|
||||
"type": "string",
|
||||
"description":
|
||||
"Update the router configuration of the parent module using loadChildren or children, depending on what `lazy` is set to."
|
||||
}
|
||||
},
|
||||
"required": ["name"]
|
||||
}
|
||||
|
||||
@ -17,46 +17,27 @@ import {
|
||||
} from '@angular-devkit/schematics';
|
||||
|
||||
import { Schema } from './schema';
|
||||
|
||||
class FormatFiles implements TaskConfigurationGenerator<any> {
|
||||
toConfiguration(): TaskConfiguration<any> {
|
||||
return {
|
||||
name: 'node-package',
|
||||
options: {
|
||||
command: 'run format',
|
||||
quiet: true
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function wrapIntoFormat(fn: Function): any {
|
||||
return (host: Tree, context: SchematicContext) => {
|
||||
context.addTask(new FormatFiles());
|
||||
return fn(context)(host, context);
|
||||
};
|
||||
}
|
||||
import { formatFiles } from '../../utils/rules/format-files';
|
||||
|
||||
export default function(schema: Schema): Rule {
|
||||
return wrapIntoFormat(() => {
|
||||
schema.path = schema.path ? normalize(schema.path) : schema.path;
|
||||
const sourceDir = schema.sourceDir;
|
||||
if (!sourceDir) {
|
||||
throw new SchematicsException(`sourceDir option is required.`);
|
||||
}
|
||||
schema.path = schema.path ? normalize(schema.path) : schema.path;
|
||||
const sourceDir = schema.sourceDir;
|
||||
if (!sourceDir) {
|
||||
throw new SchematicsException(`sourceDir option is required.`);
|
||||
}
|
||||
|
||||
const templateSource = apply(url('./files'), [
|
||||
template({
|
||||
...strings,
|
||||
'if-flat': (s: string) => (schema.flat ? '' : s),
|
||||
...schema
|
||||
}),
|
||||
move(sourceDir)
|
||||
]);
|
||||
const templateSource = apply(url('./files'), [
|
||||
template({
|
||||
...strings,
|
||||
'if-flat': (s: string) => (schema.flat ? '' : s),
|
||||
...schema
|
||||
}),
|
||||
move(sourceDir)
|
||||
]);
|
||||
|
||||
return chain([
|
||||
branchAndMerge(chain([mergeWith(templateSource)])),
|
||||
externalSchematic('@schematics/angular', 'module', schema)
|
||||
]);
|
||||
});
|
||||
return chain([
|
||||
branchAndMerge(chain([mergeWith(templateSource)])),
|
||||
externalSchematic('@schematics/angular', 'module', schema),
|
||||
formatFiles(schema)
|
||||
]);
|
||||
}
|
||||
|
||||
@ -11,6 +11,11 @@ export interface Schema {
|
||||
* The name of the module.
|
||||
*/
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* Skip formatting of files
|
||||
*/
|
||||
skipFormat: boolean;
|
||||
/**
|
||||
* The path to create the module.
|
||||
*/
|
||||
|
||||
@ -1,70 +1,70 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/schema",
|
||||
"id": "module",
|
||||
"title": "NX Module Options Schema",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the module."
|
||||
},
|
||||
"path": {
|
||||
"type": "string",
|
||||
"format": "path",
|
||||
"description": "The path to create the module.",
|
||||
"default": "app",
|
||||
"visible": false
|
||||
},
|
||||
"sourceDir": {
|
||||
"type": "string",
|
||||
"format": "path",
|
||||
"description": "The path of the source directory.",
|
||||
"default": "src",
|
||||
"visible": false
|
||||
},
|
||||
"appRoot": {
|
||||
"type": "string",
|
||||
"format": "path",
|
||||
"description": "The root of the application.",
|
||||
"visible": false
|
||||
},
|
||||
"routing": {
|
||||
"type": "boolean",
|
||||
"description": "Generates a routing module.",
|
||||
"default": false
|
||||
},
|
||||
"routingScope": {
|
||||
"enum": [
|
||||
"Child",
|
||||
"Root"
|
||||
],
|
||||
"type": "string",
|
||||
"description": "The scope for the generated routing.",
|
||||
"default": "Child"
|
||||
},
|
||||
"spec": {
|
||||
"type": "boolean",
|
||||
"description": "Specifies if a spec file is generated.",
|
||||
"default": true
|
||||
},
|
||||
"flat": {
|
||||
"type": "boolean",
|
||||
"description": "Flag to indicate if a dir is created.",
|
||||
"default": false
|
||||
},
|
||||
"commonModule": {
|
||||
"type": "boolean",
|
||||
"description": "Flag to control whether the CommonModule is imported.",
|
||||
"default": true,
|
||||
"visible": false
|
||||
},
|
||||
"module": {
|
||||
"type": "string",
|
||||
"description": "Allows specification of the declaring module.",
|
||||
"alias": "m"
|
||||
}
|
||||
"$schema": "http://json-schema.org/schema",
|
||||
"id": "module",
|
||||
"title": "NX Module Options Schema",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the module."
|
||||
},
|
||||
"required": [
|
||||
"name"
|
||||
]
|
||||
}
|
||||
"path": {
|
||||
"type": "string",
|
||||
"format": "path",
|
||||
"description": "The path to create the module.",
|
||||
"default": "app",
|
||||
"visible": false
|
||||
},
|
||||
"sourceDir": {
|
||||
"type": "string",
|
||||
"format": "path",
|
||||
"description": "The path of the source directory.",
|
||||
"default": "src",
|
||||
"visible": false
|
||||
},
|
||||
"appRoot": {
|
||||
"type": "string",
|
||||
"format": "path",
|
||||
"description": "The root of the application.",
|
||||
"visible": false
|
||||
},
|
||||
"routing": {
|
||||
"type": "boolean",
|
||||
"description": "Generates a routing module.",
|
||||
"default": false
|
||||
},
|
||||
"routingScope": {
|
||||
"enum": ["Child", "Root"],
|
||||
"type": "string",
|
||||
"description": "The scope for the generated routing.",
|
||||
"default": "Child"
|
||||
},
|
||||
"spec": {
|
||||
"type": "boolean",
|
||||
"description": "Specifies if a spec file is generated.",
|
||||
"default": true
|
||||
},
|
||||
"flat": {
|
||||
"type": "boolean",
|
||||
"description": "Flag to indicate if a dir is created.",
|
||||
"default": false
|
||||
},
|
||||
"commonModule": {
|
||||
"type": "boolean",
|
||||
"description": "Flag to control whether the CommonModule is imported.",
|
||||
"default": true,
|
||||
"visible": false
|
||||
},
|
||||
"skipFormat": {
|
||||
"description": "Skip formatting files",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"module": {
|
||||
"type": "string",
|
||||
"description": "Allows specification of the declaring module.",
|
||||
"alias": "m"
|
||||
}
|
||||
},
|
||||
"required": ["name"]
|
||||
}
|
||||
|
||||
40
packages/bazel/src/utils/rules/format-files.ts
Normal file
40
packages/bazel/src/utils/rules/format-files.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import { readJsonInTree } from '../ast-utils';
|
||||
import {
|
||||
TaskConfigurationGenerator,
|
||||
TaskConfiguration,
|
||||
Tree,
|
||||
SchematicContext,
|
||||
Rule,
|
||||
noop
|
||||
} from '@angular-devkit/schematics';
|
||||
|
||||
class FormatFiles implements TaskConfigurationGenerator<any> {
|
||||
toConfiguration(): TaskConfiguration<any> {
|
||||
return {
|
||||
name: 'node-package',
|
||||
options: {
|
||||
packageName: 'run format -- --untracked', // workaround. we should define a custom task executor.
|
||||
quiet: true
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function formatFiles(options: { skipFormat: boolean }): Rule {
|
||||
if (options.skipFormat) {
|
||||
return noop();
|
||||
}
|
||||
return (host: Tree, context: SchematicContext) => {
|
||||
const packageJson = readJsonInTree(host, 'package.json');
|
||||
if (packageJson.scripts && packageJson.scripts.format) {
|
||||
context.addTask(new FormatFiles());
|
||||
} else {
|
||||
context.logger.warn(
|
||||
'The "format" npm script is missing in your package.json'
|
||||
);
|
||||
context.logger.warn(
|
||||
'Your files were not formated during this code generation'
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
import {
|
||||
TaskConfigurationGenerator,
|
||||
TaskConfiguration,
|
||||
Tree,
|
||||
SchematicContext
|
||||
} from '@angular-devkit/schematics';
|
||||
|
||||
export class FormatFiles implements TaskConfigurationGenerator<any> {
|
||||
toConfiguration(): TaskConfiguration<any> {
|
||||
return {
|
||||
name: 'node-package',
|
||||
options: {
|
||||
command: 'run format -- --untracked',
|
||||
quiet: true
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function wrapIntoFormat(fn: Function): any {
|
||||
return (host: Tree, context: SchematicContext) => {
|
||||
context.addTask(new FormatFiles());
|
||||
return fn(context)(host, context);
|
||||
};
|
||||
}
|
||||
@ -8,11 +8,11 @@ import { stripIndents } from '@angular-devkit/core/src/utils/literals';
|
||||
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
|
||||
|
||||
import { readJsonInTree, updateJsonInTree } from '../../src/utils/ast-utils';
|
||||
import { FormatFiles } from '../../src/utils/tasks';
|
||||
import { serializeJson, renameSync } from '../../src/utils/fileutils';
|
||||
import { parseTarget, serializeTarget } from '../../src/utils/cli-config-utils';
|
||||
import * as fs from 'fs';
|
||||
import { offsetFromRoot } from '@nrwl/schematics/src/utils/common';
|
||||
import { offsetFromRoot } from '../../src/utils/common';
|
||||
import { formatFiles } from '../../src/utils/rules/format-files';
|
||||
|
||||
function createKarma(host: Tree, project: any) {
|
||||
const offset = offsetFromRoot(project.root);
|
||||
@ -648,9 +648,8 @@ const updateAngularJson = updateJsonInTree('angular.json', json => {
|
||||
return json;
|
||||
});
|
||||
|
||||
function addTasks(host: Tree, context: SchematicContext) {
|
||||
function addInstallTask(host: Tree, context: SchematicContext) {
|
||||
context.addTask(new NodePackageInstallTask());
|
||||
context.addTask(new FormatFiles());
|
||||
}
|
||||
|
||||
function checkCli6Upgraded(host: Tree) {
|
||||
@ -688,6 +687,7 @@ export default function(): Rule {
|
||||
createAdditionalFiles,
|
||||
deleteUnneededFiles,
|
||||
patchLibIndexFiles,
|
||||
addTasks
|
||||
addInstallTask,
|
||||
formatFiles()
|
||||
]);
|
||||
}
|
||||
|
||||
@ -4,7 +4,8 @@ import {
|
||||
move,
|
||||
noop,
|
||||
Rule,
|
||||
Tree
|
||||
Tree,
|
||||
SchematicContext
|
||||
} from '@angular-devkit/schematics';
|
||||
import { Schema } from './schema';
|
||||
import * as ts from 'typescript';
|
||||
@ -15,7 +16,6 @@ import {
|
||||
insert,
|
||||
updateJsonInTree
|
||||
} from '../../utils/ast-utils';
|
||||
import { wrapIntoFormat } from '../../utils/tasks';
|
||||
import { toFileName } from '../../utils/name-utils';
|
||||
import { offsetFromRoot } from '@nrwl/schematics/src/utils/common';
|
||||
import {
|
||||
@ -23,7 +23,7 @@ import {
|
||||
getWorkspacePath,
|
||||
replaceAppNameWithPath
|
||||
} from '@nrwl/schematics/src/utils/cli-config-utils';
|
||||
import { k } from '@angular/core/src/render3';
|
||||
import { formatFiles } from '../../utils/rules/format-files';
|
||||
|
||||
interface NormalizedSchema extends Schema {
|
||||
appProjectRoot: string;
|
||||
@ -232,7 +232,7 @@ function updateE2eProject(options: NormalizedSchema): Rule {
|
||||
}
|
||||
|
||||
export default function(schema: Schema): Rule {
|
||||
return wrapIntoFormat((host: Tree) => {
|
||||
return (host: Tree, context: SchematicContext) => {
|
||||
const options = normalizeOptions(host, schema);
|
||||
return chain([
|
||||
externalSchematic('@schematics/angular', 'application', {
|
||||
@ -248,9 +248,10 @@ export default function(schema: Schema): Rule {
|
||||
|
||||
updateComponentTemplate(options),
|
||||
addNxModule(options),
|
||||
options.routing ? addRouterRootConfiguration(options) : noop()
|
||||
]);
|
||||
});
|
||||
options.routing ? addRouterRootConfiguration(options) : noop(),
|
||||
formatFiles(options)
|
||||
])(host, context);
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeOptions(host: Tree, options: Schema): NormalizedSchema {
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
export interface Schema {
|
||||
name: string;
|
||||
skipFormat: boolean;
|
||||
inlineStyle?: boolean;
|
||||
inlineTemplate?: boolean;
|
||||
viewEncapsulation?: 'Emulated' | 'Native' | 'None';
|
||||
|
||||
@ -55,6 +55,11 @@
|
||||
"default": false,
|
||||
"alias": "S"
|
||||
},
|
||||
"skipFormat": {
|
||||
"description": "Skip formatting files",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"skipPackageJson": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
|
||||
@ -8,7 +8,7 @@ import {
|
||||
} from '../../utils/ast-utils';
|
||||
import { Schema } from './schema';
|
||||
import { addUpgradeToPackageJson } from '../../utils/common';
|
||||
import { wrapIntoFormat } from '../../utils/tasks';
|
||||
import { formatFiles } from '../../utils/rules/format-files';
|
||||
|
||||
function updateMain(angularJsImport: string, options: Schema): Rule {
|
||||
return (host: Tree) => {
|
||||
@ -89,16 +89,15 @@ function addEntryComponentsToModule(options: Schema): Rule {
|
||||
}
|
||||
|
||||
export default function(options: Schema): Rule {
|
||||
return wrapIntoFormat(() => {
|
||||
const angularJsImport = options.angularJsImport
|
||||
? options.angularJsImport
|
||||
: options.name;
|
||||
const angularJsImport = options.angularJsImport
|
||||
? options.angularJsImport
|
||||
: options.name;
|
||||
|
||||
return chain([
|
||||
updateMain(angularJsImport, options),
|
||||
addEntryComponentsToModule(options),
|
||||
rewriteBootstrapLogic(options),
|
||||
options.skipPackageJson ? noop() : addUpgradeToPackageJson()
|
||||
]);
|
||||
});
|
||||
return chain([
|
||||
updateMain(angularJsImport, options),
|
||||
addEntryComponentsToModule(options),
|
||||
rewriteBootstrapLogic(options),
|
||||
options.skipPackageJson ? noop() : addUpgradeToPackageJson(),
|
||||
formatFiles(options)
|
||||
]);
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
export interface Schema {
|
||||
angularJsImport: string;
|
||||
name: string;
|
||||
skipFormat: boolean;
|
||||
skipPackageJson: boolean;
|
||||
project: string;
|
||||
}
|
||||
|
||||
@ -18,15 +18,20 @@
|
||||
},
|
||||
"angularJsImport": {
|
||||
"type": "string",
|
||||
"description": "Import expression of the AngularJS application (e.g., --angularJsImport=some_node_module/my_app)."
|
||||
"description":
|
||||
"Import expression of the AngularJS application (e.g., --angularJsImport=some_node_module/my_app)."
|
||||
},
|
||||
"skipFormat": {
|
||||
"description": "Skip formatting files",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"skipPackageJson": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Do not add @angular/upgrade to package.json (e.g., --skipPackageJson)"
|
||||
"description":
|
||||
"Do not add @angular/upgrade to package.json (e.g., --skipPackageJson)"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"project"
|
||||
]
|
||||
"required": ["project"]
|
||||
}
|
||||
|
||||
@ -20,7 +20,6 @@ import {
|
||||
updateJsonInTree
|
||||
} from '../../utils/ast-utils';
|
||||
import { offsetFromRoot } from '../../utils/common';
|
||||
import { wrapIntoFormat } from '../../utils/tasks';
|
||||
import {
|
||||
toClassName,
|
||||
toFileName,
|
||||
@ -32,6 +31,7 @@ import {
|
||||
replaceAppNameWithPath
|
||||
} from '@nrwl/schematics/src/utils/cli-config-utils';
|
||||
import * as fs from 'fs';
|
||||
import { formatFiles } from '../../utils/rules/format-files';
|
||||
|
||||
interface NormalizedSchema extends Schema {
|
||||
name: string;
|
||||
@ -337,7 +337,7 @@ function updateTsConfig(options: NormalizedSchema): Rule {
|
||||
}
|
||||
|
||||
export default function(schema: Schema): Rule {
|
||||
return wrapIntoFormat((host: Tree) => {
|
||||
return (host: Tree, context: SchematicContext) => {
|
||||
const options = normalizeOptions(host, schema);
|
||||
if (!options.routing && options.lazy) {
|
||||
throw new Error(`routing must be set`);
|
||||
@ -402,9 +402,10 @@ export default function(schema: Schema): Rule {
|
||||
: noop(),
|
||||
options.routing && !options.lazy && options.parentModule
|
||||
? addChildren(options)
|
||||
: noop()
|
||||
]);
|
||||
});
|
||||
: noop(),
|
||||
formatFiles(options)
|
||||
])(host, context);
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeOptions(host: Tree, options: Schema): NormalizedSchema {
|
||||
|
||||
@ -17,8 +17,6 @@ describe('lib', () => {
|
||||
beforeEach(() => {
|
||||
appTree = new VirtualTree();
|
||||
appTree = createEmptyWorkspace(appTree);
|
||||
|
||||
schematicRunner.logger.subscribe(s => console.log(s));
|
||||
});
|
||||
|
||||
describe('not nested', () => {
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
export interface Schema {
|
||||
name: string;
|
||||
skipFormat: boolean;
|
||||
directory?: string;
|
||||
sourceDir?: string;
|
||||
publishable: boolean;
|
||||
|
||||
@ -27,6 +27,11 @@
|
||||
"description": "The prefix to apply to generated selectors.",
|
||||
"alias": "p"
|
||||
},
|
||||
"skipFormat": {
|
||||
"description": "Skip formatting files",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"skipPackageJson": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
@ -45,11 +50,13 @@
|
||||
"lazy": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Add RouterModule.forChild when set to true, and a simple array of routes when set to false."
|
||||
"description":
|
||||
"Add RouterModule.forChild when set to true, and a simple array of routes when set to false."
|
||||
},
|
||||
"parentModule": {
|
||||
"type": "string",
|
||||
"description": "Update the router configuration of the parent module using loadChildren or children, depending on what `lazy` is set to."
|
||||
"description":
|
||||
"Update the router configuration of the parent module using loadChildren or children, depending on what `lazy` is set to."
|
||||
},
|
||||
"tags": {
|
||||
"type": "string",
|
||||
|
||||
@ -3,14 +3,14 @@ import {
|
||||
externalSchematic,
|
||||
move,
|
||||
Rule,
|
||||
Tree
|
||||
Tree,
|
||||
SchematicContext
|
||||
} from '@angular-devkit/schematics';
|
||||
|
||||
import { Schema } from './schema';
|
||||
import * as path from 'path';
|
||||
|
||||
import { names, toFileName } from '../../utils/name-utils';
|
||||
import { wrapIntoFormat } from '../../utils/tasks';
|
||||
|
||||
import {
|
||||
addImportsToModule,
|
||||
@ -20,6 +20,7 @@ import {
|
||||
updateNgrxEffects,
|
||||
updateNgrxReducers
|
||||
} from './rules';
|
||||
import { formatFiles } from '../../utils/rules/format-files';
|
||||
|
||||
function effectsSpec(className: string, fileName: string) {
|
||||
return `
|
||||
@ -88,9 +89,9 @@ describe('${propertyName}Reducer', () => {
|
||||
* Rule to generate the Nx 'ngrx' Collection
|
||||
*/
|
||||
export default function generateNgrxCollection(_options: Schema): Rule {
|
||||
return wrapIntoFormat((host: Tree) => {
|
||||
return (host: Tree, context: SchematicContext) => {
|
||||
const options = normalizeOptions(_options);
|
||||
const context: RequestContext = {
|
||||
const requestContext: RequestContext = {
|
||||
featureName: options.name,
|
||||
moduleDir: path.dirname(options.module),
|
||||
options,
|
||||
@ -100,16 +101,16 @@ export default function generateNgrxCollection(_options: Schema): Rule {
|
||||
const fileGeneration = options.onlyEmptyRoot
|
||||
? []
|
||||
: [
|
||||
generateNgrxFiles(context),
|
||||
generateNxFiles(context),
|
||||
updateNgrxActions(context),
|
||||
updateNgrxReducers(context),
|
||||
updateNgrxEffects(context)
|
||||
generateNgrxFiles(requestContext),
|
||||
generateNxFiles(requestContext),
|
||||
updateNgrxActions(requestContext),
|
||||
updateNgrxReducers(requestContext),
|
||||
updateNgrxEffects(requestContext)
|
||||
];
|
||||
|
||||
const moduleModification = options.onlyAddFiles
|
||||
? []
|
||||
: [addImportsToModule(context)];
|
||||
: [addImportsToModule(requestContext)];
|
||||
const packageJsonModification = options.skipPackageJson
|
||||
? []
|
||||
: [addNgRxToPackageJson()];
|
||||
@ -117,9 +118,10 @@ export default function generateNgrxCollection(_options: Schema): Rule {
|
||||
return chain([
|
||||
...fileGeneration,
|
||||
...moduleModification,
|
||||
...packageJsonModification
|
||||
]);
|
||||
});
|
||||
...packageJsonModification,
|
||||
formatFiles(options)
|
||||
])(host, context);
|
||||
};
|
||||
}
|
||||
|
||||
// ********************************************************
|
||||
|
||||
@ -2,6 +2,7 @@ export interface Schema {
|
||||
name: string;
|
||||
onlyEmptyRoot: boolean;
|
||||
root: boolean;
|
||||
skipFormat: boolean;
|
||||
onlyAddFiles: boolean;
|
||||
module: string;
|
||||
skipPackageJson: boolean;
|
||||
|
||||
@ -14,35 +14,44 @@
|
||||
},
|
||||
"module": {
|
||||
"type": "string",
|
||||
"description": "Path to ngModule; host directory will contain the new '+state' directory (e.g., src/libs/mylib/mylib.module.ts)."
|
||||
"description":
|
||||
"Path to ngModule; host directory will contain the new '+state' directory (e.g., src/libs/mylib/mylib.module.ts)."
|
||||
},
|
||||
"onlyAddFiles": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Only add new NgRx files, without changing the module file (e.g., --onlyAddFiles)."
|
||||
"description":
|
||||
"Only add new NgRx files, without changing the module file (e.g., --onlyAddFiles)."
|
||||
},
|
||||
"directory": {
|
||||
"type": "string",
|
||||
"default": "+state",
|
||||
"description": "The directory name for the ngrx files: contains actions, effects, reducers. (e.g., +state)"
|
||||
"description":
|
||||
"The directory name for the ngrx files: contains actions, effects, reducers. (e.g., +state)"
|
||||
},
|
||||
"root": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Add StoreModule.forRoot and EffectsModule.forRoot instead of forFeature (e.g., --root)."
|
||||
"description":
|
||||
"Add StoreModule.forRoot and EffectsModule.forRoot instead of forFeature (e.g., --root)."
|
||||
},
|
||||
"onlyEmptyRoot": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Do not generate any files. Only generate StoreModule.forRoot and EffectsModule.forRoot (e.g., --onlyEmptyRoot)."
|
||||
"description":
|
||||
"Do not generate any files. Only generate StoreModule.forRoot and EffectsModule.forRoot (e.g., --onlyEmptyRoot)."
|
||||
},
|
||||
"skipFormat": {
|
||||
"description": "Skip formatting files",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"skipPackageJson": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Do not add ngrx dependencies to package.json (e.g., --skipPackageJson)"
|
||||
"description":
|
||||
"Do not add ngrx dependencies to package.json (e.g., --skipPackageJson)"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"module"
|
||||
]
|
||||
"required": ["module"]
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ import {
|
||||
import { insertImport } from '@schematics/angular/utility/route-utils';
|
||||
import { Schema } from './schema';
|
||||
import { addUpgradeToPackageJson } from '../../utils/common';
|
||||
import { wrapIntoFormat } from '../../utils/tasks';
|
||||
import { formatFiles } from '../../utils/rules/format-files';
|
||||
|
||||
function addImportsToModule(options: Schema): Rule {
|
||||
return (host: Tree) => {
|
||||
@ -125,16 +125,15 @@ function createFiles(angularJsImport: string, options: Schema): Rule {
|
||||
}
|
||||
|
||||
export default function(options: Schema): Rule {
|
||||
return wrapIntoFormat(() => {
|
||||
const angularJsImport = options.angularJsImport
|
||||
? options.angularJsImport
|
||||
: options.name;
|
||||
const angularJsImport = options.angularJsImport
|
||||
? options.angularJsImport
|
||||
: options.name;
|
||||
|
||||
return chain([
|
||||
createFiles(angularJsImport, options),
|
||||
addImportsToModule(options),
|
||||
addNgDoBootstrapToModule(options),
|
||||
options.skipPackageJson ? noop() : addUpgradeToPackageJson()
|
||||
]);
|
||||
});
|
||||
return chain([
|
||||
createFiles(angularJsImport, options),
|
||||
addImportsToModule(options),
|
||||
addNgDoBootstrapToModule(options),
|
||||
options.skipPackageJson ? noop() : addUpgradeToPackageJson(),
|
||||
formatFiles(options)
|
||||
]);
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ export interface Schema {
|
||||
angularJsImport: string;
|
||||
angularJsCmpSelector: string;
|
||||
name: string;
|
||||
skipFormat: boolean;
|
||||
skipPackageJson: boolean;
|
||||
router: boolean;
|
||||
}
|
||||
|
||||
@ -18,16 +18,24 @@
|
||||
},
|
||||
"angularJsImport": {
|
||||
"type": "string",
|
||||
"description": "Import expression of the AngularJS application (e.g., --angularJsImport=some_node_module/my_app)."
|
||||
"description":
|
||||
"Import expression of the AngularJS application (e.g., --angularJsImport=some_node_module/my_app)."
|
||||
},
|
||||
"angularJsCmpSelector": {
|
||||
"type": "string",
|
||||
"description": "The selector of an AngularJS component (e.g., --angularJsCmpSelector=myComponent)"
|
||||
"description":
|
||||
"The selector of an AngularJS component (e.g., --angularJsCmpSelector=myComponent)"
|
||||
},
|
||||
"skipFormat": {
|
||||
"description": "Skip formatting files",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"skipPackageJson": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Do not add @angular/upgrade to package.json (e.g., --skipPackageJson)"
|
||||
"description":
|
||||
"Do not add @angular/upgrade to package.json (e.g., --skipPackageJson)"
|
||||
},
|
||||
"router": {
|
||||
"type": "boolean",
|
||||
@ -35,7 +43,5 @@
|
||||
"description": "Sets up router synchronization (e.g., --router)"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"project"
|
||||
]
|
||||
"required": ["project"]
|
||||
}
|
||||
|
||||
@ -9,22 +9,23 @@ import {
|
||||
move
|
||||
} from '@angular-devkit/schematics';
|
||||
import { Schema } from './schema';
|
||||
import { wrapIntoFormat } from '@nrwl/schematics/src/utils/tasks';
|
||||
import { toFileName } from '@nrwl/schematics/src/utils/name-utils';
|
||||
import { toFileName } from '../../utils/name-utils';
|
||||
import { formatFiles } from '../../utils/rules/format-files';
|
||||
|
||||
export default function(schema: Schema): Rule {
|
||||
return wrapIntoFormat(() => {
|
||||
const options = normalizeOptions(schema);
|
||||
const templateSource = apply(url('./files'), [
|
||||
template({
|
||||
dot: '.',
|
||||
tmpl: '',
|
||||
...(options as any)
|
||||
}),
|
||||
move('tools/schematics')
|
||||
]);
|
||||
return chain([branchAndMerge(chain([mergeWith(templateSource)]))]);
|
||||
});
|
||||
const options = normalizeOptions(schema);
|
||||
const templateSource = apply(url('./files'), [
|
||||
template({
|
||||
dot: '.',
|
||||
tmpl: '',
|
||||
...(options as any)
|
||||
}),
|
||||
move('tools/schematics')
|
||||
]);
|
||||
return chain([
|
||||
branchAndMerge(chain([mergeWith(templateSource)])),
|
||||
formatFiles(options)
|
||||
]);
|
||||
}
|
||||
|
||||
function normalizeOptions(options: Schema): Schema {
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
export interface Schema {
|
||||
name: string;
|
||||
skipFormat: boolean;
|
||||
}
|
||||
|
||||
@ -11,8 +11,12 @@
|
||||
"$source": "argv",
|
||||
"index": 0
|
||||
}
|
||||
},
|
||||
"skipFormat": {
|
||||
"description": "Skip formatting files",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
]
|
||||
"required": []
|
||||
}
|
||||
|
||||
@ -14,8 +14,6 @@ describe('workspace-schematic', () => {
|
||||
beforeEach(() => {
|
||||
appTree = new VirtualTree();
|
||||
appTree = createEmptyWorkspace(appTree);
|
||||
|
||||
schematicRunner.logger.subscribe(s => console.log(s));
|
||||
});
|
||||
|
||||
it('should generate files', () => {
|
||||
|
||||
66
packages/schematics/src/utils/rules/format-files.spec.ts
Normal file
66
packages/schematics/src/utils/rules/format-files.spec.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import { SchematicTestRunner } from '@angular-devkit/schematics/testing';
|
||||
import { Tree } from '@angular-devkit/schematics';
|
||||
|
||||
import * as path from 'path';
|
||||
|
||||
import { createEmptyWorkspace } from '../testing-utils';
|
||||
import { formatFiles } from './format-files';
|
||||
import { serializeJson } from '../fileutils';
|
||||
|
||||
describe('formatFiles', () => {
|
||||
let tree: Tree;
|
||||
let schematicRunner: SchematicTestRunner;
|
||||
beforeEach(() => {
|
||||
schematicRunner = new SchematicTestRunner(
|
||||
'@nrwl/schematics',
|
||||
path.join(__dirname, '../../collection.json')
|
||||
);
|
||||
tree = createEmptyWorkspace(Tree.empty());
|
||||
tree.overwrite(
|
||||
'package.json',
|
||||
serializeJson({
|
||||
scripts: {
|
||||
format: 'prettier'
|
||||
}
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should format files', done => {
|
||||
schematicRunner.callRule(formatFiles(), tree).subscribe(result => {
|
||||
expect(schematicRunner.tasks.length).toBe(1);
|
||||
expect(schematicRunner.tasks[0]).toEqual({
|
||||
name: 'node-package',
|
||||
options: {
|
||||
packageName: 'run format -- --untracked',
|
||||
quiet: true
|
||||
}
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not format files if there is no format npm script', done => {
|
||||
tree.overwrite(
|
||||
'package.json',
|
||||
serializeJson({
|
||||
scripts: {}
|
||||
})
|
||||
);
|
||||
schematicRunner.callRule(formatFiles(), tree).subscribe(result => {
|
||||
expect(schematicRunner.tasks.length).toBe(0);
|
||||
//TODO: test that a warning is emitted.
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not format files if skipFormat is passed', done => {
|
||||
schematicRunner
|
||||
.callRule(formatFiles({ skipFormat: true }), tree)
|
||||
.subscribe(result => {
|
||||
expect(schematicRunner.tasks.length).toBe(0);
|
||||
//TODO: test that a warning is not emitted.
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
42
packages/schematics/src/utils/rules/format-files.ts
Normal file
42
packages/schematics/src/utils/rules/format-files.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import { readJsonInTree } from '../ast-utils';
|
||||
import {
|
||||
TaskConfigurationGenerator,
|
||||
TaskConfiguration,
|
||||
Tree,
|
||||
SchematicContext,
|
||||
Rule,
|
||||
noop
|
||||
} from '@angular-devkit/schematics';
|
||||
import { stripIndents } from '@angular-devkit/core/src/utils/literals';
|
||||
|
||||
class FormatFiles implements TaskConfigurationGenerator<any> {
|
||||
toConfiguration(): TaskConfiguration<any> {
|
||||
return {
|
||||
name: 'node-package',
|
||||
options: {
|
||||
packageName: 'run format -- --untracked', // workaround. we should define a custom task executor.
|
||||
quiet: true
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function formatFiles(
|
||||
options: { skipFormat: boolean } = { skipFormat: false }
|
||||
): Rule {
|
||||
if (options.skipFormat) {
|
||||
return noop();
|
||||
}
|
||||
return (host: Tree, context: SchematicContext) => {
|
||||
const packageJson = readJsonInTree(host, 'package.json');
|
||||
if (packageJson.scripts && packageJson.scripts.format) {
|
||||
context.addTask(new FormatFiles());
|
||||
} else {
|
||||
context.logger.warn(stripIndents`
|
||||
Files were not formated during this code generation.
|
||||
The "format" npm script is missing in your package.json.
|
||||
Please either add a format script or pass --skip-format.
|
||||
`);
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
import {
|
||||
TaskConfigurationGenerator,
|
||||
TaskConfiguration,
|
||||
Tree,
|
||||
SchematicContext
|
||||
} from '@angular-devkit/schematics';
|
||||
|
||||
export class FormatFiles implements TaskConfigurationGenerator<any> {
|
||||
toConfiguration(): TaskConfiguration<any> {
|
||||
return {
|
||||
name: 'node-package',
|
||||
options: {
|
||||
packageName: 'run format -- --untracked', // workaround. we should define a custom task executor.
|
||||
quiet: true
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function wrapIntoFormat(fn: Function): any {
|
||||
return (host: Tree, context: SchematicContext) => {
|
||||
context.addTask(new FormatFiles());
|
||||
return fn(host, context)(host, context);
|
||||
};
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user