From 99304c41e7d1f30f0f44cc5eb4c646c47deb19bb Mon Sep 17 00:00:00 2001 From: "devid.farinelli@gmail.com" Date: Fri, 19 Apr 2019 15:09:21 +0200 Subject: [PATCH 01/31] chore(utils): fix deprecated babylon --- packages/utils/package-lock.json | 6 +++--- packages/utils/package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/utils/package-lock.json b/packages/utils/package-lock.json index 30e688d29c6..313aec15a47 100644 --- a/packages/utils/package-lock.json +++ b/packages/utils/package-lock.json @@ -121,9 +121,9 @@ "dev": true }, "@types/prettier": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-1.15.2.tgz", - "integrity": "sha512-XIB0ZCaFZmWUHAa9dBqP5UKXXHwuukmVlP+XcyU94dui2k+l2lG+CHAbt2ffenHPUqoIs5Beh8Pdf2YEq/CZ7A==", + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-1.16.1.tgz", + "integrity": "sha512-db6pZL5QY3JrlCHBhYQzYDci0xnoDuxfseUuguLRr3JNk+bnCfpkK6p8quiUDyO8A0vbpBKkk59Fw125etrNeA==", "dev": true }, "@types/through": { diff --git a/packages/utils/package.json b/packages/utils/package.json index 3a243bbb1ad..fc26be79aa3 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -31,7 +31,7 @@ "@types/log-symbols": "^2.0.0", "@types/node": "^10.12.9", "@types/p-each-series": "^1.0.0", - "@types/prettier": "^1.15.0", + "@types/prettier": "^1.16.1", "@types/yeoman-generator": "^3.1.2", "typescript": "^3.1.6" }, From 0a648f7d461a659c81971f0af6428a63614e0aa6 Mon Sep 17 00:00:00 2001 From: "devid.farinelli@gmail.com" Date: Fri, 19 Apr 2019 15:11:11 +0200 Subject: [PATCH 02/31] feat(webpack-scaffold): adds Input defaults, doc & tests --- packages/webpack-scaffold/README.md | 51 ++++++++++--------- .../__snapshots__/index.test.ts.snap | 11 ++++ .../webpack-scaffold/__tests__/index.test.ts | 14 +++++ packages/webpack-scaffold/index.ts | 12 ++++- 4 files changed, 63 insertions(+), 25 deletions(-) diff --git a/packages/webpack-scaffold/README.md b/packages/webpack-scaffold/README.md index a0a92441b40..33282ca8cf9 100755 --- a/packages/webpack-scaffold/README.md +++ b/packages/webpack-scaffold/README.md @@ -11,15 +11,20 @@ npm i -D webpack-cli @webpack-cli/webpack-scaffold ``` # API - -- [parseValue](#parsevalue) -- [createArrowFunction](#createarrowfunction) -- [createRegularFunction](#createregularfunction) -- [createDynamicPromise](#createdynamicpromise) -- [createAssetFilterFunction](#createassetfilterfunction) -- [createExternalFunction](#createexternalfunction) -- [createRequire](#createrequire) -- [Inquirer](#inquirer) - [List](#list) - [RawList](#rawlist) - [CheckList](#checklist) - [Input](#input) - [InputValidate](#inputvalidate) - [Confirm](#confirm) +- [parseValue](#parsevalue) +- [createArrowFunction](#createarrowfunction) +- [createRegularFunction](#createregularfunction) +- [createDynamicPromise](#createdynamicpromise) +- [createAssetFilterFunction](#createassetfilterfunction) +- [createExternalFunction](#createexternalfunction) +- [createRequire](#createrequire) +- [Inquirer](#inquirer) + - [List](#list) + - [RawList](#rawlist) + - [CheckList](#checklist) + - [Input](#input) + - [InputValidate](#inputvalidate) + - [Confirm](#confirm) ## parseValue @@ -163,38 +168,38 @@ CheckList("entry", "what kind of entry do you want?", ["Array", "Function"]); ### Input -Param: `name, message` +Param: `name, message, [default]` Creates an Input from Inquirer ```js const Input = require("@webpack-cli/webpack-scaffold").Input; -Input("entry", "what is your entry point?"); +Input('entry', 'what is your entry point?', 'src/index') ``` ### InputValidate -Param: `name, message, validate` +Param: `name, message, [validate, default]` Creates an Input from Inquirer ```js -const InputValidate = require("@webpack-cli/webpack-scaffold").InputValidate; - -const validation = value => { - if (value.length > 4) { - return true; - } else { - return "Wow, that was short!"; - } -}; -InputValidate("entry", "what is your entry point?", validation); +const InputValidate = require('@webpack-cli/webpack-scaffold').InputValidate; + +const validation = (value) => { + if(value.length > 4) { + return true; + } else { + return 'Wow, that was short!' + } +} +InputValidate('entry', 'what is your entry point?', validation, 'src/index') ``` ### Confirm -Param: `name, message, default` +Param: `name, message, [default]` Creates an Input from Inquirer diff --git a/packages/webpack-scaffold/__tests__/__snapshots__/index.test.ts.snap b/packages/webpack-scaffold/__tests__/__snapshots__/index.test.ts.snap index 0304203ef64..0e06b120337 100755 --- a/packages/webpack-scaffold/__tests__/__snapshots__/index.test.ts.snap +++ b/packages/webpack-scaffold/__tests__/__snapshots__/index.test.ts.snap @@ -2,6 +2,17 @@ exports[`utils Inquirer should make an Input object with validation 1`] = ` Object { + "default": undefined, + "message": "what is your plugin?", + "name": "plugins", + "type": "input", + "validate": [Function], +} +`; + +exports[`utils Inquirer should make an Input object with validation and default value 1`] = ` +Object { + "default": "my-plugin", "message": "what is your plugin?", "name": "plugins", "type": "input", diff --git a/packages/webpack-scaffold/__tests__/index.test.ts b/packages/webpack-scaffold/__tests__/index.test.ts index 0db475e179a..586bbcd9a58 100755 --- a/packages/webpack-scaffold/__tests__/index.test.ts +++ b/packages/webpack-scaffold/__tests__/index.test.ts @@ -76,6 +76,15 @@ describe("utils", () => { }); it("should make an Input object", () => { expect(utils.Input("plugins", "what is your plugin?")).toEqual({ + default: undefined, + message: "what is your plugin?", + name: "plugins", + type: "input", + }); + }); + it("should make an Input object", () => { + expect(utils.Input("plugins", "what is your plugin?", "my-plugin")).toEqual({ + default: "my-plugin", message: "what is your plugin?", name: "plugins", type: "input", @@ -102,5 +111,10 @@ describe("utils", () => { utils.InputValidate("plugins", "what is your plugin?", () => true), ).toMatchSnapshot(); }); + it("should make an Input object with validation and default value", () => { + expect( + utils.InputValidate("plugins", "what is your plugin?", () => true, "my-plugin"), + ).toMatchSnapshot(); + }); }); }); diff --git a/packages/webpack-scaffold/index.ts b/packages/webpack-scaffold/index.ts index 0a4ceb38d5c..6477030c331 100755 --- a/packages/webpack-scaffold/index.ts +++ b/packages/webpack-scaffold/index.ts @@ -76,16 +76,24 @@ export function CheckList(name: string, message: string, choices: string[]): Gen }; } -export function Input(name: string, message: string): Generator.Question { +export function Input(name: string, message: string, defaultChoice?: string): Generator.Question { return { + default: defaultChoice, message, name, type: "input" }; } -export function InputValidate(name: string, message: string, cb?: (input: string) => string | boolean): Generator.Question { +export function InputValidate( + name: string, + message: string, + cb?: (input: string) => string | boolean, + defaultChoice?: string, + ): Generator.Question { + return { + default: defaultChoice, message, name, type: "input", From 17e45119c77c75fe8e299c7f07647864559e83d3 Mon Sep 17 00:00:00 2001 From: "devid.farinelli@gmail.com" Date: Fri, 19 Apr 2019 15:16:24 +0200 Subject: [PATCH 03/31] misc(generators): refactor utils --- packages/generators/utils/entry.ts | 77 ++++++++---- packages/generators/utils/plugins.ts | 11 -- packages/generators/utils/style.ts | 172 +++++++++++++++++++++++++++ packages/utils/run-prettier.ts | 2 +- packages/utils/scaffold.ts | 42 +++---- 5 files changed, 246 insertions(+), 58 deletions(-) delete mode 100644 packages/generators/utils/plugins.ts create mode 100644 packages/generators/utils/style.ts diff --git a/packages/generators/utils/entry.ts b/packages/generators/utils/entry.ts index 47762928337..5f136e0b940 100644 --- a/packages/generators/utils/entry.ts +++ b/packages/generators/utils/entry.ts @@ -1,5 +1,5 @@ import * as Generator from "yeoman-generator"; -import { InputValidate } from "@webpack-cli/webpack-scaffold"; +import { Input, InputValidate } from "@webpack-cli/webpack-scaffold"; import validate from "./validate"; @@ -16,22 +16,18 @@ interface CustomGenerator extends Generator { * @returns {Object} An Object that holds the answers given by the user, later used to scaffold */ -export default function entry( - self: CustomGenerator, - answer: { - entryType: boolean; - } -): Promise { +export default function entry(self: CustomGenerator, multiEntries: boolean): Promise<{}> { let entryIdentifiers: string[]; - let result: Promise; - if (answer.entryType) { + let result: Promise<{}>; + if (multiEntries) { result = self .prompt([ InputValidate( "multipleEntries", - "Type the names you want for your modules (entry files), separated by comma [example: app,vendor]", - validate - ) + "Type the names you want for your modules (entry files) separated by comma", + validate, + "pageOne, pageTwo", + ), ]) .then( (multipleEntriesAnswer: { multipleEntries: string }): Promise => { @@ -91,27 +87,56 @@ export default function entry( !entryPropAnswer[val].includes("path") && !entryPropAnswer[val].includes("process") ) { - entryPropAnswer[val] = `\'${entryPropAnswer[val].replace(/"|'/g, "")}\'`; + n[val] = `\'./${n[val].replace(/"|'/g, "").concat(".js")}\'`; } - webpackEntryPoint[val] = entryPropAnswer[val]; - } - ); - return webpackEntryPoint; + webpackEntryPoint[val] = n[val]; + }); + } else { + n = {}; + } + return fn(trimmedProp); + }); + }, Promise.resolve()); + } + return forEachPromise(entryIdentifiers, (entryProp: string): Promise<{} | void> => + self.prompt([ + InputValidate( + `${entryProp}`, + `What is the location of "${entryProp}"?`, + validate, + `./src/${entryProp}`, + ), + ]), + ).then((entryPropAnswer: object): object => { + Object.keys(entryPropAnswer).forEach((val: string): void => { + 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")}\'`; } ); } ); } else { result = self - .prompt([InputValidate("singularEntry", "Which will be your application entry point? (src/index)")]) - .then( - (singularEntryAnswer: { singularEntry: string }): string => { - let { singularEntry } = singularEntryAnswer; - singularEntry = `\'${singularEntry.replace(/"|'/g, "")}\'`; - if (singularEntry.length <= 0) { - self.usingDefaults = true; - } - return singularEntry; + .prompt([ + Input( + "singularEntry", + "Which will be your application entry point?", + "src/index", + ), + ]) + .then((singularEntryAnswer: { + singularEntry: string, + }): string => { + let { singularEntry } = singularEntryAnswer; + singularEntry = `\'./${singularEntry.replace(/"|'/g, "").concat(".js")}\'`; + if (singularEntry.length <= 0) { + self.usingDefaults = true; } ); } diff --git a/packages/generators/utils/plugins.ts b/packages/generators/utils/plugins.ts deleted file mode 100644 index 6dedb1fab9e..00000000000 --- a/packages/generators/utils/plugins.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * - * Callable function with the initial plugins - * - * @returns {Function} An function that returns an array - * that consists of terser-webpack-plugin - */ - -export default function(): string[] { - return ["new TerserPlugin()"]; -} diff --git a/packages/generators/utils/style.ts b/packages/generators/utils/style.ts new file mode 100644 index 00000000000..9d4d428ace8 --- /dev/null +++ b/packages/generators/utils/style.ts @@ -0,0 +1,172 @@ +import tooltip from "./tooltip"; + +export default function style(self, stylingType) { + const ExtractUseProps = []; + let regExpForStyles = null; + switch (stylingType) { + case "SASS": + self.dependencies.push( + "sass-loader", + "node-sass", + "style-loader", + "css-loader", + ); + regExpForStyles = `${new RegExp(/\.(scss|css)$/)}`; + if (self.isProd) { + ExtractUseProps.push( + { + loader: "'css-loader'", + options: { + sourceMap: true, + }, + }, + { + loader: "'sass-loader'", + options: { + sourceMap: true, + }, + }, + ); + } else { + ExtractUseProps.push( + { + loader: "'style-loader'", + }, + { + loader: "'css-loader'", + }, + { + loader: "'sass-loader'", + }, + ); + } + break; + case "LESS": + regExpForStyles = `${new RegExp(/\.(less|css)$/)}`; + self.dependencies.push( + "less-loader", + "less", + "style-loader", + "css-loader", + ); + if (self.isProd) { + ExtractUseProps.push( + { + loader: "'css-loader'", + options: { + sourceMap: true, + }, + }, + { + loader: "'less-loader'", + options: { + sourceMap: true, + }, + }, + ); + } else { + ExtractUseProps.push( + { + loader: "'css-loader'", + options: { + sourceMap: true, + }, + }, + { + loader: "'less-loader'", + options: { + sourceMap: true, + }, + }, + ); + } + break; + case "PostCSS": + self.configuration.config.topScope.push( + tooltip.postcss(), + "const autoprefixer = require('autoprefixer');", + "const precss = require('precss');", + "\n", + ); + self.dependencies.push( + "style-loader", + "css-loader", + "postcss-loader", + "precss", + "autoprefixer", + ); + regExpForStyles = `${new RegExp(/\.css$/)}`; + if (self.isProd) { + ExtractUseProps.push( + { + loader: "'css-loader'", + options: { + importLoaders: 1, + sourceMap: true, + }, + }, + { + loader: "'postcss-loader'", + options: { + plugins: `function () { + return [ + precss, + autoprefixer + ]; + }`, + }, + }, + ); + } else { + ExtractUseProps.push( + { + loader: "'style-loader'", + }, + { + loader: "'css-loader'", + options: { + importLoaders: 1, + sourceMap: true, + }, + }, + { + loader: "'postcss-loader'", + options: { + plugins: `function () { + return [ + precss, + autoprefixer + ]; + }`, + }, + }, + ); + } + break; + case "CSS": + self.dependencies.push("style-loader", "css-loader"); + regExpForStyles = `${new RegExp(/\.css$/)}`; + if (self.isProd) { + ExtractUseProps.push({ + loader: "'css-loader'", + options: { + sourceMap: true, + }, + }); + } else { + ExtractUseProps.push( + { + loader: "'style-loader'", + options: { + sourceMap: true, + }, + }, + { + loader: "'css-loader'", + }, + ); + } + break; + } + return { ExtractUseProps, regExpForStyles }; +} diff --git a/packages/utils/run-prettier.ts b/packages/utils/run-prettier.ts index 794f86923b8..254f94029d7 100644 --- a/packages/utils/run-prettier.ts +++ b/packages/utils/run-prettier.ts @@ -19,7 +19,7 @@ export default function runPrettier(outputPath: string, source: string, cb?: Fun try { prettySource = prettier.format(source, { filepath: outputPath, - parser: "babylon", + parser: "babel", singleQuote: true, tabWidth: 1, useTabs: true, diff --git a/packages/utils/scaffold.ts b/packages/utils/scaffold.ts index 2d2ece8a390..3fc8bf20c0f 100644 --- a/packages/utils/scaffold.ts +++ b/packages/utils/scaffold.ts @@ -81,26 +81,28 @@ export default function runTransform(transformConfig: TransformConfig, action: s configurationName = "webpack." + config.configName + ".js"; } - const projectRoot = findProjectRoot(); - const outputPath: string = initActionNotDefined - ? transformConfig.configPath - : path.join(projectRoot || process.cwd(), configurationName); - const source: string = ast.toSource({ - quote: "single" - }); - runPrettier(outputPath, source); - } - ) - .catch( - (err: Error): void => { - console.error(err.message ? err.message : err); - } - ); - } - ); - let successMessage: string = `Congratulations! Your new webpack configuration file has been created!\n`; + const projectRoot = findProjectRoot(); + const outputPath: string = initActionNotDefined + ? transformConfig.configPath + : path.join(projectRoot || process.cwd(), configurationName); + const source: string = ast.toSource({ + quote: "single", + }); + runPrettier(outputPath, source); + + }) + .catch((err: IError) => { + console.error(err.message ? err.message : err); + }); + }); + let successMessage: string = + chalk.green(`Congratulations! Your new webpack configuration file has been created!\n\n`) + + `You can now run ${chalk.green("npm run start")} to run your project!\n\n`; + if (initActionNotDefined && transformConfig.config.item) { - successMessage = `Congratulations! ${transformConfig.config.item} has been ${action}ed!\n`; + successMessage = chalk.green(`Congratulations! ${ + transformConfig.config.item + } has been ${action}ed!\n`); } - process.stdout.write("\n" + chalk.green(successMessage)); + process.stdout.write(`\n${successMessage}`); } From d5748461fe8b298b918bab00b681d3d4388c8fd0 Mon Sep 17 00:00:00 2001 From: "devid.farinelli@gmail.com" Date: Fri, 19 Apr 2019 15:17:18 +0200 Subject: [PATCH 04/31] misc(generators): refactor init-generator --- packages/generators/add-generator.ts | 2 +- packages/generators/init-generator.ts | 574 +++++++++++--------------- 2 files changed, 235 insertions(+), 341 deletions(-) diff --git a/packages/generators/add-generator.ts b/packages/generators/add-generator.ts index bef3c32afd3..d9d7ec7d79f 100644 --- a/packages/generators/add-generator.ts +++ b/packages/generators/add-generator.ts @@ -140,7 +140,7 @@ export default class AddGenerator extends Generator { .then( (entryTypeAnswer: { entryType: boolean }): Promise => { // Ask different questions for entry points - return entryQuestions(self, entryTypeAnswer); + return entryQuestions(self, entryTypeAnswer.entryType); } ) .then( diff --git a/packages/generators/init-generator.ts b/packages/generators/init-generator.ts index 715b44e8797..d3a458307bb 100644 --- a/packages/generators/init-generator.ts +++ b/packages/generators/init-generator.ts @@ -1,7 +1,9 @@ + import chalk from "chalk"; import * as logSymbols from "log-symbols"; import * as Generator from "yeoman-generator"; import * as Inquirer from "inquirer"; +import * as path from "path"; import { getPackageManager } from "@webpack-cli/utils/package-manager"; import { Confirm, Input, List } from "@webpack-cli/webpack-scaffold"; @@ -9,7 +11,7 @@ import { Confirm, Input, List } from "@webpack-cli/webpack-scaffold"; import { WebpackOptions } from "./types"; import entryQuestions from "./utils/entry"; import getBabelPlugin from "./utils/module"; -import getDefaultPlugins from "./utils/plugins"; +import styleQuestionHandler from "./utils/style"; import tooltip from "./utils/tooltip"; /** @@ -35,20 +37,98 @@ export default class InitGenerator extends Generator { public constructor(args, opts) { super(args, opts); - this.isProd = false; - (this.usingDefaults = false), - (this.dependencies = [ - "webpack", - "webpack-cli", - "terser-webpack-plugin", - "babel-plugin-syntax-dynamic-import" - ]); + this.usingDefaults = false, + this.isProd = this.usingDefaults ? true : false; + + this.dependencies = [ + "webpack", + "webpack-cli", + "babel-plugin-syntax-dynamic-import", + ]; + if (this.isProd) { + this.dependencies.push("terser-webpack-plugin"); + } else { + this.dependencies.push("webpack-dev-server"); + } + this.configuration = { config: { + configName: this.isProd ? "prod" : "config", topScope: [], - webpackOptions: {} - } + // tslint:disable: object-literal-sort-keys + webpackOptions: { + mode: this.isProd ? "'production'" : "'development'", + entry: undefined, + output: undefined, + plugins: [], + module: { + rules: [], + }, + }, + // tslint:enable: object-literal-sort-keys + }, }; + + // add splitChunks options for transparency + // defaults coming from: https://webpack.js.org/plugins/split-chunks-plugin/#optimization-splitchunks + this.configuration.config.topScope.push( + "const path = require('path');", + "const webpack = require('webpack');", + "\n", + tooltip.splitChunks(), + ); + + if (this.isProd) { + this.configuration.config.topScope.push( + tooltip.terser(), + "const TerserPlugin = require('terser-webpack-plugin');", + "\n", + ); + } + + this.configuration.config.webpackOptions.plugins.push( + "new webpack.ProgressPlugin()", + ); + + if (this.isProd) { + this.configuration.config.webpackOptions.plugins.push( + "new TerserPlugin()", + ); + } + + let optimizationObj; + + if (!this.isProd) { + optimizationObj = { + splitChunks: { + chunks: "'all'", + }, + }; + } else { + optimizationObj = { + splitChunks: { + cacheGroups: { + vendors: { + priority: -10, + test: "/[\\\\/]node_modules[\\\\/]/", + }, + }, + chunks: "'async'", + minChunks: 1, + minSize: 30000, + // for production name is recommended to be off + name: !this.isProd, + }, + }; + } + + this.configuration.config.webpackOptions.optimization = optimizationObj; + + if (!this.isProd) { + this.configuration.config.webpackOptions.devServer = { + open: true, + }; + } } // eslint-disable-next-line @@ -59,363 +139,177 @@ export default class InitGenerator extends Generator { let ExtractUseProps: object[]; 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") + - "\n" + `\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")}\n`, ); process.stdout.write( - logSymbols.info + - chalk.blue(" INFO ") + - "Alternatively, run `webpack(-cli) --help` for usage info." + - "\n\n" + `${logSymbols.info}${chalk.blue(" INFO ")} ` + + `Alternatively, run "webpack(-cli) --help" for usage info\n\n`, ); - this.configuration.config.webpackOptions.module = { - rules: [] - }; - this.configuration.config.topScope.push( - "const webpack = require('webpack')", - "const path = require('path')", - "\n" - ); - - return this.prompt([Confirm("entryType", "Will your application have multiple bundles?", false)]) - .then( - (entryTypeAnswer: { entryType: boolean }): Promise => { - // Ask different questions for entry points - return entryQuestions(self, entryTypeAnswer); - } + return this.prompt([ + Confirm("multiEntries", "Will your application have multiple bundles?", false), + ]) + .then((multiEntriesAnswer: any) => + entryQuestions(self, multiEntriesAnswer.multiEntries), ) - .then( - (entryOptions: object | string): Promise<{}> => { - if (typeof entryOptions === "string" && entryOptions.length > 0) { - if (entryOptions !== "\"\"" && entryOptions !== "\'\'") { - this.configuration.config.webpackOptions.entry = entryOptions; - } - } - - return this.prompt([ - Input("outputType", "In which folder do you want to store your generated bundles? (dist):") - ]); + .then((entryOption: object | string) => { + if (typeof entryOption === "string" && entryOption.length > 0) { + this.configuration.config.webpackOptions.entry = `${entryOption}`; } + }) + .then((_: void) => + this.prompt([ + Input( + "outputDir", + "In which folder do you want to store your generated bundles?", + "dist", + ), + ]), ) - .then( - (outputTypeAnswer: { outputType: string }): void => { - // 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 (!this.configuration.config.webpackOptions.entry && !this.usingDefaults) { - this.configuration.config.webpackOptions.output = { - chunkFilename: "'[name].[chunkhash].js'", - filename: "'[name].[chunkhash].js'" - }; - } else if (!this.usingDefaults) { - this.configuration.config.webpackOptions.output = { - filename: "'[name].[chunkhash].js'" - }; - } - if (!this.usingDefaults && outputTypeAnswer.outputType.length) { - this.configuration.config.webpackOptions.output.path = `path.resolve(__dirname, '${ - outputTypeAnswer.outputType - }')`; - } + .then((outputDirAnswer: { + outputDir: 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 ( + !this.configuration.config.webpackOptions.entry && + !this.usingDefaults + ) { + this.configuration.config.webpackOptions.output = { + chunkFilename: "'[name].[chunkhash].js'", + filename: "'[name].[chunkhash].js'", + }; + } else if (!this.usingDefaults) { + this.configuration.config.webpackOptions.output = { + filename: "'[name].[chunkhash].js'", + }; } - ) - .then( - (): Promise => { - this.isProd = this.usingDefaults ? true : false; - this.configuration.config.configName = this.isProd ? "prod" : "config"; - if (!this.isProd) { - this.configuration.config.webpackOptions.mode = "'development'"; - } - this.configuration.config.webpackOptions.plugins = this.isProd ? [] : getDefaultPlugins(); - return this.prompt([Confirm("babelConfirm", "Will you be using ES2015?")]); + if (!this.usingDefaults && outputDirAnswer.outputDir.length) { + this.configuration.config.webpackOptions.output.path = + `path.resolve(__dirname, '${outputDirAnswer.outputDir}')`; } + }) + .then((_: void) => + this.prompt([ + Confirm("useBabel", "Will you be using ES2015?"), + ]), ) - .then( - (babelConfirmAnswer: { babelConfirm: boolean }): void => { - if (babelConfirmAnswer.babelConfirm) { - this.configuration.config.webpackOptions.module.rules.push(getBabelPlugin()); - this.dependencies.push("babel-loader", "@babel/core", "@babel/preset-env"); - } + .then((useBabelAnswer: { + useBabel: boolean; + }) => { + if (useBabelAnswer.useBabel) { + this.configuration.config.webpackOptions.module.rules.push( + getBabelPlugin(), + ); + this.dependencies.push( + "babel-loader", + "@babel/core", + "@babel/preset-env", + ); } + }) + .then((_: void) => + this.prompt([ + List("stylingType", "Will you use one of the below CSS solutions?", [ + "No", + "CSS", + "SASS", + "LESS", + "PostCSS", + ]), + ])) + .then((stylingTypeAnswer: { + stylingType: string; + }) => + ({ ExtractUseProps, regExpForStyles } = styleQuestionHandler(self, stylingTypeAnswer.stylingType)), ) - .then( - (): Promise => { + .then((): Promise => { + if (this.isProd) { + // Ask if the user wants to use extractPlugin return this.prompt([ - List("stylingType", "Will you use one of the below CSS solutions?", [ - "No", - "CSS", - "SASS", - "LESS", - "PostCSS" - ]) + Input( + "useExtractPlugin", + "If you want to bundle your CSS files, what will you name the bundle? (press enter to skip)", + ), ]); } - ) - .then( - (stylingTypeAnswer: { stylingType: string }): void => { - ExtractUseProps = []; - switch (stylingTypeAnswer.stylingType) { - case "SASS": - this.dependencies.push("sass-loader", "node-sass", "style-loader", "css-loader"); - regExpForStyles = `${new RegExp(/\.(scss|css)$/)}`; - if (this.isProd) { - ExtractUseProps.push( - { - loader: "'css-loader'", - options: { - sourceMap: true - } - }, - { - loader: "'sass-loader'", - options: { - sourceMap: true - } - } - ); - } else { - ExtractUseProps.push( - { - loader: "'style-loader'" - }, - { - loader: "'css-loader'" - }, - { - loader: "'sass-loader'" - } - ); - } - break; - case "LESS": - regExpForStyles = `${new RegExp(/\.(less|css)$/)}`; - this.dependencies.push("less-loader", "less", "style-loader", "css-loader"); - if (this.isProd) { - ExtractUseProps.push( - { - loader: "'css-loader'", - options: { - sourceMap: true - } - }, - { - loader: "'less-loader'", - options: { - sourceMap: true - } - } - ); - } else { - ExtractUseProps.push( - { - loader: "'css-loader'", - options: { - sourceMap: true - } - }, - { - loader: "'less-loader'", - options: { - sourceMap: true - } - } - ); - } - break; - case "PostCSS": - this.configuration.config.topScope.push( - tooltip.postcss(), - "const autoprefixer = require('autoprefixer');", - "const precss = require('precss');", - "\n" - ); - this.dependencies.push( - "style-loader", - "css-loader", - "postcss-loader", - "precss", - "autoprefixer" - ); - regExpForStyles = `${new RegExp(/\.css$/)}`; - if (this.isProd) { - ExtractUseProps.push( - { - loader: "'css-loader'", - options: { - importLoaders: 1, - sourceMap: true - } - }, - { - loader: "'postcss-loader'", - options: { - plugins: `function () { - return [ - precss, - autoprefixer - ]; - }` - } - } - ); - } else { - ExtractUseProps.push( - { - loader: "'style-loader'" - }, - { - loader: "'css-loader'", - options: { - importLoaders: 1, - sourceMap: true - } - }, - { - loader: "'postcss-loader'", - options: { - plugins: `function () { - return [ - precss, - autoprefixer - ]; - }` - } - } - ); - } - break; - case "CSS": - this.dependencies.push("style-loader", "css-loader"); - regExpForStyles = `${new RegExp(/\.css$/)}`; - if (this.isProd) { - ExtractUseProps.push({ - loader: "'css-loader'", - options: { - sourceMap: true - } - }); - } else { - ExtractUseProps.push( - { - loader: "'style-loader'", - options: { - sourceMap: true - } - }, - { - loader: "'css-loader'" - } - ); - } - break; - default: - regExpForStyles = null; - } - } - ) - .then( - (): Promise => { + }) + .then((useExtractPluginAnswer: { + useExtractPlugin: string; + }) => { + if (regExpForStyles) { 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)" - ) - ]); - } - } - ) - .then( - (extractPluginAnswer: { extractPlugin: string }): void => { - if (regExpForStyles) { - if (this.isProd) { - 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 as string[]).push( - // TODO: use [contenthash] after it is supported - `new MiniCssExtractPlugin({ filename:'${cssBundleName}.[chunkhash].css' })` - ); - } else { - (this.configuration.config.webpackOptions.plugins as string[]).push( - "new MiniCssExtractPlugin({ filename:'style.css' })" - ); - } - - ExtractUseProps.unshift({ - loader: "MiniCssExtractPlugin.loader" - }); - - const moduleRulesObj = { - test: regExpForStyles, - use: ExtractUseProps - }; - - this.configuration.config.webpackOptions.module.rules.push(moduleRulesObj); - this.configuration.config.topScope.push( - "const MiniCssExtractPlugin = require('mini-css-extract-plugin');", - "\n" + const cssBundleName: string = useExtractPluginAnswer.useExtractPlugin; + this.dependencies.push("mini-css-extract-plugin"); + this.configuration.config.topScope.push( + tooltip.cssPlugin(), + "const MiniCssExtractPlugin = require('mini-css-extract-plugin');", + "\n", + ); + if (cssBundleName.length !== 0) { + this.configuration.config.webpackOptions.plugins.push( + // TODO: use [contenthash] after it is supported + `new MiniCssExtractPlugin({ filename:'${cssBundleName}.[chunkhash].css' })`, ); } else { - const moduleRulesObj: { - test: string; - use: object[]; - } = { - test: regExpForStyles, - use: ExtractUseProps - }; - - this.configuration.config.webpackOptions.module.rules.push(moduleRulesObj); + this.configuration.config.webpackOptions.plugins.push( + "new MiniCssExtractPlugin({ filename:'style.css' })", + ); } + + ExtractUseProps.unshift({ + loader: "MiniCssExtractPlugin.loader", + }); } - // add splitChunks options for transparency - // defaults coming from: https://webpack.js.org/plugins/split-chunks-plugin/#optimization-splitchunks - this.configuration.config.topScope.push(tooltip.splitChunks()); - this.configuration.config.webpackOptions.optimization = { - splitChunks: { - cacheGroups: { - vendors: { - priority: -10, - test: "/[\\\\/]node_modules[\\\\/]/" - } - }, - chunks: "'async'", - minChunks: 1, - minSize: 30000, - // for production name is recommended to be off - name: !this.isProd - } - }; - done(); + + this.configuration.config.webpackOptions.module.rules.push( + { + test: regExpForStyles, + use: ExtractUseProps, + }, + ); } - ); + + done(); + }); } - public installPlugins(): void { - if (this.isProd) { - this.dependencies = this.dependencies.filter((p: string): boolean => p !== "terser-webpack-plugin"); - } else { - this.configuration.config.topScope.push( - tooltip.terser(), - "const TerserPlugin = require('terser-webpack-plugin');", - "\n" - ); - } + + public installPlugins() { const packager = getPackageManager(); const opts: { - dev?: boolean; - "save-dev"?: boolean; - } = packager === "yarn" ? { dev: true } : { "save-dev": true }; + dev?: boolean, + "save-dev"?: boolean, + } = packager === "yarn" ? + { dev: true } : + { "save-dev": true }; + this.scheduleInstallTask(packager, this.dependencies, opts); } public writing(): void { this.config.set("configuration", this.configuration); + + const packageJsonTemplatePath = "./templates/package.json.js"; + this.fs.extendJSON(this.destinationPath("package.json"), require(packageJsonTemplatePath)(this.isProd)); + + const entry = this.configuration.config.webpackOptions.entry; + const generateEntryFile = (entryPath: string, name: string) => { + entryPath = entryPath.replace(/'/g, ""); + this.fs.copyTpl( + path.resolve(__dirname, "./templates/index.js"), + this.destinationPath(entryPath), + { name }, + ); + }; + + if ( typeof entry === "string" ) { + generateEntryFile(entry, "your main file!"); + } else if (typeof entry === "object") { + Object.keys(entry).forEach((name) => + generateEntryFile(entry[name], `${name} main file!`), + ); + } } } From 6be92918cbe1f577bf7b386ab992d020d537a68b Mon Sep 17 00:00:00 2001 From: "devid.farinelli@gmail.com" Date: Fri, 19 Apr 2019 15:18:42 +0200 Subject: [PATCH 05/31] feat(generators): add generated file templates --- packages/generators/.gitignore | 1 + packages/generators/templates/index.js | 5 +++++ packages/generators/templates/package.json.js | 15 +++++++++++++++ 3 files changed, 21 insertions(+) create mode 100644 packages/generators/templates/index.js create mode 100644 packages/generators/templates/package.json.js diff --git a/packages/generators/.gitignore b/packages/generators/.gitignore index 74dcaf3ce86..b3cdf36cae7 100644 --- a/packages/generators/.gitignore +++ b/packages/generators/.gitignore @@ -2,3 +2,4 @@ **/*.js !*.test.js !/**/*.test.js +!/templates/*.js diff --git a/packages/generators/templates/index.js b/packages/generators/templates/index.js new file mode 100644 index 00000000000..05b1c0677bc --- /dev/null +++ b/packages/generators/templates/index.js @@ -0,0 +1,5 @@ +/** + * Application entry point + */ + +console.log("Hello World from <%= name %>"); diff --git a/packages/generators/templates/package.json.js b/packages/generators/templates/package.json.js new file mode 100644 index 00000000000..54089edb516 --- /dev/null +++ b/packages/generators/templates/package.json.js @@ -0,0 +1,15 @@ +module.exports = (isProd) => { + + let scripts = { + build: "webpack" + }; + if (!isProd) { + scripts.start = "webpack-dev-server"; + } + + return { + version: "1.0.0", + description: "My webpack project", + scripts, + }; +}; From 9856babdd88eb2d289c5d6306c5a5b874d5bdef2 Mon Sep 17 00:00:00 2001 From: "devid.farinelli@gmail.com" Date: Fri, 19 Apr 2019 15:19:55 +0200 Subject: [PATCH 06/31] docs(init): improve description --- INIT.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/INIT.md b/INIT.md index 112ca86eb77..bc9350b8245 100644 --- a/INIT.md +++ b/INIT.md @@ -1,6 +1,6 @@ # webpack-cli init -`webpack-cli init` is used to initialize `webpack` projects quickly by scaffolding configuration and installing modules required for the project as per user preferences. +`webpack-cli init` is used to initialize `webpack` projects quickly by scaffolding configuration and a runnable project with all the dependencies based on the user preferences. ## Initial Setup From fb23aa40eb7b5683af51f1eb55d0ca6a932a60c6 Mon Sep 17 00:00:00 2001 From: "devid.farinelli@gmail.com" Date: Sat, 20 Apr 2019 17:36:58 +0200 Subject: [PATCH 07/31] misc(init-generator): improve types & defaults --- INIT.md | 2 +- packages/generators/init-generator.ts | 12 ++++++++---- packages/generators/utils/entry.ts | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/INIT.md b/INIT.md index bc9350b8245..8dd9a3a7099 100644 --- a/INIT.md +++ b/INIT.md @@ -1,6 +1,6 @@ # webpack-cli init -`webpack-cli init` is used to initialize `webpack` projects quickly by scaffolding configuration and a runnable project with all the dependencies based on the user preferences. +`webpack-cli init` is used to initialize `webpack` projects quickly by scaffolding configuration and creating a runnable project with all the dependencies based on the user preferences. ## Initial Setup diff --git a/packages/generators/init-generator.ts b/packages/generators/init-generator.ts index d3a458307bb..d06a1efa097 100644 --- a/packages/generators/init-generator.ts +++ b/packages/generators/init-generator.ts @@ -151,15 +151,19 @@ export default class InitGenerator extends Generator { return this.prompt([ Confirm("multiEntries", "Will your application have multiple bundles?", false), ]) - .then((multiEntriesAnswer: any) => + .then((multiEntriesAnswer: { + multiEntries: boolean, + }) => entryQuestions(self, multiEntriesAnswer.multiEntries), ) .then((entryOption: object | string) => { if (typeof entryOption === "string" && entryOption.length > 0) { this.configuration.config.webpackOptions.entry = `${entryOption}`; + } else if (typeof entryOption === "object") { + this.configuration.config.webpackOptions.entry = entryOption; } }) - .then((_: void) => + .then(() => this.prompt([ Input( "outputDir", @@ -191,7 +195,7 @@ export default class InitGenerator extends Generator { `path.resolve(__dirname, '${outputDirAnswer.outputDir}')`; } }) - .then((_: void) => + .then(() => this.prompt([ Confirm("useBabel", "Will you be using ES2015?"), ]), @@ -210,7 +214,7 @@ export default class InitGenerator extends Generator { ); } }) - .then((_: void) => + .then(() => this.prompt([ List("stylingType", "Will you use one of the below CSS solutions?", [ "No", diff --git a/packages/generators/utils/entry.ts b/packages/generators/utils/entry.ts index 5f136e0b940..e9cb3d85587 100644 --- a/packages/generators/utils/entry.ts +++ b/packages/generators/utils/entry.ts @@ -104,7 +104,7 @@ export default function entry(self: CustomGenerator, multiEntries: boolean): Pro `${entryProp}`, `What is the location of "${entryProp}"?`, validate, - `./src/${entryProp}`, + `src/${entryProp}`, ), ]), ).then((entryPropAnswer: object): object => { From 392fcfec1e50b8655e569d7adcbfc896b235fc6f Mon Sep 17 00:00:00 2001 From: "devid.farinelli@gmail.com" Date: Sat, 20 Apr 2019 19:23:34 +0200 Subject: [PATCH 08/31] misc(generators/utils/style): refactor --- packages/generators/init-generator.ts | 21 +-- packages/generators/utils/style.ts | 258 ++++++++++++++------------ 2 files changed, 148 insertions(+), 131 deletions(-) diff --git a/packages/generators/init-generator.ts b/packages/generators/init-generator.ts index d06a1efa097..8ef4cdfdc23 100644 --- a/packages/generators/init-generator.ts +++ b/packages/generators/init-generator.ts @@ -11,7 +11,7 @@ import { Confirm, Input, List } from "@webpack-cli/webpack-scaffold"; import { WebpackOptions } from "./types"; import entryQuestions from "./utils/entry"; import getBabelPlugin from "./utils/module"; -import styleQuestionHandler from "./utils/style"; +import styleQuestionHandler, { ILoader, StylingType } from "./utils/style"; import tooltip from "./utils/tooltip"; /** @@ -90,12 +90,6 @@ export default class InitGenerator extends Generator { "new webpack.ProgressPlugin()", ); - if (this.isProd) { - this.configuration.config.webpackOptions.plugins.push( - "new TerserPlugin()", - ); - } - let optimizationObj; if (!this.isProd) { @@ -106,6 +100,9 @@ export default class InitGenerator extends Generator { }; } else { optimizationObj = { + minimizer: [ + "new TerserPlugin()", + ], splitChunks: { cacheGroups: { vendors: { @@ -136,7 +133,7 @@ export default class InitGenerator extends Generator { const done: () => {} = this.async(); const self: this = this; let regExpForStyles: string; - let ExtractUseProps: object[]; + let ExtractUseProps: ILoader[]; process.stdout.write( `\n${logSymbols.info}${chalk.blue(" INFO ")} ` + @@ -218,10 +215,10 @@ export default class InitGenerator extends Generator { this.prompt([ List("stylingType", "Will you use one of the below CSS solutions?", [ "No", - "CSS", - "SASS", - "LESS", - "PostCSS", + StylingType.CSS, + StylingType.SASS, + StylingType.LESS, + StylingType.PostCSS, ]), ])) .then((stylingTypeAnswer: { diff --git a/packages/generators/utils/style.ts b/packages/generators/utils/style.ts index 9d4d428ace8..5a642932f97 100644 --- a/packages/generators/utils/style.ts +++ b/packages/generators/utils/style.ts @@ -1,168 +1,188 @@ import tooltip from "./tooltip"; +export enum StylingType { + CSS = "CSS", + SASS = "SASS", + LESS = "LESS", + PostCSS = "PostCSS", +} + +export enum Loader { + CSS = "css-loader", + SASS = "sass-loader", + STYLE = "style-loader", + LESS = "less-loader", + POSTCSS = "postcss-loader", +} + +export enum StyleRegex { + CSS = "/\.css$/", + SASS = "/\.(scss|css)$/", + LESS = "/\.(less|css)$/", + PostCSS = "/\.css$/", +} + +export interface ILoader { + loader: string; + options?: { + importLoaders?: number; + sourceMap?: boolean; + plugins?: string; + }; +} + export default function style(self, stylingType) { - const ExtractUseProps = []; + const ExtractUseProps: ILoader[] = []; let regExpForStyles = null; + switch (stylingType) { - case "SASS": + case StylingType.CSS: + regExpForStyles = StyleRegex.CSS; + self.dependencies.push( - "sass-loader", - "node-sass", - "style-loader", - "css-loader", + Loader.CSS, ); - regExpForStyles = `${new RegExp(/\.(scss|css)$/)}`; - if (self.isProd) { + if (self.isProf) { + self.dependencies.push( + Loader.STYLE, + ); + } + + ExtractUseProps.push({ + loader: `"${Loader.CSS}"`, + options: { + sourceMap: true, + }, + }); + if (!self.isProd) { ExtractUseProps.push( { - loader: "'css-loader'", - options: { - sourceMap: true, - }, - }, - { - loader: "'sass-loader'", - options: { - sourceMap: true, - }, + loader: `"${Loader.STYLE}"`, }, ); - } else { - ExtractUseProps.push( - { - loader: "'style-loader'", + } + break; + + case StylingType.SASS: + regExpForStyles = StyleRegex.SASS; + + self.dependencies.push( + "node-sass", + Loader.SASS, + Loader.CSS, + ); + if (self.isProf) { + self.dependencies.push( + Loader.STYLE, + ); + } + + ExtractUseProps.push( + { + loader: `"${Loader.CSS}"`, + options: { + sourceMap: true, }, - { - loader: "'css-loader'", + }, + { + loader: `"${Loader.SASS}"`, + options: { + sourceMap: true, }, + }, + ); + if (!self.isProd) { + ExtractUseProps.push( { - loader: "'sass-loader'", + loader: `"${Loader.STYLE}"`, }, ); } break; - case "LESS": - regExpForStyles = `${new RegExp(/\.(less|css)$/)}`; + + case StylingType.LESS: + regExpForStyles = StyleRegex.LESS; + self.dependencies.push( - "less-loader", "less", - "style-loader", - "css-loader", + Loader.LESS, + Loader.CSS, ); - if (self.isProd) { - ExtractUseProps.push( - { - loader: "'css-loader'", - options: { - sourceMap: true, - }, + if (self.isProf) { + self.dependencies.push( + Loader.STYLE, + ); + } + + ExtractUseProps.push( + { + loader: `"${Loader.CSS}"`, + options: { + sourceMap: true, }, - { - loader: "'less-loader'", - options: { - sourceMap: true, - }, + }, + { + loader: `"${Loader.LESS}"`, + options: { + sourceMap: true, }, - ); - } else { + }, + ); + if (!self.isProd) { ExtractUseProps.push( { - loader: "'css-loader'", - options: { - sourceMap: true, - }, - }, - { - loader: "'less-loader'", - options: { - sourceMap: true, - }, + loader: `"${Loader.STYLE}"`, }, ); } break; - case "PostCSS": + + case StylingType.PostCSS: + regExpForStyles = StyleRegex.PostCSS; + self.configuration.config.topScope.push( tooltip.postcss(), "const autoprefixer = require('autoprefixer');", "const precss = require('precss');", "\n", ); + self.dependencies.push( - "style-loader", - "css-loader", - "postcss-loader", "precss", "autoprefixer", + Loader.CSS, + Loader.POSTCSS, ); - regExpForStyles = `${new RegExp(/\.css$/)}`; - if (self.isProd) { - ExtractUseProps.push( - { - loader: "'css-loader'", - options: { - importLoaders: 1, - sourceMap: true, - }, - }, - { - loader: "'postcss-loader'", - options: { - plugins: `function () { - return [ - precss, - autoprefixer - ]; - }`, - }, - }, - ); - } else { - ExtractUseProps.push( - { - loader: "'style-loader'", - }, - { - loader: "'css-loader'", - options: { - importLoaders: 1, - sourceMap: true, - }, - }, - { - loader: "'postcss-loader'", - options: { - plugins: `function () { - return [ - precss, - autoprefixer - ]; - }`, - }, - }, + if (self.isProf) { + self.dependencies.push( + Loader.STYLE, ); } - break; - case "CSS": - self.dependencies.push("style-loader", "css-loader"); - regExpForStyles = `${new RegExp(/\.css$/)}`; - if (self.isProd) { - ExtractUseProps.push({ - loader: "'css-loader'", + + ExtractUseProps.push( + { + loader: `"${Loader.CSS}"`, options: { + importLoaders: 1, sourceMap: true, }, - }); - } else { + }, + { + loader: `"${Loader.POSTCSS}"`, + options: { + plugins: `function () { + return [ + precss, + autoprefixer + ]; + }`, + }, + }, + ); + if (!self.isProd) { ExtractUseProps.push( { - loader: "'style-loader'", - options: { - sourceMap: true, - }, - }, - { - loader: "'css-loader'", + loader: `"${Loader.STYLE}"`, }, ); } From f46f4e539e1bff414085295abaa900413e983da9 Mon Sep 17 00:00:00 2001 From: "devid.farinelli@gmail.com" Date: Sat, 20 Apr 2019 19:53:04 +0200 Subject: [PATCH 09/31] fix(genrators/utils/style): typo & fix --- packages/generators/utils/style.ts | 60 ++++++++++++------------------ 1 file changed, 24 insertions(+), 36 deletions(-) diff --git a/packages/generators/utils/style.ts b/packages/generators/utils/style.ts index 5a642932f97..cc07306d3c0 100644 --- a/packages/generators/utils/style.ts +++ b/packages/generators/utils/style.ts @@ -42,25 +42,22 @@ export default function style(self, stylingType) { self.dependencies.push( Loader.CSS, ); - if (self.isProf) { + if (!self.isProd) { self.dependencies.push( Loader.STYLE, ); + ExtractUseProps.push( + { + loader: `"${Loader.STYLE}"`, + }, + ); } - ExtractUseProps.push({ loader: `"${Loader.CSS}"`, options: { sourceMap: true, }, }); - if (!self.isProd) { - ExtractUseProps.push( - { - loader: `"${Loader.STYLE}"`, - }, - ); - } break; case StylingType.SASS: @@ -71,12 +68,16 @@ export default function style(self, stylingType) { Loader.SASS, Loader.CSS, ); - if (self.isProf) { + if (!self.isProd) { self.dependencies.push( Loader.STYLE, ); + ExtractUseProps.push( + { + loader: `"${Loader.STYLE}"`, + }, + ); } - ExtractUseProps.push( { loader: `"${Loader.CSS}"`, @@ -91,13 +92,6 @@ export default function style(self, stylingType) { }, }, ); - if (!self.isProd) { - ExtractUseProps.push( - { - loader: `"${Loader.STYLE}"`, - }, - ); - } break; case StylingType.LESS: @@ -108,12 +102,16 @@ export default function style(self, stylingType) { Loader.LESS, Loader.CSS, ); - if (self.isProf) { + if (!self.isProd) { self.dependencies.push( Loader.STYLE, ); + ExtractUseProps.push( + { + loader: `"${Loader.STYLE}"`, + }, + ); } - ExtractUseProps.push( { loader: `"${Loader.CSS}"`, @@ -128,13 +126,6 @@ export default function style(self, stylingType) { }, }, ); - if (!self.isProd) { - ExtractUseProps.push( - { - loader: `"${Loader.STYLE}"`, - }, - ); - } break; case StylingType.PostCSS: @@ -153,12 +144,16 @@ export default function style(self, stylingType) { Loader.CSS, Loader.POSTCSS, ); - if (self.isProf) { + if (!self.isProd) { self.dependencies.push( Loader.STYLE, ); + ExtractUseProps.push( + { + loader: `"${Loader.STYLE}"`, + }, + ); } - ExtractUseProps.push( { loader: `"${Loader.CSS}"`, @@ -179,13 +174,6 @@ export default function style(self, stylingType) { }, }, ); - if (!self.isProd) { - ExtractUseProps.push( - { - loader: `"${Loader.STYLE}"`, - }, - ); - } break; } return { ExtractUseProps, regExpForStyles }; From 093a36d8ddf129bb646dc2d1213dd708fba4c219 Mon Sep 17 00:00:00 2001 From: "devid.farinelli@gmail.com" Date: Mon, 22 Apr 2019 12:43:50 +0200 Subject: [PATCH 10/31] feat(init): wip typescript support --- packages/generators/init-generator.ts | 23 +++++++--------- .../utils/{module.ts => language.ts} | 26 +++++++++++++++++-- 2 files changed, 33 insertions(+), 16 deletions(-) rename packages/generators/utils/{module.ts => language.ts} (55%) diff --git a/packages/generators/init-generator.ts b/packages/generators/init-generator.ts index 8ef4cdfdc23..e54995c3327 100644 --- a/packages/generators/init-generator.ts +++ b/packages/generators/init-generator.ts @@ -10,7 +10,7 @@ import { Confirm, Input, List } from "@webpack-cli/webpack-scaffold"; import { WebpackOptions } from "./types"; import entryQuestions from "./utils/entry"; -import getBabelPlugin from "./utils/module"; +import langQuestionHandler from "./utils/language"; import styleQuestionHandler, { ILoader, StylingType } from "./utils/style"; import tooltip from "./utils/tooltip"; @@ -194,22 +194,17 @@ export default class InitGenerator extends Generator { }) .then(() => this.prompt([ - Confirm("useBabel", "Will you be using ES2015?"), + List("langType", "Will you use one of the below JS solutions?", [ + "ES6", + "Typescript", + "No", + ]), ]), ) - .then((useBabelAnswer: { - useBabel: boolean; + .then((langTypeAnswer: { + langType: boolean; }) => { - if (useBabelAnswer.useBabel) { - this.configuration.config.webpackOptions.module.rules.push( - getBabelPlugin(), - ); - this.dependencies.push( - "babel-loader", - "@babel/core", - "@babel/preset-env", - ); - } + langQuestionHandler(this, langTypeAnswer.langType); }) .then(() => this.prompt([ diff --git a/packages/generators/utils/module.ts b/packages/generators/utils/language.ts similarity index 55% rename from packages/generators/utils/module.ts rename to packages/generators/utils/language.ts index 25b846723f1..ffa887de45d 100644 --- a/packages/generators/utils/module.ts +++ b/packages/generators/utils/language.ts @@ -1,4 +1,9 @@ -interface Module extends Object { +export enum LangType { + ES6 = "ES6", + Typescript = "Typescript", +} + +interface ModuleRule extends Object { include: string[]; loader: string; options: { @@ -16,7 +21,7 @@ type Preset = string | object; * * @returns {Function} A callable function that adds the babel-loader with env preset */ -export default function(): Module { +export function getBabelPlugin(): ModuleRule { return { include: ["path.resolve(__dirname, 'src')"], loader: "'babel-loader'", @@ -34,3 +39,20 @@ export default function(): Module { test: `${new RegExp(/\.js$/)}` }; } + +export default function language(self, langType) { + switch (langType) { + case LangType.ES6: + self.configuration.config.webpackOptions.module.rules.push( + getBabelPlugin(), + ); + self.dependencies.push( + "babel-loader", + "@babel/core", + "@babel/preset-env", + ); + break; + case LangType.Typescript: + break; + } +} From 283e0892ef12ff13c6be4fb12eb02734861ffb98 Mon Sep 17 00:00:00 2001 From: Devid Farinelli Date: Tue, 23 Apr 2019 03:20:01 +0200 Subject: [PATCH 11/31] feat(init): support ts in configuration --- packages/generators/init-generator.ts | 1 + .../generators/templates/tsconfig.json.js | 9 +++ packages/generators/utils/language.ts | 55 ++++++++++++++++--- packages/utils/run-prettier.ts | 5 +- 4 files changed, 61 insertions(+), 9 deletions(-) create mode 100644 packages/generators/templates/tsconfig.json.js diff --git a/packages/generators/init-generator.ts b/packages/generators/init-generator.ts index e54995c3327..5230434d4c0 100644 --- a/packages/generators/init-generator.ts +++ b/packages/generators/init-generator.ts @@ -55,6 +55,7 @@ export default class InitGenerator extends Generator { config: { configName: this.isProd ? "prod" : "config", topScope: [], + // TODO migrate tslint // tslint:disable: object-literal-sort-keys webpackOptions: { mode: this.isProd ? "'production'" : "'development'", diff --git a/packages/generators/templates/tsconfig.json.js b/packages/generators/templates/tsconfig.json.js new file mode 100644 index 00000000000..418c8927cab --- /dev/null +++ b/packages/generators/templates/tsconfig.json.js @@ -0,0 +1,9 @@ +module.exports = { + compilerOptions: { + noImplicitAny: true, + module: "es6", + target: "es5", + jsx: "react", + allowJs: true + } +}; diff --git a/packages/generators/utils/language.ts b/packages/generators/utils/language.ts index ffa887de45d..0d479e229e7 100644 --- a/packages/generators/utils/language.ts +++ b/packages/generators/utils/language.ts @@ -4,9 +4,10 @@ export enum LangType { } interface ModuleRule extends Object { - include: string[]; + include?: string[]; + exclude?: string[]; loader: string; - options: { + options?: { plugins: string[]; presets: Preset[][]; }; @@ -21,8 +22,11 @@ type Preset = string | object; * * @returns {Function} A callable function that adds the babel-loader with env preset */ -export function getBabelPlugin(): ModuleRule { +export function getBabelLoader(): ModuleRule { return { + // TODO migrate tslint + // tslint:disable: object-literal-sort-keys + test: "/\.js$/", include: ["path.resolve(__dirname, 'src')"], loader: "'babel-loader'", options: { @@ -36,23 +40,60 @@ export function getBabelPlugin(): ModuleRule { ] ] }, - test: `${new RegExp(/\.js$/)}` + // tslint:enable: object-literal-sort-keys + }; +} + +export function getTypescriptLoader(): ModuleRule { + return { + // TODO migrate tslint + // tslint:disable: object-literal-sort-keys + test: "/\.tsx?$/", + loader: "'ts-loader'", + include: ["path.resolve(__dirname, 'src')"], + exclude: ["/node_modules/"], + // tslint:enable: object-literal-sort-keys }; } export default function language(self, langType) { switch (langType) { case LangType.ES6: - self.configuration.config.webpackOptions.module.rules.push( - getBabelPlugin(), - ); self.dependencies.push( "babel-loader", "@babel/core", "@babel/preset-env", ); + self.configuration.config.webpackOptions.module.rules.push( + getBabelLoader(), + ); break; + case LangType.Typescript: + self.dependencies.push( + "typescript", + "ts-loader", + ); + self.configuration.config.webpackOptions.module.rules.push( + getTypescriptLoader(), + ); + self.configuration.config.webpackOptions.resolve = { + extensions: [ "'.tsx'", "'.ts'", "'.js'" ], + }; + + // Update the entry files extensions to .ts + const jsEntryOption = self.configuration.config.webpackOptions.entry; + const jsExtension = new RegExp("\.js(?!.*\.js)"); + let tsEntryOption = {}; + if (typeof jsEntryOption === "string") { + tsEntryOption = jsEntryOption.replace(jsExtension, ".ts"); + } else if (typeof jsEntryOption === "object") { + Object.keys(jsEntryOption).map((entry) => { + tsEntryOption[entry] = jsEntryOption[entry].replace(jsExtension, ".ts"); + }); + } + self.configuration.config.webpackOptions.entry = tsEntryOption; + self.log(jsEntryOption.replace(jsExtension, ".ts"), jsEntryOption); break; } } diff --git a/packages/utils/run-prettier.ts b/packages/utils/run-prettier.ts index 254f94029d7..2b9e7083a8f 100644 --- a/packages/utils/run-prettier.ts +++ b/packages/utils/run-prettier.ts @@ -19,7 +19,7 @@ export default function runPrettier(outputPath: string, source: string, cb?: Fun try { prettySource = prettier.format(source, { filepath: outputPath, - parser: "babel", + parser: "babylon", singleQuote: true, tabWidth: 1, useTabs: true, @@ -29,7 +29,8 @@ export default function runPrettier(outputPath: string, source: string, cb?: Fun "\n" + chalk.yellow( `WARNING: Could not apply prettier to ${outputPath}` + - " due validation error, but the file has been created\n", + " due validation error, but the file has been created\n" + + err, ), ); prettySource = source; From 4627ea1da4bc467cdc05a546b45026ca7b0793b4 Mon Sep 17 00:00:00 2001 From: "devid.farinelli@gmail.com" Date: Tue, 23 Apr 2019 19:13:43 +0200 Subject: [PATCH 12/31] misc(init): small refactor --- packages/generators/templates/index.js | 4 ---- packages/generators/templates/tsconfig.json.js | 2 +- packages/generators/utils/language.ts | 3 +-- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/generators/templates/index.js b/packages/generators/templates/index.js index 05b1c0677bc..0eec583fb7e 100644 --- a/packages/generators/templates/index.js +++ b/packages/generators/templates/index.js @@ -1,5 +1 @@ -/** - * Application entry point - */ - console.log("Hello World from <%= name %>"); diff --git a/packages/generators/templates/tsconfig.json.js b/packages/generators/templates/tsconfig.json.js index 418c8927cab..0eae50cb36f 100644 --- a/packages/generators/templates/tsconfig.json.js +++ b/packages/generators/templates/tsconfig.json.js @@ -1,9 +1,9 @@ module.exports = { compilerOptions: { + allowSyntheticDefaultImports: true, noImplicitAny: true, module: "es6", target: "es5", - jsx: "react", allowJs: true } }; diff --git a/packages/generators/utils/language.ts b/packages/generators/utils/language.ts index 0d479e229e7..a71f1b9f4b5 100644 --- a/packages/generators/utils/language.ts +++ b/packages/generators/utils/language.ts @@ -88,12 +88,11 @@ export default function language(self, langType) { if (typeof jsEntryOption === "string") { tsEntryOption = jsEntryOption.replace(jsExtension, ".ts"); } else if (typeof jsEntryOption === "object") { - Object.keys(jsEntryOption).map((entry) => { + Object.keys(jsEntryOption).forEach((entry) => { tsEntryOption[entry] = jsEntryOption[entry].replace(jsExtension, ".ts"); }); } self.configuration.config.webpackOptions.entry = tsEntryOption; - self.log(jsEntryOption.replace(jsExtension, ".ts"), jsEntryOption); break; } } From ac35a315789ad9179700cda9e1991566ec6efe3e Mon Sep 17 00:00:00 2001 From: "devid.farinelli@gmail.com" Date: Tue, 23 Apr 2019 19:14:25 +0200 Subject: [PATCH 13/31] misc(generators): improve prompts --- packages/generators/utils/entry.ts | 4 ++-- packages/utils/run-prettier.ts | 12 +++++------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/packages/generators/utils/entry.ts b/packages/generators/utils/entry.ts index e9cb3d85587..6feca520301 100644 --- a/packages/generators/utils/entry.ts +++ b/packages/generators/utils/entry.ts @@ -24,7 +24,7 @@ export default function entry(self: CustomGenerator, multiEntries: boolean): Pro .prompt([ InputValidate( "multipleEntries", - "Type the names you want for your modules (entry files) separated by comma", + "How do you want to name your bundles? (separated by comma)", validate, "pageOne, pageTwo", ), @@ -102,7 +102,7 @@ export default function entry(self: CustomGenerator, multiEntries: boolean): Pro self.prompt([ InputValidate( `${entryProp}`, - `What is the location of "${entryProp}"?`, + `Which will be the entry point of "${entryProp}"?`, validate, `src/${entryProp}`, ), diff --git a/packages/utils/run-prettier.ts b/packages/utils/run-prettier.ts index 2b9e7083a8f..bf4980150d9 100644 --- a/packages/utils/run-prettier.ts +++ b/packages/utils/run-prettier.ts @@ -19,19 +19,17 @@ export default function runPrettier(outputPath: string, source: string, cb?: Fun try { prettySource = prettier.format(source, { filepath: outputPath, - parser: "babylon", + parser: "babel", singleQuote: true, tabWidth: 1, useTabs: true, }); } catch (err) { process.stdout.write( - "\n" + - chalk.yellow( - `WARNING: Could not apply prettier to ${outputPath}` + - " due validation error, but the file has been created\n" + - err, - ), + `\n${chalk.yellow( + `WARNING: Could not apply prettier to ${outputPath}` + + " due validation error, but the file has been created\n", + )}`, ); prettySource = source; error = err; From 488b06cc32e25e4956b5f0c018023d62e32cd74c Mon Sep 17 00:00:00 2001 From: "devid.farinelli@gmail.com" Date: Tue, 23 Apr 2019 21:40:17 +0200 Subject: [PATCH 14/31] misc(packages): complete rebase --- packages/generators/init-generator.ts | 46 ++++++------- packages/generators/utils/entry.ts | 96 +++++++++++---------------- packages/generators/utils/language.ts | 35 +++++----- packages/generators/utils/style.ts | 57 ++++++++-------- packages/utils/scaffold.ts | 42 ++++++------ 5 files changed, 125 insertions(+), 151 deletions(-) diff --git a/packages/generators/init-generator.ts b/packages/generators/init-generator.ts index 5230434d4c0..a40c0bc9d12 100644 --- a/packages/generators/init-generator.ts +++ b/packages/generators/init-generator.ts @@ -1,4 +1,3 @@ - import chalk from "chalk"; import * as logSymbols from "log-symbols"; import * as Generator from "yeoman-generator"; @@ -11,7 +10,7 @@ import { Confirm, Input, List } from "@webpack-cli/webpack-scaffold"; import { WebpackOptions } from "./types"; import entryQuestions from "./utils/entry"; import langQuestionHandler from "./utils/language"; -import styleQuestionHandler, { ILoader, StylingType } from "./utils/style"; +import styleQuestionHandler, { Loader, StylingType } from "./utils/style"; import tooltip from "./utils/tooltip"; /** @@ -55,8 +54,6 @@ export default class InitGenerator extends Generator { config: { configName: this.isProd ? "prod" : "config", topScope: [], - // TODO migrate tslint - // tslint:disable: object-literal-sort-keys webpackOptions: { mode: this.isProd ? "'production'" : "'development'", entry: undefined, @@ -66,7 +63,6 @@ export default class InitGenerator extends Generator { rules: [], }, }, - // tslint:enable: object-literal-sort-keys }, }; @@ -87,7 +83,7 @@ export default class InitGenerator extends Generator { ); } - this.configuration.config.webpackOptions.plugins.push( + (this.configuration.config.webpackOptions.plugins as string[]).push( "new webpack.ProgressPlugin()", ); @@ -134,7 +130,7 @@ export default class InitGenerator extends Generator { const done: () => {} = this.async(); const self: this = this; let regExpForStyles: string; - let ExtractUseProps: ILoader[]; + let ExtractUseProps: Loader[]; process.stdout.write( `\n${logSymbols.info}${chalk.blue(" INFO ")} ` + @@ -151,17 +147,17 @@ export default class InitGenerator extends Generator { ]) .then((multiEntriesAnswer: { multiEntries: boolean, - }) => + }): Promise<{}> => entryQuestions(self, multiEntriesAnswer.multiEntries), ) - .then((entryOption: object | string) => { + .then((entryOption: object | string): void => { if (typeof entryOption === "string" && entryOption.length > 0) { this.configuration.config.webpackOptions.entry = `${entryOption}`; } else if (typeof entryOption === "object") { this.configuration.config.webpackOptions.entry = entryOption; } }) - .then(() => + .then((): Promise<{}> => this.prompt([ Input( "outputDir", @@ -172,7 +168,7 @@ export default class InitGenerator extends Generator { ) .then((outputDirAnswer: { outputDir: string; - }) => { + }): void => { // 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 ( @@ -193,7 +189,7 @@ export default class InitGenerator extends Generator { `path.resolve(__dirname, '${outputDirAnswer.outputDir}')`; } }) - .then(() => + .then((): Promise<{}> => this.prompt([ List("langType", "Will you use one of the below JS solutions?", [ "ES6", @@ -203,11 +199,11 @@ export default class InitGenerator extends Generator { ]), ) .then((langTypeAnswer: { - langType: boolean; - }) => { + langType: string; + }): void => { langQuestionHandler(this, langTypeAnswer.langType); }) - .then(() => + .then((): Promise<{}> => this.prompt([ List("stylingType", "Will you use one of the below CSS solutions?", [ "No", @@ -219,10 +215,10 @@ export default class InitGenerator extends Generator { ])) .then((stylingTypeAnswer: { stylingType: string; - }) => - ({ ExtractUseProps, regExpForStyles } = styleQuestionHandler(self, stylingTypeAnswer.stylingType)), - ) - .then((): Promise => { + }): void => { + ({ ExtractUseProps, regExpForStyles } = styleQuestionHandler(self, stylingTypeAnswer.stylingType)); + }) + .then((): Promise<{}> | void => { if (this.isProd) { // Ask if the user wants to use extractPlugin return this.prompt([ @@ -235,7 +231,7 @@ export default class InitGenerator extends Generator { }) .then((useExtractPluginAnswer: { useExtractPlugin: string; - }) => { + }): void => { if (regExpForStyles) { if (this.isProd) { const cssBundleName: string = useExtractPluginAnswer.useExtractPlugin; @@ -246,12 +242,12 @@ export default class InitGenerator extends Generator { "\n", ); if (cssBundleName.length !== 0) { - this.configuration.config.webpackOptions.plugins.push( + (this.configuration.config.webpackOptions.plugins as string[]).push( // TODO: use [contenthash] after it is supported `new MiniCssExtractPlugin({ filename:'${cssBundleName}.[chunkhash].css' })`, ); } else { - this.configuration.config.webpackOptions.plugins.push( + (this.configuration.config.webpackOptions.plugins as string[]).push( "new MiniCssExtractPlugin({ filename:'style.css' })", ); } @@ -273,7 +269,7 @@ export default class InitGenerator extends Generator { }); } - public installPlugins() { + public installPlugins(): void { const packager = getPackageManager(); const opts: { dev?: boolean, @@ -292,7 +288,7 @@ export default class InitGenerator extends Generator { this.fs.extendJSON(this.destinationPath("package.json"), require(packageJsonTemplatePath)(this.isProd)); const entry = this.configuration.config.webpackOptions.entry; - const generateEntryFile = (entryPath: string, name: string) => { + const generateEntryFile = (entryPath: string, name: string): void => { entryPath = entryPath.replace(/'/g, ""); this.fs.copyTpl( path.resolve(__dirname, "./templates/index.js"), @@ -304,7 +300,7 @@ export default class InitGenerator extends Generator { if ( typeof entry === "string" ) { generateEntryFile(entry, "your main file!"); } else if (typeof entry === "object") { - Object.keys(entry).forEach((name) => + Object.keys(entry).forEach((name: string): void => generateEntryFile(entry[name], `${name} main file!`), ); } diff --git a/packages/generators/utils/entry.ts b/packages/generators/utils/entry.ts index 6feca520301..4e3798a2a29 100644 --- a/packages/generators/utils/entry.ts +++ b/packages/generators/utils/entry.ts @@ -16,7 +16,10 @@ interface CustomGenerator extends Generator { * @returns {Object} An Object that holds the answers given by the user, later used to scaffold */ -export default function entry(self: CustomGenerator, multiEntries: boolean): Promise<{}> { +export default function entry( + self: CustomGenerator, + multiEntries: boolean, +): Promise<{}> { let entryIdentifiers: string[]; let result: Promise<{}>; if (multiEntries) { @@ -26,8 +29,8 @@ export default function entry(self: CustomGenerator, multiEntries: boolean): Pro "multipleEntries", "How do you want to name your bundles? (separated by comma)", validate, - "pageOne, pageTwo", - ), + "pageOne, pageTwo" + ) ]) .then( (multipleEntriesAnswer: { multipleEntries: string }): Promise => { @@ -53,7 +56,7 @@ export default function entry(self: CustomGenerator, multiEntries: boolean): Pro !n[val].includes("path") && !n[val].includes("process") ) { - n[val] = `\'${n[val].replace(/"|'/g, "").concat(".js")}\'`; + n[val] = `\'./${n[val].replace(/"|'/g, "").concat(".js")}\'`; } webpackEntryPoint[val] = n[val]; } @@ -66,59 +69,37 @@ export default function entry(self: CustomGenerator, multiEntries: boolean): Pro ); }, Promise.resolve()); } + return forEachPromise( entryIdentifiers, (entryProp: string): Promise => self.prompt([ InputValidate( `${entryProp}`, - `What is the location of "${entryProp}"? [example: ./src/${entryProp}]`, - validate - ) - ]) - ).then( - (entryPropAnswer: object): object => { - Object.keys(entryPropAnswer).forEach( - (val: string): void => { - if ( - entryPropAnswer[val].charAt(0) !== "(" && - entryPropAnswer[val].charAt(0) !== "[" && - !entryPropAnswer[val].includes("function") && - !entryPropAnswer[val].includes("path") && - !entryPropAnswer[val].includes("process") - ) { - n[val] = `\'./${n[val].replace(/"|'/g, "").concat(".js")}\'`; + `What is the location of "${entryProp}"?`, + validate, + `src/${entryProp}`, + ), + ])) + .then( + (entryPropAnswer: object): object => { + Object.keys(entryPropAnswer).forEach( + (val: string): void => { + 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]; } - webpackEntryPoint[val] = n[val]; - }); - } else { - n = {}; + ); + return webpackEntryPoint; } - return fn(trimmedProp); - }); - }, Promise.resolve()); - } - return forEachPromise(entryIdentifiers, (entryProp: string): Promise<{} | void> => - self.prompt([ - InputValidate( - `${entryProp}`, - `Which will be the entry point of "${entryProp}"?`, - validate, - `src/${entryProp}`, - ), - ]), - ).then((entryPropAnswer: object): object => { - Object.keys(entryPropAnswer).forEach((val: string): void => { - 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")}\'`; - } - ); + ); } ); } else { @@ -128,15 +109,16 @@ export default function entry(self: CustomGenerator, multiEntries: boolean): Pro "singularEntry", "Which will be your application entry point?", "src/index", - ), + ) ]) - .then((singularEntryAnswer: { - singularEntry: string, - }): string => { - let { singularEntry } = singularEntryAnswer; - singularEntry = `\'./${singularEntry.replace(/"|'/g, "").concat(".js")}\'`; - if (singularEntry.length <= 0) { - self.usingDefaults = true; + .then( + (singularEntryAnswer: { singularEntry: string }): string => { + let { singularEntry } = singularEntryAnswer; + singularEntry = `\'./${singularEntry.replace(/"|'/g, "").concat(".js")}\'`; + if (singularEntry.length <= 0) { + self.usingDefaults = true; + } + return singularEntry; } ); } diff --git a/packages/generators/utils/language.ts b/packages/generators/utils/language.ts index a71f1b9f4b5..a722eeeff53 100644 --- a/packages/generators/utils/language.ts +++ b/packages/generators/utils/language.ts @@ -16,6 +16,20 @@ interface ModuleRule extends Object { type Preset = string | object; +function updateEntryExt(self, newExt: string): void { + const jsEntryOption = self.configuration.config.webpackOptions.entry; + const jsExtension = new RegExp("\.js(?!.*\.js)"); + let tsEntryOption = {}; + if (typeof jsEntryOption === "string") { + tsEntryOption = jsEntryOption.replace(jsExtension, newExt); + } else if (typeof jsEntryOption === "object") { + Object.keys(jsEntryOption).forEach((entry: string): void => { + tsEntryOption[entry] = jsEntryOption[entry].replace(jsExtension, newExt); + }); + } + self.configuration.config.webpackOptions.entry = tsEntryOption; +} + /** * * Returns an module.rule object that has the babel loader if invoked @@ -24,8 +38,6 @@ type Preset = string | object; */ export function getBabelLoader(): ModuleRule { return { - // TODO migrate tslint - // tslint:disable: object-literal-sort-keys test: "/\.js$/", include: ["path.resolve(__dirname, 'src')"], loader: "'babel-loader'", @@ -40,23 +52,19 @@ export function getBabelLoader(): ModuleRule { ] ] }, - // tslint:enable: object-literal-sort-keys }; } export function getTypescriptLoader(): ModuleRule { return { - // TODO migrate tslint - // tslint:disable: object-literal-sort-keys test: "/\.tsx?$/", loader: "'ts-loader'", include: ["path.resolve(__dirname, 'src')"], exclude: ["/node_modules/"], - // tslint:enable: object-literal-sort-keys }; } -export default function language(self, langType) { +export default function language(self, langType: string): void { switch (langType) { case LangType.ES6: self.dependencies.push( @@ -81,18 +89,7 @@ export default function language(self, langType) { extensions: [ "'.tsx'", "'.ts'", "'.js'" ], }; - // Update the entry files extensions to .ts - const jsEntryOption = self.configuration.config.webpackOptions.entry; - const jsExtension = new RegExp("\.js(?!.*\.js)"); - let tsEntryOption = {}; - if (typeof jsEntryOption === "string") { - tsEntryOption = jsEntryOption.replace(jsExtension, ".ts"); - } else if (typeof jsEntryOption === "object") { - Object.keys(jsEntryOption).forEach((entry) => { - tsEntryOption[entry] = jsEntryOption[entry].replace(jsExtension, ".ts"); - }); - } - self.configuration.config.webpackOptions.entry = tsEntryOption; + updateEntryExt(self, ".ts"); break; } } diff --git a/packages/generators/utils/style.ts b/packages/generators/utils/style.ts index cc07306d3c0..d05a5241a75 100644 --- a/packages/generators/utils/style.ts +++ b/packages/generators/utils/style.ts @@ -7,7 +7,7 @@ export enum StylingType { PostCSS = "PostCSS", } -export enum Loader { +export enum LoaderName { CSS = "css-loader", SASS = "sass-loader", STYLE = "style-loader", @@ -22,7 +22,7 @@ export enum StyleRegex { PostCSS = "/\.css$/", } -export interface ILoader { +export interface Loader { loader: string; options?: { importLoaders?: number; @@ -31,29 +31,32 @@ export interface ILoader { }; } -export default function style(self, stylingType) { - const ExtractUseProps: ILoader[] = []; - let regExpForStyles = null; +export default function style(self, stylingType: string): { + ExtractUseProps: Loader[], + regExpForStyles: StyleRegex, +} { + const ExtractUseProps: Loader[] = []; + let regExpForStyles: StyleRegex = null; switch (stylingType) { case StylingType.CSS: regExpForStyles = StyleRegex.CSS; self.dependencies.push( - Loader.CSS, + LoaderName.CSS, ); if (!self.isProd) { self.dependencies.push( - Loader.STYLE, + LoaderName.STYLE, ); ExtractUseProps.push( { - loader: `"${Loader.STYLE}"`, + loader: `"${LoaderName.STYLE}"`, }, ); } ExtractUseProps.push({ - loader: `"${Loader.CSS}"`, + loader: `"${LoaderName.CSS}"`, options: { sourceMap: true, }, @@ -65,28 +68,28 @@ export default function style(self, stylingType) { self.dependencies.push( "node-sass", - Loader.SASS, - Loader.CSS, + LoaderName.SASS, + LoaderName.CSS, ); if (!self.isProd) { self.dependencies.push( - Loader.STYLE, + LoaderName.STYLE, ); ExtractUseProps.push( { - loader: `"${Loader.STYLE}"`, + loader: `"${LoaderName.STYLE}"`, }, ); } ExtractUseProps.push( { - loader: `"${Loader.CSS}"`, + loader: `"${LoaderName.CSS}"`, options: { sourceMap: true, }, }, { - loader: `"${Loader.SASS}"`, + loader: `"${LoaderName.SASS}"`, options: { sourceMap: true, }, @@ -99,28 +102,28 @@ export default function style(self, stylingType) { self.dependencies.push( "less", - Loader.LESS, - Loader.CSS, + LoaderName.LESS, + LoaderName.CSS, ); if (!self.isProd) { self.dependencies.push( - Loader.STYLE, + LoaderName.STYLE, ); ExtractUseProps.push( { - loader: `"${Loader.STYLE}"`, + loader: `"${LoaderName.STYLE}"`, }, ); } ExtractUseProps.push( { - loader: `"${Loader.CSS}"`, + loader: `"${LoaderName.CSS}"`, options: { sourceMap: true, }, }, { - loader: `"${Loader.LESS}"`, + loader: `"${LoaderName.LESS}"`, options: { sourceMap: true, }, @@ -141,29 +144,29 @@ export default function style(self, stylingType) { self.dependencies.push( "precss", "autoprefixer", - Loader.CSS, - Loader.POSTCSS, + LoaderName.CSS, + LoaderName.POSTCSS, ); if (!self.isProd) { self.dependencies.push( - Loader.STYLE, + LoaderName.STYLE, ); ExtractUseProps.push( { - loader: `"${Loader.STYLE}"`, + loader: `"${LoaderName.STYLE}"`, }, ); } ExtractUseProps.push( { - loader: `"${Loader.CSS}"`, + loader: `"${LoaderName.CSS}"`, options: { importLoaders: 1, sourceMap: true, }, }, { - loader: `"${Loader.POSTCSS}"`, + loader: `"${LoaderName.POSTCSS}"`, options: { plugins: `function () { return [ diff --git a/packages/utils/scaffold.ts b/packages/utils/scaffold.ts index 3fc8bf20c0f..7f0eb1acd48 100644 --- a/packages/utils/scaffold.ts +++ b/packages/utils/scaffold.ts @@ -70,30 +70,26 @@ export default function runTransform(transformConfig: TransformConfig, action: s return astTransform(j, ast, f, config[f] as any, transformAction); } return astTransform(j, ast, f, config.webpackOptions[f], transformAction); - } - ) - .then( - (): void | PromiseLike => { - let configurationName: string; - if (!config.configName) { - configurationName = "webpack.config.js"; - } else { - configurationName = "webpack." + config.configName + ".js"; - } - - const projectRoot = findProjectRoot(); - const outputPath: string = initActionNotDefined - ? transformConfig.configPath - : path.join(projectRoot || process.cwd(), configurationName); - const source: string = ast.toSource({ - quote: "single", + }) + .then((): void | PromiseLike => { + let configurationName: string; + if (!config.configName) { + configurationName = "webpack.config.js"; + } else { + configurationName = "webpack." + config.configName + ".js"; + } + const projectRoot = findProjectRoot(); + const outputPath: string = initActionNotDefined + ? transformConfig.configPath + : path.join(projectRoot || process.cwd(), configurationName); + const source: string = ast.toSource({ + quote: "single", + }); + runPrettier(outputPath, source); + }) + .catch((err: Error): void => { + console.error(err.message ? err.message : err); }); - runPrettier(outputPath, source); - - }) - .catch((err: IError) => { - console.error(err.message ? err.message : err); - }); }); let successMessage: string = chalk.green(`Congratulations! Your new webpack configuration file has been created!\n\n`) + From 1b07d2bcf6fca415d0924f1ccd59f9bf8d202a03 Mon Sep 17 00:00:00 2001 From: "devid.farinelli@gmail.com" Date: Tue, 23 Apr 2019 22:40:40 +0200 Subject: [PATCH 15/31] misc(init): refactor with async/await --- packages/generators/init-generator.ts | 209 ++++++++++++-------------- packages/generators/utils/entry.ts | 2 +- 2 files changed, 98 insertions(+), 113 deletions(-) diff --git a/packages/generators/init-generator.ts b/packages/generators/init-generator.ts index a40c0bc9d12..2958f5cf0b2 100644 --- a/packages/generators/init-generator.ts +++ b/packages/generators/init-generator.ts @@ -12,6 +12,7 @@ import entryQuestions from "./utils/entry"; import langQuestionHandler from "./utils/language"; import styleQuestionHandler, { Loader, StylingType } from "./utils/style"; import tooltip from "./utils/tooltip"; +import { type } from "os"; /** * @@ -126,7 +127,7 @@ export default class InitGenerator extends Generator { } // eslint-disable-next-line - public prompting(): any { + public async prompting() { const done: () => {} = this.async(); const self: this = this; let regExpForStyles: string; @@ -142,69 +143,61 @@ export default class InitGenerator extends Generator { `Alternatively, run "webpack(-cli) --help" for usage info\n\n`, ); - return this.prompt([ - Confirm("multiEntries", "Will your application have multiple bundles?", false), - ]) - .then((multiEntriesAnswer: { - multiEntries: boolean, - }): Promise<{}> => - entryQuestions(self, multiEntriesAnswer.multiEntries), - ) - .then((entryOption: object | string): void => { - if (typeof entryOption === "string" && entryOption.length > 0) { - this.configuration.config.webpackOptions.entry = `${entryOption}`; - } else if (typeof entryOption === "object") { - this.configuration.config.webpackOptions.entry = entryOption; - } - }) - .then((): Promise<{}> => - this.prompt([ - Input( - "outputDir", - "In which folder do you want to store your generated bundles?", - "dist", - ), - ]), - ) - .then((outputDirAnswer: { - outputDir: string; - }): void => { - // 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 ( - !this.configuration.config.webpackOptions.entry && - !this.usingDefaults - ) { - this.configuration.config.webpackOptions.output = { - chunkFilename: "'[name].[chunkhash].js'", - filename: "'[name].[chunkhash].js'", - }; - } else if (!this.usingDefaults) { - this.configuration.config.webpackOptions.output = { - filename: "'[name].[chunkhash].js'", - }; - } - if (!this.usingDefaults && outputDirAnswer.outputDir.length) { - this.configuration.config.webpackOptions.output.path = - `path.resolve(__dirname, '${outputDirAnswer.outputDir}')`; - } - }) - .then((): Promise<{}> => - this.prompt([ - List("langType", "Will you use one of the below JS solutions?", [ - "ES6", - "Typescript", - "No", - ]), + const { multiEntries }: { multiEntries: boolean } = await this.prompt([ + Confirm( + "multiEntries", + "Will your application have multiple bundles?", + false + ), + ]); + + const entryOption: string | object = await entryQuestions(self, multiEntries); + + if (typeof entryOption === "string" && entryOption.length > 0) { + this.configuration.config.webpackOptions.entry = `${entryOption}`; + } else if (typeof entryOption === "object") { + this.configuration.config.webpackOptions.entry = entryOption; + } + + const { outputDir }: { outputDir: string } = await this.prompt([ + Input( + "outputDir", + "In which folder do you want to store your generated bundles?", + "dist", + ), + ]); + + // 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 ( + !this.configuration.config.webpackOptions.entry && + !this.usingDefaults + ) { + this.configuration.config.webpackOptions.output = { + chunkFilename: "'[name].[chunkhash].js'", + filename: "'[name].[chunkhash].js'", + }; + } else if (!this.usingDefaults) { + this.configuration.config.webpackOptions.output = { + filename: "'[name].[chunkhash].js'", + }; + } + if (!this.usingDefaults && outputDir.length) { + this.configuration.config.webpackOptions.output.path = + `path.resolve(__dirname, '${outputDir}')`; + } + + const { langType }: { langType: string } = await this.prompt([ + List("langType", "Will you use one of the below JS solutions?", [ + "ES6", + "Typescript", + "No", ]), - ) - .then((langTypeAnswer: { - langType: string; - }): void => { - langQuestionHandler(this, langTypeAnswer.langType); - }) - .then((): Promise<{}> => - this.prompt([ + ]); + + langQuestionHandler(this, langType); + + const { stylingType }: { stylingType: string } = await this.prompt([ List("stylingType", "Will you use one of the below CSS solutions?", [ "No", StylingType.CSS, @@ -212,61 +205,53 @@ export default class InitGenerator extends Generator { StylingType.LESS, StylingType.PostCSS, ]), - ])) - .then((stylingTypeAnswer: { - stylingType: string; - }): void => { - ({ ExtractUseProps, regExpForStyles } = styleQuestionHandler(self, stylingTypeAnswer.stylingType)); - }) - .then((): Promise<{}> | void => { + ]); + + ({ ExtractUseProps, regExpForStyles } = styleQuestionHandler(self, stylingType)); + + if (this.isProd) { + // Ask if the user wants to use extractPlugin + const { useExtractPlugin }: { useExtractPlugin: string } = await this.prompt([ + Input( + "useExtractPlugin", + "If you want to bundle your CSS files, what will you name the bundle? (press enter to skip)", + ), + ]); + + if (regExpForStyles) { if (this.isProd) { - // Ask if the user wants to use extractPlugin - return this.prompt([ - Input( - "useExtractPlugin", - "If you want to bundle your CSS files, what will you name the bundle? (press enter to skip)", - ), - ]); - } - }) - .then((useExtractPluginAnswer: { - useExtractPlugin: string; - }): void => { - if (regExpForStyles) { - if (this.isProd) { - const cssBundleName: string = useExtractPluginAnswer.useExtractPlugin; - this.dependencies.push("mini-css-extract-plugin"); - this.configuration.config.topScope.push( - tooltip.cssPlugin(), - "const MiniCssExtractPlugin = require('mini-css-extract-plugin');", - "\n", + const cssBundleName: string = useExtractPlugin; + this.dependencies.push("mini-css-extract-plugin"); + this.configuration.config.topScope.push( + tooltip.cssPlugin(), + "const MiniCssExtractPlugin = require('mini-css-extract-plugin');", + "\n", + ); + if (cssBundleName.length !== 0) { + (this.configuration.config.webpackOptions.plugins as string[]).push( + // TODO: use [contenthash] after it is supported + `new MiniCssExtractPlugin({ filename:'${cssBundleName}.[chunkhash].css' })`, + ); + } else { + (this.configuration.config.webpackOptions.plugins as string[]).push( + "new MiniCssExtractPlugin({ filename:'style.css' })", ); - if (cssBundleName.length !== 0) { - (this.configuration.config.webpackOptions.plugins as string[]).push( - // TODO: use [contenthash] after it is supported - `new MiniCssExtractPlugin({ filename:'${cssBundleName}.[chunkhash].css' })`, - ); - } else { - (this.configuration.config.webpackOptions.plugins as string[]).push( - "new MiniCssExtractPlugin({ filename:'style.css' })", - ); - } - - ExtractUseProps.unshift({ - loader: "MiniCssExtractPlugin.loader", - }); } - this.configuration.config.webpackOptions.module.rules.push( - { - test: regExpForStyles, - use: ExtractUseProps, - }, - ); + ExtractUseProps.unshift({ + loader: "MiniCssExtractPlugin.loader", + }); } - done(); - }); + this.configuration.config.webpackOptions.module.rules.push( + { + test: regExpForStyles, + use: ExtractUseProps, + }, + ); + } + } + done(); } public installPlugins(): void { diff --git a/packages/generators/utils/entry.ts b/packages/generators/utils/entry.ts index 4e3798a2a29..8748abcb1e5 100644 --- a/packages/generators/utils/entry.ts +++ b/packages/generators/utils/entry.ts @@ -19,7 +19,7 @@ interface CustomGenerator extends Generator { export default function entry( self: CustomGenerator, multiEntries: boolean, -): Promise<{}> { +): Promise { let entryIdentifiers: string[]; let result: Promise<{}>; if (multiEntries) { From 263b83c8916200d41c4c681301d1df6f8e08833b Mon Sep 17 00:00:00 2001 From: "devid.farinelli@gmail.com" Date: Fri, 26 Apr 2019 22:36:46 +0200 Subject: [PATCH 16/31] fix(generator): generate correct module.rule for babel & ts --- packages/generators/init-generator.ts | 2 - packages/generators/utils/language.ts | 57 +++++++++++++++++++++------ 2 files changed, 45 insertions(+), 14 deletions(-) diff --git a/packages/generators/init-generator.ts b/packages/generators/init-generator.ts index 2958f5cf0b2..88bc41604e4 100644 --- a/packages/generators/init-generator.ts +++ b/packages/generators/init-generator.ts @@ -12,7 +12,6 @@ import entryQuestions from "./utils/entry"; import langQuestionHandler from "./utils/language"; import styleQuestionHandler, { Loader, StylingType } from "./utils/style"; import tooltip from "./utils/tooltip"; -import { type } from "os"; /** * @@ -126,7 +125,6 @@ export default class InitGenerator extends Generator { } } - // eslint-disable-next-line public async prompting() { const done: () => {} = this.async(); const self: this = this; diff --git a/packages/generators/utils/language.ts b/packages/generators/utils/language.ts index a722eeeff53..46883fcf4af 100644 --- a/packages/generators/utils/language.ts +++ b/packages/generators/utils/language.ts @@ -16,30 +16,53 @@ interface ModuleRule extends Object { type Preset = string | object; +const replaceExt = (path: string, ext: string): string => + path.substr(0, path.lastIndexOf(".")) + `${ext}'`; + function updateEntryExt(self, newExt: string): void { const jsEntryOption = self.configuration.config.webpackOptions.entry; - const jsExtension = new RegExp("\.js(?!.*\.js)"); let tsEntryOption = {}; if (typeof jsEntryOption === "string") { - tsEntryOption = jsEntryOption.replace(jsExtension, newExt); + tsEntryOption = replaceExt(jsEntryOption, newExt); } else if (typeof jsEntryOption === "object") { Object.keys(jsEntryOption).forEach((entry: string): void => { - tsEntryOption[entry] = jsEntryOption[entry].replace(jsExtension, newExt); + tsEntryOption[entry] = replaceExt(jsEntryOption[entry], newExt); }); } self.configuration.config.webpackOptions.entry = tsEntryOption; } +const getFolder = (path: string): string => + path.replace("'./", "").split("/").slice(0, -1).join("/"); + +function getEntryFolders(self): string[] { + const entryOption = self.configuration.config.webpackOptions.entry; + let entryFolders = {}; + if (typeof entryOption === "string") { + const folder = getFolder(entryOption); + if (folder.length > 0) entryFolders[folder] = true; + } else if (typeof entryOption === "object") { + Object.keys(entryOption).forEach((entry: string): void => { + const folder = getFolder(entryOption[entry]); + if (folder.length > 0) entryFolders[folder] = true; + }); + } + return Object.keys(entryFolders); +} + /** * - * 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 + * Returns an module.rule object for the babel loader + * @param {string[]} includeFolders An array of folders to include + * @returns {ModuleRule} A configuration containing the babel-loader with env preset */ -export function getBabelLoader(): ModuleRule { +export function getBabelLoader(includeFolders: string[]): ModuleRule { + const include = includeFolders.map((folder: string) => + `path.resolve(__dirname, '${folder}')` + ); return { test: "/\.js$/", - include: ["path.resolve(__dirname, 'src')"], + include, loader: "'babel-loader'", options: { plugins: ["'syntax-dynamic-import'"], @@ -55,16 +78,26 @@ export function getBabelLoader(): ModuleRule { }; } -export function getTypescriptLoader(): ModuleRule { +/** + * + * Returns an module.rule object for the typescript loader + * @param {string[]} includeFolders An array of folders to include + * @returns {ModuleRule} A configuration containing the ts-loader + */ +export function getTypescriptLoader(includeFolders: string[]): ModuleRule { + const include = includeFolders.map((folder: string) => + `path.resolve(__dirname, '${folder}')` + ); return { test: "/\.tsx?$/", loader: "'ts-loader'", - include: ["path.resolve(__dirname, 'src')"], + include, exclude: ["/node_modules/"], }; } export default function language(self, langType: string): void { + const entryFolders = getEntryFolders(self); switch (langType) { case LangType.ES6: self.dependencies.push( @@ -73,7 +106,7 @@ export default function language(self, langType: string): void { "@babel/preset-env", ); self.configuration.config.webpackOptions.module.rules.push( - getBabelLoader(), + getBabelLoader(entryFolders), ); break; @@ -83,7 +116,7 @@ export default function language(self, langType: string): void { "ts-loader", ); self.configuration.config.webpackOptions.module.rules.push( - getTypescriptLoader(), + getTypescriptLoader(entryFolders), ); self.configuration.config.webpackOptions.resolve = { extensions: [ "'.tsx'", "'.ts'", "'.js'" ], From 25ab7e67b72d4b2b747198adeee8bec2efb75db4 Mon Sep 17 00:00:00 2001 From: "devid.farinelli@gmail.com" Date: Fri, 26 Apr 2019 22:54:40 +0200 Subject: [PATCH 17/31] feat(init): generate tsconfig --- packages/generators/init-generator.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/generators/init-generator.ts b/packages/generators/init-generator.ts index 88bc41604e4..29428aed471 100644 --- a/packages/generators/init-generator.ts +++ b/packages/generators/init-generator.ts @@ -9,7 +9,7 @@ import { Confirm, Input, List } from "@webpack-cli/webpack-scaffold"; import { WebpackOptions } from "./types"; import entryQuestions from "./utils/entry"; -import langQuestionHandler from "./utils/language"; +import langQuestionHandler, { LangType } from "./utils/language"; import styleQuestionHandler, { Loader, StylingType } from "./utils/style"; import tooltip from "./utils/tooltip"; @@ -33,6 +33,7 @@ export default class InitGenerator extends Generator { webpackOptions?: WebpackOptions; }; }; + private langType: string; public constructor(args, opts) { super(args, opts); @@ -187,13 +188,14 @@ export default class InitGenerator extends Generator { const { langType }: { langType: string } = await this.prompt([ List("langType", "Will you use one of the below JS solutions?", [ - "ES6", - "Typescript", + LangType.ES6, + LangType.Typescript, "No", ]), ]); langQuestionHandler(this, langType); + this.langType = langType; const { stylingType }: { stylingType: string } = await this.prompt([ List("stylingType", "Will you use one of the below CSS solutions?", [ @@ -270,7 +272,6 @@ export default class InitGenerator extends Generator { const packageJsonTemplatePath = "./templates/package.json.js"; this.fs.extendJSON(this.destinationPath("package.json"), require(packageJsonTemplatePath)(this.isProd)); - const entry = this.configuration.config.webpackOptions.entry; const generateEntryFile = (entryPath: string, name: string): void => { entryPath = entryPath.replace(/'/g, ""); this.fs.copyTpl( @@ -280,6 +281,8 @@ export default class InitGenerator extends Generator { ); }; + // Generate entry file/files + const entry = this.configuration.config.webpackOptions.entry; if ( typeof entry === "string" ) { generateEntryFile(entry, "your main file!"); } else if (typeof entry === "object") { @@ -287,5 +290,10 @@ export default class InitGenerator extends Generator { generateEntryFile(entry[name], `${name} main file!`), ); } + + if (this.langType === LangType.Typescript) { + const tsConfigTemplatePath = "./templates/tsconfig.json.js"; + this.fs.extendJSON(this.destinationPath("tsconfig.json"), require(tsConfigTemplatePath)); + } } } From c090b17c4afc3bf12b31ee5dc017da95fdb8f344 Mon Sep 17 00:00:00 2001 From: "devid.farinelli@gmail.com" Date: Fri, 26 Apr 2019 23:09:08 +0200 Subject: [PATCH 18/31] feat(init): generate README --- packages/generators/init-generator.ts | 7 +++++++ packages/generators/templates/README.md | 15 +++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 packages/generators/templates/README.md diff --git a/packages/generators/init-generator.ts b/packages/generators/init-generator.ts index 29428aed471..ebe49ee6c13 100644 --- a/packages/generators/init-generator.ts +++ b/packages/generators/init-generator.ts @@ -291,6 +291,13 @@ export default class InitGenerator extends Generator { ); } + // Generate README + this.fs.copyTpl( + path.resolve(__dirname, "./templates/README.md"), + this.destinationPath("README.md") + ); + + // Genrate tsconfig if (this.langType === LangType.Typescript) { const tsConfigTemplatePath = "./templates/tsconfig.json.js"; this.fs.extendJSON(this.destinationPath("tsconfig.json"), require(tsConfigTemplatePath)); diff --git a/packages/generators/templates/README.md b/packages/generators/templates/README.md new file mode 100644 index 00000000000..92053bba6ec --- /dev/null +++ b/packages/generators/templates/README.md @@ -0,0 +1,15 @@ +# 🚀 Welcome to your new awesome project! + +This project has been created using **webpack scaffold**, you can now run + +``` +npm run build +``` + +or + +``` +yarn build +``` + +to bundle your application From 8b6d47bfaa0d1a921e7c6af4f60ab9666a3eeb43 Mon Sep 17 00:00:00 2001 From: "devid.farinelli@gmail.com" Date: Fri, 26 Apr 2019 23:22:03 +0200 Subject: [PATCH 19/31] docs(utils): update prettier --- packages/utils/package-lock.json | 6 +++--- packages/utils/package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/utils/package-lock.json b/packages/utils/package-lock.json index 313aec15a47..efe1c450fdd 100644 --- a/packages/utils/package-lock.json +++ b/packages/utils/package-lock.json @@ -5665,9 +5665,9 @@ "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=" }, "prettier": { - "version": "1.15.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.15.3.tgz", - "integrity": "sha512-gAU9AGAPMaKb3NNSUUuhhFAS7SCO4ALTN4nRIn6PJ075Qd28Yn2Ig2ahEJWdJwJmlEBTUfC7mMUSFy8MwsOCfg==" + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.17.0.tgz", + "integrity": "sha512-sXe5lSt2WQlCbydGETgfm1YBShgOX4HxQkFPvbxkcwgDvGDeqVau8h+12+lmSVlP3rHPz0oavfddSZg/q+Szjw==" }, "pretty-bytes": { "version": "5.1.0", diff --git a/packages/utils/package.json b/packages/utils/package.json index fc26be79aa3..f234ab8f8d1 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -19,7 +19,7 @@ "jscodeshift": "^0.5.1", "log-symbols": "^2.2.0", "p-each-series": "^1.0.0", - "prettier": "^1.15.2", + "prettier": "^1.17.0", "yeoman-environment": "^2.3.4", "yeoman-generator": "^3.1.1" }, From 85ef3e77bf81c20c9cced220d1654f07cebe100c Mon Sep 17 00:00:00 2001 From: Devid Farinelli Date: Fri, 3 May 2019 14:58:58 +0200 Subject: [PATCH 20/31] misc(types): correct types --- packages/generators/init-generator.ts | 17 +++++++++-------- packages/generators/utils/entry.ts | 4 ++-- packages/generators/utils/language.ts | 4 ++-- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/packages/generators/init-generator.ts b/packages/generators/init-generator.ts index ebe49ee6c13..7a4df24f43c 100644 --- a/packages/generators/init-generator.ts +++ b/packages/generators/init-generator.ts @@ -1,7 +1,6 @@ import chalk from "chalk"; import * as logSymbols from "log-symbols"; import * as Generator from "yeoman-generator"; -import * as Inquirer from "inquirer"; import * as path from "path"; import { getPackageManager } from "@webpack-cli/utils/package-manager"; @@ -142,7 +141,7 @@ export default class InitGenerator extends Generator { `Alternatively, run "webpack(-cli) --help" for usage info\n\n`, ); - const { multiEntries }: { multiEntries: boolean } = await this.prompt([ + const { multiEntries } = await this.prompt([ Confirm( "multiEntries", "Will your application have multiple bundles?", @@ -150,7 +149,8 @@ export default class InitGenerator extends Generator { ), ]); - const entryOption: string | object = await entryQuestions(self, multiEntries); + // TODO string | object + const entryOption: void | {} = await entryQuestions(self, multiEntries); if (typeof entryOption === "string" && entryOption.length > 0) { this.configuration.config.webpackOptions.entry = `${entryOption}`; @@ -158,7 +158,7 @@ export default class InitGenerator extends Generator { this.configuration.config.webpackOptions.entry = entryOption; } - const { outputDir }: { outputDir: string } = await this.prompt([ + const { outputDir } = await this.prompt([ Input( "outputDir", "In which folder do you want to store your generated bundles?", @@ -186,7 +186,7 @@ export default class InitGenerator extends Generator { `path.resolve(__dirname, '${outputDir}')`; } - const { langType }: { langType: string } = await this.prompt([ + const { langType } = await this.prompt([ List("langType", "Will you use one of the below JS solutions?", [ LangType.ES6, LangType.Typescript, @@ -197,7 +197,7 @@ export default class InitGenerator extends Generator { langQuestionHandler(this, langType); this.langType = langType; - const { stylingType }: { stylingType: string } = await this.prompt([ + const { stylingType } = await this.prompt([ List("stylingType", "Will you use one of the below CSS solutions?", [ "No", StylingType.CSS, @@ -211,7 +211,7 @@ export default class InitGenerator extends Generator { if (this.isProd) { // Ask if the user wants to use extractPlugin - const { useExtractPlugin }: { useExtractPlugin: string } = await this.prompt([ + const { useExtractPlugin } = await this.prompt([ Input( "useExtractPlugin", "If you want to bundle your CSS files, what will you name the bundle? (press enter to skip)", @@ -294,7 +294,8 @@ export default class InitGenerator extends Generator { // Generate README this.fs.copyTpl( path.resolve(__dirname, "./templates/README.md"), - this.destinationPath("README.md") + this.destinationPath("README.md"), + {} ); // Genrate tsconfig diff --git a/packages/generators/utils/entry.ts b/packages/generators/utils/entry.ts index 8748abcb1e5..ea979b4dc94 100644 --- a/packages/generators/utils/entry.ts +++ b/packages/generators/utils/entry.ts @@ -19,9 +19,9 @@ interface CustomGenerator extends Generator { export default function entry( self: CustomGenerator, multiEntries: boolean, -): Promise { +): Promise { let entryIdentifiers: string[]; - let result: Promise<{}>; + let result: Promise; if (multiEntries) { result = self .prompt([ diff --git a/packages/generators/utils/language.ts b/packages/generators/utils/language.ts index 46883fcf4af..266a16c8ccb 100644 --- a/packages/generators/utils/language.ts +++ b/packages/generators/utils/language.ts @@ -57,7 +57,7 @@ function getEntryFolders(self): string[] { * @returns {ModuleRule} A configuration containing the babel-loader with env preset */ export function getBabelLoader(includeFolders: string[]): ModuleRule { - const include = includeFolders.map((folder: string) => + const include = includeFolders.map((folder: string): string => `path.resolve(__dirname, '${folder}')` ); return { @@ -85,7 +85,7 @@ export function getBabelLoader(includeFolders: string[]): ModuleRule { * @returns {ModuleRule} A configuration containing the ts-loader */ export function getTypescriptLoader(includeFolders: string[]): ModuleRule { - const include = includeFolders.map((folder: string) => + const include = includeFolders.map((folder: string): string => `path.resolve(__dirname, '${folder}')` ); return { From a650e0eb8b979e93758496fcf4ff98ff05619766 Mon Sep 17 00:00:00 2001 From: Devid Farinelli Date: Wed, 22 May 2019 16:33:44 +0200 Subject: [PATCH 21/31] misc(init-generator): use webpackOption types, improve test rules --- packages/generators/types/index.ts | 2 +- packages/generators/utils/language.ts | 27 ++++++++------------------- 2 files changed, 9 insertions(+), 20 deletions(-) diff --git a/packages/generators/types/index.ts b/packages/generators/types/index.ts index 449dc0cc477..32ed902ab23 100644 --- a/packages/generators/types/index.ts +++ b/packages/generators/types/index.ts @@ -214,7 +214,7 @@ export interface WebpackOptions { }; } -interface Rule { +export interface Rule { enforce?: "pre" | "post"; exclude?: IRuleSetCondition; include?: IRuleSetCondition; diff --git a/packages/generators/utils/language.ts b/packages/generators/utils/language.ts index 266a16c8ccb..7d3c1f901cf 100644 --- a/packages/generators/utils/language.ts +++ b/packages/generators/utils/language.ts @@ -1,21 +1,10 @@ +import { Rule } from "../types"; + export enum LangType { ES6 = "ES6", Typescript = "Typescript", } -interface ModuleRule extends Object { - include?: string[]; - exclude?: string[]; - loader: string; - options?: { - plugins: string[]; - presets: Preset[][]; - }; - test: string; -} - -type Preset = string | object; - const replaceExt = (path: string, ext: string): string => path.substr(0, path.lastIndexOf(".")) + `${ext}'`; @@ -54,14 +43,14 @@ function getEntryFolders(self): string[] { * * Returns an module.rule object for the babel loader * @param {string[]} includeFolders An array of folders to include - * @returns {ModuleRule} A configuration containing the babel-loader with env preset + * @returns {Rule} A configuration containing the babel-loader with env preset */ -export function getBabelLoader(includeFolders: string[]): ModuleRule { +export function getBabelLoader(includeFolders: string[]): Rule { const include = includeFolders.map((folder: string): string => `path.resolve(__dirname, '${folder}')` ); return { - test: "/\.js$/", + test: "/\.(js|jsx)$/", include, loader: "'babel-loader'", options: { @@ -82,14 +71,14 @@ export function getBabelLoader(includeFolders: string[]): ModuleRule { * * Returns an module.rule object for the typescript loader * @param {string[]} includeFolders An array of folders to include - * @returns {ModuleRule} A configuration containing the ts-loader + * @returns {Rule} A configuration containing the ts-loader */ -export function getTypescriptLoader(includeFolders: string[]): ModuleRule { +export function getTypescriptLoader(includeFolders: string[]): Rule { const include = includeFolders.map((folder: string): string => `path.resolve(__dirname, '${folder}')` ); return { - test: "/\.tsx?$/", + test: "/\.(ts|tsx)?$/", loader: "'ts-loader'", include, exclude: ["/node_modules/"], From 782f56c1b3f7cf1899aeeb49e3df67f1fd4c672c Mon Sep 17 00:00:00 2001 From: "devid.farinelli@gmail.com" Date: Tue, 28 May 2019 14:19:38 +0200 Subject: [PATCH 22/31] misc(generators): small text improvements --- packages/generators/utils/entry.ts | 2 +- packages/webpack-scaffold/README.md | 51 +++++++++++++++-------------- 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/packages/generators/utils/entry.ts b/packages/generators/utils/entry.ts index ea979b4dc94..2d29c7dda6b 100644 --- a/packages/generators/utils/entry.ts +++ b/packages/generators/utils/entry.ts @@ -27,7 +27,7 @@ export default function entry( .prompt([ InputValidate( "multipleEntries", - "How do you want to name your bundles? (separated by comma)", + "What do you want to name your bundles? (separated by comma)", validate, "pageOne, pageTwo" ) diff --git a/packages/webpack-scaffold/README.md b/packages/webpack-scaffold/README.md index 33282ca8cf9..2d727cc00bf 100755 --- a/packages/webpack-scaffold/README.md +++ b/packages/webpack-scaffold/README.md @@ -11,20 +11,21 @@ npm i -D webpack-cli @webpack-cli/webpack-scaffold ``` # API -- [parseValue](#parsevalue) -- [createArrowFunction](#createarrowfunction) -- [createRegularFunction](#createregularfunction) -- [createDynamicPromise](#createdynamicpromise) -- [createAssetFilterFunction](#createassetfilterfunction) -- [createExternalFunction](#createexternalfunction) -- [createRequire](#createrequire) -- [Inquirer](#inquirer) - - [List](#list) - - [RawList](#rawlist) - - [CheckList](#checklist) - - [Input](#input) - - [InputValidate](#inputvalidate) - - [Confirm](#confirm) + +- [parseValue](#parsevalue) +- [createArrowFunction](#createarrowfunction) +- [createRegularFunction](#createregularfunction) +- [createDynamicPromise](#createdynamicpromise) +- [createAssetFilterFunction](#createassetfilterfunction) +- [createExternalFunction](#createexternalfunction) +- [createRequire](#createrequire) +- [Inquirer](#inquirer) + - [List](#list) + - [RawList](#rawlist) + - [CheckList](#checklist) + - [Input](#input) + - [InputValidate](#inputvalidate) + - [Confirm](#confirm) ## parseValue @@ -175,7 +176,7 @@ Creates an Input from Inquirer ```js const Input = require("@webpack-cli/webpack-scaffold").Input; -Input('entry', 'what is your entry point?', 'src/index') +Input("entry", "what is your entry point?", "src/index"); ``` ### InputValidate @@ -185,16 +186,16 @@ Param: `name, message, [validate, default]` Creates an Input from Inquirer ```js -const InputValidate = require('@webpack-cli/webpack-scaffold').InputValidate; - -const validation = (value) => { - if(value.length > 4) { - return true; - } else { - return 'Wow, that was short!' - } -} -InputValidate('entry', 'what is your entry point?', validation, 'src/index') +const InputValidate = require("@webpack-cli/webpack-scaffold").InputValidate; + +const validation = value => { + if (value.length > 4) { + return true; + } else { + return "The entry point must be longer than 4, try again"; + } +}; +InputValidate("entry", "what is your entry point?", validation, "src/index"); ``` ### Confirm From 376dcbd8ca8c3d6121f5d0e4fe5b0b65f5dcfe86 Mon Sep 17 00:00:00 2001 From: Devid Farinelli Date: Wed, 29 May 2019 16:01:58 +0200 Subject: [PATCH 23/31] misc(generators): refactor --- packages/generators/init-generator.ts | 37 +++---------------- .../utils/{language.ts => languageSupport.ts} | 0 .../utils/{style.ts => styleSupport.ts} | 0 packages/generators/utils/webpackConfig.ts | 32 ++++++++++++++++ packages/webpack-scaffold/README.md | 2 +- 5 files changed, 38 insertions(+), 33 deletions(-) rename packages/generators/utils/{language.ts => languageSupport.ts} (100%) rename packages/generators/utils/{style.ts => styleSupport.ts} (100%) create mode 100644 packages/generators/utils/webpackConfig.ts diff --git a/packages/generators/init-generator.ts b/packages/generators/init-generator.ts index 7a4df24f43c..a2733828d8b 100644 --- a/packages/generators/init-generator.ts +++ b/packages/generators/init-generator.ts @@ -6,10 +6,11 @@ import * as path from "path"; import { getPackageManager } from "@webpack-cli/utils/package-manager"; import { Confirm, Input, List } from "@webpack-cli/webpack-scaffold"; +import { getDefaultOptimization } from "./utils/webpackConfig"; import { WebpackOptions } from "./types"; import entryQuestions from "./utils/entry"; -import langQuestionHandler, { LangType } from "./utils/language"; -import styleQuestionHandler, { Loader, StylingType } from "./utils/style"; +import langQuestionHandler, { LangType } from "./utils/languageSupport"; +import styleQuestionHandler, { Loader, StylingType } from "./utils/styleSupport"; import tooltip from "./utils/tooltip"; /** @@ -87,36 +88,8 @@ export default class InitGenerator extends Generator { "new webpack.ProgressPlugin()", ); - let optimizationObj; - - if (!this.isProd) { - optimizationObj = { - splitChunks: { - chunks: "'all'", - }, - }; - } else { - optimizationObj = { - minimizer: [ - "new TerserPlugin()", - ], - splitChunks: { - cacheGroups: { - vendors: { - priority: -10, - test: "/[\\\\/]node_modules[\\\\/]/", - }, - }, - chunks: "'async'", - minChunks: 1, - minSize: 30000, - // for production name is recommended to be off - name: !this.isProd, - }, - }; - } - - this.configuration.config.webpackOptions.optimization = optimizationObj; + let optimizationConfig = getDefaultOptimization(this.isProd); + this.configuration.config.webpackOptions.optimization = optimizationConfig; if (!this.isProd) { this.configuration.config.webpackOptions.devServer = { diff --git a/packages/generators/utils/language.ts b/packages/generators/utils/languageSupport.ts similarity index 100% rename from packages/generators/utils/language.ts rename to packages/generators/utils/languageSupport.ts diff --git a/packages/generators/utils/style.ts b/packages/generators/utils/styleSupport.ts similarity index 100% rename from packages/generators/utils/style.ts rename to packages/generators/utils/styleSupport.ts diff --git a/packages/generators/utils/webpackConfig.ts b/packages/generators/utils/webpackConfig.ts new file mode 100644 index 00000000000..1fddf5ee84d --- /dev/null +++ b/packages/generators/utils/webpackConfig.ts @@ -0,0 +1,32 @@ +import { WebpackOptions } from '../types'; + +export function getDefaultOptimization(isProd: boolean): WebpackOptions["optimization"] { + let optimizationOptions; + if (isProd) { + optimizationOptions = { + splitChunks: { + chunks: "'all'", + }, + }; + } else { + optimizationOptions = { + minimizer: [ + "new TerserPlugin()", + ], + splitChunks: { + cacheGroups: { + vendors: { + priority: -10, + test: "/[\\\\/]node_modules[\\\\/]/", + }, + }, + chunks: "'async'", + minChunks: 1, + minSize: 30000, + // for production name is recommended to be off + name: !this.isProd, + }, + }; + } + return optimizationOptions; +} \ No newline at end of file diff --git a/packages/webpack-scaffold/README.md b/packages/webpack-scaffold/README.md index 2d727cc00bf..5c430fe9495 100755 --- a/packages/webpack-scaffold/README.md +++ b/packages/webpack-scaffold/README.md @@ -192,7 +192,7 @@ const validation = value => { if (value.length > 4) { return true; } else { - return "The entry point must be longer than 4, try again"; + return "Your answer must be longer than 4, try again"; } }; InputValidate("entry", "what is your entry point?", validation, "src/index"); From fb25bd2cb1d72def40e14ba431fe368cd6df6653 Mon Sep 17 00:00:00 2001 From: Devid Farinelli Date: Wed, 29 May 2019 16:03:20 +0200 Subject: [PATCH 24/31] tests(inputvalidate): remove undefined --- .../__tests__/__snapshots__/index.test.ts.snap | 1 - packages/webpack-scaffold/__tests__/index.test.ts | 1 - packages/webpack-scaffold/index.ts | 5 +++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/webpack-scaffold/__tests__/__snapshots__/index.test.ts.snap b/packages/webpack-scaffold/__tests__/__snapshots__/index.test.ts.snap index 0e06b120337..2bb1ed2117f 100755 --- a/packages/webpack-scaffold/__tests__/__snapshots__/index.test.ts.snap +++ b/packages/webpack-scaffold/__tests__/__snapshots__/index.test.ts.snap @@ -2,7 +2,6 @@ exports[`utils Inquirer should make an Input object with validation 1`] = ` Object { - "default": undefined, "message": "what is your plugin?", "name": "plugins", "type": "input", diff --git a/packages/webpack-scaffold/__tests__/index.test.ts b/packages/webpack-scaffold/__tests__/index.test.ts index 586bbcd9a58..2d0ccef8582 100755 --- a/packages/webpack-scaffold/__tests__/index.test.ts +++ b/packages/webpack-scaffold/__tests__/index.test.ts @@ -76,7 +76,6 @@ describe("utils", () => { }); it("should make an Input object", () => { expect(utils.Input("plugins", "what is your plugin?")).toEqual({ - default: undefined, message: "what is your plugin?", name: "plugins", type: "input", diff --git a/packages/webpack-scaffold/index.ts b/packages/webpack-scaffold/index.ts index 6477030c331..8eea29f9f95 100755 --- a/packages/webpack-scaffold/index.ts +++ b/packages/webpack-scaffold/index.ts @@ -92,13 +92,14 @@ export function InputValidate( defaultChoice?: string, ): Generator.Question { - return { - default: defaultChoice, + const input: Generator.Question = { message, name, type: "input", validate: cb }; + if (defaultChoice !== undefined) input.default = defaultChoice; + return input; } export function Confirm(name: string, message: string, defaultChoice: boolean = true): Generator.Question { From dcf44c155c6900f599d09219723ccf7b5b4cf109 Mon Sep 17 00:00:00 2001 From: Devid Farinelli Date: Thu, 30 May 2019 01:44:25 +0200 Subject: [PATCH 25/31] misc(init-generator): small refactor --- packages/webpack-scaffold/index.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/webpack-scaffold/index.ts b/packages/webpack-scaffold/index.ts index 8eea29f9f95..6c609b8e522 100755 --- a/packages/webpack-scaffold/index.ts +++ b/packages/webpack-scaffold/index.ts @@ -91,14 +91,13 @@ export function InputValidate( cb?: (input: string) => string | boolean, defaultChoice?: string, ): Generator.Question { - const input: Generator.Question = { message, name, type: "input", validate: cb }; - if (defaultChoice !== undefined) input.default = defaultChoice; + if (defaultChoice) input.default = defaultChoice; return input; } From f9716327050dcd6eacae3e6057ec5c72874e57c7 Mon Sep 17 00:00:00 2001 From: Devid Farinelli Date: Thu, 30 May 2019 15:55:59 +0200 Subject: [PATCH 26/31] misc(init-generator): improve readme --- packages/webpack-scaffold/README.md | 33 ++++++++++++++++------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/packages/webpack-scaffold/README.md b/packages/webpack-scaffold/README.md index 5c430fe9495..413c223a51a 100755 --- a/packages/webpack-scaffold/README.md +++ b/packages/webpack-scaffold/README.md @@ -12,20 +12,23 @@ npm i -D webpack-cli @webpack-cli/webpack-scaffold # API -- [parseValue](#parsevalue) -- [createArrowFunction](#createarrowfunction) -- [createRegularFunction](#createregularfunction) -- [createDynamicPromise](#createdynamicpromise) -- [createAssetFilterFunction](#createassetfilterfunction) -- [createExternalFunction](#createexternalfunction) -- [createRequire](#createrequire) -- [Inquirer](#inquirer) - - [List](#list) - - [RawList](#rawlist) - - [CheckList](#checklist) - - [Input](#input) - - [InputValidate](#inputvalidate) - - [Confirm](#confirm) +- [webpack-scaffold](#webpack-scaffold) +- [Installation](#installation) +- [API](#api) + - [parseValue](#parsevalue) + - [createArrowFunction](#createarrowfunction) + - [createRegularFunction](#createregularfunction) + - [createDynamicPromise](#createdynamicpromise) + - [createAssetFilterFunction](#createassetfilterfunction) + - [createExternalFunction](#createexternalfunction) + - [createRequire](#createrequire) + - [Inquirer](#inquirer) + - [List](#list) + - [RawList](#rawlist) + - [CheckList](#checklist) + - [Input](#input) + - [InputValidate](#inputvalidate) + - [Confirm](#confirm) ## parseValue @@ -192,7 +195,7 @@ const validation = value => { if (value.length > 4) { return true; } else { - return "Your answer must be longer than 4, try again"; + return "Your answer must be longer than 4 characters, try again"; } }; InputValidate("entry", "what is your entry point?", validation, "src/index"); From 4e8bc760c622bc1fe6d91c0ae7a366adc2163d3b Mon Sep 17 00:00:00 2001 From: ev1stensberg Date: Thu, 30 May 2019 17:44:05 +0200 Subject: [PATCH 27/31] fix: change parser options --- package-lock.json | 3 +-- packages/utils/run-prettier.ts | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7b47fe3b74d..4e17dad8491 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13179,8 +13179,7 @@ "prettier": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.17.0.tgz", - "integrity": "sha512-sXe5lSt2WQlCbydGETgfm1YBShgOX4HxQkFPvbxkcwgDvGDeqVau8h+12+lmSVlP3rHPz0oavfddSZg/q+Szjw==", - "dev": true + "integrity": "sha512-sXe5lSt2WQlCbydGETgfm1YBShgOX4HxQkFPvbxkcwgDvGDeqVau8h+12+lmSVlP3rHPz0oavfddSZg/q+Szjw==" }, "prettier-eslint": { "version": "8.8.2", diff --git a/packages/utils/run-prettier.ts b/packages/utils/run-prettier.ts index bf4980150d9..da5cc0a890a 100644 --- a/packages/utils/run-prettier.ts +++ b/packages/utils/run-prettier.ts @@ -19,7 +19,7 @@ export default function runPrettier(outputPath: string, source: string, cb?: Fun try { prettySource = prettier.format(source, { filepath: outputPath, - parser: "babel", + parser: "babylon", singleQuote: true, tabWidth: 1, useTabs: true, From bd06a69076d95863827fe43fa76b8fb554e7dbb1 Mon Sep 17 00:00:00 2001 From: Devid Farinelli Date: Thu, 30 May 2019 17:58:33 +0200 Subject: [PATCH 28/31] misc(generators): remove comment --- packages/generators/utils/webpackConfig.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/generators/utils/webpackConfig.ts b/packages/generators/utils/webpackConfig.ts index 1fddf5ee84d..3ae9ce88d5a 100644 --- a/packages/generators/utils/webpackConfig.ts +++ b/packages/generators/utils/webpackConfig.ts @@ -23,7 +23,6 @@ export function getDefaultOptimization(isProd: boolean): WebpackOptions["optimiz chunks: "'async'", minChunks: 1, minSize: 30000, - // for production name is recommended to be off name: !this.isProd, }, }; From 88fcfa8222f32593b3d8adae46a24b545f7d1c78 Mon Sep 17 00:00:00 2001 From: ev1stensberg Date: Thu, 30 May 2019 18:23:17 +0200 Subject: [PATCH 29/31] feat: add htmlWebpackPlugin in development --- packages/generators/init-generator.ts | 14 ++++++++++++++ packages/generators/utils/tooltip.ts | 11 +++++++++++ packages/generators/utils/webpackConfig.ts | 6 +++--- packages/utils/run-prettier.ts | 2 +- 4 files changed, 29 insertions(+), 4 deletions(-) diff --git a/packages/generators/init-generator.ts b/packages/generators/init-generator.ts index a2733828d8b..8997c3f66e5 100644 --- a/packages/generators/init-generator.ts +++ b/packages/generators/init-generator.ts @@ -12,6 +12,7 @@ import entryQuestions from "./utils/entry"; import langQuestionHandler, { LangType } from "./utils/languageSupport"; import styleQuestionHandler, { Loader, StylingType } from "./utils/styleSupport"; import tooltip from "./utils/tooltip"; +import { generatePluginName } from "./utils/plugins"; /** * @@ -224,6 +225,19 @@ export default class InitGenerator extends Generator { ); } } + if(!this.isProd) { + this.dependencies.push("html-webpack-plugin"); + const htmlWebpackDependency: string = "html-webpack-plugin"; + const htmlwebpackPlugin: string = generatePluginName(htmlWebpackDependency); + (this.configuration.config.topScope as string[]).push( + `const ${htmlwebpackPlugin} = require('${htmlWebpackDependency}')`, + "\n", + tooltip.html(), + ); + (this.configuration.config.webpackOptions.plugins as string[]).push( + `new ${htmlwebpackPlugin}()`, + ); + } done(); } diff --git a/packages/generators/utils/tooltip.ts b/packages/generators/utils/tooltip.ts index cd9dba4e7ec..c58c7fba922 100644 --- a/packages/generators/utils/tooltip.ts +++ b/packages/generators/utils/tooltip.ts @@ -57,6 +57,17 @@ export default { * * https://github.com/webpack-contrib/terser-webpack-plugin * + */`; + }, + + html: (): string => { + return `/* + * We've enabled HtmlWebpackPlugin for you! This generates a html + * page for you when you compile webpack, which will make you start + * developing and prototyping faster. + * + * https://github.com/jantimon/html-webpack-plugin + * */`; } }; diff --git a/packages/generators/utils/webpackConfig.ts b/packages/generators/utils/webpackConfig.ts index 3ae9ce88d5a..b67d3c6d0c5 100644 --- a/packages/generators/utils/webpackConfig.ts +++ b/packages/generators/utils/webpackConfig.ts @@ -4,15 +4,15 @@ export function getDefaultOptimization(isProd: boolean): WebpackOptions["optimiz let optimizationOptions; if (isProd) { optimizationOptions = { + minimizer: [ + "new TerserPlugin()", + ], splitChunks: { chunks: "'all'", }, }; } else { optimizationOptions = { - minimizer: [ - "new TerserPlugin()", - ], splitChunks: { cacheGroups: { vendors: { diff --git a/packages/utils/run-prettier.ts b/packages/utils/run-prettier.ts index da5cc0a890a..bf4980150d9 100644 --- a/packages/utils/run-prettier.ts +++ b/packages/utils/run-prettier.ts @@ -19,7 +19,7 @@ export default function runPrettier(outputPath: string, source: string, cb?: Fun try { prettySource = prettier.format(source, { filepath: outputPath, - parser: "babylon", + parser: "babel", singleQuote: true, tabWidth: 1, useTabs: true, From 0552f76922452203a9765bd8aa371393b9fe09a2 Mon Sep 17 00:00:00 2001 From: ev1stensberg Date: Thu, 30 May 2019 18:31:50 +0200 Subject: [PATCH 30/31] fix: remove type from inherited type --- packages/generators/init-generator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/generators/init-generator.ts b/packages/generators/init-generator.ts index 8997c3f66e5..dbaa16c6058 100644 --- a/packages/generators/init-generator.ts +++ b/packages/generators/init-generator.ts @@ -228,7 +228,7 @@ export default class InitGenerator extends Generator { if(!this.isProd) { this.dependencies.push("html-webpack-plugin"); const htmlWebpackDependency: string = "html-webpack-plugin"; - const htmlwebpackPlugin: string = generatePluginName(htmlWebpackDependency); + const htmlwebpackPlugin = generatePluginName(htmlWebpackDependency); (this.configuration.config.topScope as string[]).push( `const ${htmlwebpackPlugin} = require('${htmlWebpackDependency}')`, "\n", From 960e73a47715cadbe2b850065b1ce0a4acf63a29 Mon Sep 17 00:00:00 2001 From: ev1stensberg Date: Thu, 30 May 2019 18:38:45 +0200 Subject: [PATCH 31/31] fix: remove type from inherited type --- packages/generators/init-generator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/generators/init-generator.ts b/packages/generators/init-generator.ts index dbaa16c6058..9d5b33f0b51 100644 --- a/packages/generators/init-generator.ts +++ b/packages/generators/init-generator.ts @@ -227,7 +227,7 @@ export default class InitGenerator extends Generator { } if(!this.isProd) { this.dependencies.push("html-webpack-plugin"); - const htmlWebpackDependency: string = "html-webpack-plugin"; + const htmlWebpackDependency = "html-webpack-plugin"; const htmlwebpackPlugin = generatePluginName(htmlWebpackDependency); (this.configuration.config.topScope as string[]).push( `const ${htmlwebpackPlugin} = require('${htmlWebpackDependency}')`,