diff --git a/CHANGELOG.md b/CHANGELOG.md index 48cb0a65..c1808133 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## TSGeneratorGenerator v1.3.0 + - Replaced generated code by modules + - Improved user-experience + - Provided the functionality to customize the context based on the source- and destination-file + - Provided the functionality to set custom processors for file-mappings + +[Show differences][v1.3.0] + ## TSGeneratorGenerator v1.2.3 - Improved the `package.json`-template - Reworked the ignore-files @@ -48,4 +56,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [v1.2.0]: https://github.com/manuth/TSGeneratorGenerator/compare/v1.0.1...v1.2.0 [v1.2.1]: https://github.com/manuth/TSGeneratorGenerator/compare/v1.2.0...v1.2.1 [v1.2.2]: https://github.com/manuth/TSGeneratorGenerator/compare/v1.2.1...v1.2.2 -[v1.2.3]: https://github.com/manuth/TSGeneratorGenerator/compare/v1.2.2...v1.2.3 \ No newline at end of file +[v1.2.3]: https://github.com/manuth/TSGeneratorGenerator/compare/v1.2.2...v1.2.3 +[v1.3.0]: https://github.com/manuth/TSGeneratorGenerator/compare/v1.2.3...v1.3.0 \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 57a4c8b5..6b988c13 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "generator-ts-generator", - "version": "1.2.3", + "version": "1.3.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -67,7 +67,6 @@ "version": "0.0.43", "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-0.0.43.tgz", "integrity": "sha512-xgyfKZVMFqE8aIKy1xfFVsX2MxyXUNgjgmbF6dRbR3sL+ZM5K4ka/9L4mmTwX8eTeVYtduyXu0gUVwVJa1HbNw==", - "dev": true, "requires": { "@types/rx": "*", "@types/through": "*" @@ -104,16 +103,14 @@ "dev": true }, "@types/node": { - "version": "10.12.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.9.tgz", - "integrity": "sha512-eajkMXG812/w3w4a1OcBlaTwsFPO5F7fJ/amy+tieQxEMWBlbV1JGSjkFM+zkHNf81Cad+dfIRA+IBkvmvdAeA==", - "dev": true + "version": "10.12.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.10.tgz", + "integrity": "sha512-8xZEYckCbUVgK8Eg7lf5Iy4COKJ5uXlnIOnePN0WUwSQggy9tolM+tDJf7wMOnT/JT/W9xDYIaYggt3mRV2O5w==" }, "@types/rx": { "version": "4.1.1", "resolved": "http://registry.npmjs.org/@types/rx/-/rx-4.1.1.tgz", "integrity": "sha1-WY/JSla67ZdfGUV04PVy/Y5iekg=", - "dev": true, "requires": { "@types/rx-core": "*", "@types/rx-core-binding": "*", @@ -132,14 +129,12 @@ "@types/rx-core": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/@types/rx-core/-/rx-core-4.0.3.tgz", - "integrity": "sha1-CzNUsSOM7b4rdPYybxOdvHpZHWA=", - "dev": true + "integrity": "sha1-CzNUsSOM7b4rdPYybxOdvHpZHWA=" }, "@types/rx-core-binding": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/rx-core-binding/-/rx-core-binding-4.0.4.tgz", "integrity": "sha512-5pkfxnC4w810LqBPUwP5bg7SFR/USwhMSaAeZQQbEHeBp57pjKXRlXmqpMrLJB4y1oglR/c2502853uN0I+DAQ==", - "dev": true, "requires": { "@types/rx-core": "*" } @@ -148,7 +143,6 @@ "version": "4.0.6", "resolved": "https://registry.npmjs.org/@types/rx-lite/-/rx-lite-4.0.6.tgz", "integrity": "sha512-oYiDrFIcor9zDm0VDUca1UbROiMYBxMLMaM6qzz4ADAfOmA9r1dYEcAFH+2fsPI5BCCjPvV9pWC3X3flbrvs7w==", - "dev": true, "requires": { "@types/rx-core": "*", "@types/rx-core-binding": "*" @@ -158,7 +152,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/@types/rx-lite-aggregates/-/rx-lite-aggregates-4.0.3.tgz", "integrity": "sha512-MAGDAHy8cRatm94FDduhJF+iNS5//jrZ/PIfm+QYw9OCeDgbymFHChM8YVIvN2zArwsRftKgE33QfRWvQk4DPg==", - "dev": true, "requires": { "@types/rx-lite": "*" } @@ -167,7 +160,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/rx-lite-async/-/rx-lite-async-4.0.2.tgz", "integrity": "sha512-vTEv5o8l6702ZwfAM5aOeVDfUwBSDOs+ARoGmWAKQ6LOInQ8J4/zjM7ov12fuTpktUKdMQjkeCp07Vd73mPkxw==", - "dev": true, "requires": { "@types/rx-lite": "*" } @@ -176,7 +168,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/@types/rx-lite-backpressure/-/rx-lite-backpressure-4.0.3.tgz", "integrity": "sha512-Y6aIeQCtNban5XSAF4B8dffhIKu6aAy/TXFlScHzSxh6ivfQBQw6UjxyEJxIOt3IT49YkS+siuayM2H/Q0cmgA==", - "dev": true, "requires": { "@types/rx-lite": "*" } @@ -185,7 +176,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/@types/rx-lite-coincidence/-/rx-lite-coincidence-4.0.3.tgz", "integrity": "sha512-1VNJqzE9gALUyMGypDXZZXzR0Tt7LC9DdAZQ3Ou/Q0MubNU35agVUNXKGHKpNTba+fr8GdIdkC26bRDqtCQBeQ==", - "dev": true, "requires": { "@types/rx-lite": "*" } @@ -194,7 +184,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/@types/rx-lite-experimental/-/rx-lite-experimental-4.0.1.tgz", "integrity": "sha1-xTL1y98/LBXaFt7Ykw0bKYQCPL0=", - "dev": true, "requires": { "@types/rx-lite": "*" } @@ -203,7 +192,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/@types/rx-lite-joinpatterns/-/rx-lite-joinpatterns-4.0.1.tgz", "integrity": "sha1-9w/jcFGKhDLykVjMkv+1a05K/D4=", - "dev": true, "requires": { "@types/rx-lite": "*" } @@ -212,7 +200,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/@types/rx-lite-testing/-/rx-lite-testing-4.0.1.tgz", "integrity": "sha1-IbGdEfTf1v/vWp0WSOnIh5v+Iek=", - "dev": true, "requires": { "@types/rx-lite-virtualtime": "*" } @@ -221,7 +208,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/@types/rx-lite-time/-/rx-lite-time-4.0.3.tgz", "integrity": "sha512-ukO5sPKDRwCGWRZRqPlaAU0SKVxmWwSjiOrLhoQDoWxZWg6vyB9XLEZViKOzIO6LnTIQBlk4UylYV0rnhJLxQw==", - "dev": true, "requires": { "@types/rx-lite": "*" } @@ -230,7 +216,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/@types/rx-lite-virtualtime/-/rx-lite-virtualtime-4.0.3.tgz", "integrity": "sha512-3uC6sGmjpOKatZSVHI2xB1+dedgml669ZRvqxy+WqmGJDVusOdyxcKfyzjW0P3/GrCiN4nmRkLVMhPwHCc5QLg==", - "dev": true, "requires": { "@types/rx-lite": "*" } @@ -239,7 +224,6 @@ "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.29.tgz", "integrity": "sha512-9a7C5VHh+1BKblaYiq+7Tfc+EOmjMdZaD1MYtkQjSoxgB69tBjW98ry6SKsi4zEIWztLOMRuL87A3bdT/Fc/4w==", - "dev": true, "requires": { "@types/node": "*" } @@ -248,7 +232,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/@types/yeoman-generator/-/yeoman-generator-3.0.1.tgz", "integrity": "sha512-Q5cx5lfH3jbGFhb8KpqHHOxL06NavoR4txD17uIgdSTQQ+zdiOl2VpJ260Yv9qRw+UgGGF9orKF/3IBkUtWIMA==", - "dev": true, "requires": { "@types/inquirer": "*" } @@ -1408,6 +1391,18 @@ } } }, + "extended-yo-generator": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/extended-yo-generator/-/extended-yo-generator-1.0.2.tgz", + "integrity": "sha512-z3keCKOCOjI8HiQbPt0uvneY/j5Io5Dvkbxc6fVD0DUbxHRjfEoHkS/6dkfqzwraaKeMo8XfahP9JtP8OMOnbw==", + "requires": { + "@types/inquirer": "0.0.43", + "@types/yeoman-generator": "^3.0.1", + "inquirer": "^6.2.0", + "pkg-up": "^2.0.0", + "yeoman-generator": "^3.1.1" + } + }, "external-editor": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz", @@ -6611,7 +6606,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=", - "dev": true, "requires": { "find-up": "^2.1.0" }, @@ -6620,7 +6614,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, "requires": { "locate-path": "^2.0.0" } @@ -6629,7 +6622,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, "requires": { "p-locate": "^2.0.0", "path-exists": "^3.0.0" @@ -6639,7 +6631,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, "requires": { "p-try": "^1.0.0" } @@ -6648,7 +6639,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, "requires": { "p-limit": "^1.1.0" } @@ -6656,8 +6646,7 @@ "p-try": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" } } }, diff --git a/package.json b/package.json index 43a2f31d..16c39c92 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "generator-ts-generator", - "version": "1.2.3", + "version": "1.3.0", "description": "A Generator for Yeoman Generators Written in TypeScript.", "author": "Manuel Thalmann ", "license": "MIT", @@ -19,17 +19,18 @@ }, "homepage": "https://github.com/manuth/TSGeneratorGenerator#readme", "scripts": { - "watch": "tsc -p . --watch", "compile": "tsc -p .", + "watch": "tsc -p . --watch", + "clean": "rimraf ./lib", "lint": "tslint -p ./ -t verbose", "test": "mocha", - "prepare": "npm run compile" + "prepare": "npm run clean && npm run compile" }, "dependencies": { "chalk": "^2.4.1", "dedent": "^0.7.0", + "extended-yo-generator": "^1.0.2", "fs-extra": "^7.0.1", - "inquirer": "^6.2.0", "lodash.camelcase": "^4.3.0", "lodash.kebabcase": "^4.1.1", "yeoman-generator": "^3.1.1", @@ -38,19 +39,19 @@ "devDependencies": { "@types/dedent": "^0.7.0", "@types/fs-extra": "^5.0.4", - "@types/inquirer": "0.0.43", "@types/lodash.camelcase": "^4.3.4", "@types/lodash.kebabcase": "^4.1.4", "@types/mocha": "^5.2.5", - "@types/node": "^10.12.9", + "@types/node": "^10.12.10", "@types/yeoman-generator": "^3.0.1", "@types/yeoman-test": "^1.7.4", "@types/yosay": "0.0.29", "mocha": "^5.2.0", "npm": "^6.4.1", + "rimraf": "^2.6.2", "tslint": "^5.11.0", "typescript": "^3.1.6", - "typescript-tslint-plugin": "0.1.0", + "typescript-tslint-plugin": "^0.1.0", "yeoman-test": "^1.9.1", "yo": "^2.0.5" } diff --git a/src/Generator.ts b/src/Generator.ts deleted file mode 100644 index 3422ae0b..00000000 --- a/src/Generator.ts +++ /dev/null @@ -1,268 +0,0 @@ -import { ChoiceType, Question, Separator } from "inquirer"; -import Path = require("path"); -import { isNullOrUndefined } from "util"; -import YeomanGenerator = require("yeoman-generator"); -import { GeneratorSetting } from "./GeneratorSetting"; -import { IComponentProvider } from "./IComponentProvider"; -import { IGeneratorSettings } from "./IGeneratorSettings"; - -/** - * Represents a yeoman-generator. - */ -export abstract class Generator extends YeomanGenerator -{ - /** - * The settings of the generator. - */ - private settings: T = {} as T; - - /** - * Initializes a new instance of the `Generator` class. - * - * @param args - * A set of arguments for the generator. - * - * @param options - * A set of options for the generator. - */ - public constructor(args: string | string[], options: {}) - { - super(args, options); - } - - /** - * Gets the name of the root of the template-folder. - */ - protected abstract get TemplateRoot(): string; - - /** - * Gets the questions to ask before executing the generator. - */ - protected get Questions(): YeomanGenerator.Question[] - { - return []; - } - - /** - * Gets the components provided by the generator. - */ - protected get ProvidedComponents(): IComponentProvider - { - return null; - } - - /** - * Gets the settings of the generator. - */ - protected get Settings() - { - return this.settings; - } - - /** - * Joins the arguments together and returns the resulting path relative to the template-directory. - * - * @param path - * The path that is to be joined. - */ - public templatePath(...path: string[]) - { - return Path.join(__dirname, "..", "templates", this.TemplateRoot, ...path); - } - - /** - * Joint the arguments together and returns the resulting path relative to the module-directory. - * - * @param path - * The path that is to be joined. - */ - public modulePath(...path: string[]) - { - return Path.join(__dirname, "..", ...path); - } - - /** - * Gathers all information for executing the generator and saves them to the `Settings`. - */ - public async prompting() - { - let questions: YeomanGenerator.Questions = []; - let components: ChoiceType[] = []; - let defaults: string[] = []; - - if (this.ProvidedComponents !== null) - { - for (let category of this.ProvidedComponents.Categories) - { - components.push(new Separator(category.DisplayName)); - - for (let component of category.Components) - { - let isDefault = !isNullOrUndefined(component.Default) && component.Default; - - components.push({ - value: component.ID, - name: component.DisplayName, - checked: isDefault - }); - - if (isDefault) - { - defaults.push(component.ID); - } - - if (typeof component.FileMappings !== "function") - { - for (let i in component.FileMappings) - { - let fileMapping = component.FileMappings[i]; - - if ( - typeof fileMapping.Destination !== "string" && - typeof fileMapping.Destination !== "function") - { - let question: Question = { - type: "input", - name: `${GeneratorSetting.ComponentPaths}[${JSON.stringify(component.ID)}][${i}]`, - message: fileMapping.Destination.Message, - when: answers => - { - return answers[GeneratorSetting.Components].includes(component.ID); - } - }; - - if (!isNullOrUndefined(fileMapping.Destination.Default)) - { - question.default = fileMapping.Destination.Default; - } - - questions.push(question as YeomanGenerator.Question); - } - } - } - - if (!isNullOrUndefined(component.Questions)) - { - for (let question of component.Questions) - { - if (isNullOrUndefined(question.when)) - { - question.when = (settings: T) => settings[GeneratorSetting.Components].includes(component.ID); - } - - questions.push(question as Question); - } - } - } - } - - questions.unshift( - { - type: "checkbox", - name: GeneratorSetting.Components, - message: this.ProvidedComponents.Question, - choices: components, - default: defaults - }); - } - - questions.unshift(...this.Questions); - Object.assign(this.Settings, await this.prompt(questions)); - } - - /** - * Writes all files for the components. - */ - public async writing() - { - for (let category of this.ProvidedComponents.Categories) - { - for (let component of category.Components) - { - if (this.Settings[GeneratorSetting.Components].includes(component.ID)) - { - let fileMappings = await this.ResolveValue(this.Settings, component.FileMappings); - - for (let fileMapping of fileMappings) - { - if ( - !isNullOrUndefined(fileMapping.Source) && - !isNullOrUndefined(fileMapping.Destination)) - { - let sourcePath: string = await this.ResolveValue(this.Settings, fileMapping.Source); - let destinationPath: string; - - if ( - typeof fileMapping.Destination === "string" || - typeof fileMapping.Destination === "function") - { - destinationPath = await this.ResolveValue(this.Settings, fileMapping.Destination); - } - else - { - destinationPath = this.Settings[GeneratorSetting.ComponentPaths][component.ID]; - } - - let context = await this.ResolveValue(this.Settings, fileMapping.Context); - sourcePath = Path.isAbsolute(sourcePath) ? sourcePath : this.templatePath(sourcePath); - destinationPath = Path.isAbsolute(destinationPath) ? destinationPath : this.destinationPath(destinationPath); - - if (isNullOrUndefined(context)) - { - this.fs.copy(sourcePath, destinationPath); - } - else - { - this.fs.copyTpl(sourcePath, destinationPath, context); - } - } - } - } - } - } - } - - /** - * Installs all required dependencies. - */ - public async install() - { - } - - /** - * Finalizes the generation-process. - */ - public async end() - { - } - - /** - * Resolves a value no matter whether it is wrapped in a function or not. - * - * @param settings - * The settings to use for resolving the value. - * - * @param value - * The value to resolve. - */ - private async ResolveValue(settings: T, value: TValue | ((settings?: T) => TValue) | ((settings?: T) => Promise)) - { - if (value instanceof Function) - { - let result = value(settings); - - if (result instanceof Promise) - { - return result; - } - else - { - return result; - } - } - else - { - return value; - } - } -} \ No newline at end of file diff --git a/src/GeneratorSetting.ts b/src/GeneratorSetting.ts deleted file mode 100644 index bf378f09..00000000 --- a/src/GeneratorSetting.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Represents a generator-setting. - */ -export enum GeneratorSetting -{ - /** - * Indicates the `Components`-setting. - */ - Components = "components", - - /** - * Indicates the `ComponentPaths`-setting. - */ - ComponentPaths = "componentPaths" -} \ No newline at end of file diff --git a/src/IComponent.ts b/src/IComponent.ts deleted file mode 100644 index b3b03e6a..00000000 --- a/src/IComponent.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Question } from "inquirer"; -import { Answers } from "yeoman-generator"; -import { IFileMapping } from "./IFileMapping"; - -/** - * Represents a component. - */ -export interface IComponent -{ - /** - * Gets or sets the id of the component. - */ - ID: string; - - /** - * Gets or sets the human-readable name of the component. - */ - DisplayName: string; - - /** - * Gets or sets a value indicating whether the component is enabled by default. - */ - Default?: boolean; - - /** - * Gets or sets the file-mappings of the component. - */ - FileMappings: IFileMapping[] | ((settings: T) => IFileMapping[]) | ((settings: T) => Promise[]>); - - /** - * Gets or sets additional quetions related to the component. - */ - Questions?: Question[]; -} \ No newline at end of file diff --git a/src/IComponentCategory.ts b/src/IComponentCategory.ts deleted file mode 100644 index 32a600bd..00000000 --- a/src/IComponentCategory.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Answers } from "yeoman-generator"; -import { IComponent } from "./IComponent"; - -/** - * Represents a category which contains components. - */ -export interface IComponentCategory -{ - /** - * Gets or sets the human-readable name of the category. - */ - DisplayName: string; - - /** - * Gets or sets the components of the category. - */ - Components: IComponent[]; -} \ No newline at end of file diff --git a/src/IComponentDestination.ts b/src/IComponentDestination.ts deleted file mode 100644 index 83c8b6af..00000000 --- a/src/IComponentDestination.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Answers } from "yeoman-generator"; - -/** - * Represents a destination of a component. - */ -export interface IComponentDestination -{ - /** - * Gets or sets the message to ask for the destination. - */ - Message: string; - - /** - * Gets or sets the default destination. - */ - Default: string | ((answers: Answers) => string) | ((answers: Answers) => Promise); -} \ No newline at end of file diff --git a/src/IComponentProvider.ts b/src/IComponentProvider.ts deleted file mode 100644 index 35dc3366..00000000 --- a/src/IComponentProvider.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Answers } from "yeoman-generator"; -import { IComponentCategory } from "./IComponentCategory"; - -/** - * Represents provided components. - */ -export interface IComponentProvider -{ - /** - * Gets or sets the question to show when asking to choose components. - */ - Question: string; - - /** - * Gets or sets the provided categories. - */ - Categories: IComponentCategory[]; -} \ No newline at end of file diff --git a/src/IFileMapping.ts b/src/IFileMapping.ts deleted file mode 100644 index 493010ed..00000000 --- a/src/IFileMapping.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Answers } from "yeoman-generator"; -import { IComponentDestination } from "./IComponentDestination"; - -/** - * Represents a file-mapping. - */ -export interface IFileMapping -{ - /** - * Gets or sets the path to the template of the component. - */ - Source: string | ((answers: T) => string) | ((answers: T) => Promise); - - /** - * The context to use for copying the file-entry. - */ - Context?: ((answers: T) => any | Promise); - - /** - * Gets or sets the destination to save the component to. - */ - Destination: IComponentDestination | string | ((answers: T) => string | Promise); -} \ No newline at end of file diff --git a/src/IGeneratorSettings.ts b/src/IGeneratorSettings.ts deleted file mode 100644 index 440eac04..00000000 --- a/src/IGeneratorSettings.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Answers } from "yeoman-generator"; -import { GeneratorSetting } from "./GeneratorSetting"; - -/** - * Represents settings of a generator. - */ -export interface IGeneratorSettings extends Answers -{ - /** - * Gets or sets a specific setting. - */ - [key: string]: any; - - /** - * Gets or sets the components to install. - */ - [GeneratorSetting.Components]: string[]; - - /** - * Gets or sets the paths to save the components to. - */ - [GeneratorSetting.ComponentPaths]: { [key: string]: string }; -} \ No newline at end of file diff --git a/src/generators/app/AppGenerator.ts b/src/generators/app/AppGenerator.ts new file mode 100644 index 00000000..375d4ea5 --- /dev/null +++ b/src/generators/app/AppGenerator.ts @@ -0,0 +1,411 @@ +import chalk from "chalk"; +import dedent = require("dedent"); +import { Generator, GeneratorSetting, IComponentProvider, IFileMapping, Question } from "extended-yo-generator"; +import FileSystem = require("fs-extra"); +import camelCase = require("lodash.camelcase"); +import kebabCase = require("lodash.kebabcase"); +import Path = require("path"); +import yosay = require("yosay"); +import { AppComponent } from "./AppComponent"; +import { AppSetting } from "./AppSetting"; +import { IAppSettings } from "./IAppSettings"; +import { LintMode } from "./LintMode"; +import { SubGeneratorSetting } from "./SubGeneratorSetting"; + +/** + * Provides the functionality to generate a generator written in TypeScript. + */ +export class AppGenerator extends Generator +{ + /** + * Initializes a new instance of the `AppGenerator` class. + * + * @param args + * A set of arguments for the generator. + * + * @param options + * A set of options for the generator. + */ + public constructor(args: string | string[], options: {}) + { + super(args, options); + } + + protected get TemplateRoot(): string + { + return "app"; + } + + protected get Questions(): Question[] + { + return [ + { + type: "input", + name: AppSetting.Destination, + message: "Where do you want to save your generator to?", + default: "./", + filter: async input => Path.isAbsolute(input) ? input : Path.resolve(process.cwd(), input) + }, + { + type: "input", + name: AppSetting.DisplayName, + message: "What's the name of your project?", + default: (answers: IAppSettings) => Path.basename(answers[AppSetting.Destination]), + validate: (input: string) => /.+/.test(input.trim()) ? true : "The name must not be empty!" + }, + { + type: "input", + name: AppSetting.Name, + message: "What's the name of the node-module?", + default: (answers: IAppSettings) => "generator-" + kebabCase(answers[AppSetting.DisplayName].replace(/(generator-)?(.*?)(generator)?$/i, "$2")), + filter: input => kebabCase(input), + validate: (input: string) => + { + if (/[\w-]+/.test(input)) + { + return input.startsWith("generator-") ? true : 'The name must start with "generator-"'; + } + else + { + return "Please provide a name according to the npm naming-conventions."; + } + } + }, + { + type: "input", + name: AppSetting.Description, + message: "Please enter a description for your generator." + } + ]; + } + + protected get ProvidedComponents(): IComponentProvider + { + return { + Question: "What do you want to include in your workspace?", + Categories: [ + { + DisplayName: "General", + Components: [ + { + ID: AppComponent.TSLint, + DisplayName: "TSLint configurations", + Default: true, + Questions: [ + { + name: AppSetting.LintMode, + type: "list", + message: "What ruleset do you want to use for linting?", + choices: [ + { + value: LintMode.Weak, + name: "manuth's weak ruleset" + }, + { + value: LintMode.Strong, + name: "manuth's strong ruleset (recommended)" + } + ], + default: LintMode.Strong + } + ], + FileMappings: [ + { + Source: settings => + { + switch (settings[AppSetting.LintMode]) + { + case LintMode.Weak: + return "tslint.weak.jsonc"; + case LintMode.Strong: + default: + return this.modulePath("tslint.json"); + } + }, + Destination: "tslint.json" + } + ] + }, + { + ID: AppComponent.VSCode, + DisplayName: "Visual Studio Code-Workspace", + Default: true, + FileMappings: [ + { + Source: this.modulePath(".vscode"), + Destination: ".vscode" + }, + { + Source: "launch.json", + Destination: () => this.destinationPath(".vscode", "launch.json") + } + ] + }, + { + ID: AppComponent.GeneratorExample, + DisplayName: "Example Generator (recommended)", + FileMappings: (settings) => this.GetGeneratorFileMappings("app", settings[AppSetting.DisplayName]) + }, + { + ID: AppComponent.SubGeneratorExample, + DisplayName: "Example Sub-Generator", + Questions: [ + { + type: "input", + name: `${AppSetting.SubGenerator}.${SubGeneratorSetting.DisplayName}`, + message: "What's the human-readable name of your sub-generator?", + validate: (input: string) => /.+/.test(input.trim()) ? true : "The name must not be empty!" + }, + { + type: "input", + name: `${AppSetting.SubGenerator}.${SubGeneratorSetting.Name}`, + message: "What's the unique name of the sub-generator?", + default: (settings: IAppSettings) => kebabCase(settings[AppSetting.SubGenerator][SubGeneratorSetting.DisplayName] || ""), + validate: (input: string) => /[\w-]+/.test(input) ? true : "Please provide a name according to the npm naming-conventions." + } + ], + FileMappings: (settings) => this.GetGeneratorFileMappings(settings[AppSetting.SubGenerator][SubGeneratorSetting.Name], settings[AppSetting.SubGenerator][SubGeneratorSetting.DisplayName]) + } + ] + } + ] + }; + } + + public async prompting() + { + this.log(yosay(`Welcome to the ${chalk.whiteBright("TypeScript Generator")} generator!`)); + return super.prompting(); + } + + public async writing() + { + let sourceRoot = "src"; + this.destinationRoot(this.Settings[AppSetting.Destination]); + this.fs.writeJSON(this.destinationPath("package.json"), this.GetPackageJSON()); + this.fs.copy(this.templatePath(".gitignore.ejs"), this.destinationPath(".gitignore")); + this.fs.copy(this.templatePath(".npmignore.ejs"), this.destinationPath(".npmignore")); + this.fs.copy(this.modulePath("tsconfig.json"), this.destinationPath("tsconfig.json")); + this.fs.copy(this.modulePath("test", "mocha.opts"), this.destinationPath("test", "mocha.opts")); + this.fs.copyTpl( + this.templatePath("GettingStarted.md.ejs"), + this.destinationPath("GettingStarted.md"), + { + Name: this.Settings[AppSetting.Name], + HasCodeWorkspace: this.Settings[GeneratorSetting.Components].includes(AppComponent.VSCode), + SubGeneratorName: this.Settings[GeneratorSetting.Components].includes(AppComponent.SubGeneratorExample) ? this.Settings[AppSetting.SubGenerator][SubGeneratorSetting.Name] : null + }); + this.fs.copyTpl( + this.templatePath("README.md.ejs"), + this.destinationPath("README.md"), + { + Name: this.Settings[AppSetting.Name], + DisplayName: this.Settings[AppSetting.DisplayName], + Description: this.Settings[AppSetting.Description] + }); + this.fs.copyTpl( + this.templatePath("tests", "main.test.ts.ejs"), + this.destinationPath(sourceRoot, "tests", "main.test.ts"), + { + Name: this.Settings[AppSetting.DisplayName] + }); + this.fs.copyTpl( + this.templatePath("tests", "Generators", "index.test.ts.ejs"), + this.destinationPath(sourceRoot, "tests", "Generators", "index.test.ts"), + { + Name: this.Settings[AppSetting.Name] + }); + this.fs.copyTpl( + this.templatePath("tests", "Generators", "app.test.ts.ejs"), + this.destinationPath(sourceRoot, "tests", "Generators", `${this.Settings[AppSetting.Name]}.test.ts`), + { + Name: this.Settings[AppSetting.DisplayName] + }); + FileSystem.ensureDir(this.destinationPath(sourceRoot, "generators")); + FileSystem.ensureDir(this.destinationPath("templates")); + return super.writing(); + } + + public async install() + { + this.log("Your workspace has been generated!"); + this.log(); + this.log(chalk.whiteBright("Installing dependencies...")); + this.npmInstall(); + } + + public async end() + { + this.log(dedent(` + Your package "${this.Settings[AppSetting.DisplayName]}" has been created! + To start editing with Visual Studio Code use following commands: + + code "${this.Settings[AppSetting.Destination]}" + + Open "GettingStarted.md" in order to learn more about how to create your very own generator.`)); + this.log(); + } + + /** + * Creates file-mappings for a generator. + * + * @param id + * The id of the generator. + * + * @param displayName + * The human readable name of the generator. + * + * @returns + * File-mappings for a generator. + */ + protected GetGeneratorFileMappings = (id: string, displayName: string): IFileMapping[] => + { + let name = (id.charAt(0).toUpperCase() + camelCase(id).slice(1)); + let source = "generator"; + let destination = `src/generators/${id}`; + let generatorName = `${name}Generator`; + let identities = `${name}Setting`; + let settings = `I${name}Settings`; + + return [ + { + Source: Path.join(source, "LicenseType.ts.ejs"), + Destination: Path.join(destination, "LicenseType.ts") + }, + { + Source: Path.join(source, "Setting.ts.ejs"), + Context: () => + { + return { Name: identities }; + }, + Destination: Path.join(destination, `${identities}.ts`) + }, + { + Source: Path.join(source, "ISettings.ts.ejs"), + Context: () => + { + return { + Name: generatorName, + SettingsInterface: settings, + Identities: identities + }; + }, + Destination: Path.join(destination, `${settings}.ts`) + }, + { + Source: Path.join(source, "Generator.ts.ejs"), + Context: () => + { + return { + Name: generatorName, + SettingsInterface: settings, + Identities: identities, + ID: id, + DisplayName: displayName + }; + }, + Destination: Path.join(destination, `${generatorName}.ts`) + }, + { + Source: Path.join(source, "index.ts.ejs"), + Context: () => + { + return { + Name: generatorName + }; + }, + Destination: Path.join(destination, "index.ts") + }, + { + Source: Path.join(source, "templates"), + Destination: Path.join("templates", id) + } + ]; + } + + /** + * Gets the package-manifest for the generator to generate. + */ + protected GetPackageJSON = (): {} => + { + let scripts = [ + "compile", + "watch", + "clean", + "lint", + "test", + "prepare" + ]; + + let dependencies = [ + "extended-yo-generator" + ]; + + let devDependencies = [ + "@types/mocha", + "@types/node", + "@types/yeoman-generator", + "mocha", + "tslint", + "typescript", + "typescript-tslint-plugin", + "yo" + ]; + + if ( + this.Settings[GeneratorSetting.Components].includes(AppComponent.GeneratorExample) || + this.Settings[GeneratorSetting.Components].includes(AppComponent.SubGeneratorExample)) + { + dependencies.push( + "chalk", + "dedent", + "yosay"); + + devDependencies.push( + "@types/dedent", + "@types/yosay"); + } + + let result = { + name: this.Settings[AppSetting.Name], + version: "0.0.0", + description: this.Settings[AppSetting.Description], + author: { + name: this.user.git.name(), + email: this.user.git.email() + }, + keywords: ["yeoman-generator"], + scripts: {} as { [key: string]: string }, + dependencies: {} as { [key: string]: string }, + devDependencies: {} as { [key: string]: string } + }; + + let packageJSON: typeof result = require(Path.join(__dirname, "..", "..", "..", "package.json")); + + for (let script of scripts) + { + if (script in packageJSON.scripts) + { + result.scripts[script] = packageJSON.scripts[script]; + } + } + + for (let devDependency of devDependencies.sort()) + { + if (devDependency in packageJSON.devDependencies) + { + result.devDependencies[devDependency] = packageJSON.devDependencies[devDependency]; + } + } + + for (let dependency of dependencies.sort()) + { + if (dependency in packageJSON.dependencies) + { + result.dependencies[dependency] = packageJSON.dependencies[dependency]; + } + } + + return result; + } +} \ No newline at end of file diff --git a/src/generators/app/AppSetting.ts b/src/generators/app/AppSetting.ts index 623acb17..fdb7ce0f 100644 --- a/src/generators/app/AppSetting.ts +++ b/src/generators/app/AppSetting.ts @@ -9,14 +9,14 @@ export enum AppSetting Destination = "destination", /** - * Indicates the `Name`-setting. + * Indicates the `DisplayName`-setting. */ - Name = "name", + DisplayName = "displayName", /** - * Indicates the `ModuleName`-setting. + * Indicates the `Name`-setting. */ - ModuleName = "moduleName", + Name = "name", /** * Indicates the `Description`-setting. diff --git a/src/generators/app/IAppSettings.ts b/src/generators/app/IAppSettings.ts index 49c6495e..9525eeeb 100644 --- a/src/generators/app/IAppSettings.ts +++ b/src/generators/app/IAppSettings.ts @@ -1,4 +1,4 @@ -import { IGeneratorSettings } from "../../IGeneratorSettings"; +import { IGeneratorSettings } from "extended-yo-generator"; import { AppSetting } from "./AppSetting"; import { ISubGenerator } from "./ISubGenerator"; import { LintMode } from "./LintMode"; @@ -11,12 +11,12 @@ export interface IAppSettings extends IGeneratorSettings /** * Gets or sets the name. */ - [AppSetting.Name]: string; + [AppSetting.DisplayName]: string; /** * Gets or sets the module-name. */ - [AppSetting.ModuleName]: string; + [AppSetting.Name]: string; /** * Gets or sets the description. diff --git a/src/generators/app/index.ts b/src/generators/app/index.ts index 75b15013..4267937d 100644 --- a/src/generators/app/index.ts +++ b/src/generators/app/index.ts @@ -1,416 +1,2 @@ -import chalk from "chalk"; -import dedent = require("dedent"); -import FileSystem = require("fs-extra"); -import camelCase = require("lodash.camelcase"); -import kebabCase = require("lodash.kebabcase"); -import Path = require("path"); -import { Question } from "yeoman-generator"; -import yosay = require("yosay"); -import { Generator } from "../../Generator"; -import { GeneratorSetting } from "../../GeneratorSetting"; -import { IComponentProvider } from "../../IComponentProvider"; -import { IFileMapping } from "../../IFileMapping"; -import { AppComponent } from "./AppComponent"; -import { AppSetting } from "./AppSetting"; -import { IAppSettings } from "./IAppSettings"; -import { LintMode } from "./LintMode"; -import { SubGeneratorSetting } from "./SubGeneratorSetting"; - -/** - * Provides the functionality to generate a generator written in TypeScript. - */ -class AppGenerator extends Generator -{ - /** - * Initializes a new instance of the `AppGenerator` class. - * - * @param args - * A set of arguments for the generator. - * - * @param options - * A set of options for the generator. - */ - public constructor(args: string | string[], options: {}) - { - super(args, options); - } - - protected get TemplateRoot(): string - { - return "app"; - } - - protected get Questions(): Question[] - { - return [ - { - type: "input", - name: AppSetting.Destination, - message: "Where do you want to save your generator to?", - default: "./", - filter: async input => Path.isAbsolute(input) ? input : Path.resolve(process.cwd(), input) - }, - { - type: "input", - name: AppSetting.Name, - message: "What's the name of your project?", - default: (answers: IAppSettings) => Path.basename(answers[AppSetting.Destination]), - validate: (input: string) => /.+/.test(input.trim()) ? true : "The name must not be empty!" - }, - { - type: "input", - name: AppSetting.ModuleName, - message: "What's the name of the node-module?", - default: (answers: IAppSettings) => "generator-" + kebabCase(answers[AppSetting.Name].replace(/(generator-)?(.*?)(generator)?$/i, "$2")), - filter: input => kebabCase(input), - validate: (input: string) => - { - if (/[\w-]+/.test(input)) - { - return input.startsWith("generator-") ? true : 'The name must start with "generator-"'; - } - else - { - return "Please provide a name according to the npm naming-conventions."; - } - } - }, - { - type: "input", - name: AppSetting.Description, - message: "Please enter a description for your generator." - } - ]; - } - - protected get ProvidedComponents(): IComponentProvider - { - return { - Question: "What do you want to include in your workspace?", - Categories: [ - { - DisplayName: "General", - Components: [ - { - ID: AppComponent.TSLint, - DisplayName: "TSLint configurations", - Default: true, - Questions: [ - { - name: AppSetting.LintMode, - type: "list", - message: "What ruleset do you want to use for linting?", - choices: [ - { - value: LintMode.Weak, - name: "manuth's weak ruleset" - }, - { - value: LintMode.Strong, - name: "manuth's strong ruleset (recommended)" - } - ], - default: LintMode.Strong - } - ], - FileMappings: [ - { - Source: settings => - { - switch (settings[AppSetting.LintMode]) - { - case LintMode.Weak: - return "tslint.weak.jsonc"; - case LintMode.Strong: - default: - return this.modulePath("tslint.json"); - } - }, - Destination: "tslint.json" - } - ] - }, - { - ID: AppComponent.VSCode, - DisplayName: "Visual Studio Code-Workspace", - Default: true, - FileMappings: [ - { - Source: this.modulePath(".vscode"), - Destination: ".vscode" - }, - { - Source: "launch.json", - Destination: () => this.destinationPath(".vscode", "launch.json") - } - ] - }, - { - ID: AppComponent.GeneratorExample, - DisplayName: "Example Generator (recommended)", - FileMappings: (settings) => this.GetGeneratorFileMappings("app", settings[AppSetting.Name]) - }, - { - ID: AppComponent.SubGeneratorExample, - DisplayName: "Example Sub-Generator", - Questions: [ - { - type: "input", - name: `${AppSetting.SubGenerator}.${SubGeneratorSetting.DisplayName}`, - message: "What's the human-readable name of your sub-generator?", - validate: (input: string) => /.+/.test(input.trim()) ? true : "The name must not be empty!" - }, - { - type: "input", - name: `${AppSetting.SubGenerator}.${SubGeneratorSetting.Name}`, - message: "What's the unique name of the sub-generator?", - default: (settings: IAppSettings) => kebabCase(settings[AppSetting.SubGenerator][SubGeneratorSetting.DisplayName] || ""), - validate: (input: string) => /[\w-]+/.test(input) ? true : "Please provide a name according to the npm naming-conventions." - } - ], - FileMappings: (settings) => this.GetGeneratorFileMappings(settings[AppSetting.SubGenerator][SubGeneratorSetting.Name], settings[AppSetting.SubGenerator][SubGeneratorSetting.DisplayName]) - } - ] - } - ] - }; - } - - public async prompting() - { - this.log(yosay(`Welcome to the ${chalk.whiteBright("TypeScript Generator")} generator!`)); - return super.prompting(); - } - - public async writing() - { - let sourceRoot = "src"; - this.destinationRoot(this.Settings[AppSetting.Destination]); - this.fs.writeJSON(this.destinationPath("package.json"), this.GetPackageJSON()); - this.fs.copy(this.templatePath(".gitignore.ejs"), this.destinationPath(".gitignore")); - this.fs.copy(this.templatePath(".npmignore.ejs"), this.destinationPath(".npmignore")); - this.fs.copy(this.modulePath("tsconfig.json"), this.destinationPath("tsconfig.json")); - this.fs.copy(this.modulePath("test", "mocha.opts"), this.destinationPath("test", "mocha.opts")); - this.fs.copyTpl( - this.templatePath("GettingStarted.md.ejs"), - this.destinationPath("GettingStarted.md"), - { - Name: this.Settings[AppSetting.ModuleName], - HasCodeWorkspace: this.Settings[GeneratorSetting.Components].includes(AppComponent.VSCode), - SubGeneratorName: this.Settings[GeneratorSetting.Components].includes(AppComponent.SubGeneratorExample) ? this.Settings[AppSetting.SubGenerator][SubGeneratorSetting.Name] : null - }); - this.fs.copyTpl( - this.templatePath("README.md.ejs"), - this.destinationPath("README.md"), - { - Name: this.Settings[AppSetting.ModuleName], - DisplayName: this.Settings[AppSetting.Name], - Description: this.Settings[AppSetting.Description] - }); - this.fs.copyTpl( - this.templatePath("tests", "main.test.ts.ejs"), - this.destinationPath(sourceRoot, "tests", "main.test.ts"), - { - Name: this.Settings[AppSetting.Name] - }); - this.fs.copyTpl( - this.templatePath("tests", "Generators", "index.test.ts.ejs"), - this.destinationPath(sourceRoot, "tests", "Generators", "index.test.ts"), - { - Name: this.Settings[AppSetting.ModuleName] - }); - this.fs.copyTpl( - this.templatePath("tests", "Generators", "app.test.ts.ejs"), - this.destinationPath(sourceRoot, "tests", "Generators", `${this.Settings[AppSetting.ModuleName]}.test.ts`), - { - Name: this.Settings[AppSetting.Name] - }); - this.fs.copy(this.modulePath(sourceRoot, "Generator.ts"), this.destinationPath(sourceRoot, "Generator.ts")); - this.fs.copy(this.modulePath(sourceRoot, "GeneratorSetting.ts"), this.destinationPath(sourceRoot, "GeneratorSetting.ts")); - this.fs.copy(this.modulePath(sourceRoot, "IComponent.ts"), this.destinationPath(sourceRoot, "IComponent.ts")); - this.fs.copy(this.modulePath(sourceRoot, "IComponentCategory.ts"), this.destinationPath(sourceRoot, "IComponentCategory.ts")); - this.fs.copy(this.modulePath(sourceRoot, "IComponentDestination.ts"), this.destinationPath(sourceRoot, "IComponentDestination.ts")); - this.fs.copy(this.modulePath(sourceRoot, "IComponentProvider.ts"), this.destinationPath(sourceRoot, "IComponentProvider.ts")); - this.fs.copy(this.modulePath(sourceRoot, "IFileMapping.ts"), this.destinationPath(sourceRoot, "IFileMapping.ts")); - this.fs.copy(this.modulePath(sourceRoot, "IGeneratorSettings.ts"), this.destinationPath(sourceRoot, "IGeneratorSettings.ts")); - FileSystem.ensureDir(this.destinationPath(sourceRoot, "generators")); - FileSystem.ensureDir(this.destinationPath("templates")); - return super.writing(); - } - - public async install() - { - this.log("Your workspace has been generated!"); - this.log(); - this.log(chalk.whiteBright("Installing dependencies...")); - this.npmInstall(); - } - - public async end() - { - this.log(dedent(` - Your package "${this.Settings[AppSetting.Name]}" has been created! - To start editing with Visual Studio Code use following commands: - - code "${this.Settings[AppSetting.Destination]}" - - Open "GettingStarted.md" in order to learn more about how to create your very own generator.`)); - this.log(); - } - - /** - * Creates file-mappings for a generator. - * - * @param id - * The id of the generator. - * - * @param displayName - * The human readable name of the generator. - * - * @returns - * File-mappings for a generator. - */ - protected GetGeneratorFileMappings = (id: string, displayName: string): IFileMapping[] => - { - let name = (id.charAt(0).toUpperCase() + camelCase(id).slice(1)); - let source = "generator"; - let destination = `src/generators/${id}`; - let generatorName = `${name}Generator`; - let identities = `${name}Setting`; - let settings = `I${name}Settings`; - - return [ - { - Source: Path.join(source, "LicenseType.ts.ejs"), - Destination: Path.join(destination, "LicenseType.ts") - }, - { - Source: Path.join(source, "Setting.ts.ejs"), - Context: () => - { - return { Name: identities }; - }, - Destination: Path.join(destination, `${identities}.ts`) - }, - { - Source: Path.join(source, "ISettings.ts.ejs"), - Context: () => - { - return { - Name: generatorName, - SettingsInterface: settings, - Identities: identities - }; - }, - Destination: Path.join(destination, `${settings}.ts`) - }, - { - Source: Path.join(source, "index.ts.ejs"), - Context: () => - { - return { - Name: generatorName, - SettingsInterface: settings, - Identities: identities, - ID: id, - DisplayName: displayName - }; - }, - Destination: Path.join(destination, "index.ts") - }, - { - Source: Path.join(source, "templates"), - Destination: Path.join("templates", id) - } - ]; - } - - /** - * Gets the package-manifest for the generator to generate. - */ - protected GetPackageJSON = (): {} => - { - let scripts = [ - "watch", - "compile", - "lint", - "test", - "prepare" - ]; - - let dependencies = [ - "inquirer", - "yeoman-generator" - ]; - - let devDependencies = [ - "@types/inquirer", - "@types/mocha", - "@types/node", - "@types/yeoman-generator", - "mocha", - "tslint", - "typescript", - "typescript-tslint-plugin", - "yo" - ]; - - if ( - this.Settings[GeneratorSetting.Components].includes(AppComponent.GeneratorExample) || - this.Settings[GeneratorSetting.Components].includes(AppComponent.SubGeneratorExample)) - { - dependencies.push( - "chalk", - "dedent", - "yosay"); - - devDependencies.push( - "@types/dedent", - "@types/yosay"); - } - - let result = { - name: this.Settings[AppSetting.ModuleName], - version: "0.0.0", - description: this.Settings[AppSetting.Description], - author: { - name: this.user.git.name(), - email: this.user.git.email() - }, - keywords: ["yeoman-generator"], - scripts: {} as { [key: string]: string }, - dependencies: {} as { [key: string]: string }, - devDependencies: {} as { [key: string]: string } - }; - - let packageJSON: typeof result = require(Path.join(__dirname, "..", "..", "..", "package.json")); - - for (let script of scripts) - { - if (script in packageJSON.scripts) - { - result.scripts[script] = packageJSON.scripts[script]; - } - } - - for (let devDependency of devDependencies.sort()) - { - if (devDependency in packageJSON.devDependencies) - { - result.devDependencies[devDependency] = packageJSON.devDependencies[devDependency]; - } - } - - for (let dependency of dependencies.sort()) - { - if (dependency in packageJSON.dependencies) - { - result.dependencies[dependency] = packageJSON.dependencies[dependency]; - } - } - - return result; - } -} - +import { AppGenerator } from "./AppGenerator"; export = AppGenerator; \ No newline at end of file diff --git a/src/tests/Generators/GeneratorGenerator.test.ts b/src/tests/Generators/GeneratorGenerator.test.ts index 853dde59..7e16db6c 100644 --- a/src/tests/Generators/GeneratorGenerator.test.ts +++ b/src/tests/Generators/GeneratorGenerator.test.ts @@ -1,5 +1,6 @@ import Assert = require("assert"); import ChildProcess = require("child_process"); +import { GeneratorSetting } from "extended-yo-generator"; import FileSystem = require("fs-extra"); import Path = require("path"); import TS = require("typescript"); @@ -8,7 +9,6 @@ import { run, RunContext } from "yeoman-test"; import { AppComponent } from "../../generators/app/AppComponent"; import { AppSetting } from "../../generators/app/AppSetting"; import { LintMode } from "../../generators/app/LintMode"; -import { GeneratorSetting } from "../../GeneratorSetting"; suite( "Generator-Generator", @@ -29,8 +29,8 @@ suite( Path.join(__dirname, "..", "..", "generators", "app")).withPrompts( { [AppSetting.Destination]: "./", + [AppSetting.DisplayName]: generatorName, [AppSetting.Name]: generatorName, - [AppSetting.ModuleName]: generatorName, [GeneratorSetting.Components]: [ AppComponent.TSLint, AppComponent.VSCode, @@ -53,8 +53,8 @@ suite( "Checking whether the generator can be executed…", async function () { - this.timeout(6 * 1000); - this.slow(3 * 1000); + this.timeout(18 * 1000); + this.slow(6 * 1000); generatorDir = await runContext.toPromise(); tsConfigFile = Path.join(generatorDir, "tsconfig.json"); }); diff --git a/templates/app/generator/Generator.ts.ejs b/templates/app/generator/Generator.ts.ejs new file mode 100644 index 00000000..7c0c38f3 --- /dev/null +++ b/templates/app/generator/Generator.ts.ejs @@ -0,0 +1,151 @@ +import chalk from "chalk"; +import dedent = require("dedent"); +import Path = require("path"); +import yosay = require("yosay"); +import { Generator, IComponentProvider, Question } from "extended-yo-generator"; +import { <%= Identities %> } from "./<%= Identities %>"; +import { <%= SettingsInterface %> } from "./<%= SettingsInterface %>"; +import { LicenseType } from "./LicenseType"; + +/** + * Provides the functionality to generate a generator written in TypeScript. + */ +export class <%= Name %> extends Generator<<%= SettingsInterface %>> +{ + /** + * Initializes a new instance of the `<%= Name %>` class. + * + * @param args + * A set of arguments for the generator. + * + * @param options + * A set of options for the generator. + */ + public constructor(args: string | string[], options: {}) + { + super(args, options); + } + + protected get TemplateRoot(): string + { + return "<%= ID %>"; + } + + protected get Questions(): Question<<%= SettingsInterface %>>[] + { + return [ + { + type: "input", + name: <%= Identities %>.Destination, + message: "Where do you want to save your generator to?", + default: "./", + filter: async input => + { + let destination = Path.isAbsolute(input) ? input : Path.resolve(process.cwd(), input); + this.destinationRoot(destination); + return destination; + } + }, + { + type: "input", + name: <%= Identities %>.Name, + message: "What's the name of your project?", + default: (answers: <%= SettingsInterface %>) => Path.basename(answers[<%= Identities %>.Destination]) + }, + { + type: "input", + name: <%= Identities %>.Description, + message: "Please enter a description." + } + ]; + } + + protected get ProvidedComponents(): IComponentProvider<<%= SettingsInterface %>> + { + return { + Question: "What do you want to include in your workspace?", + Categories: [ + { + DisplayName: "General", + Components: [ + { + ID: "readme", + DisplayName: "README.md-File", + Default: true, + FileMappings: [ + { + Source: "README.md.ejs", + Context: (settings) => + { + return { + Name: settings[<%= Identities %>.Name], + Description: settings[<%= Identities %>.Description] + } + }, + Destination: "README.md" + } + ] + }, + { + ID: "license", + DisplayName: "License-File", + Questions: [ + { + name: <%= Identities %>.LicenseType, + type: "list", + message: "What license do you want to use?", + choices: [ + { + value: LicenseType.Apache, + name: "Apache-2.0 License" + }, + { + value: LicenseType.GPL, + name: "GNU GPL License" + } + ], + default: LicenseType.GPL + } + ], + FileMappings: [ + { + Source: (settings) => + { + switch (settings[<%= Identities %>.LicenseType]) + { + case LicenseType.Apache: + return "Apache.txt"; + case LicenseType.GPL: + default: + return "GPL.txt"; + } + }, + Destination: "LICENSE" + } + ] + } + ] + } + ] + }; + } + + public async prompting() + { + this.log(yosay(`Welcome to the ${chalk.whiteBright("<%= DisplayName %>")} generator!`)); + return super.prompting(); + } + + public async writing() + { + return super.writing(); + } + + public async end() + { + this.log(dedent(` + Your project is ready! + + It lives in "${this.Settings[<%= Identities %>.Destination]}`)); + } +} \ No newline at end of file diff --git a/templates/app/generator/ISettings.ts.ejs b/templates/app/generator/ISettings.ts.ejs index 9fcd7956..6edfd17f 100644 --- a/templates/app/generator/ISettings.ts.ejs +++ b/templates/app/generator/ISettings.ts.ejs @@ -1,4 +1,4 @@ -import { IGeneratorSettings } from "../../IGeneratorSettings"; +import { IGeneratorSettings } from "extended-yo-generator"; import { <%= Identities %> } from "./<%= Identities %>"; import { LicenseType } from "./LicenseType"; diff --git a/templates/app/generator/index.ts.ejs b/templates/app/generator/index.ts.ejs index 76d9bd02..79f9e29a 100644 --- a/templates/app/generator/index.ts.ejs +++ b/templates/app/generator/index.ts.ejs @@ -1,155 +1,2 @@ -import chalk from "chalk"; -import dedent = require("dedent"); -import Path = require("path"); -import { Question } from "yeoman-generator"; -import yosay = require("yosay"); -import { Generator } from "../../Generator"; -import { IComponentProvider } from "../../IComponentProvider"; -import { <%= Identities %> } from "./<%= Identities %>"; -import { <%= SettingsInterface %> } from "./<%= SettingsInterface %>"; -import { LicenseType } from "./LicenseType"; - -/** - * Provides the functionality to generate a generator written in TypeScript. - */ -class <%= Name %> extends Generator<<%= SettingsInterface %>> -{ - /** - * Initializes a new instance of the `<%= Name %>` class. - * - * @param args - * A set of arguments for the generator. - * - * @param options - * A set of options for the generator. - */ - public constructor(args: string | string[], options: {}) - { - super(args, options); - } - - protected get TemplateRoot(): string - { - return "<%= ID %>"; - } - - protected get Questions(): Question[] - { - return [ - { - type: "input", - name: <%= Identities %>.Destination, - message: "Where do you want to save your generator to?", - default: "./", - filter: async input => - { - let destination = Path.isAbsolute(input) ? input : Path.resolve(process.cwd(), input); - this.destinationRoot(destination); - return destination; - } - }, - { - type: "input", - name: <%= Identities %>.Name, - message: "What's the name of your project?", - default: (answers: <%= SettingsInterface %>) => Path.basename(answers[<%= Identities %>.Destination]) - }, - { - type: "input", - name: <%= Identities %>.Description, - message: "Please enter a description." - } - ]; - } - - protected get ProvidedComponents(): IComponentProvider<<%= SettingsInterface %>> - { - return { - Question: "What do you want to include in your workspace?", - Categories: [ - { - DisplayName: "General", - Components: [ - { - ID: "readme", - DisplayName: "README.md-File", - Default: true, - FileMappings: [ - { - Source: "README.md.ejs", - Context: (settings) => - { - return { - Name: settings[<%= Identities %>.Name], - Description: settings[<%= Identities %>.Description] - } - }, - Destination: "README.md" - } - ] - }, - { - ID: "license", - DisplayName: "License-File", - Questions: [ - { - name: <%= Identities %>.LicenseType, - type: "list", - message: "What license do you want to use?", - choices: [ - { - value: LicenseType.Apache, - name: "Apache-2.0 License" - }, - { - value: LicenseType.GPL, - name: "GNU GPL License" - } - ], - default: LicenseType.GPL - } - ], - FileMappings: [ - { - Source: (settings) => - { - switch (settings[<%= Identities %>.LicenseType]) - { - case LicenseType.Apache: - return "Apache.txt"; - case LicenseType.GPL: - default: - return "GPL.txt"; - } - }, - Destination: "LICENSE" - } - ] - } - ] - } - ] - }; - } - - public async prompting() - { - this.log(yosay(`Welcome to the ${chalk.whiteBright("<%= DisplayName %>")} generator!`)); - return super.prompting(); - } - - public async writing() - { - return super.writing(); - } - - public async end() - { - this.log(dedent(` - Your project is ready! - - It lives in "${this.Settings[<%= Identities %>.Destination]}`)); - } -} - -export = <%= Name %>; \ No newline at end of file +import { <%- Name %> } from "./<%- Name %>"; +export = <%- Name %>; \ No newline at end of file