From c22eae5968075c720072165dac0f7b03e2c8736a Mon Sep 17 00:00:00 2001 From: Hsuan Lee Date: Fri, 19 Apr 2019 18:07:03 +0800 Subject: [PATCH] feat(schematics): enhance component generator (#3265) --- schematics/collection.json | 6 + .../__name@dasherize__.component.__style__ | 0 .../__name@dasherize__.component.html | 3 + .../__name@dasherize__.component.spec.ts | 25 ++ .../__name@dasherize__.component.ts | 23 ++ schematics/ng-component/index.ts | 15 + schematics/ng-component/schema.json | 134 ++++++++ schematics/ng-component/schema.ts | 5 + schematics/utils/build-component.ts | 302 ++++++++++++++++++ scripts/schematics/copy-resources.js | 2 +- scripts/schematics/demo2schematics.js | 9 +- .../template/{index.ts => index.ts.template} | 10 +- scripts/schematics/template/schema.json | 89 ------ .../schematics/template/schema.json.template | 121 +++++++ scripts/schematics/template/schema.ts | 5 - .../schematics/template/schema.ts.template | 5 + 16 files changed, 653 insertions(+), 101 deletions(-) create mode 100644 schematics/ng-component/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.__style__ create mode 100644 schematics/ng-component/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.html create mode 100644 schematics/ng-component/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.spec.ts create mode 100644 schematics/ng-component/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.ts create mode 100644 schematics/ng-component/index.ts create mode 100644 schematics/ng-component/schema.json create mode 100644 schematics/ng-component/schema.ts create mode 100644 schematics/utils/build-component.ts rename scripts/schematics/template/{index.ts => index.ts.template} (67%) delete mode 100644 scripts/schematics/template/schema.json create mode 100644 scripts/schematics/template/schema.json.template delete mode 100644 scripts/schematics/template/schema.ts create mode 100644 scripts/schematics/template/schema.ts.template diff --git a/schematics/collection.json b/schematics/collection.json index 5cba48c3cc4..647f0d59d94 100644 --- a/schematics/collection.json +++ b/schematics/collection.json @@ -23,6 +23,12 @@ "factory": "./ng-add/setup-project/add-icon-assets#addIconToAssets", "schema": "./ng-generate/boot-page/schema.json", "aliases": ["fix-icon"] + }, + "component": { + "aliases": [ "c" ], + "factory": "./ng-component", + "description": "Create an Angular component.", + "schema": "./ng-component/schema.json" } } } diff --git a/schematics/ng-component/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.__style__ b/schematics/ng-component/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.__style__ new file mode 100644 index 00000000000..e69de29bb2d diff --git a/schematics/ng-component/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.html b/schematics/ng-component/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.html new file mode 100644 index 00000000000..5c94e5c533f --- /dev/null +++ b/schematics/ng-component/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.html @@ -0,0 +1,3 @@ +

+ <%= dasherize(name) %> works! +

\ No newline at end of file diff --git a/schematics/ng-component/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.spec.ts b/schematics/ng-component/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.spec.ts new file mode 100644 index 00000000000..3f2c28fdccd --- /dev/null +++ b/schematics/ng-component/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { <%= classify(name) %>Component } from './<%= dasherize(name) %>.component'; + +describe('<%= classify(name) %>Component', () => { + let component: <%= classify(name) %>Component; + let fixture: ComponentFixture<<%= classify(name) %>Component>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ <%= classify(name) %>Component ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(<%= classify(name) %>Component); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); \ No newline at end of file diff --git a/schematics/ng-component/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.ts b/schematics/ng-component/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.ts new file mode 100644 index 00000000000..f319d70e6d5 --- /dev/null +++ b/schematics/ng-component/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.ts @@ -0,0 +1,23 @@ +import { Component, OnInit<% if(!!viewEncapsulation) { %>, ViewEncapsulation<% }%><% if(changeDetection !== 'Default') { %>, ChangeDetectionStrategy<% }%> } from '@angular/core'; + +@Component({ + selector: '<%= selector %>',<% if(inlineTemplate) { %> + template: ` +

+ <%= dasherize(name) %> works! +

+ `,<% } else { %> + templateUrl: './<%= dasherize(name) %>.component.html',<% } if(inlineStyle) { %> + styles: []<% } else { %> + styleUrls: ['./<%= dasherize(name) %>.component.<%= style %>']<% } %><% if(!!viewEncapsulation) { %>, + encapsulation: ViewEncapsulation.<%= viewEncapsulation %><% } if (changeDetection !== 'Default') { %>, + changeDetection: ChangeDetectionStrategy.<%= changeDetection %><% } %> +}) +export class <%= classify(name) %>Component implements OnInit { + + constructor() { } + + ngOnInit() { + } + +} \ No newline at end of file diff --git a/schematics/ng-component/index.ts b/schematics/ng-component/index.ts new file mode 100644 index 00000000000..5f6c020f35a --- /dev/null +++ b/schematics/ng-component/index.ts @@ -0,0 +1,15 @@ +import { + chain, + Rule +} from '@angular-devkit/schematics'; +import { buildComponent } from '../utils/build-component'; + +import { Schema } from './schema'; + +export default function(options: Schema): Rule { + return chain([ + buildComponent( + { ...options } + ) + ]); +} diff --git a/schematics/ng-component/schema.json b/schematics/ng-component/schema.json new file mode 100644 index 00000000000..ff329f887cf --- /dev/null +++ b/schematics/ng-component/schema.json @@ -0,0 +1,134 @@ +{ + "$schema": "http://json-schema.org/schema", + "id": "PLACEHOLDER_SCHEMATICS_ID", + "title": "PLACEHOLDER_SCHEMATICS_TITLE", + "type": "object", + "properties": { + "path": { + "type": "string", + "format": "path", + "description": "The path to create the component.", + "visible": false + }, + "project": { + "type": "string", + "description": "The name of the project.", + "$default": { + "$source": "projectName" + } + }, + "name": { + "type": "string", + "description": "The name of the component.", + "$default": { + "$source": "argv", + "index": 0 + }, + "x-prompt": "What should be the name of the component?" + }, + "inlineStyle": { + "description": "When true, includes styles inline in the component.ts file. Only CSS styles can be included inline. By default, an external styles file is created and referenced in the component.ts file.", + "type": "boolean", + "default": false, + "alias": "s" + }, + "inlineTemplate": { + "description": "When true, includes template inline in the component.ts file. By default, an external template file is created and referenced in the component.ts file.", + "type": "boolean", + "default": false, + "alias": "t" + }, + "viewEncapsulation": { + "description": "The view encapsulation strategy to use in the new component.", + "enum": ["Emulated", "Native", "None", "ShadowDom"], + "type": "string", + "alias": "v" + }, + "changeDetection": { + "description": "Specifies the change detection strategy.", + "enum": ["Default", "OnPush"], + "type": "string", + "default": "Default", + "alias": "c" + }, + "prefix": { + "type": "string", + "description": "The prefix to apply to the generated component selector.", + "alias": "p", + "oneOf": [ + { + "maxLength": 0 + }, + { + "minLength": 1, + "format": "html-selector" + } + ] + }, + "styleext": { + "description": "The file extension to use for style files.", + "type": "string", + "default": "css", + "x-deprecated": "Use \"style\" instead." + }, + "style": { + "description": "The file extension or preprocessor to use for style files.", + "type": "string", + "default": "css", + "enum": [ + "css", + "scss", + "sass", + "less", + "styl" + ] + }, + "spec": { + "type": "boolean", + "description": "When true (the default), generates a \"spec.ts\" test file for the new component.", + "default": true, + "x-deprecated": "Use \"skipTests\" instead." + }, + "skipTests": { + "type": "boolean", + "description": "When true, does not create \"spec.ts\" test files for the new component." + }, + "flat": { + "type": "boolean", + "description": "Flag to indicate if a dir is created.", + "default": false + }, + "skipImport": { + "type": "boolean", + "description": "When true, does not import this component into the owning NgModule." + }, + "selector": { + "type": "string", + "format": "html-selector", + "description": "The selector to use for the component." + }, + "module": { + "type": "string", + "description": "Allows specification of the declaring module.", + "alias": "m" + }, + "export": { + "type": "boolean", + "default": false, + "description": "When true, the declaring NgModule exports this component." + }, + "entryComponent": { + "type": "boolean", + "default": false, + "description": "When true, the new component is the entry component of the declaring NgModule." + }, + "classnameWithModule": { + "type": "boolean", + "description": "When true, Use module class name as additional prefix for the component classname.", + "default": false + } + }, + "required": [ + "name" + ] +} diff --git a/schematics/ng-component/schema.ts b/schematics/ng-component/schema.ts new file mode 100644 index 00000000000..d4f5a2e93a1 --- /dev/null +++ b/schematics/ng-component/schema.ts @@ -0,0 +1,5 @@ +import { ZorroComponentOptions } from '../utils/build-component'; + +export interface Schema extends ZorroComponentOptions { + [key: string]: string | boolean; +} diff --git a/schematics/utils/build-component.ts b/schematics/utils/build-component.ts new file mode 100644 index 00000000000..a22574febf2 --- /dev/null +++ b/schematics/utils/build-component.ts @@ -0,0 +1,302 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import { strings, template as interpolateTemplate } from '@angular-devkit/core'; +import { + apply, + branchAndMerge, + chain, + filter, + mergeWith, + move, + noop, + template, + url, + Rule, + SchematicsException, + Tree +} from '@angular-devkit/schematics'; +import { FileSystemSchematicContext } from '@angular-devkit/schematics/tools'; +import { getDefaultComponentOptions, getProjectFromWorkspace, ts } from '@angular/cdk/schematics'; +import { Schema as ComponentOptions } from '@schematics/angular/component/schema'; +import { + addDeclarationToModule, + addEntryComponentToModule, + addExportToModule, + getFirstNgModuleName +} from '@schematics/angular/utility/ast-utils'; +import { InsertChange } from '@schematics/angular/utility/change'; +import { getWorkspace } from '@schematics/angular/utility/config'; +import { buildRelativePath, findModuleFromOptions } from '@schematics/angular/utility/find-module'; +import { parseName } from '@schematics/angular/utility/parse-name'; +import { buildDefaultPath } from '@schematics/angular/utility/project'; +import { validateHtmlSelector, validateName } from '@schematics/angular/utility/validation'; +import { readFileSync, statSync } from 'fs'; +import { dirname, join, resolve } from 'path'; + +export interface ZorroComponentOptions extends ComponentOptions { + classnameWithModule: boolean; +} + +/** + * List of style extensions which are CSS compatible. All supported CLI style extensions can be + * found here: angular/angular-cli/master/packages/schematics/angular/ng-new/schema.json#L118-L122 + */ +const supportedCssExtensions = [ 'css', 'scss', 'sass', 'less' ]; + +// tslint:disable-next-line no-any +function readIntoSourceFile(host: Tree, modulePath: string): any { + const text = host.read(modulePath); + if (text === null) { + throw new SchematicsException(`File ${modulePath} does not exist.`); + } + + return ts.createSourceFile(modulePath, text.toString('utf-8'), ts.ScriptTarget.Latest, true); +} + +// tslint:disable-next-line no-any +function getModuleClassnamePrefix(source: any): string { + const className = getFirstNgModuleName(source); + if (className) { + const execArray = /(\w+)Module/gi.exec(className); + if (execArray && execArray[1]) { + return execArray[1]; + } else { + return null; + } + } else { + return null; + } +} + +function addDeclarationToNgModule(options: ZorroComponentOptions): Rule { + return (host: Tree) => { + if (options.skipImport || !options.module) { + return host; + } + + const modulePath = options.module; + let source = readIntoSourceFile(host, modulePath); + const componentPath = `/${options.path}/${options.flat ? '' : strings.dasherize(options.name) + '/'}${strings.dasherize(options.name)}.component`; + const relativePath = buildRelativePath(modulePath, componentPath); + let classifiedName = strings.classify(`${options.name}Component`); + if (options.classnameWithModule) { + const modulePrefix = getModuleClassnamePrefix(source); + if (modulePrefix) { + classifiedName = `${modulePrefix}${classifiedName}` + } + } + + const declarationChanges = addDeclarationToModule( + // TODO: TypeScript version mismatch due to @schematics/angular using a different version + // than Material. Cast to any to avoid the type assignment failure. + // tslint:disable-next-line no-any + source as any, + modulePath, + classifiedName, + relativePath); + + const declarationRecorder = host.beginUpdate(modulePath); + for (const change of declarationChanges) { + if (change instanceof InsertChange) { + declarationRecorder.insertLeft(change.pos, change.toAdd); + } + } + host.commitUpdate(declarationRecorder); + + if (options.export) { + // Need to refresh the AST because we overwrote the file in the host. + source = readIntoSourceFile(host, modulePath); + + const exportRecorder = host.beginUpdate(modulePath); + const exportChanges = addExportToModule( + // TODO: TypeScript version mismatch due to @schematics/angular using a different version + // than Material. Cast to any to avoid the type assignment failure. + // tslint:disable-next-line no-any + source as any, + modulePath, + classifiedName, + relativePath); + + for (const change of exportChanges) { + if (change instanceof InsertChange) { + exportRecorder.insertLeft(change.pos, change.toAdd); + } + } + host.commitUpdate(exportRecorder); + } + + if (options.entryComponent) { + // Need to refresh the AST because we overwrote the file in the host. + source = readIntoSourceFile(host, modulePath); + + const entryComponentRecorder = host.beginUpdate(modulePath); + const entryComponentChanges = addEntryComponentToModule( + // TODO: TypeScript version mismatch due to @schematics/angular using a different version + // than Material. Cast to any to avoid the type assignment failure. + // tslint:disable-next-line no-any + source as any, + modulePath, + classifiedName, + relativePath); + + for (const change of entryComponentChanges) { + if (change instanceof InsertChange) { + entryComponentRecorder.insertLeft(change.pos, change.toAdd); + } + } + host.commitUpdate(entryComponentRecorder); + } + + return host; + }; +} + +function buildSelector(options: ZorroComponentOptions, projectPrefix: string, modulePrefixName: string): string { + let selector = strings.dasherize(options.name); + let modulePrefix = ''; + if (modulePrefixName) { + modulePrefix = strings.dasherize(modulePrefixName) + '-'; + } + if (options.prefix) { + selector = `${options.prefix}-${modulePrefix}${selector}`; + } else if (options.prefix === undefined && projectPrefix) { + selector = `${projectPrefix}-${modulePrefix}${selector}`; + } + return selector; + +} + +/** + * Indents the text content with the amount of specified spaces. The spaces will be added after + * every line-break. This utility function can be used inside of EJS templates to properly + * include the additional files. + */ +function indentTextContent(text: string, numSpaces: number): string { + // In the Material project there should be only LF line-endings, but the schematic files + // are not being linted and therefore there can be also CRLF or just CR line-endings. + return text.replace(/(\r\n|\r|\n)/g, `$1${' '.repeat(numSpaces)}`); +} + +/** + * Rule that copies and interpolates the files that belong to this schematic context. Additionally + * a list of file paths can be passed to this rule in order to expose them inside the EJS + * template context. + * + * This allows inlining the external template or stylesheet files in EJS without having + * to manually duplicate the file content. + */ +export function buildComponent(options: ZorroComponentOptions, + additionalFiles: { [ key: string ]: string } = {}): Rule { + + return (host: Tree, context: FileSystemSchematicContext) => { + const workspace = getWorkspace(host); + const project = getProjectFromWorkspace(workspace, options.project); + const defaultZorroComponentOptions = getDefaultComponentOptions(project); + let modulePrefix = ''; + // TODO(devversion): Remove if we drop support for older CLI versions. + // This handles an unreported breaking change from the @angular-devkit/schematics. Previously + // the description path resolved to the factory file, but starting from 6.2.0, it resolves + // to the factory directory. + const schematicPath = statSync(context.schematic.description.path).isDirectory() ? + context.schematic.description.path : + dirname(context.schematic.description.path); + + const schematicFilesUrl = './files'; + const schematicFilesPath = resolve(schematicPath, schematicFilesUrl); + + options.style = ( + options.style && options.style !== 'css' + ? options.style : options.styleext + ) || 'css'; + options.skipTests = options.skipTests || !options.spec; + + // Add the default component option values to the options if an option is not explicitly + // specified but a default component option is available. + Object.keys(options) + .filter(optionName => options[ optionName ] == null && defaultZorroComponentOptions[ optionName ]) + .forEach(optionName => options[ optionName ] = defaultZorroComponentOptions[ optionName ]); + + if (options.path === undefined) { + // TODO(jelbourn): figure out if the need for this `as any` is a bug due to two different + // incompatible `WorkspaceProject` classes in @angular-devkit + // tslint:disable-next-line no-any + options.path = buildDefaultPath(project as any); + } + + options.module = findModuleFromOptions(host, options); + + const parsedPath = parseName(options.path!, options.name); + const source = readIntoSourceFile(host, options.module); + if (options.classnameWithModule) { + modulePrefix = getModuleClassnamePrefix(source); + } + + options.name = parsedPath.name; + options.path = parsedPath.path; + options.selector = options.selector || buildSelector(options, project.prefix, modulePrefix); + + validateName(options.name); + validateHtmlSelector(options.selector!); + + // In case the specified style extension is not part of the supported CSS supersets, + // we generate the stylesheets with the "css" extension. This ensures that we don't + // accidentally generate invalid stylesheets (e.g. drag-drop-comp.styl) which will + // break the Angular CLI project. See: https://github.com/angular/material2/issues/15164 + if (!supportedCssExtensions.includes(options.style!)) { + // TODO: Cast is necessary as we can't use the Style enum which has been introduced + // within CLI v7.3.0-rc.0. This would break the schematic for older CLI versions. + options.style = 'css'; + } + + const classifyCovered = (name: string) => { + return `${modulePrefix}${strings.classify(name)}` + }; + // Object that will be used as context for the EJS templates. + const baseTemplateContext = { + ...strings, + 'if-flat': (s: string) => options.flat ? '' : s, + classify: classifyCovered, + ...options + }; + + // Key-value object that includes the specified additional files with their loaded content. + // The resolved contents can be used inside EJS templates. + const resolvedFiles = {}; + + for (const key in additionalFiles) { + if (additionalFiles[ key ]) { + const fileContent = readFileSync(join(schematicFilesPath, additionalFiles[ key ]), 'utf-8'); + + // Interpolate the additional files with the base EJS template context. + resolvedFiles[ key ] = interpolateTemplate(fileContent)(baseTemplateContext); + } + } + + const templateSource = apply(url(schematicFilesUrl), [ + options.skipTests ? filter(path => !path.endsWith('.spec.ts')) : noop(), + options.inlineStyle ? filter(path => !path.endsWith('.__style__')) : noop(), + options.inlineTemplate ? filter(path => !path.endsWith('.html')) : noop(), + // Treat the template options as any, because the type definition for the template options + // is made unnecessarily explicit. Every type of object can be used in the EJS template. + // tslint:disable-next-line no-any + template({ indentTextContent, resolvedFiles, ...baseTemplateContext } as any), + // TODO(devversion): figure out why we cannot just remove the first parameter + // See for example: angular-cli#schematics/angular/component/index.ts#L160 + // tslint:disable-next-line no-any + move(null as any, parsedPath.path) + ]); + + return chain([ + branchAndMerge(chain([ + addDeclarationToNgModule(options), + mergeWith(templateSource) + ])) + ])(host, context) as Rule; + }; +} diff --git a/scripts/schematics/copy-resources.js b/scripts/schematics/copy-resources.js index a61f929c6bc..0e78cf419b1 100644 --- a/scripts/schematics/copy-resources.js +++ b/scripts/schematics/copy-resources.js @@ -3,7 +3,7 @@ const path = require('path'); const srcPath = path.resolve(__dirname, `../../schematics`); const targetPath = path.resolve(__dirname, `../../publish/schematics`); -const copyFilter = (path) => (!/.+\.ts/.test(path)) || (/files\/__path__/.test(path)); +const copyFilter = (path) => (/files\/__path__/.test(path) || (!/.+\.ts/.test(path))); function mergeDemoCollection() { diff --git a/scripts/schematics/demo2schematics.js b/scripts/schematics/demo2schematics.js index df998f7df6e..12142a10112 100644 --- a/scripts/schematics/demo2schematics.js +++ b/scripts/schematics/demo2schematics.js @@ -102,7 +102,7 @@ function replaceTemplate(demoComponent) { return demoComponent.fileContent .replace(/selector\s*:\s*'(.+?)'\s*/, () => `selector: '<%= selector %>'`) .replace(new RegExp(demoComponent.className), () => `<%= classify(name) %>Component`) - .replace(/styles\s*:\s*\[\s*`([\s\S]*?)`\s*\]/, () => `<% if(inlineStyle) { %>styles: [\`${demoComponent.styles}\`]<% } else { %>styleUrls: ['./<%= dasherize(name) %>.component.<%= styleext %>']<% } %>`) + .replace(/styles\s*:\s*\[\s*`([\s\S]*?)`\s*\]/, () => `<% if(inlineStyle) { %>styles: [\`${demoComponent.styles}\`]<% } else { %>styleUrls: ['./<%= dasherize(name) %>.component.<%= style %>']<% } %>`) .replace(/template\s*:\s*`([\s\S]*?)`/, () => `<% if(inlineTemplate) { %>template: \`${demoComponent.template}\`<% } else { %>templateUrl: './<%= dasherize(name) %>.component.html'<% } %>`) } @@ -114,14 +114,17 @@ function createSchematic(demoComponent) { const filesPath = path.resolve(__dirname, `${demoPath}/files/__path__/__name@dasherize@if-flat__`); const schemaPath = `${demoPath}/schema.json`; fs.mkdirsSync(filesPath); - fs.copySync(path.resolve(__dirname, `./template`), demoPath); + + fs.copySync(path.resolve(__dirname, `./template/index.ts.template`), `${demoPath}/index.ts`); + fs.copySync(path.resolve(__dirname, `./template/schema.json.template`), `${demoPath}/schema.json`); + fs.copySync(path.resolve(__dirname, `./template/schema.ts.template`), `${demoPath}/schema.ts`); const schemaJson = fs.readJsonSync(schemaPath); schemaJson.id = `${demoComponent.demoName}-${demoComponent.componentName}`; schemaJson.title = `NG-ZORRO ${demoComponent.demoName} ${demoComponent.componentName}`; fs.outputJsonSync(schemaPath, schemaJson); - fs.outputFileSync(`${filesPath}/__name@dasherize__.component.__styleext__`, demoComponent.styles); + fs.outputFileSync(`${filesPath}/__name@dasherize__.component.__style__`, demoComponent.styles); fs.outputFileSync(`${filesPath}/__name@dasherize__.component.html`, demoComponent.template); fs.outputFileSync(`${filesPath}/__name@dasherize__.component.spec.ts`, TEST_FILE_CONTENT); fs.outputFileSync(`${filesPath}/__name@dasherize__.component.ts`, replaceTemplate(demoComponent)); diff --git a/scripts/schematics/template/index.ts b/scripts/schematics/template/index.ts.template similarity index 67% rename from scripts/schematics/template/index.ts rename to scripts/schematics/template/index.ts.template index 9b8502cc25b..8eb88d9bcb4 100644 --- a/scripts/schematics/template/index.ts +++ b/scripts/schematics/template/index.ts.template @@ -1,5 +1,9 @@ -import { chain, Rule } from '@angular-devkit/schematics'; -import { buildComponent } from '@angular/cdk/schematics'; +import { + chain, + Rule +} from '@angular-devkit/schematics'; +import { buildComponent } from '../../utils/build-component'; + import { Schema } from './schema'; export default function(options: Schema): Rule { @@ -8,7 +12,7 @@ export default function(options: Schema): Rule { { ...options }, { template: './__path__/__name@dasherize@if-flat__/__name@dasherize__.component.html', - stylesheet: './__path__/__name@dasherize@if-flat__/__name@dasherize__.component.__styleext__' + stylesheet: './__path__/__name@dasherize@if-flat__/__name@dasherize__.component.__style__' } ) ]); diff --git a/scripts/schematics/template/schema.json b/scripts/schematics/template/schema.json deleted file mode 100644 index a862c365e6c..00000000000 --- a/scripts/schematics/template/schema.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "$schema": "http://json-schema.org/schema", - "id": "PLACEHOLDER_SCHEMATICS_ID", - "title": "PLACEHOLDER_SCHEMATICS_TITLE", - "type": "object", - "properties": { - "path": { - "type": "string", - "format": "path", - "description": "The path to create the component.", - "visible": false - }, - "project": { - "type": "string", - "description": "The name of the project.", - "$default": { - "$source": "projectName" - } - }, - "name": { - "type": "string", - "description": "The name of the component.", - "$default": { - "$source": "argv", - "index": 0 - }, - "x-prompt": "What should be the name of the component?" - }, - "inlineStyle": { - "description": "Specifies if the style will be in the ts file.", - "type": "boolean", - "default": false, - "alias": "s" - }, - "inlineTemplate": { - "description": "Specifies if the template will be in the ts file.", - "type": "boolean", - "default": false, - "alias": "t" - }, - "prefix": { - "type": "string", - "format": "html-selector", - "description": "The prefix to apply to generated selectors.", - "alias": "p" - }, - "styleext": { - "description": "The file extension to be used for style files.", - "type": "string" - }, - "spec": { - "type": "boolean", - "description": "Specifies if a spec file is generated." - }, - "flat": { - "type": "boolean", - "description": "Flag to indicate if a dir is created.", - "default": false - }, - "skipImport": { - "type": "boolean", - "description": "Flag to skip the module import.", - "default": false - }, - "selector": { - "type": "string", - "format": "html-selector", - "description": "The selector to use for the component." - }, - "module": { - "type": "string", - "description": "Allows specification of the declaring module.", - "alias": "m" - }, - "export": { - "type": "boolean", - "default": false, - "description": "Specifies if declaring module exports the component." - }, - "entryComponent": { - "type": "boolean", - "default": false, - "description": "Specifies if the component is an entry component of declaring module." - } - }, - "required": [ - "name" - ] -} diff --git a/scripts/schematics/template/schema.json.template b/scripts/schematics/template/schema.json.template new file mode 100644 index 00000000000..0487a4a923c --- /dev/null +++ b/scripts/schematics/template/schema.json.template @@ -0,0 +1,121 @@ +{ + "$schema": "http://json-schema.org/schema", + "id": "PLACEHOLDER_SCHEMATICS_ID", + "title": "PLACEHOLDER_SCHEMATICS_TITLE", + "type": "object", + "properties": { + "path": { + "type": "string", + "format": "path", + "description": "The path to create the component.", + "visible": false + }, + "project": { + "type": "string", + "description": "The name of the project.", + "$default": { + "$source": "projectName" + } + }, + "name": { + "type": "string", + "description": "The name of the component.", + "$default": { + "$source": "argv", + "index": 0 + }, + "x-prompt": "What should be the name of the component?" + }, + "inlineStyle": { + "description": "When true, includes styles inline in the component.ts file. Only CSS styles can be included inline. By default, an external styles file is created and referenced in the component.ts file.", + "type": "boolean", + "default": false, + "alias": "s" + }, + "inlineTemplate": { + "description": "When true, includes template inline in the component.ts file. By default, an external template file is created and referenced in the component.ts file.", + "type": "boolean", + "default": false, + "alias": "t" + }, + "prefix": { + "type": "string", + "description": "The prefix to apply to the generated component selector.", + "alias": "p", + "oneOf": [ + { + "maxLength": 0 + }, + { + "minLength": 1, + "format": "html-selector" + } + ] + }, + "styleext": { + "description": "The file extension to use for style files.", + "type": "string", + "default": "css", + "x-deprecated": "Use \"style\" instead." + }, + "style": { + "description": "The file extension or preprocessor to use for style files.", + "type": "string", + "default": "css", + "enum": [ + "css", + "scss", + "sass", + "less", + "styl" + ] + }, + "spec": { + "type": "boolean", + "description": "When true (the default), generates a \"spec.ts\" test file for the new component.", + "default": true, + "x-deprecated": "Use \"skipTests\" instead." + }, + "skipTests": { + "type": "boolean", + "description": "When true, does not create \"spec.ts\" test files for the new component." + }, + "flat": { + "type": "boolean", + "description": "Flag to indicate if a dir is created.", + "default": false + }, + "skipImport": { + "type": "boolean", + "description": "When true, does not import this component into the owning NgModule." + }, + "selector": { + "type": "string", + "format": "html-selector", + "description": "The selector to use for the component." + }, + "module": { + "type": "string", + "description": "Allows specification of the declaring module.", + "alias": "m" + }, + "export": { + "type": "boolean", + "default": false, + "description": "When true, the declaring NgModule exports this component." + }, + "entryComponent": { + "type": "boolean", + "default": false, + "description": "When true, the new component is the entry component of the declaring NgModule." + }, + "classnameWithModule": { + "type": "boolean", + "description": "When true, Use module class name as additional prefix for the component classname.", + "default": false + } + }, + "required": [ + "name" + ] +} diff --git a/scripts/schematics/template/schema.ts b/scripts/schematics/template/schema.ts deleted file mode 100644 index 92ebf7eec68..00000000000 --- a/scripts/schematics/template/schema.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { Schema as ComponentSchema } from '@schematics/angular/component/schema'; - -export interface Schema extends ComponentSchema { - [key: string]: string | boolean; -} diff --git a/scripts/schematics/template/schema.ts.template b/scripts/schematics/template/schema.ts.template new file mode 100644 index 00000000000..e9a257b45b2 --- /dev/null +++ b/scripts/schematics/template/schema.ts.template @@ -0,0 +1,5 @@ +import { ZorroComponentOptions } from '../../utils/build-component'; + +export interface Schema extends ZorroComponentOptions { + [key: string]: string | boolean; +}