diff --git a/packages/igx-templates/IgniteUIForAngularTemplate.ts b/packages/igx-templates/IgniteUIForAngularTemplate.ts index f8a718cb3..082c925ee 100644 --- a/packages/igx-templates/IgniteUIForAngularTemplate.ts +++ b/packages/igx-templates/IgniteUIForAngularTemplate.ts @@ -1,10 +1,10 @@ import { - AddTemplateArgs, ControlExtraConfiguration, - TemplateDependency, TypeScriptFileUpdate, Util + AddTemplateArgs, ControlExtraConfiguration, FsFileSystem, IFileSystem, + Template, TemplateDependency, TypeScriptFileUpdate, Util } from "@igniteui-cli/core"; import * as path from "path"; -export class IgniteUIForAngularTemplate { +export class IgniteUIForAngularTemplate implements Template { public components: string[]; public controlGroup: string; public listInComponentTemplates: boolean = true; @@ -15,7 +15,7 @@ export class IgniteUIForAngularTemplate { public framework: string = "angular"; public projectType: string = "igx-ts"; public hasExtraConfiguration: boolean = false; - public packages = []; + public packages: string[] = []; public dependencies: TemplateDependency[] = []; @@ -34,6 +34,17 @@ export class IgniteUIForAngularTemplate { return [path.join(this.rootPath, "files")]; } + private _fs: IFileSystem; + public get virtFs(): IFileSystem { + if (!this._fs) { + this._fs = new FsFileSystem(); + } + return this._fs; + } + public set virtFs(v: IFileSystem) { + this._fs = v; + } + constructor(private rootPath: string) { } @@ -78,11 +89,11 @@ export class IgniteUIForAngularTemplate { const TsUpdate: typeof TypeScriptFileUpdate = require("@igniteui-cli/core/typescript").TypeScriptFileUpdate; - if (!(options && options.skipRoute) && Util.fileExists("src/app/app-routing.module.ts")) { + if (!(options && options.skipRoute) && this.virtFs.fileExists("src/app/app-routing.module.ts")) { //1) import the component class name, //2) and populate the Routes array with the path and component //for example: { path: 'combo', component: ComboComponent } - const routingModule = new TsUpdate(path.join(projectPath, "src/app/app-routing.module.ts")); + const routingModule = new TsUpdate(path.join(projectPath, "src/app/app-routing.module.ts"), this.virtFs); routingModule.addRoute( path.join(projectPath, `src/app/${this.folderName(name)}/${this.fileName(name)}.component.ts`), this.fileName(name), //path @@ -93,7 +104,7 @@ export class IgniteUIForAngularTemplate { //3) add an import of the component class from its file location. //4) populate the declarations portion of the @NgModule with the component class name. const mainModulePath = path.join(projectPath, `src/app/${modulePath}`); - const mainModule = new TsUpdate(mainModulePath); + const mainModule = new TsUpdate(mainModulePath, this.virtFs); mainModule.addDeclaration( path.join(projectPath, `src/app/${this.folderName(name)}/${this.fileName(name)}.component.ts`), modulePath !== "app.module.ts" diff --git a/packages/ng-schematics/src/component/index.ts b/packages/ng-schematics/src/component/index.ts new file mode 100644 index 000000000..66b200280 --- /dev/null +++ b/packages/ng-schematics/src/component/index.ts @@ -0,0 +1,48 @@ +import { + apply, chain, MergeStrategy, mergeWith, + Rule, SchematicContext, SchematicsException, template, Tree, url } from "@angular-devkit/schematics"; +import { NodePackageInstallTask } from "@angular-devkit/schematics/tasks"; +import { IgniteUIForAngularTemplate } from "@igniteui-angular/templates"; +import { NgTreeFileSystem, Util } from "@igniteui-cli/core"; +import { SchematicsTemplateManager } from "../SchematicsTemplateManager"; +import { ComponentOptions } from "./schema"; + +export function component(options: ComponentOptions): Rule { + return (_tree: Tree, context: SchematicContext) => { + + // TODO: validate template name + nameFromPath, id, modulePath, skipRoute + // TODO: jump into prompt session w/o provided id? + + if (!options.templateInst) { + if (!options.template) { + throw new SchematicsException("argument template id must be provided"); + } + const templateManager = new SchematicsTemplateManager(); + const projLib = templateManager.getProjectLibrary("angular", "igx-ts"); + options.templateInst = projLib.getTemplateById(options.template) as IgniteUIForAngularTemplate; + } + + const config = options.templateInst.generateConfig(options.name, {}); + + context.logger.info(`Generating ${options.templateInst.name} with name: ${options.name}`); + // TODO: reuse component schematic? + return chain([ + ...options.templateInst.templatePaths.map(templatePath => + mergeWith( + apply(url(Util.relativePath(__filename, templatePath, true)), [ + template(config) + ] + ), MergeStrategy.Overwrite) + ), + (host: Tree, _context: SchematicContext) => { + if (options.templateInst) { + for (const packageName of options.templateInst.packages) { + context.addTask(new NodePackageInstallTask({ packageName, workingDirectory: options.projectName })); + } + options.templateInst.virtFs = new NgTreeFileSystem(host); + options.templateInst.registerInProject("", options.name, { skipRoute: options.skipRoute }); + } + } + ]); + }; +} diff --git a/packages/ng-schematics/src/component/index_spec.ts b/packages/ng-schematics/src/component/index_spec.ts new file mode 100644 index 000000000..424865915 --- /dev/null +++ b/packages/ng-schematics/src/component/index_spec.ts @@ -0,0 +1,14 @@ +import { Tree } from "@angular-devkit/schematics"; +import { SchematicTestRunner } from "@angular-devkit/schematics/testing"; +import * as path from "path"; + +const collectionPath = path.join(__dirname, "../collection.json"); + +describe("component", () => { + it("works", () => { + const runner = new SchematicTestRunner("schematics", collectionPath); + const tree = runner.runSchematic("component", {}, Tree.empty()); + + expect(tree.files).toEqual([]); + }); +}); diff --git a/packages/ng-schematics/src/component/schema.json b/packages/ng-schematics/src/component/schema.json new file mode 100644 index 000000000..c1a9996d1 --- /dev/null +++ b/packages/ng-schematics/src/component/schema.json @@ -0,0 +1,29 @@ +{ + "$schema": "http://json-schema.org/schema", + "id": "SchematicsAngularComponent", + "title": "Angular Component Options Schema", + "type": "object", + "description": "Creates a new Ignite UI for Angular component in the given or default project.", + "properties": { + "name": { + "type": "string", + "description": "The name of the component.", + "$default": { + "$source": "argv", + "index": 1 + }, + "x-prompt": "What name would you like to use for the component?" + }, + "template": { + "type": "string", + "description": "Template ID, such as 'grid', 'combo', etc.", + "$default": { + "$source": "argv", + "index": 0 + } + } + }, + "required": [ + "name" + ] +} \ No newline at end of file diff --git a/packages/ng-schematics/src/component/schema.ts b/packages/ng-schematics/src/component/schema.ts new file mode 100644 index 000000000..9b43f32d2 --- /dev/null +++ b/packages/ng-schematics/src/component/schema.ts @@ -0,0 +1,9 @@ +import { IgniteUIForAngularTemplate } from "@igniteui-angular/templates"; + +export abstract class ComponentOptions { + public templateInst?: IgniteUIForAngularTemplate; + public template?: string; + public name: string; + public skipRoute?: boolean; + public projectName: string; +} diff --git a/packages/ng-schematics/src/igniteui-angular-schematics/index.ts b/packages/ng-schematics/src/igniteui-angular-schematics/index.ts deleted file mode 100644 index c0e3c4e44..000000000 --- a/packages/ng-schematics/src/igniteui-angular-schematics/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics'; - - -// You don't have to export the function as default. You can also have more than one rule factory -// per file. -export function igniteuiAngularSchematics(_options: any): Rule { - return (tree: Tree, _context: SchematicContext) => { - return tree; - }; -} diff --git a/packages/ng-schematics/src/igniteui-angular-schematics/index_spec.ts b/packages/ng-schematics/src/igniteui-angular-schematics/index_spec.ts deleted file mode 100644 index e8244a8f6..000000000 --- a/packages/ng-schematics/src/igniteui-angular-schematics/index_spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Tree } from '@angular-devkit/schematics'; -import { SchematicTestRunner } from '@angular-devkit/schematics/testing'; -import * as path from 'path'; - - -const collectionPath = path.join(__dirname, '../collection.json'); - - -describe('igniteui-angular-schematics', () => { - it('works', () => { - const runner = new SchematicTestRunner('schematics', collectionPath); - const tree = runner.runSchematic('igniteui-angular-schematics', {}, Tree.empty()); - - expect(tree.files).toEqual([]); - }); -}); diff --git a/packages/ng-schematics/src/ng-new/index.ts b/packages/ng-schematics/src/ng-new/index.ts index b318d180b..97e474ae7 100644 --- a/packages/ng-schematics/src/ng-new/index.ts +++ b/packages/ng-schematics/src/ng-new/index.ts @@ -9,6 +9,7 @@ import { import { ProjectLibrary, Util } from "@igniteui-cli/core"; import { defer, Observable } from 'rxjs'; import { NewProjectOptions } from "../app-projects/schema"; +import { ComponentOptions } from "../component/schema"; import { SchematicsPromptSession } from "../prompt/SchematicsPromptSession"; import { SchematicsTemplateManager } from "../SchematicsTemplateManager"; import { OptionsSchema } from "./schema"; @@ -22,9 +23,10 @@ export function newProject(options: OptionsSchema): Rule { return (_host: Tree, context: IgxSchematicContext) => { context.logger.info(`Generating ${options.name}`); + let projectName = options.name; let projLibrary: ProjectLibrary; let projectOptions: NewProjectOptions; - const addedComponents: any[] = []; + const addedComponents: ComponentOptions[] = []; const templateManager = new SchematicsTemplateManager(); const prompt = new SchematicsPromptSession(templateManager, addedComponents); @@ -34,7 +36,6 @@ export function newProject(options: OptionsSchema): Rule { return chain([ (tree: Tree, context: IgxSchematicContext): Observable => { return defer(async function() { // TODO rxjs types? - let projectName = options.name; if (!prompt.nameIsValid(options.name)) { projectName = await prompt.getUserInput({ @@ -89,7 +90,7 @@ export function newProject(options: OptionsSchema): Rule { }, (_tree: Tree, _context: IgxSchematicContext) => { if (addedComponents.length) { - return chain(addedComponents); + return chain(addedComponents.map(x => schematic("component", Object.assign(x, { projectName })))); } }, move(options.name) diff --git a/packages/ng-schematics/src/prompt/SchematicsPromptSession.ts b/packages/ng-schematics/src/prompt/SchematicsPromptSession.ts index cf27dc2b2..a25701a0e 100644 --- a/packages/ng-schematics/src/prompt/SchematicsPromptSession.ts +++ b/packages/ng-schematics/src/prompt/SchematicsPromptSession.ts @@ -1,11 +1,13 @@ -import { Rule, schematic, Tree } from "@angular-devkit/schematics"; +import { Tree } from "@angular-devkit/schematics"; +import { IgniteUIForAngularTemplate } from "@igniteui-angular/templates"; import { BasePromptSession, BaseTemplateManager, Framework, IUserInputOptions, - ProjectLibrary, ProjectTemplate, PromptTaskContext, Task, ProjectConfig, NgTreeFileSystem } from "@igniteui-cli/core"; + NgTreeFileSystem, ProjectConfig, ProjectLibrary, ProjectTemplate, PromptTaskContext, Task } from "@igniteui-cli/core"; +import { ComponentOptions } from "../component/schema"; export class SchematicsPromptSession extends BasePromptSession { - constructor(templateManager: BaseTemplateManager, private rulesChain: Rule[]) { + constructor(templateManager: BaseTemplateManager, private rulesChain: ComponentOptions[]) { super(templateManager); this.config = ProjectConfig.getConfig(); } @@ -47,10 +49,13 @@ export class SchematicsPromptSession extends BasePromptSession { if (context.template.hasExtraConfiguration) { await this.customizeTemplateTask(context.template); } - this.rulesChain.push(schematic("component", { + + const options: ComponentOptions = { name, - template: context.template - })); + projectName: "", + templateInst: context.template as IgniteUIForAngularTemplate + }; + this.rulesChain.push(options); return true; }; }