From 4561b577d5479b275375b2e0489feac18b71e228 Mon Sep 17 00:00:00 2001 From: dhruvdutt Date: Sat, 30 Jun 2018 19:15:57 +0530 Subject: [PATCH] feat(generators): add typescript support --- package-lock.json | 6 +- packages/generators/.gitignore | 2 + packages/generators/.npmignore | 2 + .../{add-generator.js => add-generator.ts} | 317 ++++++++++-------- packages/generators/addon-generator.js | 85 ++--- packages/generators/addon-generator.ts | 83 +++++ packages/generators/index.js | 33 +- packages/generators/index.ts | 17 + .../{init-generator.js => init-generator.ts} | 263 ++++++++------- ...oader-generator.js => loader-generator.ts} | 28 +- packages/generators/package-lock.json | 170 +++++++++- packages/generators/package.json | 11 + ...lugin-generator.js => plugin-generator.ts} | 25 +- packages/generators/remove-generator.js | 3 - packages/generators/remove-generator.ts | 3 + packages/generators/tsconfig.json | 3 + packages/generators/tslint.json | 3 + packages/generators/types/json-loader.d.ts | 4 + .../generators/types/yeoman-generator.d.ts | 55 +++ packages/generators/update-generator.js | 3 - packages/generators/update-generator.ts | 3 + packages/generators/utils/entry.js | 80 ++--- packages/generators/utils/entry.ts | 107 ++++++ packages/generators/utils/module.js | 14 +- packages/generators/utils/module.ts | 27 ++ packages/generators/utils/plugins.js | 9 +- packages/generators/utils/plugins.ts | 11 + packages/generators/utils/tooltip.js | 38 +-- packages/generators/utils/tooltip.ts | 62 ++++ packages/generators/utils/validate.js | 7 +- packages/generators/utils/validate.ts | 15 + packages/webpack-scaffold/index.ts | 4 +- tslint.json | 4 + 33 files changed, 1039 insertions(+), 458 deletions(-) create mode 100644 packages/generators/.gitignore rename packages/generators/{add-generator.js => add-generator.ts} (63%) create mode 100644 packages/generators/addon-generator.ts create mode 100644 packages/generators/index.ts rename packages/generators/{init-generator.js => init-generator.ts} (70%) rename packages/generators/{loader-generator.js => loader-generator.ts} (74%) rename packages/generators/{plugin-generator.js => plugin-generator.ts} (68%) delete mode 100644 packages/generators/remove-generator.js create mode 100644 packages/generators/remove-generator.ts create mode 100644 packages/generators/tsconfig.json create mode 100644 packages/generators/tslint.json create mode 100644 packages/generators/types/json-loader.d.ts create mode 100644 packages/generators/types/yeoman-generator.d.ts delete mode 100644 packages/generators/update-generator.js create mode 100644 packages/generators/update-generator.ts create mode 100644 packages/generators/utils/entry.ts create mode 100644 packages/generators/utils/module.ts create mode 100644 packages/generators/utils/plugins.ts create mode 100644 packages/generators/utils/tooltip.ts create mode 100644 packages/generators/utils/validate.ts diff --git a/package-lock.json b/package-lock.json index 9aef6ddd2d9..2e804041f53 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18530,9 +18530,9 @@ "dev": true }, "webpack": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.13.0.tgz", - "integrity": "sha512-3KMX0uPjJ4cXjl9V/AY+goRQPs7jtKRQn3hhNOG6s8Sx3mmGCQUjQJvjVoGNABVo5svgujIcSLBN8g62EzqIMA==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.14.0.tgz", + "integrity": "sha512-CgZPUwobJbQlZqpylDNtEazZLfNnGuyFmpk1dHIP2kFchtyMWB+W2wBKPImSnSQ2rbX/WZMKiQax+SZmlUXuQQ==", "dev": true, "requires": { "@webassemblyjs/ast": "1.5.12", diff --git a/packages/generators/.gitignore b/packages/generators/.gitignore new file mode 100644 index 00000000000..2c183db245b --- /dev/null +++ b/packages/generators/.gitignore @@ -0,0 +1,2 @@ +types/*.js +*.js diff --git a/packages/generators/.npmignore b/packages/generators/.npmignore index 9db08315811..837d0dd36b6 100644 --- a/packages/generators/.npmignore +++ b/packages/generators/.npmignore @@ -1 +1,3 @@ *.test.* +*.ts +tsconfig.json diff --git a/packages/generators/add-generator.js b/packages/generators/add-generator.ts similarity index 63% rename from packages/generators/add-generator.js rename to packages/generators/add-generator.ts index 55bcbc80775..2d2d510e1c5 100644 --- a/packages/generators/add-generator.js +++ b/packages/generators/add-generator.ts @@ -1,19 +1,24 @@ -const Generator = require("yeoman-generator"); -const glob = require("glob-all"); -const path = require("path"); -const inquirerAutoComplete = require("inquirer-autocomplete-prompt"); -const { AutoComplete, Confirm, Input, List } = require("@webpack-cli/webpack-scaffold"); +import Generator = require("yeoman-generator"); -const webpackSchema = require("./utils/optionsSchema.json"); -const webpackDevServerSchema = require("webpack-dev-server/lib/optionsSchema.json"); -const PROP_TYPES = require("@webpack-cli/utils/prop-types"); +import * as glob from "glob-all"; +import * as autoComplete from "inquirer-autocomplete-prompt"; +import * as path from "path"; +import webpackDevServerSchema from "webpack-dev-server/lib/optionsSchema.json"; -const getPackageManager = require("@webpack-cli/utils/package-manager") - .getPackageManager; -const npmExists = require("@webpack-cli/utils/npm-exists"); -const entryQuestions = require("./utils/entry"); +import * as npmExists from "@webpack-cli/utils/npm-exists"; +import { getPackageManager } from "@webpack-cli/utils/package-manager"; +import * as PROP_TYPES from "@webpack-cli/utils/prop-types"; +import { + AutoComplete, + Confirm, + Input, + List, +} from "@webpack-cli/webpack-scaffold"; -const PROPS = Array.from(PROP_TYPES.keys()); +import entryQuestions from "./utils/entry"; +import webpackSchema from "./utils/optionsSchema.json"; + +const PROPS: string[] = Array.from(PROP_TYPES.keys()); /** * @@ -27,8 +32,8 @@ const PROPS = Array.from(PROP_TYPES.keys()); * @returns {String} string - The newly mutated string * */ -function replaceAt(string, index, replace) { - return string.substring(0, index) + replace + string.substring(index + 1); +function replaceAt(str: string, index: number, replace: string): string { + return str.substring(0, index) + replace + str.substring(index + 1); } /** @@ -41,9 +46,9 @@ function replaceAt(string, index, replace) { * @returns {Boolean} hasProp - Boolean indicating if the property * is present */ -const traverseAndGetProperties = (arr, prop) => { - let hasProp = false; - arr.forEach(p => { +const traverseAndGetProperties = (arr: object[], prop: string): boolean => { + let hasProp: boolean = false; + arr.forEach((p: object) => { if (p[prop]) { hasProp = true; } @@ -61,12 +66,12 @@ const traverseAndGetProperties = (arr, prop) => { * @returns {Promise} Returns promise which resolves to filtered props * */ -const searchProps = (answers, input) => { +const searchProps = (answers: object, input: string): Promise => { input = input || ""; return Promise.resolve( - PROPS.filter(food => - food.toLowerCase().includes(input.toLowerCase()) - ) + PROPS.filter((prop: string) => + prop.toLowerCase().includes(input.toLowerCase()), + ), ); }; @@ -79,29 +84,39 @@ const searchProps = (answers, input) => { * */ -module.exports = class AddGenerator extends Generator { +export default class AddGenerator extends Generator { + private dependencies: string[]; + private configuration: { + config: { + configName?: string, + topScope?: string[], + item?: string; + webpackOptions?: any, + }, + }; + constructor(args, opts) { super(args, opts); this.dependencies = []; this.configuration = { config: { + topScope: ["const webpack = require('webpack')"], webpackOptions: {}, - topScope: ["const webpack = require('webpack')"] - } + }, }; const { registerPrompt } = this.env.adapter.promptModule; - registerPrompt("autocomplete", inquirerAutoComplete); + registerPrompt("autocomplete", autoComplete); } - prompting() { - let done = this.async(); - let action; - let self = this; - let manualOrListInput = action => - Input("actionAnswer", `What do you want to add to ${action}?`); + public prompting() { + const done: () => void | boolean = this.async(); + let action: string; + const self: this = this; + let manualOrListInput: any = (promptAction: string) => + Input("actionAnswer", `What do you want to add to ${promptAction}?`); // first index indicates if it has a deep prop, 2nd indicates what kind of - let isDeepProp = [false, false]; + const isDeepProp: any[] = [false, false]; return this.prompt([ AutoComplete( @@ -111,10 +126,12 @@ module.exports = class AddGenerator extends Generator { pageSize: 7, source: searchProps, suggestOnly: false, - } - ) + }, + ), ]) - .then(actionTypeAnswer => { + .then((actionTypeAnswer: { + actionType: string, + }) => { // Set initial prop, like devtool this.configuration.config.webpackOptions[ actionTypeAnswer.actionType @@ -125,44 +142,46 @@ module.exports = class AddGenerator extends Generator { .then(() => { if (action === "entry") { return this.prompt([ - Confirm("entryType", "Will your application have multiple bundles?") + Confirm("entryType", "Will your application have multiple bundles?"), ]) - .then(entryTypeAnswer => { + .then((entryTypeAnswer: { + entryType: boolean, + }) => { // Ask different questions for entry points return entryQuestions(self, entryTypeAnswer); }) - .then(entryOptions => { + .then((entryOptions: object) => { this.configuration.config.webpackOptions.entry = entryOptions; this.configuration.config.item = action; }); } - let temp = action; + const temp: string = action; if (action === "resolveLoader") { action = "resolve"; } - const webpackSchemaProp = webpackSchema.definitions[action]; + const webpackSchemaProp: any = webpackSchema.definitions[action]; /* * https://github.com/webpack/webpack/blob/next/schemas/WebpackOptions.json * Find the properties directly in the properties prop, or the anyOf prop */ - let defOrPropDescription = webpackSchemaProp + let defOrPropDescription: object = webpackSchemaProp ? webpackSchemaProp.properties : webpackSchema.properties[action].properties ? webpackSchema.properties[action].properties : webpackSchema.properties[action].anyOf ? webpackSchema.properties[action].anyOf.filter( - p => p.properties || p.enum + (p) => p.properties || p.enum, ) // eslint-disable-line : null; if (Array.isArray(defOrPropDescription)) { // Todo: Generalize these to go through the array, then merge enum with props if needed - const hasPropertiesProp = traverseAndGetProperties( + const hasPropertiesProp: boolean = traverseAndGetProperties( defOrPropDescription, - "properties" + "properties", ); - const hasEnumProp = traverseAndGetProperties( + const hasEnumProp: boolean = traverseAndGetProperties( defOrPropDescription, - "enum" + "enum", ); /* as we know he schema only has two arrays that might hold our values, * check them for either having arr.enum or arr.properties @@ -176,19 +195,19 @@ module.exports = class AddGenerator extends Generator { } // TODO: manually implement stats and devtools like sourcemaps } else if (hasEnumProp) { - const originalPropDesc = defOrPropDescription[0].enum; + const originalPropDesc: object = defOrPropDescription[0].enum; // Array -> Object -> Merge objects into one for compat in manualOrListInput defOrPropDescription = Object.keys(defOrPropDescription[0].enum) - .map(p => { + .map((p: string) => { return Object.assign( {}, { - [originalPropDesc[p]]: "noop" - } + [originalPropDesc[p]]: "noop", + }, ); }) - .reduce((result, currentObject) => { - for (let key in currentObject) { + .reduce((result: object, currentObject: object) => { + for (const key in currentObject) { if (currentObject.hasOwnProperty(key)) { result[key] = currentObject[key]; } @@ -198,25 +217,25 @@ module.exports = class AddGenerator extends Generator { } } // WDS has its own schema, so we gonna need to check that too - const webpackDevserverSchemaProp = + const webpackDevserverSchemaProp: any = action === "devServer" ? webpackDevServerSchema : null; // Watch has a boolean arg, but we need to append to it manually if (action === "watch") { defOrPropDescription = { + false: {}, true: {}, - false: {} }; } if (action === "mode") { defOrPropDescription = { development: {}, - production: {} + production: {}, }; } action = temp; if (action === "resolveLoader") { defOrPropDescription = Object.assign(defOrPropDescription, { - moduleExtensions: {} + moduleExtensions: {}, }); } // If we've got a schema prop or devServer Schema Prop @@ -226,29 +245,29 @@ module.exports = class AddGenerator extends Generator { if (action !== "devtool") { // Add the option of adding an own variable if the user wants defOrPropDescription = Object.assign(defOrPropDescription, { - other: {} + other: {}, }); } else { // The schema doesn't have the source maps we can prompt, so add those defOrPropDescription = Object.assign(defOrPropDescription, { - eval: {}, "cheap-eval-source-map": {}, "cheap-module-eval-source-map": {}, - "eval-source-map": {}, - "cheap-source-map": {}, "cheap-module-source-map": {}, - "inline-cheap-source-map": {}, + "cheap-source-map": {}, + "eval": {}, + "eval-source-map": {}, + "hidden-source-map": {}, "inline-cheap-module-source-map": {}, - "source-map": {}, + "inline-cheap-source-map": {}, "inline-source-map": {}, - "hidden-source-map": {}, - "nosources-source-map": {} + "nosources-source-map": {}, + "source-map": {}, }); } manualOrListInput = List( "actionAnswer", `What do you want to add to ${action}?`, - Object.keys(defOrPropDescription) + Object.keys(defOrPropDescription), ); // We know we're gonna append some deep prop like module.rule isDeepProp[0] = true; @@ -257,13 +276,13 @@ module.exports = class AddGenerator extends Generator { webpackDevserverSchemaProp.properties = Object.assign( webpackDevserverSchemaProp.properties, { - other: {} - } + other: {}, + }, ); manualOrListInput = List( "actionAnswer", `What do you want to add to ${action}?`, - Object.keys(webpackDevserverSchemaProp.properties) + Object.keys(webpackDevserverSchemaProp.properties), ); // We know we are in a devServer.prop scenario isDeepProp[0] = true; @@ -276,7 +295,9 @@ module.exports = class AddGenerator extends Generator { } return this.prompt([manualOrListInput]); }) - .then(answerToAction => { + .then((answerToAction: { + actionAnswer: string, + }) => { if (!answerToAction) { done(); return; @@ -286,54 +307,55 @@ module.exports = class AddGenerator extends Generator { * find the names of each natively plugin and check if it matches */ if (action === "plugins") { - const pluginExist = glob + const pluginExist: string = glob .sync([ "node_modules/webpack/lib/*Plugin.js", - "node_modules/webpack/lib/**/*Plugin.js" + "node_modules/webpack/lib/**/*Plugin.js", ]) - .map(p => + .map((p: string) => p .split("/") .pop() - .replace(".js", "") + .replace(".js", ""), ) .find( - p => p.toLowerCase().indexOf(answerToAction.actionAnswer) >= 0 + (p: string) => p.toLowerCase().indexOf(answerToAction.actionAnswer) >= 0, ); + if (pluginExist) { this.configuration.config.item = pluginExist; - const pluginsSchemaPath = glob + const pluginsSchemaPath: string = glob .sync([ "node_modules/webpack/schemas/plugins/*Plugin.json", - "node_modules/webpack/schemas/plugins/**/*Plugin.json" + "node_modules/webpack/schemas/plugins/**/*Plugin.json", ]) .find( - p => + (p: string) => p .split("/") .pop() .replace(".json", "") .toLowerCase() - .indexOf(answerToAction.actionAnswer) >= 0 + .indexOf(answerToAction.actionAnswer) >= 0, ); if (pluginsSchemaPath) { - const constructorPrefix = + const constructorPrefix: string = pluginsSchemaPath.indexOf("optimize") >= 0 ? "webpack.optimize" : "webpack"; - const resolvePluginsPath = path.resolve(pluginsSchemaPath); - const pluginSchema = resolvePluginsPath + const resolvePluginsPath: string = path.resolve(pluginsSchemaPath); + const pluginSchema: object = resolvePluginsPath ? require(resolvePluginsPath) : null; - let pluginsSchemaProps = ["other"]; + let pluginsSchemaProps: string[] = ["other"]; if (pluginSchema) { Object.keys(pluginSchema) - .filter(p => Array.isArray(pluginSchema[p])) - .forEach(p => { - Object.keys(pluginSchema[p]).forEach(n => { + .filter((p: string) => Array.isArray(pluginSchema[p])) + .forEach((p: string) => { + Object.keys(pluginSchema[p]).forEach((n: string) => { if (pluginSchema[p][n].properties) { pluginsSchemaProps = Object.keys( - pluginSchema[p][n].properties + pluginSchema[p][n].properties, ); } }); @@ -344,22 +366,26 @@ module.exports = class AddGenerator extends Generator { List( "pluginsPropType", `What property do you want to add ${pluginExist}?`, - pluginsSchemaProps - ) - ]).then(pluginsPropAnswer => { + pluginsSchemaProps, + ), + ]).then((pluginsPropAnswer: { + pluginsPropType: string, + }) => { return this.prompt([ Input( "pluginsPropTypeVal", `What value should ${pluginExist}.${ pluginsPropAnswer.pluginsPropType - } have?` - ) - ]).then(valForProp => { + } have?`, + ), + ]).then((valForProp: { + pluginsPropTypeVal: string, + }) => { this.configuration.config.webpackOptions[action] = { [`${constructorPrefix}.${pluginExist}`]: { [pluginsPropAnswer.pluginsPropType]: - valForProp.pluginsPropTypeVal - } + valForProp.pluginsPropTypeVal, + }, }; done(); }); @@ -372,39 +398,40 @@ module.exports = class AddGenerator extends Generator { } } else { // If its not in webpack, check npm - npmExists(answerToAction.actionAnswer).then(p => { - if (p) { - this.dependencies.push(answerToAction.actionAnswer); - const normalizePluginName = answerToAction.actionAnswer.replace( - "-webpack-plugin", - "Plugin" - ); - const pluginName = replaceAt( - normalizePluginName, - 0, - normalizePluginName.charAt(0).toUpperCase() - ); - this.configuration.config.topScope.push( - `const ${pluginName} = require("${ - answerToAction.actionAnswer - }")` - ); - this.configuration.config.webpackOptions[ - action - ] = `new ${pluginName}`; - this.configuration.config.item = answerToAction.actionAnswer; - done(); - this.runInstall(getPackageManager(), this.dependencies, { - "save-dev": true - }); - } else { - console.error( - answerToAction.actionAnswer, - "doesn't exist on NPM or is built in webpack, please check for any misspellings." - ); - process.exit(0); - } - }); + npmExists(answerToAction.actionAnswer) + .then((p: string) => { + if (p) { + this.dependencies.push(answerToAction.actionAnswer); + const normalizePluginName: string = answerToAction.actionAnswer.replace( + "-webpack-plugin", + "Plugin", + ); + const pluginName: string = replaceAt( + normalizePluginName, + 0, + normalizePluginName.charAt(0).toUpperCase(), + ); + this.configuration.config.topScope.push( + `const ${pluginName} = require("${ + answerToAction.actionAnswer + }")`, + ); + this.configuration.config.webpackOptions[ + action + ] = `new ${pluginName}`; + this.configuration.config.item = answerToAction.actionAnswer; + done(); + this.runInstall(getPackageManager(), this.dependencies, { + "save-dev": true, + }); + } else { + console.error( + answerToAction.actionAnswer, + "doesn't exist on NPM or is built in webpack, please check for any misspellings.", + ); + process.exit(0); + } + }); } } else { // If we're in the scenario with a deep-property @@ -421,49 +448,57 @@ module.exports = class AddGenerator extends Generator { return; } // Either we are adding directly at the property, else we're in a prop.theOne scenario - const actionMessage = + const actionMessage: string = isDeepProp[1] === "other" ? `What do you want the key on ${action} to be? (press enter if you want it directly as a value on the property)` : `What do you want the value of ${isDeepProp[1]} to be?`; - this.prompt([Input("deepProp", actionMessage)]).then( - deepPropAns => { + this.prompt([ + Input("deepProp", actionMessage), + ]).then( + (deepPropAns: { + deepProp: string, + }) => { // The other option needs to be validated of either being empty or not if (isDeepProp[1] === "other") { - let othersDeepPropKey = deepPropAns.deepProp + const othersDeepPropKey: string = deepPropAns.deepProp ? `What do you want the value of ${ deepPropAns.deepProp } to be?` // eslint-disable-line : `What do you want to be the value of ${action} to be?`; // Push the answer to the array we have created, so we can use it later isDeepProp.push(deepPropAns.deepProp); - this.prompt([Input("deepProp", othersDeepPropKey)]).then( - deepPropAns => { + this.prompt([ + Input("innerProp", othersDeepPropKey), + ]).then( + (innerPropAns: { + innerProp, + }) => { // Check length, if it has none, add the prop directly on the given action if (isDeepProp[2].length === 0) { this.configuration.config.item = action; this.configuration.config.webpackOptions[action] = - deepPropAns.deepProp; + innerPropAns.innerProp; } else { // If not, we're adding to something like devServer.myProp this.configuration.config.item = action + "." + isDeepProp[2]; this.configuration.config.webpackOptions[action] = { - [isDeepProp[2]]: deepPropAns.deepProp + [isDeepProp[2]]: innerPropAns.innerProp, }; } done(); - } + }, ); } else { // We got the schema prop, we've correctly prompted it, and can add it directly this.configuration.config.item = action + "." + isDeepProp[1]; this.configuration.config.webpackOptions[action] = { - [isDeepProp[1]]: deepPropAns.deepProp + [isDeepProp[1]]: deepPropAns.deepProp, }; done(); } - } + }, ); } else { // We're asking for input-only @@ -476,7 +511,7 @@ module.exports = class AddGenerator extends Generator { }); } - writing() { + public writing() { this.config.set("configuration", this.configuration); } -}; +} diff --git a/packages/generators/addon-generator.js b/packages/generators/addon-generator.js index 982a4c983c8..f7038edace6 100644 --- a/packages/generators/addon-generator.js +++ b/packages/generators/addon-generator.js @@ -1,8 +1,9 @@ -const path = require("path"); +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); const mkdirp = require("mkdirp"); +const path = require("path"); const Generator = require("yeoman-generator"); const copyUtils = require("@webpack-cli/utils/copy-utils"); - /** * Creates a Yeoman Generator that generates a project conforming * to webpack-defaults. @@ -24,54 +25,40 @@ const copyUtils = require("@webpack-cli/utils/copy-utils"); * * @returns {Generator} A class extending Generator */ -function addonGenerator( - prompts, - templateDir, - copyFiles, - copyTemplateFiles, - templateFn -) { - //eslint-disable-next-line - return class extends Generator { - prompting() { - return this.prompt(prompts).then(props => { - this.props = props; - }); - } - - default() { - const currentDirName = path.basename(this.destinationPath()); - if (currentDirName !== this.props.name) { - this.log(` +function addonGenerator(prompts, templateDir, copyFiles, copyTemplateFiles, templateFn) { + return class AddOnGenerator extends Generator { + prompting() { + return this.prompt(prompts).then((props) => { + this.props = props; + }); + } + default() { + const currentDirName = path.basename(this.destinationPath()); + if (currentDirName !== this.props.name) { + this.log(` Your project must be inside a folder named ${this.props.name} I will create this folder for you. `); - mkdirp(this.props.name); - const pathToProjectDir = this.destinationPath(this.props.name); - this.destinationRoot(pathToProjectDir); - } - } - - writing() { - this.copy = copyUtils.generatorCopy(this, templateDir); - this.copyTpl = copyUtils.generatorCopyTpl( - this, - templateDir, - templateFn(this) - ); - - copyFiles.forEach(this.copy); - copyTemplateFiles.forEach(this.copyTpl); - } - - install() { - this.npmInstall(["webpack-defaults", "bluebird"], { - "save-dev": true - }).then(() => { - this.spawnCommand("npm", ["run", "defaults"]); - }); - } - }; + mkdirp(this.props.name, (err) => { + console.error("Failed to create directory", err); + }); + const pathToProjectDir = this.destinationPath(this.props.name); + this.destinationRoot(pathToProjectDir); + } + } + writing() { + this.copy = copyUtils.generatorCopy(this, templateDir); + this.copyTpl = copyUtils.generatorCopyTpl(this, templateDir, templateFn(this)); + copyFiles.forEach(this.copy); + copyTemplateFiles.forEach(this.copyTpl); + } + install() { + this.npmInstall(["webpack-defaults", "bluebird"], { + "save-dev": true, + }).then((_) => { + this.spawnCommand("npm", ["run", "defaults"]); + }); + } + }; } - -module.exports = addonGenerator; +exports.default = addonGenerator; diff --git a/packages/generators/addon-generator.ts b/packages/generators/addon-generator.ts new file mode 100644 index 00000000000..a519960367e --- /dev/null +++ b/packages/generators/addon-generator.ts @@ -0,0 +1,83 @@ +import * as mkdirp from "mkdirp"; +import * as path from "path"; +import Generator = require("yeoman-generator"); + +import * as copyUtils from "@webpack-cli/utils/copy-utils"; + +/** + * Creates a Yeoman Generator that generates a project conforming + * to webpack-defaults. + * + * @param {any[]} prompts An array of Yeoman prompt objects + * + * @param {string} templateDir Absolute path to template directory + * + * @param {string[]} copyFiles An array of file paths (relative to `./templates`) + * of files to be copied to the generated project. File paths should be of the + * form `path/to/file.js.tpl`. + * + * @param {string[]} copyTemplateFiles An array of file paths (relative to + * `./templates`) of files to be copied to the generated project. Template + * file paths should be of the form `path/to/_file.js.tpl`. + * + * @param {Function} templateFn A function that is passed a generator instance and + * returns an object containing data to be supplied to the template files. + * + * @returns {Generator} A class extending Generator + */ +export default function addonGenerator( + prompts: any, + templateDir: string, + copyFiles: string[], + copyTemplateFiles: string[], + templateFn: Function, +) { + return class AddOnGenerator extends Generator { + private props: { + name?: string; + }; + private copy: (value: string, index: number, array: string[]) => void; + private copyTpl: (value: string, index: number, array: string[]) => void; + + public prompting() { + return this.prompt(prompts).then((props) => { + this.props = props; + }); + } + + public default() { + const currentDirName = path.basename(this.destinationPath()); + if (currentDirName !== this.props.name) { + this.log(` + Your project must be inside a folder named ${this.props.name} + I will create this folder for you. + `); + mkdirp(this.props.name, (err: object) => { + console.error("Failed to create directory", err); + }); + const pathToProjectDir: string = this.destinationPath(this.props.name); + this.destinationRoot(pathToProjectDir); + } + } + + public writing() { + this.copy = copyUtils.generatorCopy(this, templateDir); + this.copyTpl = copyUtils.generatorCopyTpl( + this, + templateDir, + templateFn(this), + ); + + copyFiles.forEach(this.copy); + copyTemplateFiles.forEach(this.copyTpl); + } + + public install() { + this.npmInstall(["webpack-defaults", "bluebird"], { + "save-dev": true, + }).then((_: void) => { + this.spawnCommand("npm", ["run", "defaults"]); + }); + } + }; +} diff --git a/packages/generators/index.js b/packages/generators/index.js index e7dabd70592..6c901b56efc 100644 --- a/packages/generators/index.js +++ b/packages/generators/index.js @@ -1,17 +1,16 @@ -const addGenerator = require("./add-generator"); -const initGenerator = require("./init-generator"); -const loaderGenerator = require("./loader-generator"); -const pluginGenerator = require("./plugin-generator"); -const removeGenerator = require("./remove-generator"); -const updateGenerator = require("./update-generator"); -const addonGenerator = require("./addon-generator"); - -module.exports = { - addGenerator, - initGenerator, - loaderGenerator, - pluginGenerator, - removeGenerator, - updateGenerator, - addonGenerator -}; +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const add_generator_1 = require("./add-generator"); +exports.addGenerator = add_generator_1.default; +const addon_generator_1 = require("./addon-generator"); +exports.addonGenerator = addon_generator_1.default; +const init_generator_1 = require("./init-generator"); +exports.initGenerator = init_generator_1.default; +const loader_generator_1 = require("./loader-generator"); +exports.loaderGenerator = loader_generator_1.default; +const plugin_generator_1 = require("./plugin-generator"); +exports.pluginGenerator = plugin_generator_1.default; +const remove_generator_1 = require("./remove-generator"); +exports.removeGenerator = remove_generator_1.default; +const update_generator_1 = require("./update-generator"); +exports.updateGenerator = update_generator_1.default; diff --git a/packages/generators/index.ts b/packages/generators/index.ts new file mode 100644 index 00000000000..dd4bd030f27 --- /dev/null +++ b/packages/generators/index.ts @@ -0,0 +1,17 @@ +import addGenerator from "./add-generator"; +import addonGenerator from "./addon-generator"; +import initGenerator from "./init-generator"; +import loaderGenerator from "./loader-generator"; +import pluginGenerator from "./plugin-generator"; +import removeGenerator from "./remove-generator"; +import updateGenerator from "./update-generator"; + +export { + addGenerator, + addonGenerator, + initGenerator, + loaderGenerator, + pluginGenerator, + removeGenerator, + updateGenerator, +}; diff --git a/packages/generators/init-generator.js b/packages/generators/init-generator.ts similarity index 70% rename from packages/generators/init-generator.js rename to packages/generators/init-generator.ts index 39197a86127..b40457a001c 100644 --- a/packages/generators/init-generator.js +++ b/packages/generators/init-generator.ts @@ -1,20 +1,18 @@ -"use strict"; +import chalk from "chalk"; +import * as logSymbols from "log-symbols"; +import Generator = require("yeoman-generator"); -const Generator = require("yeoman-generator"); -const chalk = require("chalk"); -const logSymbols = require("log-symbols"); +import { getPackageManager } from "@webpack-cli/utils/package-manager"; +import { + Confirm, + Input, + List, +} from "@webpack-cli/webpack-scaffold"; -const Input = require("@webpack-cli/webpack-scaffold").Input; -const Confirm = require("@webpack-cli/webpack-scaffold").Confirm; -const List = require("@webpack-cli/webpack-scaffold").List; - -const getPackageManager = require("@webpack-cli/utils/package-manager") - .getPackageManager; - -const entryQuestions = require("./utils/entry"); -const getBabelPlugin = require("./utils/module"); -const getDefaultPlugins = require("./utils/plugins"); -const tooltip = require("./utils/tooltip"); +import entryQuestions from "./utils/entry"; +import getBabelPlugin from "./utils/module"; +import getDefaultPlugins from "./utils/plugins"; +import tooltip from "./utils/tooltip"; /** * @@ -25,75 +23,92 @@ const tooltip = require("./utils/tooltip"); * @returns {Void} After execution, transforms are triggered * */ -module.exports = class InitGenerator extends Generator { +export default class InitGenerator extends Generator { + private isProd: boolean; + private usingDefaults: boolean; + private dependencies: string[]; + private configuration: { + config: { + configName?: string, + topScope: string[], + webpackOptions: any, + }, + }; + constructor(args, opts) { super(args, opts); this.isProd = false; - (this.usingDefaults = false), - (this.dependencies = [ + this.usingDefaults = false, + this.dependencies = [ "webpack", "webpack-cli", "uglifyjs-webpack-plugin", - "babel-plugin-syntax-dynamic-import" - ]); + "babel-plugin-syntax-dynamic-import", + ]; this.configuration = { config: { + topScope: [], webpackOptions: {}, - topScope: [] - } + }, }; } - prompting() { - const done = this.async(); - const self = this; - let regExpForStyles; - let ExtractUseProps; - let outputPath = "dist"; + public prompting() { + const done: any = this.async(); + const self: this = this; + let regExpForStyles: string; + let ExtractUseProps: object[]; + let outputPath: string = "dist"; + process.stdout.write( "\n" + logSymbols.info + chalk.blue(" INFO ") + "For more information and a detailed description of each question, have a look at " + chalk.bold.green( - "https://github.com/webpack/webpack-cli/blob/master/INIT.md" + "https://github.com/webpack/webpack-cli/blob/master/INIT.md", ) + - "\n" + "\n", ); process.stdout.write( logSymbols.info + chalk.blue(" INFO ") + "Alternatively, run `webpack(-cli) --help` for usage info." + - "\n\n" + "\n\n", ); + this.configuration.config.webpackOptions.module = { - rules: [] + rules: [], }; this.configuration.config.topScope.push( "const webpack = require('webpack')", "const path = require('path')", - "\n" + "\n", ); this.prompt([ - Confirm("entryType", "Will your application have multiple bundles?") + Confirm("entryType", "Will your application have multiple bundles?"), ]) - .then(entryTypeAnswer => { + .then((entryTypeAnswer: { + entryType: boolean; + }) => { // Ask different questions for entry points return entryQuestions(self, entryTypeAnswer); }) - .then(entryOptions => { + .then((entryOptions: object | string) => { if (entryOptions !== "\"\"") { this.configuration.config.webpackOptions.entry = entryOptions; } return this.prompt([ Input( "outputType", - "Which folder will your generated bundles be in? [default: dist]:" - ) + "Which folder will your generated bundles be in? [default: dist]:", + ), ]); }) - .then(outputTypeAnswer => { + .then((outputTypeAnswer: { + outputType: string; + }) => { // As entry is not required anymore and we dont set it to be an empty string or """"" // it can be undefined so falsy check is enough (vs entry.length); if ( @@ -101,16 +116,16 @@ module.exports = class InitGenerator extends Generator { !this.usingDefaults ) { this.configuration.config.webpackOptions.output = { + chunkFilename: "'[name].[chunkhash].js'", filename: "'[name].[chunkhash].js'", - chunkFilename: "'[name].[chunkhash].js'" }; } else if (!this.usingDefaults) { this.configuration.config.webpackOptions.output = { - filename: "'[name].[chunkhash].js'" + filename: "'[name].[chunkhash].js'", }; } - if (outputTypeAnswer["outputType"].length) { - outputPath = outputTypeAnswer["outputType"]; + if (outputTypeAnswer.outputType.length) { + outputPath = outputTypeAnswer.outputType; } if (!this.usingDefaults) { this.configuration.config.webpackOptions.output.path = `path.resolve(__dirname, '${outputPath}')`; @@ -124,18 +139,20 @@ module.exports = class InitGenerator extends Generator { : "'development'"; this.configuration.config.webpackOptions.plugins = this.isProd ? [] : getDefaultPlugins(); return this.prompt([ - Confirm("babelConfirm", "Will you be using ES2015?") + Confirm("babelConfirm", "Will you be using ES2015?"), ]); }) - .then(babelConfirmAnswer => { - if (babelConfirmAnswer["babelConfirm"] === true) { + .then((babelConfirmAnswer: { + babelConfirm: boolean; + }) => { + if (babelConfirmAnswer.babelConfirm === true) { this.configuration.config.webpackOptions.module.rules.push( - getBabelPlugin() + getBabelPlugin(), ); this.dependencies.push( "babel-core", "babel-loader", - "babel-preset-env" + "babel-preset-env", ); } }) @@ -146,19 +163,21 @@ module.exports = class InitGenerator extends Generator { "LESS", "CSS", "PostCSS", - "No" - ]) + "No", + ]), ]); }) - .then(stylingTypeAnswer => { + .then((stylingTypeAnswer: { + stylingType: string; + }) => { ExtractUseProps = []; - switch (stylingTypeAnswer["stylingType"]) { + switch (stylingTypeAnswer.stylingType) { case "SASS": this.dependencies.push( "sass-loader", "node-sass", "style-loader", - "css-loader" + "css-loader", ); regExpForStyles = `${new RegExp(/\.(scss|css)$/)}`; if (this.isProd) { @@ -166,27 +185,27 @@ module.exports = class InitGenerator extends Generator { { loader: "'css-loader'", options: { - sourceMap: true - } + sourceMap: true, + }, }, { loader: "'sass-loader'", options: { - sourceMap: true - } - } + sourceMap: true, + }, + }, ); } else { ExtractUseProps.push( { - loader: "'style-loader'" + loader: "'style-loader'", }, { - loader: "'css-loader'" + loader: "'css-loader'", }, { - loader: "'sass-loader'" - } + loader: "'sass-loader'", + }, ); } break; @@ -196,37 +215,37 @@ module.exports = class InitGenerator extends Generator { "less-loader", "less", "style-loader", - "css-loader" + "css-loader", ); if (this.isProd) { ExtractUseProps.push( { loader: "'css-loader'", options: { - sourceMap: true - } + sourceMap: true, + }, }, { loader: "'less-loader'", options: { - sourceMap: true - } - } + sourceMap: true, + }, + }, ); } else { ExtractUseProps.push( { loader: "'css-loader'", options: { - sourceMap: true - } + sourceMap: true, + }, }, { loader: "'less-loader'", options: { - sourceMap: true - } - } + sourceMap: true, + }, + }, ); } break; @@ -235,14 +254,14 @@ module.exports = class InitGenerator extends Generator { tooltip.postcss(), "const autoprefixer = require('autoprefixer');", "const precss = require('precss');", - "\n" + "\n", ); this.dependencies.push( "style-loader", "css-loader", "postcss-loader", "precss", - "autoprefixer" + "autoprefixer", ); regExpForStyles = `${new RegExp(/\.css$/)}`; if (this.isProd) { @@ -250,9 +269,9 @@ module.exports = class InitGenerator extends Generator { { loader: "'css-loader'", options: { + importLoaders: 1, sourceMap: true, - importLoaders: 1 - } + }, }, { loader: "'postcss-loader'", @@ -262,21 +281,21 @@ module.exports = class InitGenerator extends Generator { precss, autoprefixer ]; - }` - } - } + }`, + }, + }, ); } else { ExtractUseProps.push( { - loader: "'style-loader'" + loader: "'style-loader'", }, { loader: "'css-loader'", options: { + importLoaders: 1, sourceMap: true, - importLoaders: 1 - } + }, }, { loader: "'postcss-loader'", @@ -286,9 +305,9 @@ module.exports = class InitGenerator extends Generator { precss, autoprefixer ]; - }` - } - } + }`, + }, + }, ); } break; @@ -299,20 +318,20 @@ module.exports = class InitGenerator extends Generator { ExtractUseProps.push({ loader: "'css-loader'", options: { - sourceMap: true - } + sourceMap: true, + }, }); } else { ExtractUseProps.push( { loader: "'style-loader'", options: { - sourceMap: true - } + sourceMap: true, + }, }, { - loader: "'css-loader'" - } + loader: "'css-loader'", + }, ); } break; @@ -326,53 +345,58 @@ module.exports = class InitGenerator extends Generator { return this.prompt([ Input( "extractPlugin", - "If you want to bundle your CSS files, what will you name the bundle? (press enter to skip)" - ) + "If you want to bundle your CSS files, what will you name the bundle? (press enter to skip)", + ), ]); } }) - .then(extractPluginAnswer => { + .then((extractPluginAnswer: { + extractPlugin: string; + }) => { if (regExpForStyles) { if (this.isProd) { - const cssBundleName = extractPluginAnswer["extractPlugin"]; + const cssBundleName: string = extractPluginAnswer.extractPlugin; this.configuration.config.topScope.push(tooltip.cssPlugin()); this.dependencies.push("mini-css-extract-plugin"); if (cssBundleName.length !== 0) { this.configuration.config.webpackOptions.plugins.push( // TODO: use [contenthash] after it is supported - `new MiniCssExtractPlugin({ filename:'${cssBundleName}.[chunkhash].css' })` + `new MiniCssExtractPlugin({ filename:'${cssBundleName}.[chunkhash].css' })`, ); } else { this.configuration.config.webpackOptions.plugins.push( - "new MiniCssExtractPlugin({ filename:'style.css' })" + "new MiniCssExtractPlugin({ filename:'style.css' })", ); } ExtractUseProps.unshift({ - loader: "MiniCssExtractPlugin.loader" + loader: "MiniCssExtractPlugin.loader", }); const moduleRulesObj = { test: regExpForStyles, - use: ExtractUseProps + use: ExtractUseProps, }; this.configuration.config.webpackOptions.module.rules.push( - moduleRulesObj + moduleRulesObj, ); this.configuration.config.topScope.push( "const MiniCssExtractPlugin = require('mini-css-extract-plugin');", - "\n" + "\n", ); } else { - const moduleRulesObj = { + const moduleRulesObj: { + test: string; + use: object[]; + } = { test: regExpForStyles, - use: ExtractUseProps + use: ExtractUseProps, }; this.configuration.config.webpackOptions.module.rules.push( - moduleRulesObj + moduleRulesObj, ); } } @@ -381,39 +405,40 @@ module.exports = class InitGenerator extends Generator { this.configuration.config.topScope.push(tooltip.splitChunks()); this.configuration.config.webpackOptions.optimization = { splitChunks: { + cacheGroups: { + vendors: { + priority: -10, + test: "/[\\\\/]node_modules[\\\\/]/", + }, + }, chunks: "'async'", - minSize: 30000, minChunks: 1, + minSize: 30000, // for production name is recommended to be off name: !this.isProd, - cacheGroups: { - vendors: { - test: "/[\\\\/]node_modules[\\\\/]/", - priority: -10 - } - } - } + }, }; done(); }); } - installPlugins() { + public installPlugins() { if (this.isProd) { this.dependencies = this.dependencies.filter( - p => p !== "uglifyjs-webpack-plugin" + (p: string) => p !== "uglifyjs-webpack-plugin", ); } else { this.configuration.config.topScope.push( tooltip.uglify(), "const UglifyJSPlugin = require('uglifyjs-webpack-plugin');", - "\n" + "\n", ); } - const packager = getPackageManager(); + const packager: string = getPackageManager(); const opts = packager === "yarn" ? { dev: true } : { "save-dev": true }; this.runInstall(packager, this.dependencies, opts); } - writing() { + + public writing() { this.config.set("configuration", this.configuration); } -}; +} diff --git a/packages/generators/loader-generator.js b/packages/generators/loader-generator.ts similarity index 74% rename from packages/generators/loader-generator.js rename to packages/generators/loader-generator.ts index 430b5865e8f..77415860324 100644 --- a/packages/generators/loader-generator.js +++ b/packages/generators/loader-generator.ts @@ -1,6 +1,7 @@ -const path = require("path"); -const _ = require("lodash"); -const addonGenerator = require("./addon-generator"); +import * as _ from "lodash"; +import * as path from "path"; + +import addonGenerator from "./addon-generator"; /** * Formats a string into webpack loader format @@ -9,7 +10,7 @@ const addonGenerator = require("./addon-generator"); * @param {string} name A loader name to be formatted * @returns {string} The formatted string */ -function makeLoaderName(name) { +export function makeLoaderName(name: string): string { name = _.kebabCase(name); if (!/loader$/.test(name)) { name += "-loader"; @@ -29,13 +30,13 @@ function makeLoaderName(name) { const LoaderGenerator = addonGenerator( [ { - type: "input", - name: "name", - message: "Loader name", default: "my-loader", filter: makeLoaderName, - validate: str => str.length > 0 - } + message: "Loader name", + name: "name", + type: "input", + validate: (str: string) => str.length > 0, + }, ], path.resolve(__dirname, "..", "generate-loader"), [ @@ -47,13 +48,10 @@ const LoaderGenerator = addonGenerator( "examples/simple/webpack.config.js.tpl", "examples/simple/src/index.js.tpl", "examples/simple/src/lazy-module.js.tpl", - "examples/simple/src/static-esm-module.js.tpl" + "examples/simple/src/static-esm-module.js.tpl", ], ["src/_index.js.tpl"], - gen => ({ name: gen.props.name }) + (gen: any) => ({ name: gen.props.name }), ); -module.exports = { - makeLoaderName, - LoaderGenerator -}; +export default LoaderGenerator; diff --git a/packages/generators/package-lock.json b/packages/generators/package-lock.json index 89d4094005f..3de8862acdf 100644 --- a/packages/generators/package-lock.json +++ b/packages/generators/package-lock.json @@ -16,6 +16,134 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.0.tgz", "integrity": "sha512-LAQ1d4OPfSJ/BMbI2DuizmYrrkD9JMaTdi2hQTlI53lQ4kRQPyZQRS4CYQ7O66bnBBnP/oYdRxbk++X0xuFU6A==" }, + "@types/connect": { + "version": "3.4.32", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.32.tgz", + "integrity": "sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg==", + "requires": { + "@types/node": "*" + } + }, + "@types/events": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", + "integrity": "sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA==" + }, + "@types/express-serve-static-core": { + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.16.0.tgz", + "integrity": "sha512-lTeoCu5NxJU4OD9moCgm0ESZzweAx0YqsAcab6OB0EB3+As1OaHtKnaGJvcngQxYsi9UNv0abn4/DRavrRxt4w==", + "requires": { + "@types/events": "*", + "@types/node": "*", + "@types/range-parser": "*" + } + }, + "@types/http-proxy": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.16.2.tgz", + "integrity": "sha512-GgqePmC3rlsn1nv+kx5OviPuUBU2omhnlXOaJSXFgOdsTcScNFap+OaCb2ip9Bm4m5L8EOehgT5d9M4uNB90zg==", + "requires": { + "@types/events": "*", + "@types/node": "*" + } + }, + "@types/http-proxy-middleware": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/http-proxy-middleware/-/http-proxy-middleware-0.17.4.tgz", + "integrity": "sha512-EWy/2c9onzs9PHPr3oWKnPJuMi6Mk9DUBOF8NLcdCVpDKIqb+qiMtUIKvRXZYWfh4ls1NYsdyOEoaPpp/s6o9g==", + "requires": { + "@types/connect": "*", + "@types/http-proxy": "*", + "@types/node": "*" + } + }, + "@types/lodash": { + "version": "4.14.110", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.110.tgz", + "integrity": "sha512-iXYLa6olt4tnsCA+ZXeP6eEW3tk1SulWeYyP/yooWfAtXjozqXgtX4+XUtMuOCfYjKGz3F34++qUc3Q+TJuIIw==" + }, + "@types/log-symbols": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/log-symbols/-/log-symbols-2.0.0.tgz", + "integrity": "sha512-YJhbp0sz3egFFKl3BcCNPQKzuGFOP4PACcsifhK6ROGnJUW9ViYLuLybQ9GQZm7Zejy3tkGuiXYMq3GiyGkU4g==" + }, + "@types/mime": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.0.tgz", + "integrity": "sha512-A2TAGbTFdBw9azHbpVd+/FkdW2T6msN1uct1O9bH3vTerEHKZhTXJUQXy+hNq1B0RagfU8U+KBdqiZpxjhOUQA==" + }, + "@types/mkdirp": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@types/mkdirp/-/mkdirp-0.5.2.tgz", + "integrity": "sha512-U5icWpv7YnZYGsN4/cmh3WD2onMY0aJIiTE6+51TwJCttdHvtCYmkBNOobHlXwrJRL0nkH9jH4kD+1FAdMN4Tg==", + "requires": { + "@types/node": "*" + } + }, + "@types/node": { + "version": "10.5.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.5.1.tgz", + "integrity": "sha512-AFLl1IALIuyt6oK4AYZsgWVJ/5rnyzQWud7IebaZWWV3YmgtPZkQmYio9R5Ze/2pdd7XfqF5bP+hWS11mAKoOQ==" + }, + "@types/range-parser": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.2.tgz", + "integrity": "sha512-HtKGu+qG1NPvYe1z7ezLsyIaXYyi8SoAVqWDZgDQ8dLrsZvSzUNCwZyfX33uhWxL/SU0ZDQZ3nwZ0nimt507Kw==" + }, + "@types/serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-/BZ4QRLpH/bNYgZgwhKEh+5AsboDBcUdlBYgzoLX0fpj3Y2gp6EApyOlM3bK53wQS/OE1SrdSYBAbux2D1528Q==", + "requires": { + "@types/express-serve-static-core": "*", + "@types/mime": "*" + } + }, + "@types/spdy": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/@types/spdy/-/spdy-3.4.4.tgz", + "integrity": "sha512-N9LBlbVRRYq6HgYpPkqQc3a9HJ/iEtVZToW6xlTtJiMhmRJ7jJdV7TaZQJw/Ve/1ePUsQiCTDc4JMuzzag94GA==", + "requires": { + "@types/node": "*" + } + }, + "@types/tapable": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.3.tgz", + "integrity": "sha512-yz2OUW/HSKZnA2/hdxg5xoHxLbnzAbl1g5UW1eAJSYxVl2BmwKHt72w7Lfl4RU+BL/OWp/cGfSw+GPdN5kfqYg==" + }, + "@types/uglify-js": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.0.2.tgz", + "integrity": "sha512-o8hU2+4xsyGC27Vujoklvxl88Ew5zmJuTBYMX1Uro2rYUt4HEFJKL6fuq8aGykvS+ssIsIzerWWP2DRxonownQ==", + "requires": { + "source-map": "^0.6.1" + } + }, + "@types/webpack": { + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.4.4.tgz", + "integrity": "sha512-J5dFdbyS4mm7kbxq+y0BtkdSVofTLUdrmrImchEDQFcoU689zaYnmDefwFxGPt1+iAimBwTgWU3KOxPBrT0nYg==", + "requires": { + "@types/node": "*", + "@types/tapable": "*", + "@types/uglify-js": "*", + "source-map": "^0.6.0" + } + }, + "@types/webpack-dev-server": { + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/@types/webpack-dev-server/-/webpack-dev-server-2.9.4.tgz", + "integrity": "sha512-dNYwmw6l1ro8Jo8Ft1BM0ZgwMDhJn9sKRNed1cq73PE9tEY2H2onWRT4EIre2Ht6WGqb3eHjX+mGoJqj1SBINw==", + "requires": { + "@types/express-serve-static-core": "*", + "@types/http-proxy-middleware": "*", + "@types/serve-static": "*", + "@types/spdy": "*", + "@types/webpack": "*" + } + }, "accepts": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", @@ -468,6 +596,13 @@ "private": "^0.1.8", "slash": "^1.0.0", "source-map": "^0.5.7" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } } }, "babel-generator": { @@ -489,6 +624,11 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=" + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" } } }, @@ -5134,13 +5274,6 @@ "esprima": "~4.0.0", "private": "~0.1.5", "source-map": "~0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } } }, "rechoir": { @@ -5527,6 +5660,11 @@ "requires": { "is-extendable": "^0.1.0" } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" } } }, @@ -5627,9 +5765,9 @@ } }, "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" }, "source-map-resolve": { "version": "0.5.2", @@ -5649,6 +5787,13 @@ "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", "requires": { "source-map": "^0.5.6" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } } }, "source-map-url": { @@ -5929,6 +6074,11 @@ "mime-types": "~2.1.18" } }, + "typescript": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz", + "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==" + }, "underscore": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", diff --git a/packages/generators/package.json b/packages/generators/package.json index ba96df5bb1d..9f705b33833 100644 --- a/packages/generators/package.json +++ b/packages/generators/package.json @@ -24,5 +24,16 @@ }, "peerDependencies": { "webpack": "^4.0.0" + }, + "devDependencies": { + "@types/lodash": "^4.14.110", + "@types/log-symbols": "^2.0.0", + "@types/mkdirp": "^0.5.2", + "@types/node": "^10.3.6", + "@types/webpack-dev-server": "^2.9.4", + "typescript": "^2.9.2" + }, + "scripts": { + "build": "tsc" } } diff --git a/packages/generators/plugin-generator.js b/packages/generators/plugin-generator.ts similarity index 68% rename from packages/generators/plugin-generator.js rename to packages/generators/plugin-generator.ts index 032e5d4c1d0..fe4625379cd 100644 --- a/packages/generators/plugin-generator.js +++ b/packages/generators/plugin-generator.ts @@ -1,6 +1,7 @@ -const path = require("path"); -const _ = require("lodash"); -const addonGenerator = require("./addon-generator"); +import * as _ from "lodash"; +import * as path from "path"; + +import addonGenerator from "./addon-generator"; /** * A yeoman generator class for creating a webpack @@ -13,13 +14,13 @@ const addonGenerator = require("./addon-generator"); const PluginGenerator = addonGenerator( [ { - type: "input", - name: "name", - message: "Plugin name", default: "my-webpack-plugin", filter: _.kebabCase, - validate: str => str.length > 0 - } + message: "Plugin name", + name: "name", + type: "input", + validate: (str) => str.length > 0, + }, ], path.resolve(__dirname, "..", "generate-plugin"), [ @@ -28,12 +29,10 @@ const PluginGenerator = addonGenerator( "test/functional.test.js.tpl", "examples/simple/src/index.js.tpl", "examples/simple/src/lazy-module.js.tpl", - "examples/simple/src/static-esm-module.js.tpl" + "examples/simple/src/static-esm-module.js.tpl", ], ["src/_index.js.tpl", "examples/simple/_webpack.config.js.tpl"], - gen => ({ name: _.upperFirst(_.camelCase(gen.props.name)) }) + (gen: any) => ({ name: _.upperFirst(_.camelCase(gen.props.name)) }), ); -module.exports = { - PluginGenerator -}; +export default PluginGenerator; diff --git a/packages/generators/remove-generator.js b/packages/generators/remove-generator.js deleted file mode 100644 index f249af53cfb..00000000000 --- a/packages/generators/remove-generator.js +++ /dev/null @@ -1,3 +0,0 @@ -const Generator = require("yeoman-generator"); - -module.exports = class RemoveGenerator extends Generator {}; diff --git a/packages/generators/remove-generator.ts b/packages/generators/remove-generator.ts new file mode 100644 index 00000000000..4d4a7f947a1 --- /dev/null +++ b/packages/generators/remove-generator.ts @@ -0,0 +1,3 @@ +import Generator = require("yeoman-generator"); + +export default class RemoveGenerator extends Generator {} diff --git a/packages/generators/tsconfig.json b/packages/generators/tsconfig.json new file mode 100644 index 00000000000..4082f16a5d9 --- /dev/null +++ b/packages/generators/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../../tsconfig.json" +} diff --git a/packages/generators/tslint.json b/packages/generators/tslint.json new file mode 100644 index 00000000000..0946f20963a --- /dev/null +++ b/packages/generators/tslint.json @@ -0,0 +1,3 @@ +{ + "extends": "../../tslint.json" +} diff --git a/packages/generators/types/json-loader.d.ts b/packages/generators/types/json-loader.d.ts new file mode 100644 index 00000000000..0cb4cf5f44b --- /dev/null +++ b/packages/generators/types/json-loader.d.ts @@ -0,0 +1,4 @@ +declare module "*.json" { + const value: any; + export default value; +} diff --git a/packages/generators/types/yeoman-generator.d.ts b/packages/generators/types/yeoman-generator.d.ts new file mode 100644 index 00000000000..0aa1d038b16 --- /dev/null +++ b/packages/generators/types/yeoman-generator.d.ts @@ -0,0 +1,55 @@ +// Type definitions for yeoman-generator +// Project: https://github.com/yeoman/generator +// Definitions by: Kentaro Okuno +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +/// +declare module "yeoman-generator" { + + class YeomanGeneratorBase extends NodeJS.EventEmitter { + public config: { + set: (setProperty: string, setValue: object) => void; + }; + public env: { + adapter: { + promptModule: { + registerPrompt: (promptName: string, promptModule: object) => void; + }, + }, + }; + public composeWith(namespace: string, options: any, settings?: IComposeSetting): YeomanGeneratorBase; + public destinationRoot(rootPath?: string): string; + public destinationPath(...path: string[]): string; + public run(args?: any, callback?: Function): void; + public runInstall(packager: string, dependencies: string[], options?: object): void; + public on(event: string, listener: Function): this; + public async(): any; + public prompt(opt: IPromptOptions | IPromptOptions[], callback: (answers: any) => void): void; + public prompt(opt: IPromptOptions | IPromptOptions[]): PromiseLike; + public log(message: string): void; + public npmInstall(packages?: string[] | string, options?: any, cb?: Function): Promise<>; + public spawnCommand(name: string, args?: string[], options?: Object): void; + } + + interface IPromptOptions { + type?: string; + name: string; + message: string | ((answers: Object) => string); + choices?: any[] | ((answers: Object) => any); + default?: string | number | string[] | number[] | ((answers: Object) => (string | number | string[] | number[])); + validate?: ((input: any) => boolean | string); + when?: ((answers: Object) => boolean) | boolean; + store?: boolean; + } + + // tslint:disable-next-line + class NamedBase extends YeomanGeneratorBase implements INamedBase { + constructor(args: string | string[], options: any); + } + + // tslint:disable-next-line + class Base extends NamedBase implements IBase { + public static extend(protoProps: IQueueProps, staticProps?: any): YeomanGeneratorBase; + } + + export = Base; +} diff --git a/packages/generators/update-generator.js b/packages/generators/update-generator.js deleted file mode 100644 index fc0515c3b8f..00000000000 --- a/packages/generators/update-generator.js +++ /dev/null @@ -1,3 +0,0 @@ -const Generator = require("yeoman-generator"); - -module.exports = class UpdateGenerator extends Generator {}; diff --git a/packages/generators/update-generator.ts b/packages/generators/update-generator.ts new file mode 100644 index 00000000000..ca5d7ce78ce --- /dev/null +++ b/packages/generators/update-generator.ts @@ -0,0 +1,3 @@ +import Generator = require("yeoman-generator"); + +export default class UpdateGenerator extends Generator {} diff --git a/packages/generators/utils/entry.js b/packages/generators/utils/entry.js index 6b1a246d4d0..4f098413682 100644 --- a/packages/generators/utils/entry.js +++ b/packages/generators/utils/entry.js @@ -1,8 +1,7 @@ "use strict"; - -const InputValidate = require("@webpack-cli/webpack-scaffold").InputValidate; -const validate = require("./validate"); - +Object.defineProperty(exports, "__esModule", { value: true }); +const webpack_scaffold_1 = require("@webpack-cli/webpack-scaffold"); +const validate_1 = require("./validate"); /** * * Prompts for entry points, either if it has multiple or one entry @@ -11,35 +10,28 @@ const validate = require("./validate"); * @param {Object} answer - Previous answer from asking if the user wants single or multiple entries * @returns {Object} An Object that holds the answers given by the user, later used to scaffold */ - -module.exports = (self, answer) => { +function entry(self, answer) { let entryIdentifiers; let result; - if (answer["entryType"] === true) { + if (answer.entryType === true) { result = self .prompt([ - InputValidate( - "multipleEntries", - "Type the names you want for your modules (entry files), separated by comma [example: app,vendor]", - validate - ) + webpack_scaffold_1.InputValidate("multipleEntries", "Type the names you want for your modules (entry files), separated by comma [example: app,vendor]", validate_1.default), ]) - .then(multipleEntriesAnswer => { - let webpackEntryPoint = {}; - entryIdentifiers = multipleEntriesAnswer["multipleEntries"].split(","); - function forEachPromise(obj, fn) { - return obj.reduce((promise, prop) => { + .then((multipleEntriesAnswer) => { + const webpackEntryPoint = {}; + entryIdentifiers = multipleEntriesAnswer.multipleEntries.split(","); + function forEachPromise(entries, fn) { + return entries.reduce((promise, prop) => { const trimmedProp = prop.trim(); - return promise.then(n => { + return promise.then((n) => { if (n) { - Object.keys(n).forEach(val => { - if ( - n[val].charAt(0) !== "(" && - n[val].charAt(0) !== "[" && - !n[val].includes("function") && - !n[val].includes("path") && - !n[val].includes("process") - ) { + Object.keys(n).forEach((val) => { + if (n[val].charAt(0) !== "(" && + n[val].charAt(0) !== "[" && + !n[val].includes("function") && + !n[val].includes("path") && + !n[val].includes("process")) { n[val] = `\'${n[val].replace(/"|'/g, "").concat(".js")}\'`; } webpackEntryPoint[val] = n[val]; @@ -51,23 +43,15 @@ module.exports = (self, answer) => { }); }, Promise.resolve()); } - return forEachPromise(entryIdentifiers, entryProp => - self.prompt([ - InputValidate( - `${entryProp}`, - `What is the location of "${entryProp}"? [example: ./src/${entryProp}]`, - validate - ) - ]) - ).then(entryPropAnswer => { - Object.keys(entryPropAnswer).forEach(val => { - if ( - entryPropAnswer[val].charAt(0) !== "(" && - entryPropAnswer[val].charAt(0) !== "[" && - !entryPropAnswer[val].includes("function") && - !entryPropAnswer[val].includes("path") && - !entryPropAnswer[val].includes("process") - ) { + return forEachPromise(entryIdentifiers, (entryProp) => self.prompt([ + webpack_scaffold_1.InputValidate(`${entryProp}`, `What is the location of "${entryProp}"? [example: ./src/${entryProp}]`, validate_1.default), + ])).then((entryPropAnswer) => { + Object.keys(entryPropAnswer).forEach((val) => { + if (entryPropAnswer[val].charAt(0) !== "(" && + entryPropAnswer[val].charAt(0) !== "[" && + !entryPropAnswer[val].includes("function") && + !entryPropAnswer[val].includes("path") && + !entryPropAnswer[val].includes("process")) { entryPropAnswer[val] = `\'${entryPropAnswer[val] .replace(/"|'/g, "") .concat(".js")}\'`; @@ -80,12 +64,9 @@ module.exports = (self, answer) => { } else { result = self .prompt([ - InputValidate( - "singularEntry", - "Which module will be the first to enter the application? [default: ./src/index]" - ) + webpack_scaffold_1.InputValidate("singularEntry", "Which module will be the first to enter the application? [default: ./src/index]"), ]) - .then(singularEntryAnswer => { + .then((singularEntryAnswer) => { let { singularEntry } = singularEntryAnswer; if (singularEntry.indexOf("\"") >= 0) { singularEntry = singularEntry.replace(/"/g, "'"); @@ -97,4 +78,5 @@ module.exports = (self, answer) => { }); } return result; -}; +} +exports.default = entry; diff --git a/packages/generators/utils/entry.ts b/packages/generators/utils/entry.ts new file mode 100644 index 00000000000..1328783e7c7 --- /dev/null +++ b/packages/generators/utils/entry.ts @@ -0,0 +1,107 @@ +import { InputValidate } from "@webpack-cli/webpack-scaffold"; + +import validate from "./validate"; + +/** + * + * Prompts for entry points, either if it has multiple or one entry + * + * @param {Object} self - A variable holding the instance of the prompting + * @param {Object} answer - Previous answer from asking if the user wants single or multiple entries + * @returns {Object} An Object that holds the answers given by the user, later used to scaffold + */ + +export default function entry(self: any, answer: { + entryType: boolean; +}): any { + let entryIdentifiers: string[]; + let result: any; + if (answer.entryType === true) { + result = self + .prompt([ + InputValidate( + "multipleEntries", + "Type the names you want for your modules (entry files), separated by comma [example: app,vendor]", + validate, + ), + ]) + .then((multipleEntriesAnswer: { + multipleEntries: string, + }) => { + const webpackEntryPoint: object = {}; + entryIdentifiers = multipleEntriesAnswer.multipleEntries.split(","); + + function forEachPromise(entries, fn) { + return entries.reduce((promise: Promise<{}>, prop: string) => { + const trimmedProp: string = prop.trim(); + + return promise.then((n: object) => { + if (n) { + Object.keys(n).forEach((val) => { + if ( + n[val].charAt(0) !== "(" && + n[val].charAt(0) !== "[" && + !n[val].includes("function") && + !n[val].includes("path") && + !n[val].includes("process") + ) { + n[val] = `\'${n[val].replace(/"|'/g, "").concat(".js")}\'`; + } + webpackEntryPoint[val] = n[val]; + }); + } else { + n = {}; + } + return fn(trimmedProp); + }); + }, Promise.resolve()); + } + return forEachPromise(entryIdentifiers, (entryProp: string) => + self.prompt([ + InputValidate( + `${entryProp}`, + `What is the location of "${entryProp}"? [example: ./src/${entryProp}]`, + validate, + ), + ]), + ).then((entryPropAnswer: object) => { + Object.keys(entryPropAnswer).forEach((val: string) => { + if ( + entryPropAnswer[val].charAt(0) !== "(" && + entryPropAnswer[val].charAt(0) !== "[" && + !entryPropAnswer[val].includes("function") && + !entryPropAnswer[val].includes("path") && + !entryPropAnswer[val].includes("process") + ) { + entryPropAnswer[val] = `\'${entryPropAnswer[val] + .replace(/"|'/g, "") + .concat(".js")}\'`; + } + webpackEntryPoint[val] = entryPropAnswer[val]; + }); + return webpackEntryPoint; + }); + }); + } else { + result = self + .prompt([ + InputValidate( + "singularEntry", + "Which module will be the first to enter the application? [default: ./src/index]", + ), + ]) + .then((singularEntryAnswer: { + singularEntry: string, + }) => { + let { singularEntry } = singularEntryAnswer; + if (singularEntry.indexOf("\"") >= 0) { + singularEntry = singularEntry.replace(/"/g, "'"); + } + if (singularEntry.length <= 0) { + self.usingDefaults = true; + } + return singularEntry; + }); + } + return result; +} diff --git a/packages/generators/utils/module.js b/packages/generators/utils/module.js index 3e7f286197b..572f6a17031 100644 --- a/packages/generators/utils/module.js +++ b/packages/generators/utils/module.js @@ -1,20 +1,20 @@ "use strict"; - +Object.defineProperty(exports, "__esModule", { value: true }); /** * * Returns an module.rule object that has the babel loader if invoked * - * @param {Void} _ - void value * @returns {Function} A callable function that adds the babel-loader with env preset */ -module.exports = _ => { +function default_1() { return { - test: `${new RegExp(/\.js$/)}`, include: ["path.resolve(__dirname, 'src')"], loader: "'babel-loader'", options: { + plugins: ["'syntax-dynamic-import'"], presets: [["'env'", { "'modules'": false }]], - plugins: ["'syntax-dynamic-import'"] - } + }, + test: `${new RegExp(/\.js$/)}`, }; -}; +} +exports.default = default_1; diff --git a/packages/generators/utils/module.ts b/packages/generators/utils/module.ts new file mode 100644 index 00000000000..6b9afe74627 --- /dev/null +++ b/packages/generators/utils/module.ts @@ -0,0 +1,27 @@ +interface IModule { + include: string[]; + loader: string; + options: { + plugins: string[]; + presets: any[]; + }; + test: string; +} + +/** + * + * Returns an module.rule object that has the babel loader if invoked + * + * @returns {Function} A callable function that adds the babel-loader with env preset + */ +export default function(): IModule { + return { + include: ["path.resolve(__dirname, 'src')"], + loader: "'babel-loader'", + options: { + plugins: ["'syntax-dynamic-import'"], + presets: [["'env'", { "'modules'": false }]], + }, + test: `${new RegExp(/\.js$/)}`, + }; +} diff --git a/packages/generators/utils/plugins.js b/packages/generators/utils/plugins.js index 64f0c32fa06..d55d1d79ef2 100644 --- a/packages/generators/utils/plugins.js +++ b/packages/generators/utils/plugins.js @@ -1,14 +1,13 @@ "use strict"; - /** * * Callable function with the initial plugins * - * @param {Void} _ - void value * @returns {Function} An function that returns an array * that consists of the uglify plugin */ - -module.exports = _ => { +Object.defineProperty(exports, "__esModule", { value: true }); +function default_1() { return ["new UglifyJSPlugin()"]; -}; +} +exports.default = default_1; diff --git a/packages/generators/utils/plugins.ts b/packages/generators/utils/plugins.ts new file mode 100644 index 00000000000..2c8a51fe07d --- /dev/null +++ b/packages/generators/utils/plugins.ts @@ -0,0 +1,11 @@ +/** + * + * Callable function with the initial plugins + * + * @returns {Function} An function that returns an array + * that consists of the uglify plugin + */ + +export default function(): string[] { + return ["new UglifyJSPlugin()"]; +} diff --git a/packages/generators/utils/tooltip.js b/packages/generators/utils/tooltip.js index afd530f765f..8a9bc96d888 100644 --- a/packages/generators/utils/tooltip.js +++ b/packages/generators/utils/tooltip.js @@ -6,18 +6,19 @@ * * @returns {Object} An Object that consists of tooltip methods to be invoked */ - -module.exports = { - uglify: _ => { +Object.defineProperty(exports, "__esModule", { value: true }); +exports.default = { + cssPlugin: () => { return `/* - * We've enabled UglifyJSPlugin for you! This minifies your app - * in order to load faster and run less javascript. + * We've enabled MiniCssExtractPlugin for you. This allows your app to + * use css modules that will be moved into a separate CSS file instead of inside + * one of your module entries! * - * https://github.com/webpack-contrib/uglifyjs-webpack-plugin + * https://github.com/webpack-contrib/mini-css-extract-plugin * */`; }, - splitChunks: _ => { + splitChunks: () => { return `/* * SplitChunksPlugin is enabled by default and replaced * deprecated CommonsChunkPlugin. It automatically identifies modules which @@ -31,17 +32,7 @@ module.exports = { * */`; }, - cssPlugin: _ => { - return `/* - * We've enabled MiniCssExtractPlugin for you. This allows your app to - * use css modules that will be moved into a separate CSS file instead of inside - * one of your module entries! - * - * https://github.com/webpack-contrib/mini-css-extract-plugin - * - */`; - }, - postcss: _ => { + postcss: () => { return `/* * We've enabled Postcss, autoprefixer and precss for you. This allows your app * to lint CSS, support variables and mixins, transpile future CSS syntax, @@ -56,5 +47,14 @@ module.exports = { * https://github.com/jonathantneal/precss * */`; - } + }, + uglify: () => { + return `/* + * We've enabled UglifyJSPlugin for you! This minifies your app + * in order to load faster and run less javascript. + * + * https://github.com/webpack-contrib/uglifyjs-webpack-plugin + * + */`; + }, }; diff --git a/packages/generators/utils/tooltip.ts b/packages/generators/utils/tooltip.ts new file mode 100644 index 00000000000..b70dafd90e1 --- /dev/null +++ b/packages/generators/utils/tooltip.ts @@ -0,0 +1,62 @@ +/** + * + * Tooltip object that consists of tooltips for various of + * features + * + * @returns {Object} An Object that consists of tooltip methods to be invoked + */ + +export default { + cssPlugin: (): string => { + return `/* + * We've enabled MiniCssExtractPlugin for you. This allows your app to + * use css modules that will be moved into a separate CSS file instead of inside + * one of your module entries! + * + * https://github.com/webpack-contrib/mini-css-extract-plugin + * + */`; + }, + + splitChunks: (): string => { + return `/* + * SplitChunksPlugin is enabled by default and replaced + * deprecated CommonsChunkPlugin. It automatically identifies modules which + * should be splitted of chunk by heuristics using module duplication count and + * module category (i. e. node_modules). And splits the chunks… + * + * It is safe to remove "splitChunks" from the generated configuration + * and was added as an educational example. + * + * https://webpack.js.org/plugins/split-chunks-plugin/ + * + */`; + }, + + postcss: (): string => { + return `/* + * We've enabled Postcss, autoprefixer and precss for you. This allows your app + * to lint CSS, support variables and mixins, transpile future CSS syntax, + * inline images, and more! + * + * To enable SASS or LESS, add the respective loaders to module.rules + * + * https://github.com/postcss/postcss + * + * https://github.com/postcss/autoprefixer + * + * https://github.com/jonathantneal/precss + * + */`; + }, + + uglify: (): string => { + return `/* + * We've enabled UglifyJSPlugin for you! This minifies your app + * in order to load faster and run less javascript. + * + * https://github.com/webpack-contrib/uglifyjs-webpack-plugin + * + */`; + }, +}; diff --git a/packages/generators/utils/validate.js b/packages/generators/utils/validate.js index e6d71a0cc6c..0621bf2ca40 100644 --- a/packages/generators/utils/validate.js +++ b/packages/generators/utils/validate.js @@ -1,5 +1,5 @@ "use strict"; - +Object.defineProperty(exports, "__esModule", { value: true }); /** * * Validates an input to check if an input is provided @@ -8,10 +8,11 @@ * @returns {String | Boolean } Returns truthy if its long enough * Or a string if the user hasn't written anything */ -module.exports = value => { +function validate(value) { const pass = value.length; if (pass) { return true; } return "Please specify an answer!"; -}; +} +exports.default = validate; diff --git a/packages/generators/utils/validate.ts b/packages/generators/utils/validate.ts new file mode 100644 index 00000000000..b5090a45bad --- /dev/null +++ b/packages/generators/utils/validate.ts @@ -0,0 +1,15 @@ +/** + * + * Validates an input to check if an input is provided + * + * @param {String} value - The input string to validate + * @returns {String | Boolean } Returns truthy if its long enough + * Or a string if the user hasn't written anything + */ +export default function validate(value: string): string | boolean { + const pass = value.length; + if (pass) { + return true; + } + return "Please specify an answer!"; +} diff --git a/packages/webpack-scaffold/index.ts b/packages/webpack-scaffold/index.ts index d890e839edc..56ac8589544 100755 --- a/packages/webpack-scaffold/index.ts +++ b/packages/webpack-scaffold/index.ts @@ -11,7 +11,7 @@ interface IInquirerList extends IScaffoldBaseObject { } interface IInquirerInput extends IScaffoldBaseObject { - validate?: Function; + validate?: (input: any) => string | boolean; } export function createArrowFunction(value: Function): string { @@ -95,7 +95,7 @@ export function Input(name: string, message: string): IInquirerInput { }; } -export function InputValidate(name: string, message: string, cb: Function): IInquirerInput { +export function InputValidate(name: string, message: string, cb?: (input: any) => string | boolean): IInquirerInput { return { message, name, diff --git a/tslint.json b/tslint.json index 0c4e7909be6..27feb3fe3a3 100644 --- a/tslint.json +++ b/tslint.json @@ -15,6 +15,10 @@ ], "no-console": { "severity": "warning" + }, + "max-line-length": { + "options": { "limit": 120 }, + "severity": "warn" } }, "rulesDirectory": [],