From c1844f8c1a39c1d0d01946cd5f403556a655ae53 Mon Sep 17 00:00:00 2001 From: dhruvdutt Date: Thu, 5 Jul 2018 18:44:05 +0530 Subject: [PATCH] feat(generators): add typescript support --- packages/generators/.gitignore | 4 + packages/generators/.npmignore | 3 + .../{add-generator.js => add-generator.ts} | 342 ++++++++------- ...{addon-generator.js => addon-generator.ts} | 55 +-- packages/generators/index.js | 17 - packages/generators/index.ts | 17 + .../{init-generator.js => init-generator.ts} | 277 ++++++------ ...oader-generator.js => loader-generator.ts} | 28 +- packages/generators/package-lock.json | 409 ++++++++++++++++-- packages/generators/package.json | 11 + ...lugin-generator.js => plugin-generator.ts} | 25 +- ...emove-generator.js => remove-generator.ts} | 88 ++-- packages/generators/tsconfig.json | 3 + packages/generators/tslint.json | 3 + packages/generators/types/index.ts | 37 ++ packages/generators/types/json-loader.d.ts | 4 + .../generators/types/yeoman-generator.d.ts | 90 ++++ packages/generators/update-generator.js | 3 - packages/generators/update-generator.ts | 3 + .../generators/utils/{entry.js => entry.ts} | 65 +-- packages/generators/utils/module.js | 20 - packages/generators/utils/module.ts | 36 ++ .../utils/{plugins.js => plugins.ts} | 7 +- .../utils/{tooltip.js => tooltip.ts} | 40 +- .../utils/{validate.js => validate.ts} | 6 +- packages/init/.gitignore | 1 + packages/webpack-scaffold/index.ts | 18 +- tslint.json | 6 + 28 files changed, 1123 insertions(+), 495 deletions(-) create mode 100644 packages/generators/.gitignore rename packages/generators/{add-generator.js => add-generator.ts} (61%) rename packages/generators/{addon-generator.js => addon-generator.ts} (56%) delete mode 100644 packages/generators/index.js create mode 100644 packages/generators/index.ts rename packages/generators/{init-generator.js => init-generator.ts} (67%) rename packages/generators/{loader-generator.js => loader-generator.ts} (74%) rename packages/generators/{plugin-generator.js => plugin-generator.ts} (67%) rename packages/generators/{remove-generator.js => remove-generator.ts} (50%) create mode 100644 packages/generators/tsconfig.json create mode 100644 packages/generators/tslint.json create mode 100644 packages/generators/types/index.ts 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 rename packages/generators/utils/{entry.js => entry.ts} (63%) delete mode 100644 packages/generators/utils/module.js create mode 100644 packages/generators/utils/module.ts rename packages/generators/utils/{plugins.js => plugins.ts} (71%) rename packages/generators/utils/{tooltip.js => tooltip.ts} (90%) rename packages/generators/utils/{validate.js => validate.ts} (82%) diff --git a/packages/generators/.gitignore b/packages/generators/.gitignore new file mode 100644 index 00000000000..74dcaf3ce86 --- /dev/null +++ b/packages/generators/.gitignore @@ -0,0 +1,4 @@ +*.js +**/*.js +!*.test.js +!/**/*.test.js diff --git a/packages/generators/.npmignore b/packages/generators/.npmignore index 9db08315811..573312f0347 100644 --- a/packages/generators/.npmignore +++ b/packages/generators/.npmignore @@ -1 +1,4 @@ +**/*.ts +*.ts *.test.* +tsconfig.json diff --git a/packages/generators/add-generator.js b/packages/generators/add-generator.ts similarity index 61% rename from packages/generators/add-generator.js rename to packages/generators/add-generator.ts index 55bcbc80775..3f3a52050fe 100644 --- a/packages/generators/add-generator.js +++ b/packages/generators/add-generator.ts @@ -1,19 +1,27 @@ -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, + IInquirerInput, + IInquirerList, + Input, + List, +} from "@webpack-cli/webpack-scaffold"; -const PROPS = Array.from(PROP_TYPES.keys()); +import { ISchemaProperties, IWebpackOptions } from "./types"; +import entryQuestions from "./utils/entry"; +import webpackSchema from "./utils/optionsSchema.json"; + +const PROPS: string[] = Array.from(PROP_TYPES.keys()); /** * @@ -27,8 +35,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 +49,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 +69,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 +87,40 @@ 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?: IWebpackOptions, + }, + }; + 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) => void | boolean = this.async(); + let action: string; + const self: this = this; + const manualOrListInput: (promptAction: string) => IInquirerInput = (promptAction: string) => + Input("actionAnswer", `What do you want to add to ${promptAction}?`); + let inputPrompt: IInquirerInput; // 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 +130,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 @@ -122,47 +143,54 @@ module.exports = class AddGenerator extends Generator { // update the action variable, we're using it later action = actionTypeAnswer.actionType; }) - .then(() => { + .then((_: void) => { 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: { + entryType: boolean; + }) => { 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: ISchemaProperties = 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 - ) // eslint-disable-line + (p: { + properties?: object, + enum?: any[], + }) => p.properties || p.enum, + ) : 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 +204,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 +226,25 @@ module.exports = class AddGenerator extends Generator { } } // WDS has its own schema, so we gonna need to check that too - const webpackDevserverSchemaProp = + const webpackDevserverSchemaProp: ISchemaProperties = 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 +254,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( + inputPrompt = 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,26 +285,30 @@ module.exports = class AddGenerator extends Generator { webpackDevserverSchemaProp.properties = Object.assign( webpackDevserverSchemaProp.properties, { - other: {} - } + other: {}, + }, ); - manualOrListInput = List( + inputPrompt = 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; } else { // manual input if non-existent - manualOrListInput = manualOrListInput(action); + inputPrompt = manualOrListInput(action); } } else { - manualOrListInput = manualOrListInput(action); + inputPrompt = manualOrListInput(action); } - return this.prompt([manualOrListInput]); + return this.prompt([ + inputPrompt, + ]); }) - .then(answerToAction => { + .then((answerToAction: { + actionAnswer: string, + }) => { if (!answerToAction) { done(); return; @@ -286,54 +318,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 +377,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 +409,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 +459,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 +522,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.ts similarity index 56% rename from packages/generators/addon-generator.js rename to packages/generators/addon-generator.ts index 982a4c983c8..fcbafc96d06 100644 --- a/packages/generators/addon-generator.js +++ b/packages/generators/addon-generator.ts @@ -1,7 +1,9 @@ -const path = require("path"); -const mkdirp = require("mkdirp"); -const Generator = require("yeoman-generator"); -const copyUtils = require("@webpack-cli/utils/copy-utils"); +import * as mkdirp from "mkdirp"; +import * as path from "path"; +import Generator = require("yeoman-generator"); + +import * as copyUtils from "@webpack-cli/utils/copy-utils"; +import { IScaffoldBaseObject } from "@webpack-cli/webpack-scaffold"; /** * Creates a Yeoman Generator that generates a project conforming @@ -24,54 +26,57 @@ const copyUtils = require("@webpack-cli/utils/copy-utils"); * * @returns {Generator} A class extending Generator */ -function addonGenerator( - prompts, - templateDir, - copyFiles, - copyTemplateFiles, - templateFn +export default function addonGenerator( + prompts: IScaffoldBaseObject[], + templateDir: string, + copyFiles: string[], + copyTemplateFiles: string[], + templateFn: Function, ) { - //eslint-disable-next-line - return class extends Generator { - prompting() { - return this.prompt(prompts).then(props => { + return class AddOnGenerator extends Generator { + public props: IScaffoldBaseObject; + 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: IScaffoldBaseObject) => { this.props = props; }); } - default() { - const currentDirName = path.basename(this.destinationPath()); + public default() { + const currentDirName: string = 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); + mkdirp(this.props.name, (err: object) => { + console.error("Failed to create directory", err); + }); + const pathToProjectDir: string = this.destinationPath(this.props.name); this.destinationRoot(pathToProjectDir); } } - writing() { + public writing() { this.copy = copyUtils.generatorCopy(this, templateDir); this.copyTpl = copyUtils.generatorCopyTpl( this, templateDir, - templateFn(this) + templateFn(this), ); copyFiles.forEach(this.copy); copyTemplateFiles.forEach(this.copyTpl); } - install() { + public install() { this.npmInstall(["webpack-defaults", "bluebird"], { - "save-dev": true - }).then(() => { + "save-dev": true, + }).then((_: void) => { this.spawnCommand("npm", ["run", "defaults"]); }); } }; } - -module.exports = addonGenerator; diff --git a/packages/generators/index.js b/packages/generators/index.js deleted file mode 100644 index e7dabd70592..00000000000 --- a/packages/generators/index.js +++ /dev/null @@ -1,17 +0,0 @@ -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 -}; 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 67% rename from packages/generators/init-generator.js rename to packages/generators/init-generator.ts index 39197a86127..fe8c3c31ca3 100644 --- a/packages/generators/init-generator.js +++ b/packages/generators/init-generator.ts @@ -1,20 +1,19 @@ -"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 { IWebpackOptions } from "./types"; +import entryQuestions from "./utils/entry"; +import getBabelPlugin from "./utils/module"; +import getDefaultPlugins from "./utils/plugins"; +import tooltip from "./utils/tooltip"; /** * @@ -25,75 +24,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 { + public usingDefaults: boolean; + private isProd: boolean; + private dependencies: string[]; + private configuration: { + config: { + configName?: string, + topScope?: string[], + webpackOptions?: IWebpackOptions, + }, + }; + 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: (_?: void) => void | boolean = 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?") + return this.prompt([ + 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,22 +117,22 @@ 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}')`; } }) - .then(() => { + .then((_: void) => { this.isProd = this.usingDefaults ? true : false; this.configuration.config.configName = this.isProd ? "prod" : "dev"; this.configuration.config.webpackOptions.mode = this.isProd @@ -124,41 +140,45 @@ 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", ); } }) - .then(() => { + .then((_: void) => { return this.prompt([ List("stylingType", "Will you use one of the below CSS solutions?", [ "SASS", "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 +186,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 +216,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 +255,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 +270,9 @@ module.exports = class InitGenerator extends Generator { { loader: "'css-loader'", options: { + importLoaders: 1, sourceMap: true, - importLoaders: 1 - } + }, }, { loader: "'postcss-loader'", @@ -262,21 +282,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 +306,9 @@ module.exports = class InitGenerator extends Generator { precss, autoprefixer ]; - }` - } - } + }`, + }, + }, ); } break; @@ -299,20 +319,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; @@ -320,59 +340,64 @@ module.exports = class InitGenerator extends Generator { regExpForStyles = null; } }) - .then(() => { + .then((_: void) => { if (this.isProd) { // Ask if the user wants to use extractPlugin 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 +406,43 @@ 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 opts = packager === "yarn" ? { dev: true } : { "save-dev": true }; + const packager: string = getPackageManager(); + const opts: { + dev?: boolean, + "save-dev"?: boolean, + } = 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..6bbb9b7f5fe 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: IYeoman) => ({ 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 7045419ecaa..4a86764728d 100644 --- a/packages/generators/package-lock.json +++ b/packages/generators/package-lock.json @@ -16,6 +16,145 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.0.tgz", "integrity": "sha512-LAQ1d4OPfSJ/BMbI2DuizmYrrkD9JMaTdi2hQTlI53lQ4kRQPyZQRS4CYQ7O66bnBBnP/oYdRxbk++X0xuFU6A==" }, + "@types/body-parser": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.0.tgz", + "integrity": "sha512-a2+YeUjPkztKJu5aIF2yArYFQQp8d51wZ7DavSHjFuY1mqVgidGyzEQ41JIVNy82fXj8yPgy2vJmfIywgESW6w==", + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@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": { + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.16.0.tgz", + "integrity": "sha512-TtPEYumsmSTtTetAPXlJVf3kEqb6wZK0bZojpJQrnD/djV4q1oB6QQ8aKvKqwNPACoe02GNiy5zDzcYivR5Z2w==", + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "*", + "@types/serve-static": "*" + } + }, + "@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.5", + "resolved": "https://registry.npmjs.org/@types/http-proxy-middleware/-/http-proxy-middleware-0.17.5.tgz", + "integrity": "sha512-mUqVzfaiOknDT2QJ7g8f2c37G4ZDqDNt08QdUkFCu19Ey5+2SZ0rWHMG00GRJ7g+SgHvl/9weZYuWLXr7RgiCg==", + "requires": { + "@types/connect": "*", + "@types/http-proxy": "*", + "@types/node": "*", + "winston": "^3.0.0" + } + }, + "@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/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.5", + "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.4.5.tgz", + "integrity": "sha512-CFRuLijysw6RK6ET+UozUMXO5DX57rXa7rYCr4h5pvI6YN6XQwIlyVNShGJpyRg3dWcoNfufTwH+HmjEcCkxRg==", + "requires": { + "@types/node": "*", + "@types/tapable": "*", + "@types/uglify-js": "*", + "source-map": "^0.6.0" + } + }, + "@types/webpack-dev-server": { + "version": "2.9.5", + "resolved": "https://registry.npmjs.org/@types/webpack-dev-server/-/webpack-dev-server-2.9.5.tgz", + "integrity": "sha512-fzk3xf+OUuby8CZTZY2xPWBKq3mVhAiJxqRplRIaWfX9BFqWESs0M2epibYry2/KwnVL1KRiUeHabEGm/2XyBg==", + "requires": { + "@types/express": "*", + "@types/http-proxy-middleware": "*", + "@types/serve-static": "*", + "@types/webpack": "*" + } + }, "accepts": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", @@ -46,6 +185,21 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "requires": { "color-convert": "^1.9.0" + }, + "dependencies": { + "color-convert": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.2.tgz", + "integrity": "sha512-3NUJZdhMhcdPn8vJ9v2UQJoH0qqoGUkYTgFEPZaPjEtwmmKUfNV46zZmgB2M5M4DCEQHMaCfWHCxiBflLm04Tg==", + "requires": { + "color-name": "1.1.1" + } + }, + "color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok=" + } } }, "anymatch": { @@ -383,9 +537,12 @@ "integrity": "sha512-UY7+9DPzlJ9VM8eY0b2TUZcZvF+1pO0hzMtAyjBYKhOmnvRlqYNYnWdtsMj0V16CGaMlpL0G1jnLbLo4AyotuQ==" }, "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", + "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", + "requires": { + "lodash": "^4.17.10" + } }, "async-each": { "version": "1.0.1", @@ -468,6 +625,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 +653,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=" } } }, @@ -1578,24 +1747,52 @@ "object-visit": "^1.0.0" } }, - "color-convert": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.2.tgz", - "integrity": "sha512-3NUJZdhMhcdPn8vJ9v2UQJoH0qqoGUkYTgFEPZaPjEtwmmKUfNV46zZmgB2M5M4DCEQHMaCfWHCxiBflLm04Tg==", + "color": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/color/-/color-0.8.0.tgz", + "integrity": "sha1-iQwHw/1OZJU3Y4kRz2keVFi2/KU=", "requires": { - "color-name": "1.1.1" + "color-convert": "^0.5.0", + "color-string": "^0.3.0" } }, + "color-convert": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz", + "integrity": "sha1-vbbGnOZg+t/+CwAHzER+G59ygr0=" + }, "color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok=" + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "color-string": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-0.3.0.tgz", + "integrity": "sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=", + "requires": { + "color-name": "^1.0.0" + } + }, + "colornames": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/colornames/-/colornames-0.0.2.tgz", + "integrity": "sha1-2BH9bIT1kClJmorEQ2ICk1uSvjE=" }, "colors": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.0.tgz", "integrity": "sha512-EDpX3a7wHMWFA7PUHWPHNWqOxIIRSJetuwl0AS5Oi/5FMV8kWm69RTlgm00GKjBO1xFHMtBbL49yRtMMdticBw==" }, + "colorspace": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.0.1.tgz", + "integrity": "sha1-yZx5btMRKLmHalLh7l7gOkpxl0k=", + "requires": { + "color": "0.8.x", + "text-hex": "0.0.x" + } + }, "commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", @@ -1734,6 +1931,13 @@ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "requires": { "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } } }, "decamelize": { @@ -1861,6 +2065,16 @@ "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.3.tgz", "integrity": "sha1-ogM8CcyOFY03dI+951B4Mr1s4Sc=" }, + "diagnostics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/diagnostics/-/diagnostics-1.1.0.tgz", + "integrity": "sha1-4QkJALSVI+hSe+IPCBJ1IF8q42o=", + "requires": { + "colorspace": "1.0.x", + "enabled": "1.0.x", + "kuler": "0.0.x" + } + }, "diff": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", @@ -1927,11 +2141,24 @@ "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.6.1.tgz", "integrity": "sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ==" }, + "enabled": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz", + "integrity": "sha1-ll9lE9LC0cX0ZStkouM5ZGf8L5M=", + "requires": { + "env-variable": "0.0.x" + } + }, "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, + "env-variable": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/env-variable/-/env-variable-0.0.4.tgz", + "integrity": "sha512-+jpGxSWG4vr6gVxUHOc4p+ilPnql7NzZxOZBxNldsKGjCF+97df3CbuX7XMaDa5oAVkKQj4rKp38rYdC4VcpDg==" + }, "errno": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", @@ -2457,6 +2684,11 @@ } } }, + "fast-safe-stringify": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.4.tgz", + "integrity": "sha512-mNlGUdKOeGNleyrmgbKYtbnCr9KZkZXU7eM89JRo8vY10f7Ul1Fbj07hUBW3N4fC0xM+fmfFfa2zM7mIizhpNQ==" + }, "faye-websocket": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", @@ -2465,6 +2697,11 @@ "websocket-driver": ">=0.5.1" } }, + "fecha": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz", + "integrity": "sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg==" + }, "figures": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", @@ -2540,6 +2777,11 @@ "requires": { "ms": "2.0.0" } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" } } }, @@ -4055,6 +4297,13 @@ "recast": "^0.12.5", "temp": "^0.8.1", "write-file-atomic": "^1.2.0" + }, + "dependencies": { + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + } } }, "jsesc": { @@ -4090,6 +4339,14 @@ "is-buffer": "^1.1.5" } }, + "kuler": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-0.0.0.tgz", + "integrity": "sha1-tmu0a5NOVQ9Z2BiEjgq7pPf1VTw=", + "requires": { + "colornames": "0.0.2" + } + }, "lcid": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", @@ -4144,6 +4401,18 @@ "chalk": "^2.0.1" } }, + "logform": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/logform/-/logform-1.9.1.tgz", + "integrity": "sha512-ZHrZE8VSf7K3xKxJiQ1aoTBp2yK+cEbFcgarsjzI3nt3nE/3O0heNSppoOQMUJVMZo/xiVwCxiXIabaZApsKNQ==", + "requires": { + "colors": "^1.2.1", + "fast-safe-stringify": "^2.0.4", + "fecha": "^2.3.3", + "ms": "^2.1.1", + "triple-beam": "^1.2.0" + } + }, "loglevel": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.1.tgz", @@ -4452,9 +4721,9 @@ } }, "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" }, "multicast-dns": { "version": "6.2.3", @@ -4721,6 +4990,11 @@ "wrappy": "1" } }, + "one-time": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-0.0.4.tgz", + "integrity": "sha1-+M33eISCb+Tf+T46nMN7HkSAdC4=" + }, "onetime": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", @@ -4924,6 +5198,13 @@ "async": "^1.5.2", "debug": "^2.2.0", "mkdirp": "0.5.x" + }, + "dependencies": { + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + } } }, "posix-character-classes": { @@ -5133,13 +5414,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": { @@ -5395,6 +5669,13 @@ "on-finished": "~2.3.0", "range-parser": "~1.2.0", "statuses": "~1.4.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } } }, "serve-index": { @@ -5526,6 +5807,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=" } } }, @@ -5626,9 +5912,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", @@ -5648,6 +5934,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": { @@ -5718,6 +6011,11 @@ "extend-shallow": "^3.0.0" } }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" + }, "static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", @@ -5824,6 +6122,11 @@ "rimraf": "~2.2.6" } }, + "text-hex": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-0.0.0.tgz", + "integrity": "sha1-V4+8haapJjbkLdF7QdAhjM6esrM=" + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -5919,6 +6222,11 @@ "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=" }, + "triple-beam": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", + "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" + }, "type-is": { "version": "1.6.16", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", @@ -5928,6 +6236,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", @@ -6240,6 +6553,11 @@ "ms": "2.0.0" } }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", @@ -6307,6 +6625,31 @@ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" }, + "winston": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.0.0.tgz", + "integrity": "sha512-7QyfOo1PM5zGL6qma6NIeQQMh71FBg/8fhkSAePqtf5YEi6t+UrPDcUuHhuuUasgso49ccvMEsmqr0GBG2qaMQ==", + "requires": { + "async": "^2.6.0", + "diagnostics": "^1.0.1", + "is-stream": "^1.1.0", + "logform": "^1.9.0", + "one-time": "0.0.4", + "readable-stream": "^2.3.6", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.2.0" + } + }, + "winston-transport": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.2.0.tgz", + "integrity": "sha512-0R1bvFqxSlK/ZKTH86nymOuKv/cT1PQBMuDdA7k7f0S9fM44dNH6bXnuxwXPrN8lefJgtZq08BKdyZ0DZIy/rg==", + "requires": { + "readable-stream": "^2.3.6", + "triple-beam": "^1.2.0" + } + }, "wrap-ansi": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", @@ -6477,6 +6820,11 @@ "strip-ansi": "^4.0.0", "through": "^2.3.6" } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" } } }, @@ -6512,14 +6860,6 @@ "yeoman-environment": "^2.0.5" }, "dependencies": { - "async": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", - "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", - "requires": { - "lodash": "^4.17.10" - } - }, "cross-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", @@ -6556,6 +6896,11 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, "parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.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 67% rename from packages/generators/plugin-generator.js rename to packages/generators/plugin-generator.ts index 032e5d4c1d0..4e3f2bfca9c 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: string) => 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: IYeoman) => ({ 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.ts similarity index 50% rename from packages/generators/remove-generator.js rename to packages/generators/remove-generator.ts index 0a2727e5b8c..2e84d09867e 100644 --- a/packages/generators/remove-generator.js +++ b/packages/generators/remove-generator.ts @@ -1,9 +1,10 @@ -const Generator = require("yeoman-generator"); -const path = require("path"); -const fs = require("fs"); -const { List } = require("@webpack-cli/webpack-scaffold"); +import * as fs from "fs"; +import * as path from "path"; +import Generator = require("yeoman-generator"); -const PROP_TYPES = require("@webpack-cli/utils/prop-types"); +import * as PROP_TYPES from "@webpack-cli/utils/prop-types"; +import { List } from "@webpack-cli/webpack-scaffold"; +import { IWebpackOptions } from "./types"; /** * @@ -14,17 +15,26 @@ const PROP_TYPES = require("@webpack-cli/utils/prop-types"); * */ -module.exports = class RemoveGenerator extends Generator { +export default class RemoveGenerator extends Generator { + private configuration: { + config: { + configName?: string, + topScope?: string[], + webpackOptions?: IWebpackOptions, + }, + }; + private webpackOptions: IWebpackOptions | string; + constructor(args, opts) { super(args, opts); this.configuration = { config: { webpackOptions: {}, - } + }, }; - let configPath = path.resolve(process.cwd(), "webpack.config.js"); - const webpackConfigExists = fs.existsSync(configPath); + let configPath: string = path.resolve(process.cwd(), "webpack.config.js"); + const webpackConfigExists: boolean = fs.existsSync(configPath); if (!webpackConfigExists) { configPath = null; // end the generator stating webpack config not found or to specify the config @@ -32,28 +42,30 @@ module.exports = class RemoveGenerator extends Generator { this.webpackOptions = require(configPath); } - getPropTypes() { + public getPropTypes(): string[] { return Object.keys(this.webpackOptions); } - getModuleLoaders() { - if (this.webpackOptions.module && this.webpackOptions.module.rules) { - return this.webpackOptions.module.rules.map(rule => rule ? rule.loader : null); + public getModuleLoadersNames(): string[] { + if (typeof this.webpackOptions === "object") { + if (this.webpackOptions.module && this.webpackOptions.module.rules) { + return this.webpackOptions.module.rules.map((rule: any) => rule ? rule.loader : null); + } } } - prompting() { - const done = this.async(); - let propValue; + public prompting() { + const done: (_?: void) => void | boolean = this.async(); + let propValue: object | string | boolean; return this.prompt([ List( "propType", "Which property do you want to remove?", - Array.from(this.getPropTypes()) - ) + Array.from(this.getPropTypes()), + ), ]) - .then(({ propType }) => { + .then(({ propType }: { propType: string }) => { if (!PROP_TYPES.has(propType)) { console.log("Invalid webpack config prop"); return; @@ -66,9 +78,9 @@ module.exports = class RemoveGenerator extends Generator { List( "keyType", `Which key do you want to remove from ${propType}?`, - Array.from(propValue) - ) - ]).then(({ keyType }) => { + Array.from(propValue), + ), + ]).then(({ keyType }: { keyType: string }) => { this.configuration.config.webpackOptions[propType] = [ keyType ]; }); } else { @@ -76,24 +88,26 @@ module.exports = class RemoveGenerator extends Generator { List( "keyType", `Which key do you want to remove from ${propType}?`, - Array.from(Object.keys(propValue)) - ) + Array.from(Object.keys(propValue)), + ), ]) - .then(({ keyType }) => { + .then(({ keyType }: { keyType: string }) => { if (propType === "module" && keyType === "rules") { return this.prompt([ List( "rule", "Which loader do you want to remove?", - Array.from(this.getModuleLoaders()) - ) + Array.from(this.getModuleLoadersNames()), + ), ]) - .then(({ rule }) => { - const loaderIndex = this.getModuleLoaders().indexOf(rule); - const loader = this.webpackOptions.module.rules[loaderIndex]; - this.configuration.config.webpackOptions.module = { - rules: [ loader ] - }; + .then(({ rule }: { rule: string }) => { + if (typeof this.webpackOptions === "object") { + const loaderIndex: number = this.getModuleLoadersNames().indexOf(rule); + const loader: object = this.webpackOptions.module.rules[loaderIndex]; + this.configuration.config.webpackOptions.module = { + rules: [ loader ], + }; + } }); } else { // remove the complete prop object if there is only one key @@ -101,7 +115,7 @@ module.exports = class RemoveGenerator extends Generator { this.configuration.config.webpackOptions[propType] = null; } else { this.configuration.config.webpackOptions[propType] = { - [keyType]: null + [keyType]: null, }; } } @@ -111,12 +125,12 @@ module.exports = class RemoveGenerator extends Generator { this.configuration.config.webpackOptions[propType] = null; } }) - .then(() => { + .then((_: void) => { done(); }); } - writing() { + public writing() { this.config.set("configuration", this.configuration); } -}; +} 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/index.ts b/packages/generators/types/index.ts new file mode 100644 index 00000000000..cf28dacbffa --- /dev/null +++ b/packages/generators/types/index.ts @@ -0,0 +1,37 @@ +export interface ISchemaProperties { + additionalProperties?: boolean; + definitions?: object; + properties?: object; + type?: string; +} + +export interface IWebpackOptions { + amd?: any; + bail?: any; + cache?: any; + context?: any; + devServer?: any; + devtool?: any; + entry?: any; + externals?: any; + merge?: any; + mode?: any; + module?: any; + node?: any; + output?: any; + optimization?: any; + parallelism?: any; + performance?: any; + plugins?: any; + profile?: any; + recordsInputPath?: any; + recordsOutputPath?: any; + recordsPath?: any; + resolve?: any; + resolveLoader?: any; + stats?: any; + splitChunks?: any; + target?: any; + watch?: any; + watchOptions?: any; +} 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..1b3b6bf115e --- /dev/null +++ b/packages/generators/types/yeoman-generator.d.ts @@ -0,0 +1,90 @@ +// Type definitions for yeoman-generator +// Project: https://github.com/yeoman/generator +// Definitions by: Kentaro Okuno +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +/// + +interface IYeoman { + public config: { + set: (setProperty: string, setValue: object) => void; + }; + public env: { + adapter: { + promptModule: { + registerPrompt: (promptName: string, promptModule: object) => void; + }, + }, + }; + public props: { + name: string; + }; + public composeWith(namespace: string, options?: object, settings?: IComposeSetting): YeomanGeneratorBase; + public destinationRoot(rootPath?: string): string; + public destinationPath(...path: string[]): string; + public run(target: string, options?: object, done?: Function): IRunEnv; + public runInstall(packager: string, dependencies: string[], options?: object): void; + public on(event: string, listener: Function): this; + public async(): (_?: void) => void | boolean; + public prompt(opt: IPromptOptions[]): Promise<>; + public log(message: string): void; + public npmInstall(packages?: string[] | string, options?: object, cb?: Function): Promise<>; + public spawnCommand(name: string, args?: string[], options?: Object): void; +} + +declare module "yeoman-generator" { + + class YeomanGeneratorBase extends IYeoman { + public config: { + set: (setProperty: string, setValue: object) => void; + }; + public env: { + adapter: { + promptModule: { + registerPrompt: (promptName: string, promptModule: object) => void; + }, + }, + }; + public props: { + name: string; + }; + public composeWith(namespace: string, options?: object, settings?: IComposeSetting): YeomanGeneratorBase; + public destinationRoot(rootPath?: string): string; + public destinationPath(...path: string[]): string; + public run(target: string, options?: object, done?: Function): IRunEnv; + public runInstall(packager: string, dependencies: string[], options?: object): void; + public on(event: string, listener: Function): this; + public async(): (_?: void) => void | boolean; + public prompt(opt: IPromptOptions[]): Promise<>; + public log(message: string): void; + public npmInstall(packages?: string[] | string, options?: object, cb?: Function): Promise<>; + public spawnCommand(name: string, args?: string[], options?: Object): void; + } + + interface IRunEnv extends Object { + on: (event: string, callbackFn: Function) => 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: string) => boolean | string); + when?: ((answers: Object) => boolean) | boolean; + store?: boolean; + filter?: (name: string) => string; + } + + // 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): 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.ts similarity index 63% rename from packages/generators/utils/entry.js rename to packages/generators/utils/entry.ts index 6b1a246d4d0..8fa07d8bd2d 100644 --- a/packages/generators/utils/entry.js +++ b/packages/generators/utils/entry.ts @@ -1,7 +1,10 @@ -"use strict"; +import { InputValidate } from "@webpack-cli/webpack-scaffold"; -const InputValidate = require("@webpack-cli/webpack-scaffold").InputValidate; -const validate = require("./validate"); +import validate from "./validate"; + +interface IEntry extends IYeoman { + usingDefaults?: boolean; +} /** * @@ -12,27 +15,33 @@ const validate = require("./validate"); * @returns {Object} An Object that holds the answers given by the user, later used to scaffold */ -module.exports = (self, answer) => { - let entryIdentifiers; - let result; - if (answer["entryType"] === true) { +export default function entry(self: IEntry, answer: { + entryType: boolean; +}): Promise<{}> { + let entryIdentifiers: string[]; + let result: Promise<{}>; + 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 - ) + validate, + ), ]) - .then(multipleEntriesAnswer => { - let webpackEntryPoint = {}; - entryIdentifiers = multipleEntriesAnswer["multipleEntries"].split(","); - function forEachPromise(obj, fn) { - return obj.reduce((promise, prop) => { - const trimmedProp = prop.trim(); - return promise.then(n => { + .then((multipleEntriesAnswer: { + multipleEntries: string, + }) => { + const webpackEntryPoint: object = {}; + entryIdentifiers = multipleEntriesAnswer.multipleEntries.split(","); + + function forEachPromise(entries: string[], fn: (entryProp: string) => Promise<{} | void>) { + return entries.reduce((promise: Promise<{}>, prop: string) => { + const trimmedProp: string = prop.trim(); + + return promise.then((n: object) => { if (n) { - Object.keys(n).forEach(val => { + Object.keys(n).forEach((val: string) => { if ( n[val].charAt(0) !== "(" && n[val].charAt(0) !== "[" && @@ -51,16 +60,16 @@ module.exports = (self, answer) => { }); }, Promise.resolve()); } - return forEachPromise(entryIdentifiers, entryProp => + return forEachPromise(entryIdentifiers, (entryProp: string): Promise<{} | void> => self.prompt([ InputValidate( `${entryProp}`, `What is the location of "${entryProp}"? [example: ./src/${entryProp}]`, - validate - ) - ]) - ).then(entryPropAnswer => { - Object.keys(entryPropAnswer).forEach(val => { + validate, + ), + ]), + ).then((entryPropAnswer: object) => { + Object.keys(entryPropAnswer).forEach((val: string) => { if ( entryPropAnswer[val].charAt(0) !== "(" && entryPropAnswer[val].charAt(0) !== "[" && @@ -82,10 +91,12 @@ module.exports = (self, answer) => { .prompt([ InputValidate( "singularEntry", - "Which module will be the first to enter the application? [default: ./src/index]" - ) + "Which module will be the first to enter the application? [default: ./src/index]", + ), ]) - .then(singularEntryAnswer => { + .then((singularEntryAnswer: { + singularEntry: string, + }) => { let { singularEntry } = singularEntryAnswer; if (singularEntry.indexOf("\"") >= 0) { singularEntry = singularEntry.replace(/"/g, "'"); @@ -97,4 +108,4 @@ module.exports = (self, answer) => { }); } return result; -}; +} diff --git a/packages/generators/utils/module.js b/packages/generators/utils/module.js deleted file mode 100644 index 3e7f286197b..00000000000 --- a/packages/generators/utils/module.js +++ /dev/null @@ -1,20 +0,0 @@ -"use strict"; - -/** - * - * 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 = _ => { - return { - test: `${new RegExp(/\.js$/)}`, - include: ["path.resolve(__dirname, 'src')"], - loader: "'babel-loader'", - options: { - presets: [["'env'", { "'modules'": false }]], - plugins: ["'syntax-dynamic-import'"] - } - }; -}; diff --git a/packages/generators/utils/module.ts b/packages/generators/utils/module.ts new file mode 100644 index 00000000000..34110dc131a --- /dev/null +++ b/packages/generators/utils/module.ts @@ -0,0 +1,36 @@ +interface IModule { + include: string[]; + loader: string; + options: { + plugins: string[]; + presets: Array>; + }; + 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.ts similarity index 71% rename from packages/generators/utils/plugins.js rename to packages/generators/utils/plugins.ts index 64f0c32fa06..2c8a51fe07d 100644 --- a/packages/generators/utils/plugins.js +++ b/packages/generators/utils/plugins.ts @@ -1,14 +1,11 @@ -"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 = _ => { +export default function(): string[] { return ["new UglifyJSPlugin()"]; -}; +} diff --git a/packages/generators/utils/tooltip.js b/packages/generators/utils/tooltip.ts similarity index 90% rename from packages/generators/utils/tooltip.js rename to packages/generators/utils/tooltip.ts index afd530f765f..ad0fa1c575a 100644 --- a/packages/generators/utils/tooltip.js +++ b/packages/generators/utils/tooltip.ts @@ -1,4 +1,3 @@ -"use strict"; /** * * Tooltip object that consists of tooltips for various of @@ -7,17 +6,19 @@ * @returns {Object} An Object that consists of tooltip methods to be invoked */ -module.exports = { - uglify: _ => { +export default { + cssPlugin: (_?: void): string => { 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: (_?: void): string => { return `/* * SplitChunksPlugin is enabled by default and replaced * deprecated CommonsChunkPlugin. It automatically identifies modules which @@ -31,17 +32,8 @@ 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: (_?: void): 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, @@ -56,5 +48,15 @@ module.exports = { * https://github.com/jonathantneal/precss * */`; - } + }, + + uglify: (_?: void): 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.ts similarity index 82% rename from packages/generators/utils/validate.js rename to packages/generators/utils/validate.ts index e6d71a0cc6c..b5090a45bad 100644 --- a/packages/generators/utils/validate.js +++ b/packages/generators/utils/validate.ts @@ -1,5 +1,3 @@ -"use strict"; - /** * * Validates an input to check if an input is provided @@ -8,10 +6,10 @@ * @returns {String | Boolean } Returns truthy if its long enough * Or a string if the user hasn't written anything */ -module.exports = value => { +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/init/.gitignore b/packages/init/.gitignore index a6c7c2852d0..6d6c6e81408 100644 --- a/packages/init/.gitignore +++ b/packages/init/.gitignore @@ -1 +1,2 @@ *.js +types/*.js diff --git a/packages/webpack-scaffold/index.ts b/packages/webpack-scaffold/index.ts index d890e839edc..4ca0093619e 100755 --- a/packages/webpack-scaffold/index.ts +++ b/packages/webpack-scaffold/index.ts @@ -1,17 +1,23 @@ import * as jscodeshift from "jscodeshift"; -interface IScaffoldBaseObject { - type: string; +export interface IScaffoldBaseObject { + type?: string; name: string; message: string; + choices?: ((answers: Object) => any) | any[]; + default?: string | number | string[] | number[] | ((answers: Object) => (string | number | string[] | number[])); + validate?: ((input: string) => boolean | string); + when?: ((answers: Object) => boolean) | boolean; + store?: boolean; + filter?: (name: string) => string; } -interface IInquirerList extends IScaffoldBaseObject { +export interface IInquirerList extends IScaffoldBaseObject { choices?: string[]; } -interface IInquirerInput extends IScaffoldBaseObject { - validate?: Function; +export interface IInquirerInput extends IScaffoldBaseObject { + validate?: (input: string) => string | boolean; } export function createArrowFunction(value: Function): string { @@ -95,7 +101,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: string) => string | boolean): IInquirerInput { return { message, name, diff --git a/tslint.json b/tslint.json index 0c4e7909be6..a021a8859b6 100644 --- a/tslint.json +++ b/tslint.json @@ -15,6 +15,12 @@ ], "no-console": { "severity": "warning" + }, + "max-line-length": { + "options": { + "limit": 120 + }, + "severity": "warn" } }, "rulesDirectory": [],