From e456f63233b1fbea4f6dd680934d510a0a2a3aec Mon Sep 17 00:00:00 2001 From: Rishabh Chawla Date: Mon, 29 Nov 2021 13:40:44 +0530 Subject: [PATCH 01/15] chore: setup sub package --- packages/create-webpack-app/index.ts | 1 + packages/create-webpack-app/package.json | 31 +++++++++++++++++++++++ packages/create-webpack-app/tsconfig.json | 9 +++++++ 3 files changed, 41 insertions(+) create mode 100644 packages/create-webpack-app/index.ts create mode 100644 packages/create-webpack-app/package.json create mode 100644 packages/create-webpack-app/tsconfig.json diff --git a/packages/create-webpack-app/index.ts b/packages/create-webpack-app/index.ts new file mode 100644 index 00000000000..ace46096fa4 --- /dev/null +++ b/packages/create-webpack-app/index.ts @@ -0,0 +1 @@ +import yeoman from "yeoman-environment"; diff --git a/packages/create-webpack-app/package.json b/packages/create-webpack-app/package.json new file mode 100644 index 00000000000..1b8b9154188 --- /dev/null +++ b/packages/create-webpack-app/package.json @@ -0,0 +1,31 @@ +{ + "name": "create-webpack-app", + "version": "1.0.0", + "description": "Create new project with webpack", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "license": "MIT", + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "https://github.com/webpack/webpack-cli.git" + }, + "files": [ + "lib", + "template" + ], + "dependencies": { + "yeoman-environment": "^2.10.3", + "yeoman-generator": "^4.12.0" + }, + "peerDependenciesMeta": { + "prettier": { + "optional": true + } + }, + "devDependencies": { + "@types/yeoman-generator": "^4.11.3" + } +} diff --git a/packages/create-webpack-app/tsconfig.json b/packages/create-webpack-app/tsconfig.json new file mode 100644 index 00000000000..173f75d3f62 --- /dev/null +++ b/packages/create-webpack-app/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "exclude": ["src/utils/__tests__"], + "compilerOptions": { + "outDir": "lib", + "rootDir": "src" + }, + "include": ["src"] +} From b7a346e987ab28a2dbc79ad8181415524bbb0c88 Mon Sep 17 00:00:00 2001 From: Rishabh Chawla Date: Mon, 29 Nov 2021 14:44:48 +0530 Subject: [PATCH 02/15] chore: move init.md to create-webpack-app --- packages/{generators/INIT.md => create-webpack-app/README.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/{generators/INIT.md => create-webpack-app/README.md} (100%) diff --git a/packages/generators/INIT.md b/packages/create-webpack-app/README.md similarity index 100% rename from packages/generators/INIT.md rename to packages/create-webpack-app/README.md From 1773612d019f6b485c7c8871f4566f9c33796946 Mon Sep 17 00:00:00 2001 From: Rishabh Chawla Date: Mon, 29 Nov 2021 15:10:44 +0530 Subject: [PATCH 03/15] chore: move important files --- packages/create-webpack-app/src/handlers.ts | 5 + .../src/handlers/default.ts | 217 ++++++++++++++++++ packages/create-webpack-app/src/helpers.ts | 63 +++++ .../create-webpack-app/{ => src}/index.ts | 0 .../create-webpack-app/src/init-generator.ts | 123 ++++++++++ .../create-webpack-app/src/scaffold-utils.ts | 75 ++++++ .../create-webpack-app/src/types/index.ts | 8 + 7 files changed, 491 insertions(+) create mode 100644 packages/create-webpack-app/src/handlers.ts create mode 100644 packages/create-webpack-app/src/handlers/default.ts create mode 100644 packages/create-webpack-app/src/helpers.ts rename packages/create-webpack-app/{ => src}/index.ts (100%) create mode 100644 packages/create-webpack-app/src/init-generator.ts create mode 100644 packages/create-webpack-app/src/scaffold-utils.ts create mode 100644 packages/create-webpack-app/src/types/index.ts diff --git a/packages/create-webpack-app/src/handlers.ts b/packages/create-webpack-app/src/handlers.ts new file mode 100644 index 00000000000..9d3a213686f --- /dev/null +++ b/packages/create-webpack-app/src/handlers.ts @@ -0,0 +1,5 @@ +import * as defaultHandler from "./handlers/default"; + +export default { + default: defaultHandler, +}; diff --git a/packages/create-webpack-app/src/handlers/default.ts b/packages/create-webpack-app/src/handlers/default.ts new file mode 100644 index 00000000000..d025b0bce3b --- /dev/null +++ b/packages/create-webpack-app/src/handlers/default.ts @@ -0,0 +1,217 @@ +import path from "path"; +import { CustomGenerator } from "../types"; + +const templatePath = path.resolve(__dirname, "../../init-template/default"); +const resolveFile = (file: string): string => { + return path.resolve(templatePath, file); +}; + +/** + * Asks questions to the user used to modify generation + * @param self Generator values + * @param Question Contains questions + */ + +export async function questions( + self: CustomGenerator, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + Question: Record, +): Promise { + // Handle JS language solutions + const { langType } = await Question.List( + self, + "langType", + "Which of the following JS solutions do you want to use?", + ["none", "ES6", "Typescript"], + "none", + self.force, + ); + + switch (langType) { + case "ES6": + self.dependencies.push("babel-loader", "@babel/core", "@babel/preset-env"); + break; + case "Typescript": + self.dependencies.push("typescript", "ts-loader"); + break; + } + + // Configure devServer configuraion + const { devServer } = await Question.Confirm( + self, + "devServer", + "Do you want to use webpack-dev-server?", + true, + self.force, + ); + if (devServer) { + self.dependencies.push("webpack-dev-server"); + } + + // Handle addition of html-webpack-plugin + const { htmlWebpackPlugin } = await Question.Confirm( + self, + "htmlWebpackPlugin", + "Do you want to simplify the creation of HTML files for your bundle?", + true, + self.force, + ); + if (htmlWebpackPlugin) { + self.dependencies.push("html-webpack-plugin"); + } + + // Handle addition of workbox-webpack-plugin + const { workboxWebpackPlugin } = await Question.Confirm( + self, + "workboxWebpackPlugin", + "Do you want to add PWA support?", + true, + self.force, + ); + if (workboxWebpackPlugin) { + self.dependencies.push("workbox-webpack-plugin"); + } + + // Store all answers for generation + self.answers = { + ...self.answers, + langType, + devServer, + htmlWebpackPlugin, + workboxWebpackPlugin, + }; + + // Handle CSS solutions + const { cssType } = await Question.List( + self, + "cssType", + "Which of the following CSS solutions do you want to use?", + ["none", "CSS only", "SASS", "LESS", "Stylus"], + "none", + self.force, + ); + + if (cssType == "none") { + self.answers = { + ...self.answers, + cssType, + isCSS: false, + isPostCSS: false, + extractPlugin: "No", + }; + return; + } + + const { isCSS } = + cssType != "CSS only" + ? await Question.Confirm( + self, + "isCSS", + `Will you be using CSS styles along with ${cssType} in your project?`, + true, + self.force, + ) + : { isCSS: true }; + + const { isPostCSS } = await Question.Confirm( + self, + "isPostCSS", + "Will you be using PostCSS in your project?", + cssType == "CSS only", + self.force, + ); + + const { extractPlugin } = await Question.List( + self, + "extractPlugin", + "Do you want to extract CSS for every file?", + ["No", "Only for Production", "Yes"], + "No", + self.force, + ); + + switch (cssType) { + case "SASS": + self.dependencies.push("sass-loader", "sass"); + break; + case "LESS": + self.dependencies.push("less-loader", "less"); + break; + case "Stylus": + self.dependencies.push("stylus-loader", "stylus"); + break; + } + + if (isCSS) { + self.dependencies.push("style-loader", "css-loader"); + } + + if (isPostCSS) { + self.dependencies.push("postcss-loader", "postcss", "autoprefixer"); + } + + if (extractPlugin !== "No") { + self.dependencies.push("mini-css-extract-plugin"); + } + + self.answers = { + ...self.answers, + cssType, + isCSS, + isPostCSS, + extractPlugin, + }; +} + +/** + * Handles generation of project files + * @param self Generator values + */ +export function generate(self: CustomGenerator): void { + self.fs.extendJSON( + self.destinationPath("package.json"), + // eslint-disable-next-line @typescript-eslint/no-var-requires + require(resolveFile("package.json.js"))(self.answers.devServer), + ); + + // Generate entry file + let entry = "./src/index."; + if (self.answers.langType == "Typescript") { + entry += "ts"; + } else { + entry += "js"; + } + self.fs.copyTpl(resolveFile("index.js"), self.destinationPath(entry)); + + // Generate README + self.fs.copyTpl(resolveFile("README.md"), self.destinationPath("README.md"), {}); + + // Generate HTML file + self.fs.copyTpl( + resolveFile("template.html.tpl"), + self.destinationPath("index.html"), + self.answers, + ); + + // Generate webpack configuration + self.fs.copyTpl(resolveFile("webpack.configjs.tpl"), self.destinationPath("webpack.config.js"), { + ...self.answers, + entry, + }); + self.configurationPath = self.destinationPath("webpack.config.js"); + + // Generate JS language essentials + switch (self.answers.langType) { + case "ES6": + self.fs.copyTpl(resolveFile(".babelrc"), self.destinationPath(".babelrc")); + break; + case "Typescript": + self.fs.copyTpl(resolveFile("tsconfig.json"), self.destinationPath("tsconfig.json")); + break; + } + + // Generate postcss configuration + if (self.answers.isPostCSS) { + self.fs.copyTpl(resolveFile("postcss.config.js"), self.destinationPath("postcss.config.js")); + } +} diff --git a/packages/create-webpack-app/src/helpers.ts b/packages/create-webpack-app/src/helpers.ts new file mode 100644 index 00000000000..d63b2187988 --- /dev/null +++ b/packages/create-webpack-app/src/helpers.ts @@ -0,0 +1,63 @@ +import { List } from "./scaffold-utils"; + +const regex = /[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g; + +/** + * Convert str to kebab-case + * @param str input string + * @returns output string + */ +export function toKebabCase(str: string): string { + return str.match(regex).join("-").toLowerCase(); +} + +/** + * Convert str to UpperCamelCase + * @param str import string + * @returns {string} output string + */ +export function toUpperCamelCase(str: string): string { + return str + .match(regex) + .map((x) => x.slice(0, 1).toUpperCase() + x.slice(1).toLowerCase()) + .join(""); +} + +export async function getInstaller(): Promise { + const installers = this.cli.getAvailablePackageManagers(); + + if (installers.length === 1) { + return installers[0]; + } + + // Prompt for the package manager of choice + const defaultPackager = this.cli.getDefaultPackageManager(); + const { packager } = await List( + this, + "packager", + "Pick a package manager:", + installers, + defaultPackager, + this.force, + ); + return packager; +} + +export async function getTemplate(): Promise { + if (this.supportedTemplates.includes(this.template)) { + return this.template; + } + + this.cli.logger.warn(`⚠ ${this.template} is not a valid template, please select one from below`); + + const { selectedTemplate } = await List( + this, + "selectedTemplate", + "Select a valid template from below:", + this.supportedTemplates, + "default", + false, + ); + + return selectedTemplate; +} diff --git a/packages/create-webpack-app/index.ts b/packages/create-webpack-app/src/index.ts similarity index 100% rename from packages/create-webpack-app/index.ts rename to packages/create-webpack-app/src/index.ts diff --git a/packages/create-webpack-app/src/init-generator.ts b/packages/create-webpack-app/src/init-generator.ts new file mode 100644 index 00000000000..b104d6b1c82 --- /dev/null +++ b/packages/create-webpack-app/src/init-generator.ts @@ -0,0 +1,123 @@ +import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs"; +import path from "path"; + +import { CustomGenerator } from "./types"; +import { getInstaller, getTemplate } from "./helpers"; +import * as Question from "./scaffold-utils"; +import handlers from "./handlers"; + +/** + * + * Generator for initializing a webpack config + * + * @class InitGenerator + * @extends CustomGenerator + * @returns {Void} After execution, transforms are triggered + * + */ +export default class InitGenerator extends CustomGenerator { + public answers: Record; + public configurationPath: string; + public force: boolean; + public generationPath: string; + public packageManager: string; + public resolvedGenerationPath: string; + public supportedTemplates: string[]; + public template: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + public cli: any; + + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any + public constructor(args: any, opts: any) { + super(args, opts); + + const { options } = opts; + + this.template = options.template; + this.generationPath = options.generationPath; + this.resolvedGenerationPath = path.resolve(process.cwd(), this.generationPath); + this.force = options.force; + this.dependencies = ["webpack", "webpack-cli"]; + this.supportedTemplates = Object.keys(handlers); + this.answers = {}; + this.cli = opts.cli; + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + public async prompting(): Promise { + if (!existsSync(this.resolvedGenerationPath)) { + this.cli.logger.log( + `${this.cli.colors.blue( + "ℹ INFO ", + )} supplied generation path doesn't exist, required folders will be created.`, + ); + try { + mkdirSync(this.resolvedGenerationPath, { recursive: true }); + } catch (error) { + this.cli.logger.error(`Failed to create directory.\n ${error}`); + process.exit(2); + } + } + + this.template = await getTemplate.call(this); + + await handlers[this.template].questions(this, Question); + + // Handle installation of prettier + try { + // eslint-disable-next-line node/no-extraneous-require + require.resolve("prettier"); + } catch (err) { + const { installPrettier } = await Question.Confirm( + this, + "installPrettier", + "Do you like to install prettier to format generated configuration?", + true, + false, + ); + + if (installPrettier) { + this.dependencies.push("prettier"); + } + } + } + + public async installPlugins(): Promise { + this.packageManager = await getInstaller.call(this); + + const opts: { + dev?: boolean; + "save-dev"?: boolean; + } = this.packageManager === "yarn" ? { dev: true } : { "save-dev": true }; + + this.scheduleInstallTask(this.packageManager, this.dependencies, opts, { + cwd: this.generationPath, + }); + } + + public writing(): void { + this.cli.logger.log(`${this.cli.colors.blue("ℹ INFO ")} Initialising project...`); + + handlers[this.template].generate(this); + } + + public end(): void { + // Prettify configuration file if possible + try { + // eslint-disable-next-line node/no-extraneous-require, @typescript-eslint/no-var-requires + const prettier = require("prettier"); + const source = readFileSync(this.configurationPath, { encoding: "utf8" }); + const formattedSource = prettier.format(source, { parser: "babel" }); + + writeFileSync(this.configurationPath, formattedSource); + } catch (err) { + this.cli.logger.log( + `${this.cli.colors.yellow( + `⚠ Generated configuration may not be properly formatted as prettier is not installed.`, + )}`, + ); + + return; + } + } +} diff --git a/packages/create-webpack-app/src/scaffold-utils.ts b/packages/create-webpack-app/src/scaffold-utils.ts new file mode 100644 index 00000000000..5c07b15513f --- /dev/null +++ b/packages/create-webpack-app/src/scaffold-utils.ts @@ -0,0 +1,75 @@ +import Generator from "yeoman-generator"; + +type CustomGeneratorStringPrompt = { [x: string]: string } | Promise<{ [x: string]: string }>; +type CustomGeneratorBoolPrompt = { [x: string]: boolean } | Promise<{ [x: string]: boolean }>; + +/* eslint-disable @typescript-eslint/no-explicit-any */ + +export function List( + self: Generator, + name: string, + message: string, + choices: string[], + defaultChoice?: string, + skip = false, +): CustomGeneratorStringPrompt { + if (skip) { + return { [name]: defaultChoice }; + } + + return self.prompt([{ choices, message, name, type: "list", default: defaultChoice }]); +} + +export function Input( + self: Generator, + name: string, + message: string, + defaultChoice?: string, + skip = false, +): CustomGeneratorStringPrompt { + if (skip) { + return { [name]: defaultChoice }; + } + + return self.prompt([{ default: defaultChoice, message, name, type: "input" }]); +} + +export function InputValidate( + self: Generator, + name: string, + message: string, + cb?: (input: string) => string | boolean, + defaultChoice?: string, + skip = false, +): Record | any { + if (skip) { + return { [name]: defaultChoice }; + } + + const input: Generator.Question = { + message, + name, + type: "input", + validate: cb, + }; + + if (defaultChoice) { + input.default = defaultChoice; + } + + return self.prompt([input]); +} + +export function Confirm( + self: Generator, + name: string, + message: string, + defaultChoice = true, + skip = false, +): CustomGeneratorBoolPrompt { + if (skip) { + return { [name]: defaultChoice }; + } + + return self.prompt([{ default: defaultChoice, message, name, type: "confirm" }]); +} diff --git a/packages/create-webpack-app/src/types/index.ts b/packages/create-webpack-app/src/types/index.ts new file mode 100644 index 00000000000..791abe1ae2f --- /dev/null +++ b/packages/create-webpack-app/src/types/index.ts @@ -0,0 +1,8 @@ +import Generator from "yeoman-generator"; + +export class CustomGenerator extends Generator { + public force: boolean; + public dependencies: string[]; + public answers: Record; + public configurationPath: string; +} From 014ba9d5257aeeb06291f1b2303903eb9fcefd00 Mon Sep 17 00:00:00 2001 From: Rishabh Chawla Date: Mon, 29 Nov 2021 15:51:47 +0530 Subject: [PATCH 04/15] chore: add commander as dependency --- packages/create-webpack-app/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/create-webpack-app/package.json b/packages/create-webpack-app/package.json index 1b8b9154188..a4f518afe78 100644 --- a/packages/create-webpack-app/package.json +++ b/packages/create-webpack-app/package.json @@ -18,7 +18,8 @@ ], "dependencies": { "yeoman-environment": "^2.10.3", - "yeoman-generator": "^4.12.0" + "yeoman-generator": "^4.12.0", + "commander": "^7.0.0" }, "peerDependenciesMeta": { "prettier": { From 5d4165ff393432b5ed24c3f720b2ca6f3b546981 Mon Sep 17 00:00:00 2001 From: Rishabh Chawla Date: Mon, 29 Nov 2021 16:16:06 +0530 Subject: [PATCH 05/15] chore: update docs --- packages/create-webpack-app/README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/create-webpack-app/README.md b/packages/create-webpack-app/README.md index 6961aba88d1..3acfe8d62a5 100644 --- a/packages/create-webpack-app/README.md +++ b/packages/create-webpack-app/README.md @@ -1,6 +1,6 @@ -# webpack-cli init +# create-webpack-app -`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. +`create-webpack-app` is used to initialize `webpack` projects quickly by scaffolding configuration and creating a runnable project with all the dependencies based on the user preferences. ## Table of Contents @@ -27,13 +27,13 @@ npm install --save-dev webpack webpack-cli ### Running Locally ```bash -npx webpack-cli init +npx create-webpack-app ``` ### Running Globally ```shell -webpack-cli init +create-webpack-app ``` ### CLI options @@ -41,25 +41,25 @@ webpack-cli init **To generate default template** ```bash -webpack-cli init +create-webpack-app ``` **To generate with default answers** ```bash -webpack-cli init -f, --force +create-webpack-app -f, --force ``` **To scaffold in a specified path** ```bash -webpack-cli init [generation-path] +create-webpack-app [generation-path] ``` **To scaffold specified template** ```bash -webpack-cli init -t, --template +create-webpack-app -t, --template ``` ## Description of questions asked by the generator From f15e0c2f012925c509a32a68b4c62199b6c7dfce Mon Sep 17 00:00:00 2001 From: Rishabh Chawla Date: Tue, 30 Nov 2021 11:57:09 +0530 Subject: [PATCH 06/15] chore: add templates folder --- .../templates/default/.babelrc | 11 +++ .../templates/default/README.md | 15 +++ .../templates/default/index.js | 1 + .../templates/default/package.json.js | 18 ++++ .../templates/default/postcss.config.js | 5 + .../templates/default/template.html.tpl | 26 +++++ .../templates/default/tsconfig.json | 10 ++ .../templates/default/webpack.configjs.tpl | 96 +++++++++++++++++++ 8 files changed, 182 insertions(+) create mode 100644 packages/create-webpack-app/templates/default/.babelrc create mode 100644 packages/create-webpack-app/templates/default/README.md create mode 100644 packages/create-webpack-app/templates/default/index.js create mode 100644 packages/create-webpack-app/templates/default/package.json.js create mode 100644 packages/create-webpack-app/templates/default/postcss.config.js create mode 100644 packages/create-webpack-app/templates/default/template.html.tpl create mode 100644 packages/create-webpack-app/templates/default/tsconfig.json create mode 100644 packages/create-webpack-app/templates/default/webpack.configjs.tpl diff --git a/packages/create-webpack-app/templates/default/.babelrc b/packages/create-webpack-app/templates/default/.babelrc new file mode 100644 index 00000000000..f2c9da3ae6e --- /dev/null +++ b/packages/create-webpack-app/templates/default/.babelrc @@ -0,0 +1,11 @@ +{ + "plugins": ["@babel/syntax-dynamic-import"], + "presets": [ + [ + "@babel/preset-env", + { + "modules": false + } + ] + ] +} diff --git a/packages/create-webpack-app/templates/default/README.md b/packages/create-webpack-app/templates/default/README.md new file mode 100644 index 00000000000..fa32788f72a --- /dev/null +++ b/packages/create-webpack-app/templates/default/README.md @@ -0,0 +1,15 @@ +# 🚀 Welcome to your new awesome project! + +This project has been created using **webpack-cli**, you can now run + +``` +npm run build +``` + +or + +``` +yarn build +``` + +to bundle your application diff --git a/packages/create-webpack-app/templates/default/index.js b/packages/create-webpack-app/templates/default/index.js new file mode 100644 index 00000000000..019c0f4bc8e --- /dev/null +++ b/packages/create-webpack-app/templates/default/index.js @@ -0,0 +1 @@ +console.log("Hello World!"); diff --git a/packages/create-webpack-app/templates/default/package.json.js b/packages/create-webpack-app/templates/default/package.json.js new file mode 100644 index 00000000000..79d967ebe16 --- /dev/null +++ b/packages/create-webpack-app/templates/default/package.json.js @@ -0,0 +1,18 @@ +module.exports = (isUsingDevServer) => { + const scripts = { + build: "webpack --mode=production --node-env=production", + "build:dev": "webpack --mode=development", + "build:prod": "webpack --mode=production --node-env=production", + watch: "webpack --watch", + }; + if (isUsingDevServer) { + scripts.serve = "webpack serve"; + } + + return { + version: "1.0.0", + description: "My webpack project", + name: "my-webpack-project", + scripts, + }; +}; diff --git a/packages/create-webpack-app/templates/default/postcss.config.js b/packages/create-webpack-app/templates/default/postcss.config.js new file mode 100644 index 00000000000..3fa4289052e --- /dev/null +++ b/packages/create-webpack-app/templates/default/postcss.config.js @@ -0,0 +1,5 @@ +module.exports = { + // Add you postcss configuration here + // Learn more about it at https://github.com/webpack-contrib/postcss-loader#config-files + plugins: [["autoprefixer"]], +}; diff --git a/packages/create-webpack-app/templates/default/template.html.tpl b/packages/create-webpack-app/templates/default/template.html.tpl new file mode 100644 index 00000000000..d61cf83b04b --- /dev/null +++ b/packages/create-webpack-app/templates/default/template.html.tpl @@ -0,0 +1,26 @@ + + + + + Webpack App + + +

Hello world!

+

Tip: Check your console

+ + <% if (workboxWebpackPlugin) { %> + <% } %> + diff --git a/packages/create-webpack-app/templates/default/tsconfig.json b/packages/create-webpack-app/templates/default/tsconfig.json new file mode 100644 index 00000000000..31176e658c2 --- /dev/null +++ b/packages/create-webpack-app/templates/default/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "allowSyntheticDefaultImports": true, + "noImplicitAny": true, + "module": "es6", + "target": "es5", + "allowJs": true + }, + "files": ["src/index.ts"] +} diff --git a/packages/create-webpack-app/templates/default/webpack.configjs.tpl b/packages/create-webpack-app/templates/default/webpack.configjs.tpl new file mode 100644 index 00000000000..68d9a7ddc59 --- /dev/null +++ b/packages/create-webpack-app/templates/default/webpack.configjs.tpl @@ -0,0 +1,96 @@ +// Generated using webpack-cli https://github.com/webpack/webpack-cli + +const path = require('path');<% if (htmlWebpackPlugin) { %> +const HtmlWebpackPlugin = require('html-webpack-plugin');<% } %><% if (extractPlugin !== 'No') { %> +const MiniCssExtractPlugin = require('mini-css-extract-plugin');<% } %><% if (workboxWebpackPlugin) { %> +const WorkboxWebpackPlugin = require('workbox-webpack-plugin');<% } %> + +const isProduction = process.env.NODE_ENV == 'production'; +<% if (isCSS) { %> +<% if (extractPlugin === "Yes") { %> +const stylesHandler = MiniCssExtractPlugin.loader; +<% } else if (extractPlugin === "Only for Production") { %> +const stylesHandler = isProduction ? MiniCssExtractPlugin.loader : 'style-loader'; +<% } else { %> +const stylesHandler = 'style-loader'; +<% } %> +<% } %> + +const config = { + entry: '<%= entry %>', + output: { + path: path.resolve(__dirname, 'dist'), + },<% if (devServer) { %> + devServer: { + open: true, + host: 'localhost', + },<% } %> + plugins: [<% if (htmlWebpackPlugin) { %> + new HtmlWebpackPlugin({ + template: 'index.html', + }), +<% } %><% if (extractPlugin === "Yes") { %> + new MiniCssExtractPlugin(), +<% } %> + // Add your plugins here + // Learn more about plugins from https://webpack.js.org/configuration/plugins/ + ], + module: { + rules: [<% if (langType == "ES6") { %> + { + test: /\.(js|jsx)$/i, + loader: 'babel-loader', + },<% } %><% if (langType == "Typescript") { %> + { + test: /\.(ts|tsx)$/i, + loader: 'ts-loader', + exclude: ['/node_modules/'], + },<% } %><% if (isCSS && !isPostCSS) { %> + { + test: /\.css$/i, + use: [stylesHandler,'css-loader'], + },<% } %><% if (cssType == 'SASS') { %> + { + test: /\.s[ac]ss$/i, + use: [stylesHandler, 'css-loader', <% if (isPostCSS) { %>'postcss-loader', <% } %>'sass-loader'], + },<% } %><% if (cssType == 'LESS') { %> + { + test: /\.less$/i, + use: [<% if (isPostCSS) { %>stylesHandler, 'css-loader', 'postcss-loader', <% } %>'less-loader'], + },<% } %><% if (cssType == 'Stylus') { %> + { + test: /\.styl$/i, + use: [<% if (isPostCSS) { %>stylesHandler, 'css-loader', 'postcss-loader', <% } %>'stylus-loader'], + },<% } %><% if (isPostCSS && isCSS) { %> + { + test: /\.css$/i, + use: [stylesHandler, 'css-loader', 'postcss-loader'], + },<% } %> + { + test: /\.(eot|svg|ttf|woff|woff2|png|jpg|gif)$/i, + type: 'asset', + }, + + // Add your rules for custom modules here + // Learn more about loaders from https://webpack.js.org/loaders/ + ], + },<% if (langType == "Typescript") {%> + resolve: { + extensions: ['.tsx', '.ts', '.js'], + },<% } %> +}; + +module.exports = () => { + if (isProduction) { + config.mode = 'production'; + <% if (extractPlugin === "Only for Production") { %> + config.plugins.push(new MiniCssExtractPlugin()); + <% } %> + <% if (workboxWebpackPlugin) { %> + config.plugins.push(new WorkboxWebpackPlugin.GenerateSW()); + <% } %> + } else { + config.mode = 'development'; + } + return config; +}; From 088cd26e9181f1ffba66da3a5a75acc9df87a78d Mon Sep 17 00:00:00 2001 From: Rishabh Chawla Date: Tue, 30 Nov 2021 12:17:25 +0530 Subject: [PATCH 07/15] chore: update template path --- packages/create-webpack-app/src/handlers/default.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/create-webpack-app/src/handlers/default.ts b/packages/create-webpack-app/src/handlers/default.ts index d025b0bce3b..0c04fb8bf3a 100644 --- a/packages/create-webpack-app/src/handlers/default.ts +++ b/packages/create-webpack-app/src/handlers/default.ts @@ -1,7 +1,7 @@ import path from "path"; import { CustomGenerator } from "../types"; -const templatePath = path.resolve(__dirname, "../../init-template/default"); +const templatePath = path.resolve(__dirname, "../../templates/default"); const resolveFile = (file: string): string => { return path.resolve(templatePath, file); }; From 04ed059779d6f49868471ce2990028abac015438 Mon Sep 17 00:00:00 2001 From: Rishabh Chawla Date: Tue, 30 Nov 2021 13:21:39 +0530 Subject: [PATCH 08/15] chore: complete index file --- packages/create-webpack-app/src/index.ts | 64 ++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/packages/create-webpack-app/src/index.ts b/packages/create-webpack-app/src/index.ts index ace46096fa4..94aedd4fe9c 100644 --- a/packages/create-webpack-app/src/index.ts +++ b/packages/create-webpack-app/src/index.ts @@ -1 +1,65 @@ import yeoman from "yeoman-environment"; +import { Command } from "commander"; +import InitGenerator from "./init-generator"; +import util from "util"; +import { createColors, isColorSupported } from "colorette"; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function getLogger(colors: any) { + return { + error: (val: string) => console.error(`[create-webpack-app] ${colors.red(util.format(val))}`), + warn: (val: string) => console.warn(`[create-webpack-app] ${colors.yellow(val)}`), + info: (val: string) => console.info(`[create-webpack-app] ${colors.cyan(val)}`), + success: (val: string) => console.log(`[create-webpack-app] ${colors.green(val)}`), + log: (val: string) => console.log(`[create-webpack-app] ${val}`), + raw: (val: string) => console.log(val), + }; +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function getColors(useColor?: any) { + let shouldUseColor; + + if (useColor) { + shouldUseColor = useColor; + } else { + shouldUseColor = isColorSupported; + } + + return { ...createColors({ useColor: shouldUseColor }), isColorSupported: shouldUseColor }; +} + +function getOptions() { + const program = new Command(); + + program.name("create-webpack-app"); + program.description("create new webpack project", { + "generation-path": "Path to the installation directory, e.g. ./projectName", + }); + program.usage("[generation-path] [options]"); + program.option("-t, --template [type]", "Type of template", "default"); + program.option("-f, --force", "Generate without questions (ideally) using default answers"); + + program.parse(); + + return program.opts(); +} + +export default function (): void { + const colors = getColors(); + const logger = getLogger(colors); + const { generationPath, options } = getOptions(); + + options.generationPath = generationPath || "."; + + const env = yeoman.createEnv([], { + cwd: options.generationPath, + }); + const generatorName = "webpack-init-generator"; + + env.registerStub(InitGenerator, generatorName); + + env.run(generatorName, { cli: { colors, logger }, options }, () => { + logger.success("Project has been initialised with webpack!"); + }); +} From 00d755e3de3539d72862c3a628e95d6d532abe71 Mon Sep 17 00:00:00 2001 From: Rishabh Chawla Date: Tue, 30 Nov 2021 13:42:14 +0530 Subject: [PATCH 09/15] chore: setup bin --- packages/create-webpack-app/bin/index.js | 5 +++++ packages/create-webpack-app/package.json | 5 ++++- tsconfig.json | 3 +++ 3 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 packages/create-webpack-app/bin/index.js diff --git a/packages/create-webpack-app/bin/index.js b/packages/create-webpack-app/bin/index.js new file mode 100644 index 00000000000..66ad0b226cf --- /dev/null +++ b/packages/create-webpack-app/bin/index.js @@ -0,0 +1,5 @@ +#!/usr/bin/env node + +const run = require("../lib/index"); + +run(); diff --git a/packages/create-webpack-app/package.json b/packages/create-webpack-app/package.json index a4f518afe78..37f838a3f24 100644 --- a/packages/create-webpack-app/package.json +++ b/packages/create-webpack-app/package.json @@ -3,6 +3,7 @@ "version": "1.0.0", "description": "Create new project with webpack", "main": "lib/index.js", + "bin": "bin/index.js", "types": "lib/index.d.ts", "license": "MIT", "publishConfig": { @@ -14,11 +15,13 @@ }, "files": [ "lib", - "template" + "templates", + "bin" ], "dependencies": { "yeoman-environment": "^2.10.3", "yeoman-generator": "^4.12.0", + "colorette": "^2.0.14", "commander": "^7.0.0" }, "peerDependenciesMeta": { diff --git a/tsconfig.json b/tsconfig.json index d5d1c44c8f5..5df23b5964e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -31,6 +31,9 @@ }, { "path": "packages/configtest" + }, + { + "path": "packages/create-webpack-app" } ] } From 31fd69a7ae37366527f6b5380f7bb5a1c62cdde6 Mon Sep 17 00:00:00 2001 From: Rishabh Chawla Date: Sat, 11 Dec 2021 17:53:35 +0530 Subject: [PATCH 10/15] chore: remove init mentions --- .../create-webpack-app/src/{init-generator.ts => generator.ts} | 0 packages/create-webpack-app/src/index.ts | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename packages/create-webpack-app/src/{init-generator.ts => generator.ts} (100%) diff --git a/packages/create-webpack-app/src/init-generator.ts b/packages/create-webpack-app/src/generator.ts similarity index 100% rename from packages/create-webpack-app/src/init-generator.ts rename to packages/create-webpack-app/src/generator.ts diff --git a/packages/create-webpack-app/src/index.ts b/packages/create-webpack-app/src/index.ts index 94aedd4fe9c..c732b547ae7 100644 --- a/packages/create-webpack-app/src/index.ts +++ b/packages/create-webpack-app/src/index.ts @@ -1,6 +1,6 @@ import yeoman from "yeoman-environment"; import { Command } from "commander"; -import InitGenerator from "./init-generator"; +import InitGenerator from "./generator"; import util from "util"; import { createColors, isColorSupported } from "colorette"; From 49346c7c8c5e1547e125487abd2bedf532543e7b Mon Sep 17 00:00:00 2001 From: Rishabh Chawla Date: Sat, 11 Dec 2021 18:14:15 +0530 Subject: [PATCH 11/15] chore: update generator names --- packages/create-webpack-app/src/generator.ts | 4 ++-- packages/create-webpack-app/src/index.ts | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/create-webpack-app/src/generator.ts b/packages/create-webpack-app/src/generator.ts index b104d6b1c82..7d97caa84c4 100644 --- a/packages/create-webpack-app/src/generator.ts +++ b/packages/create-webpack-app/src/generator.ts @@ -10,12 +10,12 @@ import handlers from "./handlers"; * * Generator for initializing a webpack config * - * @class InitGenerator + * @class Generator * @extends CustomGenerator * @returns {Void} After execution, transforms are triggered * */ -export default class InitGenerator extends CustomGenerator { +export default class Generator extends CustomGenerator { public answers: Record; public configurationPath: string; public force: boolean; diff --git a/packages/create-webpack-app/src/index.ts b/packages/create-webpack-app/src/index.ts index c732b547ae7..a0894434fe4 100644 --- a/packages/create-webpack-app/src/index.ts +++ b/packages/create-webpack-app/src/index.ts @@ -1,6 +1,6 @@ import yeoman from "yeoman-environment"; import { Command } from "commander"; -import InitGenerator from "./generator"; +import Generator from "./generator"; import util from "util"; import { createColors, isColorSupported } from "colorette"; @@ -18,7 +18,7 @@ function getLogger(colors: any) { // eslint-disable-next-line @typescript-eslint/no-explicit-any function getColors(useColor?: any) { - let shouldUseColor; + let shouldUseColor: boolean; if (useColor) { shouldUseColor = useColor; @@ -55,9 +55,9 @@ export default function (): void { const env = yeoman.createEnv([], { cwd: options.generationPath, }); - const generatorName = "webpack-init-generator"; + const generatorName = "create-webpack-app-generator"; - env.registerStub(InitGenerator, generatorName); + env.registerStub(Generator, generatorName); env.run(generatorName, { cli: { colors, logger }, options }, () => { logger.success("Project has been initialised with webpack!"); From ae21a562447226ccec34d6deca430632fdc4a5f3 Mon Sep 17 00:00:00 2001 From: Rishabh Chawla Date: Sat, 11 Dec 2021 19:40:38 +0530 Subject: [PATCH 12/15] chore: remove redundant docs --- packages/create-webpack-app/README.md | 30 --------------------------- 1 file changed, 30 deletions(-) diff --git a/packages/create-webpack-app/README.md b/packages/create-webpack-app/README.md index 3acfe8d62a5..dd8bd651ebe 100644 --- a/packages/create-webpack-app/README.md +++ b/packages/create-webpack-app/README.md @@ -61,33 +61,3 @@ create-webpack-app [generation-path] ```bash create-webpack-app -t, --template ``` - -## Description of questions asked by the generator - -### Default template - -1. `Which of the following JS solutions do you want to use?` - -> _Property/key resolved: [module.rules](https://webpack.js.org/configuration/module/#module-rules) (for .js, .ts and other related files)_ - -This enables webpack to parse [`ES2015`](https://babeljs.io/learn-es2015/) code or Typescript code as per choice. - -2. `Do you want to use webpack-dev-server?` - -> _Property/key resolved: [module.rules](https://webpack.js.org/configuration/dev-server/)_ - -Adds a development server to serve webpack bundles and hence make development faster. - -3. `Do you want to simplify the creation of HTML files for your bundle?` - -Adds `html-webpack-plugin` that simplifies creation of HTML files to serve your bundles. - -4. `Do you want to add PWA support?` - -Adds [`workbox-webpack-plugin`](https://developers.google.com/web/tools/workbox/modules/workbox-webpack-plugin) which generates a complete service worker for you. - -5. `Which of the following CSS solutions do you want to use?` - -> _Property/key resolved: [module.rules](https://webpack.js.org/configuration/module/#module-rules) (for .css files)_ - -If you use any sort of style in your project, such as [`.css`](https://developer.mozilla.org/en-US/docs/Web/CSS) you will need to select this here. If you don't use CSS, answer `none`. From 67e4c2f9a280694e9947c608ad7ff957d406ebe7 Mon Sep 17 00:00:00 2001 From: Rishabh Chawla Date: Wed, 23 Feb 2022 10:19:29 +0530 Subject: [PATCH 13/15] chore: rebase --- .eslintignore | 5 +- .gitignore | 1 - .prettierignore | 5 +- OPTIONS.md | 8 + SERVE-OPTIONS-v4.md | 35 +- package.json | 5 +- packages/configtest/src/index.ts | 18 +- packages/configtest/tsconfig.json | 7 +- packages/generators/src/index.ts | 4 +- packages/generators/src/types/index.ts | 7 +- packages/generators/src/utils/helpers.ts | 2 +- packages/generators/tsconfig.json | 7 +- packages/info/src/index.ts | 4 +- packages/info/tsconfig.json | 7 +- packages/serve/src/index.ts | 37 +- packages/serve/src/types.ts | 111 - packages/serve/tsconfig.json | 7 +- packages/webpack-cli/lib/bootstrap.js | 23 +- packages/webpack-cli/lib/index.js | 15 +- packages/webpack-cli/lib/plugins/CLIPlugin.js | 209 +- .../lib/utils/dynamic-import-loader.js | 20 +- packages/webpack-cli/lib/webpack-cli.js | 4090 ++++++++--------- packages/webpack-cli/src/bootstrap.ts | 18 + packages/webpack-cli/src/index.ts | 9 + packages/webpack-cli/src/plugins/CLIPlugin.ts | 141 + packages/webpack-cli/src/types.ts | 356 ++ .../src/utils/dynamic-import-loader.ts | 15 + packages/webpack-cli/src/webpack-cli.ts | 2515 ++++++++++ packages/webpack-cli/tsconfig.json | 10 + test/api/CLI.test.js | 1 + test/api/capitalizeFirstLetter.test.js | 1 + test/api/do-install.test.js | 1 + test/api/generators/helpers.test.js | 1 + test/api/get-default-package-manager.test.js | 1 + test/api/resolveConfig/resolveConfig.test.js | 1 + tsconfig.json | 3 + yarn.lock | 1239 ++--- 37 files changed, 5819 insertions(+), 3120 deletions(-) delete mode 100644 packages/serve/src/types.ts create mode 100644 packages/webpack-cli/src/bootstrap.ts create mode 100644 packages/webpack-cli/src/index.ts create mode 100644 packages/webpack-cli/src/plugins/CLIPlugin.ts create mode 100644 packages/webpack-cli/src/types.ts create mode 100644 packages/webpack-cli/src/utils/dynamic-import-loader.ts create mode 100644 packages/webpack-cli/src/webpack-cli.ts create mode 100644 packages/webpack-cli/tsconfig.json diff --git a/.eslintignore b/.eslintignore index dd36e09449b..91718ffa7a2 100644 --- a/.eslintignore +++ b/.eslintignore @@ -2,10 +2,7 @@ coverage .nyc_output node_modules dist -packages/configtest/lib -packages/generators/lib -packages/info/lib -packages/serve/lib +packages/*/lib test/**/dist/ test/**/bin/ test/**/binary/ diff --git a/.gitignore b/.gitignore index e052dc02222..d1c154e5dd0 100644 --- a/.gitignore +++ b/.gitignore @@ -55,7 +55,6 @@ packages/**/*.map # build files packages/**/lib packages/**/yarn.lock -!packages/webpack-cli/lib # test output files test/js/* diff --git a/.prettierignore b/.prettierignore index d85e46cbf30..6f73938b7ae 100644 --- a/.prettierignore +++ b/.prettierignore @@ -2,10 +2,7 @@ coverage .nyc_output node_modules dist -packages/configtest/lib -packages/generators/lib -packages/info/lib -packages/serve/lib +packages/*/lib test/**/dist/ test/**/bin/ test/**/binary/ diff --git a/OPTIONS.md b/OPTIONS.md index 0de18f3e63c..0eb8c3b8872 100644 --- a/OPTIONS.md +++ b/OPTIONS.md @@ -177,6 +177,8 @@ Options: --no-module-parser-javascript-import Negative 'module-parser-javascript-import' option. --module-parser-javascript-import-exports-presence Specifies the behavior of invalid export names in "import ... from ...". --no-module-parser-javascript-import-exports-presence Negative 'module-parser-javascript-import-exports-presence' option. + --module-parser-javascript-import-meta Enable/disable evaluating import.meta. + --no-module-parser-javascript-import-meta Negative 'module-parser-javascript-import-meta' option. --no-module-parser-javascript-node Negative 'module-parser-javascript-node' option. --module-parser-javascript-node-dirname [value] Include a polyfill for the '__dirname' variable. --no-module-parser-javascript-node-dirname Negative 'module-parser-javascript-node-dirname' option. @@ -239,6 +241,8 @@ Options: --no-module-parser-javascript-auto-import Negative 'module-parser-javascript-auto-import' option. --module-parser-javascript-auto-import-exports-presence Specifies the behavior of invalid export names in "import ... from ...". --no-module-parser-javascript-auto-import-exports-presence Negative 'module-parser-javascript-auto-import-exports-presence' option. + --module-parser-javascript-auto-import-meta Enable/disable evaluating import.meta. + --no-module-parser-javascript-auto-import-meta Negative 'module-parser-javascript-auto-import-meta' option. --no-module-parser-javascript-auto-node Negative 'module-parser-javascript-auto-node' option. --module-parser-javascript-auto-node-dirname [value] Include a polyfill for the '__dirname' variable. --no-module-parser-javascript-auto-node-dirname Negative 'module-parser-javascript-auto-node-dirname' option. @@ -301,6 +305,8 @@ Options: --no-module-parser-javascript-dynamic-import Negative 'module-parser-javascript-dynamic-import' option. --module-parser-javascript-dynamic-import-exports-presence Specifies the behavior of invalid export names in "import ... from ...". --no-module-parser-javascript-dynamic-import-exports-presence Negative 'module-parser-javascript-dynamic-import-exports-presence' option. + --module-parser-javascript-dynamic-import-meta Enable/disable evaluating import.meta. + --no-module-parser-javascript-dynamic-import-meta Negative 'module-parser-javascript-dynamic-import-meta' option. --no-module-parser-javascript-dynamic-node Negative 'module-parser-javascript-dynamic-node' option. --module-parser-javascript-dynamic-node-dirname [value] Include a polyfill for the '__dirname' variable. --no-module-parser-javascript-dynamic-node-dirname Negative 'module-parser-javascript-dynamic-node-dirname' option. @@ -363,6 +369,8 @@ Options: --no-module-parser-javascript-esm-import Negative 'module-parser-javascript-esm-import' option. --module-parser-javascript-esm-import-exports-presence Specifies the behavior of invalid export names in "import ... from ...". --no-module-parser-javascript-esm-import-exports-presence Negative 'module-parser-javascript-esm-import-exports-presence' option. + --module-parser-javascript-esm-import-meta Enable/disable evaluating import.meta. + --no-module-parser-javascript-esm-import-meta Negative 'module-parser-javascript-esm-import-meta' option. --no-module-parser-javascript-esm-node Negative 'module-parser-javascript-esm-node' option. --module-parser-javascript-esm-node-dirname [value] Include a polyfill for the '__dirname' variable. --no-module-parser-javascript-esm-node-dirname Negative 'module-parser-javascript-esm-node-dirname' option. diff --git a/SERVE-OPTIONS-v4.md b/SERVE-OPTIONS-v4.md index ff71f4510c7..05de7c12dcf 100644 --- a/SERVE-OPTIONS-v4.md +++ b/SERVE-OPTIONS-v4.md @@ -11,15 +11,16 @@ Options: --node-env Sets process.env.NODE_ENV to the specified value. --progress [value] Print compilation progress during build. -j, --json [value] Prints result as JSON or store it in a file. - --entry The entry point(s) of your application e.g. ./src/main.js. - -o, --output-path Output location of the file generated by webpack e.g. ./dist/. - -t, --target Sets the build target e.g. node. -d, --devtool Determine source maps to use. --no-devtool Do not generate source maps. + --entry The entry point(s) of your application e.g. ./src/main.js. --mode Defines the mode to pass to webpack. --name Name of the configuration. Used when loading multiple configurations. + -o, --output-path Output location of the file generated by webpack e.g. ./dist/. --stats [value] It instructs webpack on how to treat the stats e.g. verbose. --no-stats Disable stats output. + -t, --target Sets the build target e.g. node. + --no-target Negative 'target' option. --watch-options-stdin Stop watching when stdin stream has ended. --no-watch-options-stdin Do not stop watching when stdin stream has ended. --allowed-hosts Allows to enumerate the hosts from which access to the dev server are allowed (useful when you are proxying dev server, by default is 'auto'). @@ -41,8 +42,8 @@ Options: --client-web-socket-transport Allows to set custom web socket transport to communicate with dev server. --client-web-socket-url Allows to specify URL to web socket server (useful when you're proxying dev server and client script does not always know where to connect to). --client-web-socket-url-hostname Tells clients connected to devServer to use the provided hostname. - --client-web-socket-url-password Tells clients connected to devServer to use the provided password to authenticate. --client-web-socket-url-pathname Tells clients connected to devServer to use the provided path to connect. + --client-web-socket-url-password Tells clients connected to devServer to use the provided password to authenticate. --client-web-socket-url-port Tells clients connected to devServer to use the provided port. --client-web-socket-url-protocol Tells clients connected to devServer to use the provided protocol. --client-web-socket-url-username Tells clients connected to devServer to use the provided username to authenticate. @@ -57,6 +58,9 @@ Options: --no-http2 Does not serve over HTTP/2 using SPDY. --https Allows to configure the server's listening socket for TLS (by default, dev server will be served over HTTP). Deprecated, use the `server` option. --no-https Disallows to configure the server's listening socket for TLS (by default, dev server will be served over HTTP). + --https-passphrase Passphrase for a pfx file. Deprecated, use the `server.options.passphrase` option. + --https-request-cert Request for an SSL certificate. Deprecated, use the `server.options.requestCert` option. + --no-https-request-cert Does not request for an SSL certificate. --https-ca Path to an SSL CA certificate or content of an SSL CA certificate. Deprecated, use the `server.options.ca` option. --https-ca-reset Clear all items provided in 'https.ca' configuration. Path to an SSL CA certificate or content of an SSL CA certificate. Deprecated, use the `server.options.ca` option. --https-cacert Path to an SSL CA certificate or content of an SSL CA certificate. Deprecated, use the `server.options.ca` option. @@ -67,25 +71,26 @@ Options: --https-crl-reset Clear all items provided in 'https.crl' configuration. Path to PEM formatted CRLs (Certificate Revocation Lists) or content of PEM formatted CRLs (Certificate Revocation Lists). Deprecated, use the `server.options.crl` option. --https-key Path to an SSL key or content of an SSL key. Deprecated, use the `server.options.key` option. --https-key-reset Clear all items provided in 'https.key' configuration. Path to an SSL key or content of an SSL key. Deprecated, use the `server.options.key` option. - --https-passphrase Passphrase for a pfx file. Deprecated, use the `server.options.passphrase` option. --https-pfx Path to an SSL pfx file or content of an SSL pfx file. Deprecated, use the `server.options.pfx` option. --https-pfx-reset Clear all items provided in 'https.pfx' configuration. Path to an SSL pfx file or content of an SSL pfx file. Deprecated, use the `server.options.pfx` option. - --https-request-cert Request for an SSL certificate. Deprecated, use the `server.options.requestCert` option. - --no-https-request-cert Does not request for an SSL certificate. --ipc [value] Listen to a unix socket. --live-reload Enables reload/refresh the page(s) when file changes are detected (enabled by default). - --no-live-reload Disables reload/refresh the page(s) when file changes are detected (enabled by default) + --no-live-reload Disables reload/refresh the page(s) when file changes are detected (enabled by default). --magic-html Tells dev-server whether to enable magic HTML routes (routes corresponding to your webpack output, for example '/main' for 'main.js'). --no-magic-html Disables magic HTML routes (routes corresponding to your webpack output, for example '/main' for 'main.js'). --open [value...] Allows to configure dev server to open the browser(s) and page(s) after server had been started (set it to true to open your default browser). --no-open Does not open the default browser. - --open-app Open specified browser. Deprecated: please use '--open-app-name'. + --open-target Opens specified page in browser. --open-app-name Open specified browser. - --open-app-name-reset Clear all items provided in 'open.app.name' configuration. Open specified browser. + --open-app Open specified browser. Deprecated: please use '--open-app-name'. --open-reset Clear all items provided in 'open' configuration. Allows to configure dev server to open the browser(s) and page(s) after server had been started (set it to true to open your default browser). - --open-target Opens specified page in browser. --open-target-reset Clear all items provided in 'open.target' configuration. Opens specified page in browser. + --open-app-name-reset Clear all items provided in 'open.app.name' configuration. Open specified browser. --port Allows to specify a port to use. + --server-type Allows to set server and options (by default 'http'). + --server-options-passphrase Passphrase for a pfx file. + --server-options-request-cert Request for an SSL certificate. + --no-server-options-request-cert Negative 'server-options-request-cert' option. --server-options-ca Path to an SSL CA certificate or content of an SSL CA certificate. --server-options-ca-reset Clear all items provided in 'server.options.ca' configuration. Path to an SSL CA certificate or content of an SSL CA certificate. --server-options-cacert Path to an SSL CA certificate or content of an SSL CA certificate. Deprecated, use the `server.options.ca` option. @@ -96,22 +101,18 @@ Options: --server-options-crl-reset Clear all items provided in 'server.options.crl' configuration. Path to PEM formatted CRLs (Certificate Revocation Lists) or content of PEM formatted CRLs (Certificate Revocation Lists). --server-options-key Path to an SSL key or content of an SSL key. --server-options-key-reset Clear all items provided in 'server.options.key' configuration. Path to an SSL key or content of an SSL key. - --server-options-passphrase Passphrase for a pfx file. --server-options-pfx Path to an SSL pfx file or content of an SSL pfx file. --server-options-pfx-reset Clear all items provided in 'server.options.pfx' configuration. Path to an SSL pfx file or content of an SSL pfx file. - --server-options-request-cert Request for an SSL certificate. - --no-server-options-request-cert Negative 'server-options-request-cert' option. - --server-type Allows to set server and options (by default 'http'). --static [value...] Allows to configure options for serving static files from directory (by default 'public' directory). --no-static Negative 'static' option. --static-directory Directory for static contents. --static-public-path The static files will be available in the browser under this public path. - --static-public-path-reset Clear all items provided in 'static.publicPath' configuration. The static files will be available in the browser under this public path. - --static-reset Clear all items provided in 'static' configuration. Allows to configure options for serving static files from directory (by default 'public' directory). --static-serve-index Tells dev server to use serveIndex middleware when enabled. --no-static-serve-index Does not tell dev server to use serveIndex middleware. --static-watch Watches for files in static content directory. --no-static-watch Does not watch for files in static content directory. + --static-reset Clear all items provided in 'static' configuration. Allows to configure options for serving static files from directory (by default 'public' directory). + --static-public-path-reset Clear all items provided in 'static.publicPath' configuration. The static files will be available in the browser under this public path. --watch-files Allows to configure list of globs/directories/files to watch for file changes. --watch-files-reset Clear all items provided in 'watchFiles' configuration. Allows to configure list of globs/directories/files to watch for file changes. --web-socket-server Deprecated: please use '--web-socket-server-type' option. Allows to set web socket server and options (by default 'ws'). diff --git a/package.json b/package.json index 2028920dafe..4973c87891a 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "./packages/*" ], "scripts": { - "clean": "del-cli \"*.tsbuildinfo\" \"packages/**/*.tsbuildinfo\" \"packages/!(webpack-cli)/lib/!(*.tpl)\" \"**/.yo-rc.json\"", + "clean": "del-cli \"*.tsbuildinfo\" \"packages/**/*.tsbuildinfo\" \"packages/*/lib/!(*.tpl)\" \"**/.yo-rc.json\"", "prebuild": "yarn clean", "prebuild:ci": "yarn clean && node ./scripts/setupBuild.js", "build": "tsc --build", @@ -54,6 +54,7 @@ "@commitlint/config-conventional": "^16.0.0", "@types/jest": "^27.4.0", "@types/node": "^17.0.12", + "@types/rechoir": "^0.6.1", "@typescript-eslint/eslint-plugin": "^5.10.1", "@typescript-eslint/parser": "^5.10.1", "@webpack-cli/migrate": "^1.1.2", @@ -84,6 +85,6 @@ "typescript": "^4.1.3", "webpack": "^5.67.0", "webpack-bundle-analyzer": "^4.3.0", - "webpack-dev-server": "^4.7.2" + "webpack-dev-server": "^4.7.4" } } diff --git a/packages/configtest/src/index.ts b/packages/configtest/src/index.ts index 2edaaacc6ad..ab698ef2bb5 100644 --- a/packages/configtest/src/index.ts +++ b/packages/configtest/src/index.ts @@ -1,8 +1,9 @@ +import { IWebpackCLI } from "webpack-cli"; + const WEBPACK_PACKAGE = process.env.WEBPACK_PACKAGE || "webpack"; class ConfigTestCommand { - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any - async apply(cli: any): Promise { + async apply(cli: IWebpackCLI): Promise { await cli.makeCommand( { name: "configtest [config-path]", @@ -19,15 +20,14 @@ class ConfigTestCommand { const configPaths = new Set(); if (Array.isArray(config.options)) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - config.options.forEach((options: any) => { + config.options.forEach((options) => { if (config.path.get(options)) { - configPaths.add(config.path.get(options)); + configPaths.add(config.path.get(options) as string); } }); } else { if (config.path.get(config.options)) { - configPaths.add(config.path.get(config.options)); + configPaths.add(config.path.get(config.options) as string); } } @@ -39,14 +39,16 @@ class ConfigTestCommand { cli.logger.info(`Validate '${Array.from(configPaths).join(" ,")}'.`); try { - const error: Error[] = cli.webpack.validate(config.options); + // @ts-expect-error cli.webpack.validate returns void + const error: Error[] | undefined = cli.webpack.validate(config.options); // TODO remove this after drop webpack@4 if (error && error.length > 0) { + // @ts-expect-error schema argument is missing throw new cli.webpack.WebpackOptionsValidationError(error); } } catch (error) { - if (cli.isValidationError(error)) { + if (cli.isValidationError(error as Error)) { cli.logger.error((error as Error).message); } else { cli.logger.error(error); diff --git a/packages/configtest/tsconfig.json b/packages/configtest/tsconfig.json index 279b3e923cc..1c6d02ef88c 100644 --- a/packages/configtest/tsconfig.json +++ b/packages/configtest/tsconfig.json @@ -4,5 +4,10 @@ "outDir": "./lib", "rootDir": "./src" }, - "include": ["./src"] + "include": ["./src"], + "references": [ + { + "path": "../webpack-cli" + } + ] } diff --git a/packages/generators/src/index.ts b/packages/generators/src/index.ts index ef466daba92..e21f14901d2 100644 --- a/packages/generators/src/index.ts +++ b/packages/generators/src/index.ts @@ -4,10 +4,10 @@ import pluginGenerator from "./plugin-generator"; import addonGenerator from "./addon-generator"; import initGenerator from "./init-generator"; import type { InitOptions, LoaderOptions, PluginOptions } from "./types"; +import { IWebpackCLI } from "webpack-cli"; class GeneratorsCommand { - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any - async apply(cli: any): Promise { + async apply(cli: IWebpackCLI): Promise { await cli.makeCommand( { name: "init [generation-path]", diff --git a/packages/generators/src/types/index.ts b/packages/generators/src/types/index.ts index 42817691f0b..a27d7758cb3 100644 --- a/packages/generators/src/types/index.ts +++ b/packages/generators/src/types/index.ts @@ -1,5 +1,6 @@ import Generator from "yeoman-generator"; import path from "path"; +import { IWebpackCLI } from "webpack-cli"; export type InitOptions = { template: string; force?: boolean }; export type LoaderOptions = { template: string }; @@ -16,8 +17,7 @@ export type BaseCustomGeneratorOptions = { }; export type CustomGeneratorOptions = Generator.GeneratorOptions & { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - cli: any; + cli: IWebpackCLI; options: T; }; @@ -25,8 +25,7 @@ export class CustomGenerator< T extends BaseCustomGeneratorOptions = BaseCustomGeneratorOptions, Z extends CustomGeneratorOptions = CustomGeneratorOptions, > extends Generator { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - public cli: any; + public cli: IWebpackCLI; public template: string; public dependencies: string[]; public force: boolean; diff --git a/packages/generators/src/utils/helpers.ts b/packages/generators/src/utils/helpers.ts index 871a33fa1ee..fe152bc46c0 100644 --- a/packages/generators/src/utils/helpers.ts +++ b/packages/generators/src/utils/helpers.ts @@ -37,7 +37,7 @@ export async function getInstaller(this: CustomGenerator): Promise { "packager", "Pick a package manager:", installers, - defaultPackager, + defaultPackager as string, this.force, ); return packager; diff --git a/packages/generators/tsconfig.json b/packages/generators/tsconfig.json index 173f75d3f62..991ba583366 100644 --- a/packages/generators/tsconfig.json +++ b/packages/generators/tsconfig.json @@ -5,5 +5,10 @@ "outDir": "lib", "rootDir": "src" }, - "include": ["src"] + "include": ["src"], + "references": [ + { + "path": "../webpack-cli" + } + ] } diff --git a/packages/info/src/index.ts b/packages/info/src/index.ts index 6a00ae3c467..1dad6e8296d 100644 --- a/packages/info/src/index.ts +++ b/packages/info/src/index.ts @@ -1,4 +1,5 @@ import envinfo from "envinfo"; +import { IWebpackCLI } from "webpack-cli"; interface Information { Binaries?: string[]; @@ -10,8 +11,7 @@ interface Information { } class InfoCommand { - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any - async apply(cli: any): Promise { + async apply(cli: IWebpackCLI): Promise { await cli.makeCommand( { name: "info", diff --git a/packages/info/tsconfig.json b/packages/info/tsconfig.json index 279b3e923cc..1c6d02ef88c 100644 --- a/packages/info/tsconfig.json +++ b/packages/info/tsconfig.json @@ -4,5 +4,10 @@ "outDir": "./lib", "rootDir": "./src" }, - "include": ["./src"] + "include": ["./src"], + "references": [ + { + "path": "../webpack-cli" + } + ] } diff --git a/packages/serve/src/index.ts b/packages/serve/src/index.ts index 88533aad4c0..b1ca77b75ba 100644 --- a/packages/serve/src/index.ts +++ b/packages/serve/src/index.ts @@ -1,15 +1,14 @@ // eslint-disable-next-line node/no-extraneous-import import type { Compiler, cli } from "webpack"; -import { devServerOptionsType } from "./types"; +import { IWebpackCLI, WebpackDevServerOptions } from "webpack-cli"; const WEBPACK_PACKAGE = process.env.WEBPACK_PACKAGE || "webpack"; const WEBPACK_DEV_SERVER_PACKAGE = process.env.WEBPACK_DEV_SERVER_PACKAGE || "webpack-dev-server"; type Problem = NonNullable>[0]; - +type PublicPath = WebpackDevServerOptions["output"]["publicPath"]; class ServeCommand { - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any - async apply(cli: any): Promise { + async apply(cli: IWebpackCLI): Promise { const loadDevServerOptions = () => { // TODO simplify this after drop webpack v4 and webpack-dev-server v3 // eslint-disable-next-line @typescript-eslint/no-var-requires @@ -188,8 +187,7 @@ class ServeCommand { process.exit(2); } - const compilers = - typeof compiler.compilers !== "undefined" ? compiler.compilers : [compiler]; + const compilers = cli.isMultipleCompiler(compiler) ? compiler.compilers : [compiler]; const possibleCompilers = compilers.filter( (compiler: Compiler) => compiler.options.devServer, ); @@ -199,7 +197,7 @@ class ServeCommand { const usedPorts: number[] = []; for (const compilerForDevServer of compilersForDevServer) { - let devServerOptions: devServerOptionsType; + let devServerOptions: WebpackDevServerOptions; if (isNewDevServerCLIAPI) { // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -260,18 +258,23 @@ class ServeCommand { process.exit(2); } - devServerOptions = result; + devServerOptions = result as WebpackDevServerOptions; } else { // TODO remove in the next major release const mergeOptions = ( - devServerOptions: devServerOptionsType, - devServerCliOptions: devServerOptionsType, - ): devServerOptionsType => { + devServerOptions: Partial, + devServerCliOptions: Partial, + ): WebpackDevServerOptions => { // CLI options should take precedence over devServer options, // and CLI options should have no default values included const options = { ...devServerOptions, ...devServerCliOptions }; - if (devServerOptions.client && devServerCliOptions.client) { + if ( + devServerOptions.client && + devServerCliOptions.client && + typeof devServerOptions.client === "object" && + typeof devServerCliOptions.client === "object" + ) { // the user could set some client options in their devServer config, // then also specify client options on the CLI options.client = { @@ -280,7 +283,7 @@ class ServeCommand { }; } - return options; + return options as WebpackDevServerOptions; }; devServerOptions = mergeOptions( @@ -291,8 +294,8 @@ class ServeCommand { // TODO remove in the next major release if (!isDevServer4) { - const getPublicPathOption = (): string => { - const normalizePublicPath = (publicPath: string): string => + const getPublicPathOption = (): PublicPath => { + const normalizePublicPath = (publicPath: PublicPath): PublicPath => typeof publicPath === "undefined" || publicPath === "auto" ? "/" : publicPath; if (options.outputPublicPath) { @@ -307,7 +310,7 @@ class ServeCommand { return normalizePublicPath(compilerForDevServer.options.output.publicPath); }; - const getStatsOption = (): string | boolean => { + const getStatsOption = (): WebpackDevServerOptions["stats"] => { if (options.stats) { return options.stats; } @@ -361,7 +364,7 @@ class ServeCommand { servers.push(server); } catch (error) { - if (cli.isValidationError(error)) { + if (cli.isValidationError(error as Error)) { cli.logger.error((error as Error).message); } else { cli.logger.error(error); diff --git a/packages/serve/src/types.ts b/packages/serve/src/types.ts deleted file mode 100644 index 62b589a7870..00000000000 --- a/packages/serve/src/types.ts +++ /dev/null @@ -1,111 +0,0 @@ -export type devServerOptionsType = { - allowedHosts?: string[] | allowedHostsEnum; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - bonjour?: boolean | Record; - client?: false | devServerClientOptions; - compress?: boolean; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - dev?: Record; // drop in dev-server v4 - // eslint-disable-next-line @typescript-eslint/no-explicit-any - devMiddleware?: Record; - firewall?: boolean | string[]; - headers?: - | Record // eslint-disable-next-line @typescript-eslint/no-explicit-any - | ((request: any, response: any, middlewareContext: any) => Record); - historyApiFallback?: boolean | Record; - host?: string | null | hostEnum; - hot?: boolean | hotOptionEnum; - http2?: boolean; - https?: boolean | Record; - injectClient?: boolean | (() => void); - injectHot?: boolean | (() => void); - ipc?: string | true; - liveReload?: boolean; - onAfterSetupMiddleware?: () => void; - onBeforeSetupMiddleware?: () => void; - onListening?: () => void; - open?: string | boolean | openOptionObject; - openPage?: string | string[]; - overlay?: boolean | Record; - port?: number | string | null; - profile?: boolean; - progress?: boolean; - proxy?: Record | (Record | (() => void))[]; - public?: string; - setupExitSignals?: boolean; - static?: boolean | string | Record | (string | Record)[]; - transportMode?: Record | string; - useLocalIp?: boolean; - publicPath?: string | (() => void); - stats?: string | boolean; - watchFiles?: string | Record; - webSocketServer?: - | false - | string - | transportModeEnum - // eslint-disable-next-line @typescript-eslint/no-explicit-any - | (() => any) - | Record - | (Record | (() => void))[]; -}; - -enum hotOptionEnum { - only = "only", -} - -enum hostEnum { - LocalIp = "local-ip", - LocalIpv4 = "local-ipv4", - LocalIpv6 = "local-ipv6", -} - -enum allowedHostsEnum { - Auto = "auto", - All = "all", -} - -enum transportModeEnum { - SockJS = "sockjs", - Ws = "ws", -} - -type devServerClientOptions = { - host?: string; - path?: string; - port?: string | number | null; - needClientEntry?: boolean | (() => void); - needHotEntry?: boolean | (() => void); - logging?: devServerClientLogging; - overlay?: boolean | clientOverlay; - progress?: boolean; - webSocketTransport?: string | transportModeEnum; - webSocketURL?: string | webSocketURLOptions; -}; - -type webSocketURLOptions = { - hostname?: string; - pathname?: string; - port?: string | number; - password?: string; - protocol?: string | "auto"; - username?: string; -}; - -type openOptionObject = { - target?: string; - app?: string; -}; - -type clientOverlay = { - errors?: boolean; - warnings?: boolean; -}; - -enum devServerClientLogging { - none = "none", - error = "error", - warn = "warn", - info = "info", - log = "log", - verbose = "verbose", -} diff --git a/packages/serve/tsconfig.json b/packages/serve/tsconfig.json index 279b3e923cc..1c6d02ef88c 100644 --- a/packages/serve/tsconfig.json +++ b/packages/serve/tsconfig.json @@ -4,5 +4,10 @@ "outDir": "./lib", "rootDir": "./src" }, - "include": ["./src"] + "include": ["./src"], + "references": [ + { + "path": "../webpack-cli" + } + ] } diff --git a/packages/webpack-cli/lib/bootstrap.js b/packages/webpack-cli/lib/bootstrap.js index ef63a0b15da..130f61e45e5 100644 --- a/packages/webpack-cli/lib/bootstrap.js +++ b/packages/webpack-cli/lib/bootstrap.js @@ -1,15 +1,16 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +// eslint-disable-next-line @typescript-eslint/no-var-requires const WebpackCLI = require("./webpack-cli"); - const runCLI = async (args) => { - // Create a new instance of the CLI object - const cli = new WebpackCLI(); - - try { - await cli.run(args); - } catch (error) { - cli.logger.error(error); - process.exit(2); - } + // Create a new instance of the CLI object + const cli = new WebpackCLI(); + try { + await cli.run(args); + } + catch (error) { + cli.logger.error(error); + process.exit(2); + } }; - module.exports = runCLI; diff --git a/packages/webpack-cli/lib/index.js b/packages/webpack-cli/lib/index.js index 4dfb248b8fb..8d48e18bc03 100644 --- a/packages/webpack-cli/lib/index.js +++ b/packages/webpack-cli/lib/index.js @@ -1,5 +1,18 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./types"), exports); +// eslint-disable-next-line @typescript-eslint/no-var-requires const CLI = require("./webpack-cli"); - module.exports = CLI; // TODO remove after drop `@webpack-cli/migrate` module.exports.utils = { logger: console }; diff --git a/packages/webpack-cli/lib/plugins/CLIPlugin.js b/packages/webpack-cli/lib/plugins/CLIPlugin.js index 2b1c08302f1..aee28a3ac0d 100644 --- a/packages/webpack-cli/lib/plugins/CLIPlugin.js +++ b/packages/webpack-cli/lib/plugins/CLIPlugin.js @@ -1,132 +1,99 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CLIPlugin = void 0; class CLIPlugin { - constructor(options) { - this.options = options; - } - - setupHotPlugin(compiler) { - const { HotModuleReplacementPlugin } = compiler.webpack || require("webpack"); - const hotModuleReplacementPlugin = Boolean( - compiler.options.plugins.find((plugin) => plugin instanceof HotModuleReplacementPlugin), - ); - - if (!hotModuleReplacementPlugin) { - new HotModuleReplacementPlugin().apply(compiler); + constructor(options) { + this.options = options; } - } - - setupPrefetchPlugin(compiler) { - const { PrefetchPlugin } = compiler.webpack || require("webpack"); - - new PrefetchPlugin(null, this.options.prefetch).apply(compiler); - } - - async setupBundleAnalyzerPlugin(compiler) { - // eslint-disable-next-line node/no-extraneous-require - const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer"); - const bundleAnalyzerPlugin = Boolean( - compiler.options.plugins.find((plugin) => plugin instanceof BundleAnalyzerPlugin), - ); - - if (!bundleAnalyzerPlugin) { - new BundleAnalyzerPlugin().apply(compiler); + setupHotPlugin(compiler) { + const { HotModuleReplacementPlugin } = compiler.webpack || require("webpack"); + const hotModuleReplacementPlugin = Boolean(compiler.options.plugins.find((plugin) => plugin instanceof HotModuleReplacementPlugin)); + if (!hotModuleReplacementPlugin) { + new HotModuleReplacementPlugin().apply(compiler); + } } - } - - setupProgressPlugin(compiler) { - const { ProgressPlugin } = compiler.webpack || require("webpack"); - const progressPlugin = Boolean( - compiler.options.plugins.find((plugin) => plugin instanceof ProgressPlugin), - ); - - if (!progressPlugin) { - new ProgressPlugin({ - profile: this.options.progress === "profile", - }).apply(compiler); + setupPrefetchPlugin(compiler) { + const { PrefetchPlugin } = compiler.webpack || require("webpack"); + new PrefetchPlugin(null, this.options.prefetch).apply(compiler); } - } - - setupHelpfulOutput(compiler) { - const pluginName = "webpack-cli"; - const getCompilationName = () => (compiler.name ? `'${compiler.name}'` : ""); - const logCompilation = (message) => { - if (process.env.WEBPACK_CLI_START_FINISH_FORCE_LOG) { - process.stderr.write(message); - } else { - this.logger.log(message); - } - }; - - const { configPath } = this.options; - - compiler.hooks.run.tap(pluginName, () => { - const name = getCompilationName(); - - logCompilation(`Compiler${name ? ` ${name}` : ""} starting... `); - - if (configPath) { - this.logger.log(`Compiler${name ? ` ${name}` : ""} is using config: '${configPath}'`); - } - }); - - compiler.hooks.watchRun.tap(pluginName, (compiler) => { - const { bail, watch } = compiler.options; - - if (bail && watch) { - this.logger.warn( - 'You are using "bail" with "watch". "bail" will still exit webpack when the first error is found.', - ); - } - - const name = getCompilationName(); - - logCompilation(`Compiler${name ? ` ${name}` : ""} starting... `); - - if (configPath) { - this.logger.log(`Compiler${name ? ` ${name}` : ""} is using config: '${configPath}'`); - } - }); - - compiler.hooks.invalid.tap(pluginName, (filename, changeTime) => { - const date = new Date(changeTime * 1000); - - this.logger.log(`File '${filename}' was modified`); - this.logger.log(`Changed time is ${date} (timestamp is ${changeTime})`); - }); - - (compiler.webpack ? compiler.hooks.afterDone : compiler.hooks.done).tap(pluginName, () => { - const name = getCompilationName(); - - logCompilation(`Compiler${name ? ` ${name}` : ""} finished`); - - process.nextTick(() => { - if (compiler.watchMode) { - this.logger.log(`Compiler${name ? `${name}` : ""} is watching files for updates...`); + async setupBundleAnalyzerPlugin(compiler) { + // eslint-disable-next-line node/no-extraneous-require,@typescript-eslint/no-var-requires + const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer"); + const bundleAnalyzerPlugin = Boolean(compiler.options.plugins.find((plugin) => plugin instanceof BundleAnalyzerPlugin)); + if (!bundleAnalyzerPlugin) { + new BundleAnalyzerPlugin().apply(compiler); } - }); - }); - } - - apply(compiler) { - this.logger = compiler.getInfrastructureLogger("webpack-cli"); - - if (this.options.progress) { - this.setupProgressPlugin(compiler); } - - if (this.options.hot) { - this.setupHotPlugin(compiler); + setupProgressPlugin(compiler) { + const { ProgressPlugin } = compiler.webpack || require("webpack"); + const progressPlugin = Boolean(compiler.options.plugins.find((plugin) => plugin instanceof ProgressPlugin)); + if (!progressPlugin) { + new ProgressPlugin({ + profile: this.options.progress === "profile", + }).apply(compiler); + } } - - if (this.options.prefetch) { - this.setupPrefetchPlugin(compiler); + setupHelpfulOutput(compiler) { + const pluginName = "webpack-cli"; + const getCompilationName = () => (compiler.name ? `'${compiler.name}'` : ""); + const logCompilation = (message) => { + if (process.env.WEBPACK_CLI_START_FINISH_FORCE_LOG) { + process.stderr.write(message); + } + else { + this.logger.log(message); + } + }; + const { configPath } = this.options; + compiler.hooks.run.tap(pluginName, () => { + const name = getCompilationName(); + logCompilation(`Compiler${name ? ` ${name}` : ""} starting... `); + if (configPath) { + this.logger.log(`Compiler${name ? ` ${name}` : ""} is using config: '${configPath}'`); + } + }); + compiler.hooks.watchRun.tap(pluginName, (compiler) => { + const { bail, watch } = compiler.options; + if (bail && watch) { + this.logger.warn('You are using "bail" with "watch". "bail" will still exit webpack when the first error is found.'); + } + const name = getCompilationName(); + logCompilation(`Compiler${name ? ` ${name}` : ""} starting... `); + if (configPath) { + this.logger.log(`Compiler${name ? ` ${name}` : ""} is using config: '${configPath}'`); + } + }); + compiler.hooks.invalid.tap(pluginName, (filename, changeTime) => { + const date = new Date(changeTime * 1000); + this.logger.log(`File '${filename}' was modified`); + this.logger.log(`Changed time is ${date} (timestamp is ${changeTime})`); + }); + (compiler.webpack ? compiler.hooks.afterDone : compiler.hooks.done).tap(pluginName, () => { + const name = getCompilationName(); + logCompilation(`Compiler${name ? ` ${name}` : ""} finished`); + process.nextTick(() => { + if (compiler.watchMode) { + this.logger.log(`Compiler${name ? `${name}` : ""} is watching files for updates...`); + } + }); + }); } - - if (this.options.analyze) { - this.setupBundleAnalyzerPlugin(compiler); + apply(compiler) { + this.logger = compiler.getInfrastructureLogger("webpack-cli"); + if (this.options.progress) { + this.setupProgressPlugin(compiler); + } + if (this.options.hot) { + this.setupHotPlugin(compiler); + } + if (this.options.prefetch) { + this.setupPrefetchPlugin(compiler); + } + if (this.options.analyze) { + this.setupBundleAnalyzerPlugin(compiler); + } + this.setupHelpfulOutput(compiler); } - - this.setupHelpfulOutput(compiler); - } } - +exports.CLIPlugin = CLIPlugin; module.exports = CLIPlugin; diff --git a/packages/webpack-cli/lib/utils/dynamic-import-loader.js b/packages/webpack-cli/lib/utils/dynamic-import-loader.js index c1221bcaffa..324f5122457 100644 --- a/packages/webpack-cli/lib/utils/dynamic-import-loader.js +++ b/packages/webpack-cli/lib/utils/dynamic-import-loader.js @@ -1,13 +1,13 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); function dynamicImportLoader() { - let importESM; - - try { - importESM = new Function("id", "return import(id);"); - } catch (e) { - importESM = null; - } - - return importESM; + let importESM; + try { + importESM = new Function("id", "return import(id);"); + } + catch (e) { + importESM = null; + } + return importESM; } - module.exports = dynamicImportLoader; diff --git a/packages/webpack-cli/lib/webpack-cli.js b/packages/webpack-cli/lib/webpack-cli.js index b28ba64c1bb..5f1020faef3 100644 --- a/packages/webpack-cli/lib/webpack-cli.js +++ b/packages/webpack-cli/lib/webpack-cli.js @@ -1,2343 +1,1905 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); const fs = require("fs"); const path = require("path"); const { pathToFileURL } = require("url"); const util = require("util"); - const { program, Option } = require("commander"); - const WEBPACK_PACKAGE = process.env.WEBPACK_PACKAGE || "webpack"; const WEBPACK_DEV_SERVER_PACKAGE = process.env.WEBPACK_DEV_SERVER_PACKAGE || "webpack-dev-server"; - class WebpackCLI { - constructor() { - this.colors = this.createColors(); - this.logger = this.getLogger(); - - // Initialize program - this.program = program; - this.program.name("webpack"); - this.program.configureOutput({ - writeErr: this.logger.error, - outputError: (str, write) => - write(`Error: ${this.capitalizeFirstLetter(str.replace(/^error:/, "").trim())}`), - }); - } - - capitalizeFirstLetter(str) { - if (typeof str !== "string") { - return ""; - } - - return str.charAt(0).toUpperCase() + str.slice(1); - } - - toKebabCase(str) { - return str.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase(); - } - - createColors(useColor) { - const { createColors, isColorSupported } = require("colorette"); - - let shouldUseColor; - - if (useColor) { - shouldUseColor = useColor; - } else { - shouldUseColor = isColorSupported; - } - - return { ...createColors({ useColor: shouldUseColor }), isColorSupported: shouldUseColor }; - } - - getLogger() { - return { - error: (val) => console.error(`[webpack-cli] ${this.colors.red(util.format(val))}`), - warn: (val) => console.warn(`[webpack-cli] ${this.colors.yellow(val)}`), - info: (val) => console.info(`[webpack-cli] ${this.colors.cyan(val)}`), - success: (val) => console.log(`[webpack-cli] ${this.colors.green(val)}`), - log: (val) => console.log(`[webpack-cli] ${val}`), - raw: (val) => console.log(val), - }; - } - - checkPackageExists(packageName) { - if (process.versions.pnp) { - return true; - } - - let dir = __dirname; - - do { - try { - if (fs.statSync(path.join(dir, "node_modules", packageName)).isDirectory()) { - return true; - } - } catch (_error) { - // Nothing - } - } while (dir !== (dir = path.dirname(dir))); - - return false; - } - - getAvailablePackageManagers() { - const { sync } = require("execa"); - const installers = ["npm", "yarn", "pnpm"]; - const hasPackageManagerInstalled = (packageManager) => { - try { - sync(packageManager, ["--version"]); - - return packageManager; - } catch (err) { - return false; - } - }; - const availableInstallers = installers.filter((installer) => - hasPackageManagerInstalled(installer), - ); - - if (!availableInstallers.length) { - this.logger.error("No package manager found."); - - process.exit(2); - } - - return availableInstallers; - } - - getDefaultPackageManager() { - const { sync } = require("execa"); - const hasLocalNpm = fs.existsSync(path.resolve(process.cwd(), "package-lock.json")); - - if (hasLocalNpm) { - return "npm"; - } - - const hasLocalYarn = fs.existsSync(path.resolve(process.cwd(), "yarn.lock")); - - if (hasLocalYarn) { - return "yarn"; - } - - const hasLocalPnpm = fs.existsSync(path.resolve(process.cwd(), "pnpm-lock.yaml")); - - if (hasLocalPnpm) { - return "pnpm"; - } - - try { - // the sync function below will fail if npm is not installed, - // an error will be thrown - if (sync("npm", ["--version"])) { - return "npm"; - } - } catch (e) { - // Nothing - } - - try { - // the sync function below will fail if yarn is not installed, - // an error will be thrown - if (sync("yarn", ["--version"])) { - return "yarn"; - } - } catch (e) { - // Nothing - } - - try { - // the sync function below will fail if pnpm is not installed, - // an error will be thrown - if (sync("pnpm", ["--version"])) { - return "pnpm"; - } - } catch (e) { - this.logger.error("No package manager found."); - - process.exit(2); - } - } - - async doInstall(packageName, options = {}) { - const packageManager = this.getDefaultPackageManager(); - - if (!packageManager) { - this.logger.error("Can't find package manager"); - - process.exit(2); - } - - if (options.preMessage) { - options.preMessage(); - } - - // yarn uses 'add' command, rest npm and pnpm both use 'install' - const commandToBeRun = `${packageManager} ${[ - packageManager === "yarn" ? "add" : "install", - "-D", - packageName, - ].join(" ")}`; - - const prompt = ({ message, defaultResponse, stream }) => { - const readline = require("readline"); - const rl = readline.createInterface({ - input: process.stdin, - output: stream, - }); - - return new Promise((resolve) => { - rl.question(`${message} `, (answer) => { - // Close the stream - rl.close(); - - const response = (answer || defaultResponse).toLowerCase(); - - // Resolve with the input response - if (response === "y" || response === "yes") { - resolve(true); - } else { - resolve(false); - } + constructor() { + this.colors = this.createColors(); + this.logger = this.getLogger(); + // Initialize program + this.program = program; + this.program.name("webpack"); + this.program.configureOutput({ + writeErr: this.logger.error, + outputError: (str, write) => write(`Error: ${this.capitalizeFirstLetter(str.replace(/^error:/, "").trim())}`), }); - }); - }; - - let needInstall; - - try { - needInstall = await prompt({ - message: `[webpack-cli] Would you like to install '${this.colors.green( - packageName, - )}' package? (That will run '${this.colors.green(commandToBeRun)}') (${this.colors.yellow( - "Y/n", - )})`, - defaultResponse: "Y", - stream: process.stderr, - }); - } catch (error) { - this.logger.error(error); - - process.exit(error); } - - if (needInstall) { - const execa = require("execa"); - - try { - await execa(commandToBeRun, [], { stdio: "inherit", shell: true }); - } catch (error) { - this.logger.error(error); - - process.exit(2); - } - - return packageName; + isMultipleCompiler(compiler) { + return compiler.compilers; } - - process.exit(2); - } - - async tryRequireThenImport(module, handleError = true) { - let result; - - try { - result = require(module); - } catch (error) { - const dynamicImportLoader = require("./utils/dynamic-import-loader")(); - if ( - (error.code === "ERR_REQUIRE_ESM" || process.env.WEBPACK_CLI_FORCE_LOAD_ESM_CONFIG) && - pathToFileURL && - dynamicImportLoader - ) { - const urlForConfig = pathToFileURL(module); - - result = await dynamicImportLoader(urlForConfig); - result = result.default; - - return result; - } - - if (handleError) { - this.logger.error(error); - process.exit(2); - } else { - throw error; - } + isPromise(value) { + return typeof value.then === "function"; } - - // For babel/typescript - if (result && typeof result === "object" && "default" in result) { - result = result.default || {}; + isFunction(value) { + return typeof value === "function"; } - - return result || {}; - } - - loadJSONFile(pathToFile, handleError = true) { - let result; - - try { - result = require(pathToFile); - } catch (error) { - if (handleError) { - this.logger.error(error); - process.exit(2); - } else { - throw error; - } - } - - return result; - } - - async makeCommand(commandOptions, options, action) { - const alreadyLoaded = this.program.commands.find( - (command) => - command.name() === commandOptions.name.split(" ")[0] || - command.aliases().includes(commandOptions.alias), - ); - - if (alreadyLoaded) { - return; - } - - const command = this.program.command(commandOptions.name, { - noHelp: commandOptions.noHelp, - hidden: commandOptions.hidden, - isDefault: commandOptions.isDefault, - }); - - if (commandOptions.description) { - command.description(commandOptions.description, commandOptions.argsDescription); - } - - if (commandOptions.usage) { - command.usage(commandOptions.usage); - } - - if (Array.isArray(commandOptions.alias)) { - command.aliases(commandOptions.alias); - } else { - command.alias(commandOptions.alias); + capitalizeFirstLetter(str) { + if (typeof str !== "string") { + return ""; + } + return str.charAt(0).toUpperCase() + str.slice(1); } - - if (commandOptions.pkg) { - command.pkg = commandOptions.pkg; - } else { - command.pkg = "webpack-cli"; + toKebabCase(str) { + return str.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase(); } - - const { forHelp } = this.program; - - let allDependenciesInstalled = true; - - if (commandOptions.dependencies && commandOptions.dependencies.length > 0) { - for (const dependency of commandOptions.dependencies) { - const isPkgExist = this.checkPackageExists(dependency); - - if (isPkgExist) { - continue; - } else if (!isPkgExist && forHelp) { - allDependenciesInstalled = false; - continue; - } - - let skipInstallation = false; - - // Allow to use `./path/to/webpack.js` outside `node_modules` - if (dependency === WEBPACK_PACKAGE && fs.existsSync(WEBPACK_PACKAGE)) { - skipInstallation = true; - } - - // Allow to use `./path/to/webpack-dev-server.js` outside `node_modules` - if (dependency === WEBPACK_DEV_SERVER_PACKAGE && fs.existsSync(WEBPACK_PACKAGE)) { - skipInstallation = true; + createColors(useColor) { + const { createColors, isColorSupported } = require("colorette"); + let shouldUseColor; + if (useColor) { + shouldUseColor = useColor; } - - if (skipInstallation) { - continue; + else { + shouldUseColor = isColorSupported; } - - await this.doInstall(dependency, { - preMessage: () => { - this.logger.error( - `For using '${this.colors.green( - commandOptions.name.split(" ")[0], - )}' command you need to install: '${this.colors.green(dependency)}' package.`, - ); - }, - }); - } - } - - if (options) { - if (typeof options === "function") { - if (forHelp && !allDependenciesInstalled) { - command.description( - `${ - commandOptions.description - } To see all available options you need to install ${commandOptions.dependencies - .map((dependency) => `'${dependency}'`) - .join(", ")}.`, - ); - options = []; - } else { - options = await options(); - } - } - - options.forEach((optionForCommand) => { - this.makeOption(command, optionForCommand); - }); + return Object.assign(Object.assign({}, createColors({ useColor: shouldUseColor })), { isColorSupported: shouldUseColor }); } - - command.action(action); - - return command; - } - - makeOption(command, option) { - let mainOption; - let negativeOption; - - if (option.configs) { - let needNegativeOption = false; - let negatedDescription; - const mainOptionType = new Set(); - - option.configs.forEach((config) => { - // Possible value: "enum" | "string" | "path" | "number" | "boolean" | "RegExp" | "reset" - switch (config.type) { - case "reset": - mainOptionType.add(Boolean); - break; - case "boolean": - if (!needNegativeOption) { - needNegativeOption = true; - negatedDescription = config.negatedDescription; - } - - mainOptionType.add(Boolean); - break; - case "number": - mainOptionType.add(Number); - break; - case "string": - case "path": - case "RegExp": - mainOptionType.add(String); - break; - case "enum": { - let hasFalseEnum = false; - - const enumTypes = config.values.map((value) => { - switch (typeof value) { - case "string": - mainOptionType.add(String); - break; - case "number": - mainOptionType.add(Number); - break; - case "boolean": - if (!hasFalseEnum && value === false) { - hasFalseEnum = true; - break; - } - - mainOptionType.add(Boolean); - break; - } - }); - - if (!needNegativeOption) { - needNegativeOption = hasFalseEnum; - negatedDescription = config.negatedDescription; - } - - return enumTypes; - } - } - }); - - mainOption = { - flags: option.alias ? `-${option.alias}, --${option.name}` : `--${option.name}`, - description: option.description || "", - type: mainOptionType, - multiple: option.multiple, - defaultValue: option.defaultValue, - }; - - if (needNegativeOption) { - negativeOption = { - flags: `--no-${option.name}`, - description: - negatedDescription || option.negatedDescription || `Negative '${option.name}' option.`, - }; - } - } else { - mainOption = { - flags: option.alias ? `-${option.alias}, --${option.name}` : `--${option.name}`, - // TODO `describe` used by `webpack-dev-server@3` - description: option.description || option.describe || "", - type: option.type - ? new Set(Array.isArray(option.type) ? option.type : [option.type]) - : new Set([Boolean]), - multiple: option.multiple, - defaultValue: option.defaultValue, - }; - - if (option.negative) { - negativeOption = { - flags: `--no-${option.name}`, - description: option.negatedDescription - ? option.negatedDescription - : `Negative '${option.name}' option.`, + getLogger() { + return { + error: (val) => console.error(`[webpack-cli] ${this.colors.red(util.format(val))}`), + warn: (val) => console.warn(`[webpack-cli] ${this.colors.yellow(val)}`), + info: (val) => console.info(`[webpack-cli] ${this.colors.cyan(val)}`), + success: (val) => console.log(`[webpack-cli] ${this.colors.green(val)}`), + log: (val) => console.log(`[webpack-cli] ${val}`), + raw: (val) => console.log(val), }; - } - } - - if (mainOption.type.size > 1 && mainOption.type.has(Boolean)) { - mainOption.flags = `${mainOption.flags} [value${mainOption.multiple ? "..." : ""}]`; - } else if (mainOption.type.size > 0 && !mainOption.type.has(Boolean)) { - mainOption.flags = `${mainOption.flags} `; - } - - if (mainOption.type.size === 1) { - if (mainOption.type.has(Number)) { - let skipDefault = true; - - const optionForCommand = new Option(mainOption.flags, mainOption.description) - .argParser((value, prev = []) => { - if (mainOption.defaultValue && mainOption.multiple && skipDefault) { - prev = []; - skipDefault = false; - } - - return mainOption.multiple ? [].concat(prev).concat(Number(value)) : Number(value); - }) - .default(mainOption.defaultValue); - - optionForCommand.helpLevel = option.helpLevel; - - command.addOption(optionForCommand); - } else if (mainOption.type.has(String)) { - let skipDefault = true; - - const optionForCommand = new Option(mainOption.flags, mainOption.description) - .argParser((value, prev = []) => { - if (mainOption.defaultValue && mainOption.multiple && skipDefault) { - prev = []; - skipDefault = false; - } - - return mainOption.multiple ? [].concat(prev).concat(value) : value; - }) - .default(mainOption.defaultValue); - - optionForCommand.helpLevel = option.helpLevel; - - command.addOption(optionForCommand); - } else if (mainOption.type.has(Boolean)) { - const optionForCommand = new Option(mainOption.flags, mainOption.description).default( - mainOption.defaultValue, - ); - - optionForCommand.helpLevel = option.helpLevel; - - command.addOption(optionForCommand); - } else { - const optionForCommand = new Option(mainOption.flags, mainOption.description) - .argParser(Array.from(mainOption.type)[0]) - .default(mainOption.defaultValue); - - optionForCommand.helpLevel = option.helpLevel; - - command.addOption(optionForCommand); - } - } else if (mainOption.type.size > 1) { - let skipDefault = true; - - const optionForCommand = new Option( - mainOption.flags, - mainOption.description, - mainOption.defaultValue, - ) - .argParser((value, prev = []) => { - if (mainOption.defaultValue && mainOption.multiple && skipDefault) { - prev = []; - skipDefault = false; - } - - if (mainOption.type.has(Number)) { - const numberValue = Number(value); - - if (!isNaN(numberValue)) { - return mainOption.multiple ? [].concat(prev).concat(numberValue) : numberValue; - } - } - - if (mainOption.type.has(String)) { - return mainOption.multiple ? [].concat(prev).concat(value) : value; - } - - return value; - }) - .default(mainOption.defaultValue); - - optionForCommand.helpLevel = option.helpLevel; - - command.addOption(optionForCommand); - } else if (mainOption.type.size === 0 && negativeOption) { - const optionForCommand = new Option(mainOption.flags, mainOption.description); - - // Hide stub option - optionForCommand.hideHelp(); - optionForCommand.helpLevel = option.helpLevel; - - command.addOption(optionForCommand); } - - if (negativeOption) { - const optionForCommand = new Option(negativeOption.flags, negativeOption.description); - - optionForCommand.helpLevel = option.helpLevel; - - command.addOption(optionForCommand); + checkPackageExists(packageName) { + if (process.versions.pnp) { + return true; + } + let dir = __dirname; + do { + try { + if (fs.statSync(path.join(dir, "node_modules", packageName)).isDirectory()) { + return true; + } + } + catch (_error) { + // Nothing + } + } while (dir !== (dir = path.dirname(dir))); + return false; } - } - - getBuiltInOptions() { - if (this.builtInOptionsCache) { - return this.builtInOptionsCache; + getAvailablePackageManagers() { + const { sync } = require("execa"); + const installers = ["npm", "yarn", "pnpm"]; + const hasPackageManagerInstalled = (packageManager) => { + try { + sync(packageManager, ["--version"]); + return packageManager; + } + catch (err) { + return false; + } + }; + const availableInstallers = installers.filter((installer) => hasPackageManagerInstalled(installer)); + if (!availableInstallers.length) { + this.logger.error("No package manager found."); + process.exit(2); + } + return availableInstallers; } - - const minimumHelpFlags = [ - "config", - "config-name", - "merge", - "env", - "mode", - "watch", - "watch-options-stdin", - "stats", - "devtool", - "entry", - "target", - "progress", - "json", - "name", - "output-path", - "node-env", - ]; - - const builtInFlags = [ - // For configs - { - name: "config", - alias: "c", - configs: [ - { - type: "string", - }, - ], - multiple: true, - description: "Provide path to a webpack configuration file e.g. ./webpack.config.js.", - }, - { - name: "config-name", - configs: [ - { - type: "string", - }, - ], - multiple: true, - description: "Name of the configuration to use.", - }, - { - name: "merge", - alias: "m", - configs: [ - { - type: "enum", - values: [true], - }, - ], - description: "Merge two or more configurations using 'webpack-merge'.", - }, - // Complex configs - { - name: "env", - type: (value, previous = {}) => { - // for https://github.com/webpack/webpack-cli/issues/2642 - if (value.endsWith("=")) { - value.concat('""'); - } - - // This ensures we're only splitting by the first `=` - const [allKeys, val] = value.split(/=(.+)/, 2); - const splitKeys = allKeys.split(/\.(?!$)/); - - let prevRef = previous; - - splitKeys.forEach((someKey, index) => { - if (!prevRef[someKey]) { - prevRef[someKey] = {}; - } - - if (typeof prevRef[someKey] === "string") { - prevRef[someKey] = {}; - } - - if (index === splitKeys.length - 1) { - if (typeof val === "string") { - prevRef[someKey] = val; - } else { - prevRef[someKey] = true; - } - } - - prevRef = prevRef[someKey]; - }); - - return previous; - }, - multiple: true, - description: "Environment passed to the configuration when it is a function.", - }, - { - name: "node-env", - configs: [ - { - type: "string", - }, - ], - multiple: false, - description: "Sets process.env.NODE_ENV to the specified value.", - }, - - // Adding more plugins - { - name: "hot", - alias: "h", - configs: [ - { - type: "string", - }, - { - type: "boolean", - }, - ], - negative: true, - description: "Enables Hot Module Replacement", - negatedDescription: "Disables Hot Module Replacement.", - }, - { - name: "analyze", - configs: [ - { - type: "enum", - values: [true], - }, - ], - multiple: false, - description: "It invokes webpack-bundle-analyzer plugin to get bundle information.", - }, - { - name: "progress", - configs: [ - { - type: "string", - }, - { - type: "enum", - values: [true], - }, - ], - description: "Print compilation progress during build.", - }, - { - name: "prefetch", - configs: [ - { - type: "string", - }, - ], - description: "Prefetch this request.", - }, - - // Output options - { - name: "json", - configs: [ - { - type: "string", - }, - { - type: "enum", - values: [true], - }, - ], - alias: "j", - description: "Prints result as JSON or store it in a file.", - }, - - // For webpack@4 - { - name: "entry", - configs: [ - { - type: "string", - }, - ], - multiple: true, - description: "The entry point(s) of your application e.g. ./src/main.js.", - }, - { - name: "output-path", - alias: "o", - configs: [ - { - type: "string", - }, - ], - description: "Output location of the file generated by webpack e.g. ./dist/.", - }, - { - name: "target", - alias: "t", - configs: [ - { - type: "string", - }, - ], - multiple: this.webpack.cli !== undefined, - description: "Sets the build target e.g. node.", - }, - { - name: "devtool", - configs: [ - { - type: "string", - }, - { - type: "enum", - values: [false], - }, - ], - negative: true, - alias: "d", - description: "Determine source maps to use.", - negatedDescription: "Do not generate source maps.", - }, - { - name: "mode", - configs: [ - { - type: "string", - }, - ], - description: "Defines the mode to pass to webpack.", - }, - { - name: "name", - configs: [ - { - type: "string", - }, - ], - description: "Name of the configuration. Used when loading multiple configurations.", - }, - { - name: "stats", - configs: [ - { - type: "string", - }, - { - type: "boolean", - }, - ], - negative: true, - description: "It instructs webpack on how to treat the stats e.g. verbose.", - negatedDescription: "Disable stats output.", - }, - { - name: "watch", - configs: [ - { - type: "boolean", - }, - ], - negative: true, - alias: "w", - description: "Watch for files changes.", - negatedDescription: "Do not watch for file changes.", - }, - { - name: "watch-options-stdin", - configs: [ - { - type: "boolean", - }, - ], - negative: true, - description: "Stop watching when stdin stream has ended.", - negatedDescription: "Do not stop watching when stdin stream has ended.", - }, - ]; - - // Extract all the flags being exported from core. - // A list of cli flags generated by core can be found here https://github.com/webpack/webpack/blob/master/test/__snapshots__/Cli.test.js.snap - const coreFlags = this.webpack.cli - ? Object.entries(this.webpack.cli.getArguments()).map(([flag, meta]) => { - const inBuiltIn = builtInFlags.find((builtInFlag) => builtInFlag.name === flag); - - if (inBuiltIn) { - return { - ...meta, - name: flag, - group: "core", - ...inBuiltIn, - configs: meta.configs || [], - }; - } - - return { ...meta, name: flag, group: "core" }; - }) - : []; - - const options = [] - .concat( - builtInFlags.filter( - (builtInFlag) => !coreFlags.find((coreFlag) => builtInFlag.name === coreFlag.name), - ), - ) - .concat(coreFlags) - .map((option) => { - option.helpLevel = minimumHelpFlags.includes(option.name) ? "minimum" : "verbose"; - - return option; - }); - - this.builtInOptionsCache = options; - - return options; - } - - async loadWebpack(handleError = true) { - return this.tryRequireThenImport(WEBPACK_PACKAGE, handleError); - } - - async run(args, parseOptions) { - // Built-in internal commands - const buildCommandOptions = { - name: "build [entries...]", - alias: ["bundle", "b"], - description: "Run webpack (default command, can be omitted).", - usage: "[entries...] [options]", - dependencies: [WEBPACK_PACKAGE], - }; - const watchCommandOptions = { - name: "watch [entries...]", - alias: "w", - description: "Run webpack and watch for files changes.", - usage: "[entries...] [options]", - dependencies: [WEBPACK_PACKAGE], - }; - const versionCommandOptions = { - name: "version [commands...]", - alias: "v", - description: - "Output the version number of 'webpack', 'webpack-cli' and 'webpack-dev-server' and commands.", - }; - const helpCommandOptions = { - name: "help [command] [option]", - alias: "h", - description: "Display help for commands and options.", - }; - // Built-in external commands - const externalBuiltInCommandsInfo = [ - { - name: "serve [entries...]", - alias: ["server", "s"], - pkg: "@webpack-cli/serve", - }, - { - name: "info", - alias: "i", - pkg: "@webpack-cli/info", - }, - { - name: "init", - alias: ["create", "new", "c", "n"], - pkg: "@webpack-cli/generators", - }, - { - name: "loader", - alias: "l", - pkg: "@webpack-cli/generators", - }, - { - name: "plugin", - alias: "p", - pkg: "@webpack-cli/generators", - }, - { - name: "migrate", - alias: "m", - pkg: "@webpack-cli/migrate", - }, - { - name: "configtest [config-path]", - alias: "t", - pkg: "@webpack-cli/configtest", - }, - ]; - - const knownCommands = [ - buildCommandOptions, - watchCommandOptions, - versionCommandOptions, - helpCommandOptions, - ...externalBuiltInCommandsInfo, - ]; - const getCommandName = (name) => name.split(" ")[0]; - const isKnownCommand = (name) => - knownCommands.find( - (command) => - getCommandName(command.name) === name || - (Array.isArray(command.alias) ? command.alias.includes(name) : command.alias === name), - ); - const isCommand = (input, commandOptions) => { - const longName = getCommandName(commandOptions.name); - - if (input === longName) { - return true; - } - - if (commandOptions.alias) { - if (Array.isArray(commandOptions.alias)) { - return commandOptions.alias.includes(input); - } else { - return commandOptions.alias === input; + getDefaultPackageManager() { + const { sync } = require("execa"); + const hasLocalNpm = fs.existsSync(path.resolve(process.cwd(), "package-lock.json")); + if (hasLocalNpm) { + return "npm"; } - } - - return false; - }; - const findCommandByName = (name) => - this.program.commands.find( - (command) => name === command.name() || command.aliases().includes(name), - ); - const isOption = (value) => value.startsWith("-"); - const isGlobalOption = (value) => - value === "--color" || - value === "--no-color" || - value === "-v" || - value === "--version" || - value === "-h" || - value === "--help"; - - const loadCommandByName = async (commandName, allowToInstall = false) => { - const isBuildCommandUsed = isCommand(commandName, buildCommandOptions); - const isWatchCommandUsed = isCommand(commandName, watchCommandOptions); - - if (isBuildCommandUsed || isWatchCommandUsed) { - await this.makeCommand( - isBuildCommandUsed ? buildCommandOptions : watchCommandOptions, - async () => { - this.webpack = await this.loadWebpack(); - - return isWatchCommandUsed - ? this.getBuiltInOptions().filter((option) => option.name !== "watch") - : this.getBuiltInOptions(); - }, - async (entries, options) => { - if (entries.length > 0) { - options.entry = [...entries, ...(options.entry || [])]; - } - - await this.runWebpack(options, isWatchCommandUsed); - }, - ); - } else if (isCommand(commandName, helpCommandOptions)) { - // Stub for the `help` command - this.makeCommand(helpCommandOptions, [], () => {}); - } else if (isCommand(commandName, versionCommandOptions)) { - // Stub for the `version` command - this.makeCommand(versionCommandOptions, [], () => {}); - } else { - const builtInExternalCommandInfo = externalBuiltInCommandsInfo.find( - (externalBuiltInCommandInfo) => - getCommandName(externalBuiltInCommandInfo.name) === commandName || - (Array.isArray(externalBuiltInCommandInfo.alias) - ? externalBuiltInCommandInfo.alias.includes(commandName) - : externalBuiltInCommandInfo.alias === commandName), - ); - - let pkg; - - if (builtInExternalCommandInfo) { - ({ pkg } = builtInExternalCommandInfo); - } else { - pkg = commandName; + const hasLocalYarn = fs.existsSync(path.resolve(process.cwd(), "yarn.lock")); + if (hasLocalYarn) { + return "yarn"; } - - if (pkg !== "webpack-cli" && !this.checkPackageExists(pkg)) { - if (!allowToInstall) { - return; - } - - pkg = await this.doInstall(pkg, { - preMessage: () => { - this.logger.error( - `For using this command you need to install: '${this.colors.green(pkg)}' package.`, - ); - }, - }); + const hasLocalPnpm = fs.existsSync(path.resolve(process.cwd(), "pnpm-lock.yaml")); + if (hasLocalPnpm) { + return "pnpm"; } - - let loadedCommand; - try { - loadedCommand = await this.tryRequireThenImport(pkg, false); - } catch (error) { - // Ignore, command is not installed - - return; + // the sync function below will fail if npm is not installed, + // an error will be thrown + if (sync("npm", ["--version"])) { + return "npm"; + } + } + catch (e) { + // Nothing } - - let command; - try { - command = new loadedCommand(); - - await command.apply(this); - } catch (error) { - this.logger.error(`Unable to load '${pkg}' command`); - this.logger.error(error); - process.exit(2); + // the sync function below will fail if yarn is not installed, + // an error will be thrown + if (sync("yarn", ["--version"])) { + return "yarn"; + } } - } - }; - - // Register own exit - this.program.exitOverride(async (error) => { - if (error.exitCode === 0) { - process.exit(0); - } - - if (error.code === "executeSubCommandAsync") { - process.exit(2); - } - - if (error.code === "commander.help") { - process.exit(0); - } - - if (error.code === "commander.unknownOption") { - let name = error.message.match(/'(.+)'/); - - if (name) { - name = name[1].substr(2); - - if (name.includes("=")) { - name = name.split("=")[0]; - } - - const { operands } = this.program.parseOptions(this.program.args); - const operand = - typeof operands[0] !== "undefined" - ? operands[0] - : getCommandName(buildCommandOptions.name); - - if (operand) { - const command = findCommandByName(operand); - - if (!command) { - this.logger.error(`Can't find and load command '${operand}'`); - this.logger.error("Run 'webpack --help' to see available commands and options"); - process.exit(2); - } - - const levenshtein = require("fastest-levenshtein"); - - command.options.forEach((option) => { - if (!option.hidden && levenshtein.distance(name, option.long.slice(2)) < 3) { - this.logger.error(`Did you mean '--${option.name()}'?`); - } - }); - } + catch (e) { + // Nothing } - } - - // Codes: - // - commander.unknownCommand - // - commander.missingArgument - // - commander.missingMandatoryOptionValue - // - commander.optionMissingArgument - - this.logger.error("Run 'webpack --help' to see available commands and options"); - process.exit(2); - }); - - // Default `--color` and `--no-color` options - const cli = this; - this.program.option("--color", "Enable colors on console."); - this.program.on("option:color", function () { - const { color } = this.opts(); - - cli.isColorSupportChanged = color; - cli.colors = cli.createColors(color); - }); - this.program.option("--no-color", "Disable colors on console."); - this.program.on("option:no-color", function () { - const { color } = this.opts(); - - cli.isColorSupportChanged = color; - cli.colors = cli.createColors(color); - }); - - // Make `-v, --version` options - // Make `version|v [commands...]` command - const outputVersion = async (options) => { - // Filter `bundle`, `watch`, `version` and `help` commands - const possibleCommandNames = options.filter( - (option) => - !isCommand(option, buildCommandOptions) && - !isCommand(option, watchCommandOptions) && - !isCommand(option, versionCommandOptions) && - !isCommand(option, helpCommandOptions), - ); - - possibleCommandNames.forEach((possibleCommandName) => { - if (!isOption(possibleCommandName)) { - return; + try { + // the sync function below will fail if pnpm is not installed, + // an error will be thrown + if (sync("pnpm", ["--version"])) { + return "pnpm"; + } } - - this.logger.error(`Unknown option '${possibleCommandName}'`); - this.logger.error("Run 'webpack --help' to see available commands and options"); - process.exit(2); - }); - - if (possibleCommandNames.length > 0) { - await Promise.all( - possibleCommandNames.map((possibleCommand) => loadCommandByName(possibleCommand)), - ); - - for (const possibleCommandName of possibleCommandNames) { - const foundCommand = findCommandByName(possibleCommandName); - - if (!foundCommand) { - this.logger.error(`Unknown command '${possibleCommandName}'`); - this.logger.error("Run 'webpack --help' to see available commands and options"); + catch (e) { + this.logger.error("No package manager found."); process.exit(2); - } - - try { - const { name, version } = this.loadJSONFile(`${foundCommand.pkg}/package.json`); - - this.logger.raw(`${name} ${version}`); - } catch (e) { - this.logger.error(`Error: External package '${foundCommand.pkg}' not found`); + } + } + async doInstall(packageName, options = {}) { + const packageManager = this.getDefaultPackageManager(); + if (!packageManager) { + this.logger.error("Can't find package manager"); process.exit(2); - } } - } - - let webpack; - - try { - webpack = await this.loadWebpack(false); - } catch (_error) { - // Nothing - } - - this.logger.raw(`webpack: ${webpack ? webpack.version : "not installed"}`); - - const pkgJSON = this.loadJSONFile("../package.json"); - - this.logger.raw(`webpack-cli: ${pkgJSON.version}`); - - let devServer; - - try { - devServer = await this.loadJSONFile("webpack-dev-server/package.json", false); - } catch (_error) { - // Nothing - } - - this.logger.raw(`webpack-dev-server ${devServer ? devServer.version : "not installed"}`); - - process.exit(0); - }; - this.program.option( - "-v, --version", - "Output the version number of 'webpack', 'webpack-cli' and 'webpack-dev-server' and commands.", - ); - - const outputHelp = async (options, isVerbose, isHelpCommandSyntax, program) => { - const { bold } = this.colors; - const outputIncorrectUsageOfHelp = () => { - this.logger.error("Incorrect use of help"); - this.logger.error( - "Please use: 'webpack help [command] [option]' | 'webpack [command] --help'", - ); - this.logger.error("Run 'webpack --help' to see available commands and options"); - process.exit(2); - }; - - const isGlobalHelp = options.length === 0; - const isCommandHelp = options.length === 1 && !isOption(options[0]); - - if (isGlobalHelp || isCommandHelp) { - program.configureHelp({ - sortSubcommands: true, - // Support multiple aliases - commandUsage: (command) => { - let parentCmdNames = ""; - - for (let parentCmd = command.parent; parentCmd; parentCmd = parentCmd.parent) { - parentCmdNames = `${parentCmd.name()} ${parentCmdNames}`; - } - - if (isGlobalHelp) { - return `${parentCmdNames}${command.usage()}\n${bold( - "Alternative usage to run commands:", - )} ${parentCmdNames}[command] [options]`; - } - - return `${parentCmdNames}${command.name()}|${command - .aliases() - .join("|")} ${command.usage()}`; - }, - // Support multiple aliases - subcommandTerm: (command) => { - const humanReadableArgumentName = (argument) => { - const nameOutput = argument.name + (argument.variadic === true ? "..." : ""); - - return argument.required ? "<" + nameOutput + ">" : "[" + nameOutput + "]"; - }; - const args = command._args.map((arg) => humanReadableArgumentName(arg)).join(" "); - - return `${command.name()}|${command.aliases().join("|")}${args ? ` ${args}` : ""}${ - command.options.length > 0 ? " [options]" : "" - }`; - }, - visibleOptions: function visibleOptions(command) { - return command.options.filter((option) => { - if (option.hidden) { - return false; - } - - switch (option.helpLevel) { - case "verbose": - return isVerbose; - case "minimum": - default: - return true; - } + if (options.preMessage) { + options.preMessage(); + } + // yarn uses 'add' command, rest npm and pnpm both use 'install' + const commandToBeRun = `${packageManager} ${[ + packageManager === "yarn" ? "add" : "install", + "-D", + packageName, + ].join(" ")}`; + const prompt = ({ message, defaultResponse, stream }) => { + const readline = require("readline"); + const rl = readline.createInterface({ + input: process.stdin, + output: stream, }); - }, - padWidth(command, helper) { - return Math.max( - helper.longestArgumentTermLength(command, helper), - helper.longestOptionTermLength(command, helper), - // For global options - helper.longestOptionTermLength(program, helper), - helper.longestSubcommandTermLength(isGlobalHelp ? program : command, helper), - ); - }, - formatHelp: (command, helper) => { - const termWidth = helper.padWidth(command, helper); - const helpWidth = helper.helpWidth || process.env.WEBPACK_CLI_HELP_WIDTH || 80; - const itemIndentWidth = 2; - const itemSeparatorWidth = 2; // between term and description - - const formatItem = (term, description) => { - if (description) { - const fullText = `${term.padEnd(termWidth + itemSeparatorWidth)}${description}`; - - return helper.wrap( - fullText, - helpWidth - itemIndentWidth, - termWidth + itemSeparatorWidth, - ); - } - - return term; - }; - - const formatList = (textArray) => - textArray.join("\n").replace(/^/gm, " ".repeat(itemIndentWidth)); - - // Usage - let output = [`${bold("Usage:")} ${helper.commandUsage(command)}`, ""]; - - // Description - const commandDescription = isGlobalHelp - ? "The build tool for modern web applications." - : helper.commandDescription(command); - - if (commandDescription.length > 0) { - output = output.concat([commandDescription, ""]); - } - - // Arguments - const argumentList = helper - .visibleArguments(command) - .map((argument) => formatItem(argument.term, argument.description)); - - if (argumentList.length > 0) { - output = output.concat([bold("Arguments:"), formatList(argumentList), ""]); - } - - // Options - const optionList = helper - .visibleOptions(command) - .map((option) => - formatItem(helper.optionTerm(option), helper.optionDescription(option)), - ); - - if (optionList.length > 0) { - output = output.concat([bold("Options:"), formatList(optionList), ""]); - } - - // Global options - const globalOptionList = program.options.map((option) => - formatItem(helper.optionTerm(option), helper.optionDescription(option)), - ); - - if (globalOptionList.length > 0) { - output = output.concat([bold("Global options:"), formatList(globalOptionList), ""]); - } - - // Commands - const commandList = helper - .visibleCommands(isGlobalHelp ? program : command) - .map((command) => - formatItem(helper.subcommandTerm(command), helper.subcommandDescription(command)), - ); - - if (commandList.length > 0) { - output = output.concat([bold("Commands:"), formatList(commandList), ""]); - } - - return output.join("\n"); - }, - }); - - if (isGlobalHelp) { - await Promise.all( - knownCommands.map((knownCommand) => { - return loadCommandByName(getCommandName(knownCommand.name)); - }), - ); - - const buildCommand = findCommandByName(getCommandName(buildCommandOptions.name)); - - this.logger.raw(buildCommand.helpInformation()); - } else { - const name = options[0]; - - await loadCommandByName(name); - - const command = findCommandByName(name); - - if (!command) { - const builtInCommandUsed = externalBuiltInCommandsInfo.find( - (command) => command.name.includes(name) || name === command.alias, - ); - if (typeof builtInCommandUsed !== "undefined") { - this.logger.error( - `For using '${name}' command you need to install '${builtInCommandUsed.pkg}' package.`, - ); - } else { - this.logger.error(`Can't find and load command '${name}'`); - this.logger.error("Run 'webpack --help' to see available commands and options."); + return new Promise((resolve) => { + rl.question(`${message} `, (answer) => { + // Close the stream + rl.close(); + const response = (answer || defaultResponse).toLowerCase(); + // Resolve with the input response + if (response === "y" || response === "yes") { + resolve(true); + } + else { + resolve(false); + } + }); + }); + }; + let needInstall; + try { + needInstall = await prompt({ + message: `[webpack-cli] Would you like to install '${this.colors.green(packageName)}' package? (That will run '${this.colors.green(commandToBeRun)}') (${this.colors.yellow("Y/n")})`, + defaultResponse: "Y", + stream: process.stderr, + }); + } + catch (error) { + this.logger.error(error); + process.exit(error); + } + if (needInstall) { + const execa = require("execa"); + try { + await execa(commandToBeRun, [], { stdio: "inherit", shell: true }); } - process.exit(2); - } - - this.logger.raw(command.helpInformation()); + catch (error) { + this.logger.error(error); + process.exit(2); + } + return packageName; } - } else if (isHelpCommandSyntax) { - let isCommandSpecified = false; - let commandName = getCommandName(buildCommandOptions.name); - let optionName; - - if (options.length === 1) { - optionName = options[0]; - } else if (options.length === 2) { - isCommandSpecified = true; - commandName = options[0]; - optionName = options[1]; - - if (isOption(commandName)) { - outputIncorrectUsageOfHelp(); - } - } else { - outputIncorrectUsageOfHelp(); + process.exit(2); + } + async tryRequireThenImport(module, handleError = true) { + let result; + try { + result = require(module); } - - await loadCommandByName(commandName); - - const command = isGlobalOption(optionName) ? program : findCommandByName(commandName); - - if (!command) { - this.logger.error(`Can't find and load command '${commandName}'`); - this.logger.error("Run 'webpack --help' to see available commands and options"); - process.exit(2); + catch (error) { + const dynamicImportLoader = require("./utils/dynamic-import-loader")(); + if ((error.code === "ERR_REQUIRE_ESM" || + process.env.WEBPACK_CLI_FORCE_LOAD_ESM_CONFIG) && + pathToFileURL && + dynamicImportLoader) { + const urlForConfig = pathToFileURL(module); + result = await dynamicImportLoader(urlForConfig); + result = result.default; + return result; + } + if (handleError) { + this.logger.error(error); + process.exit(2); + } + else { + throw error; + } } - - const option = command.options.find( - (option) => option.short === optionName || option.long === optionName, - ); - - if (!option) { - this.logger.error(`Unknown option '${optionName}'`); - this.logger.error("Run 'webpack --help' to see available commands and options"); - process.exit(2); + // For babel/typescript + if (result && typeof result === "object" && "default" in result) { + result = result.default || {}; } - - const nameOutput = - option.flags.replace(/^.+[[<]/, "").replace(/(\.\.\.)?[\]>].*$/, "") + - (option.variadic === true ? "..." : ""); - const value = option.required - ? "<" + nameOutput + ">" - : option.optional - ? "[" + nameOutput + "]" - : ""; - - this.logger.raw( - `${bold("Usage")}: webpack${isCommandSpecified ? ` ${commandName}` : ""} ${option.long}${ - value ? ` ${value}` : "" - }`, - ); - - if (option.short) { - this.logger.raw( - `${bold("Short:")} webpack${isCommandSpecified ? ` ${commandName}` : ""} ${ - option.short - }${value ? ` ${value}` : ""}`, - ); + return result || {}; + } + loadJSONFile(pathToFile, handleError = true) { + let result; + try { + result = require(pathToFile); } - - if (option.description) { - this.logger.raw(`${bold("Description:")} ${option.description}`); + catch (error) { + if (handleError) { + this.logger.error(error); + process.exit(2); + } + else { + throw error; + } } - - if (!option.negate && option.defaultValue) { - this.logger.raw(`${bold("Default value:")} ${JSON.stringify(option.defaultValue)}`); + return result; + } + async makeCommand(commandOptions, options, action) { + const alreadyLoaded = this.program.commands.find((command) => command.name() === commandOptions.name.split(" ")[0] || + command.aliases().includes(commandOptions.alias)); + if (alreadyLoaded) { + return; } - - const flag = this.getBuiltInOptions().find((flag) => option.long === `--${flag.name}`); - - if (flag && flag.configs) { - const possibleValues = flag.configs.reduce((accumulator, currentValue) => { - if (currentValue.values) { - return accumulator.concat(currentValue.values); - } else { - return accumulator; - } - }, []); - - if (possibleValues.length > 0) { - this.logger.raw( - `${bold("Possible values:")} ${JSON.stringify(possibleValues.join(" | "))}`, - ); - } + const command = this.program.command(commandOptions.name, { + noHelp: commandOptions.noHelp, + hidden: commandOptions.hidden, + isDefault: commandOptions.isDefault, + }); + if (commandOptions.description) { + command.description(commandOptions.description, commandOptions.argsDescription); } - - this.logger.raw(""); - - // TODO implement this after refactor cli arguments - // logger.raw('Documentation: https://webpack.js.org/option/name/'); - } else { - outputIncorrectUsageOfHelp(); - } - - this.logger.raw( - "To see list of all supported commands and options run 'webpack --help=verbose'.\n", - ); - this.logger.raw(`${bold("Webpack documentation:")} https://webpack.js.org/.`); - this.logger.raw(`${bold("CLI documentation:")} https://webpack.js.org/api/cli/.`); - this.logger.raw(`${bold("Made with ♥ by the webpack team")}.`); - process.exit(0); - }; - this.program.helpOption(false); - this.program.addHelpCommand(false); - this.program.option("-h, --help [verbose]", "Display help for commands and options."); - - let isInternalActionCalled = false; - - // Default action - this.program.usage("[options]"); - this.program.allowUnknownOption(true); - this.program.action(async (options, program) => { - if (!isInternalActionCalled) { - isInternalActionCalled = true; - } else { - this.logger.error("No commands found to run"); - process.exit(2); - } - - // Command and options - const { operands, unknown } = this.program.parseOptions(program.args); - const defaultCommandToRun = getCommandName(buildCommandOptions.name); - const hasOperand = typeof operands[0] !== "undefined"; - const operand = hasOperand ? operands[0] : defaultCommandToRun; - const isHelpOption = typeof options.help !== "undefined"; - const isHelpCommandSyntax = isCommand(operand, helpCommandOptions); - - if (isHelpOption || isHelpCommandSyntax) { - let isVerbose = false; - - if (isHelpOption) { - if (typeof options.help === "string") { - if (options.help !== "verbose") { - this.logger.error("Unknown value for '--help' option, please use '--help=verbose'"); - process.exit(2); - } - - isVerbose = true; - } + if (commandOptions.usage) { + command.usage(commandOptions.usage); } - - this.program.forHelp = true; - - const optionsForHelp = [] - .concat(isHelpOption && hasOperand ? [operand] : []) - // Syntax `webpack help [command]` - .concat(operands.slice(1)) - // Syntax `webpack help [option]` - .concat(unknown) - .concat( - isHelpCommandSyntax && typeof options.color !== "undefined" - ? [options.color ? "--color" : "--no-color"] - : [], - ) - .concat( - isHelpCommandSyntax && typeof options.version !== "undefined" ? ["--version"] : [], - ); - - await outputHelp(optionsForHelp, isVerbose, isHelpCommandSyntax, program); - } - - const isVersionOption = typeof options.version !== "undefined"; - const isVersionCommandSyntax = isCommand(operand, versionCommandOptions); - - if (isVersionOption || isVersionCommandSyntax) { - const optionsForVersion = [] - .concat(isVersionOption ? [operand] : []) - .concat(operands.slice(1)) - .concat(unknown); - - await outputVersion(optionsForVersion, program); - } - - let commandToRun = operand; - let commandOperands = operands.slice(1); - - if (isKnownCommand(commandToRun)) { - await loadCommandByName(commandToRun, true); - } else { - const isEntrySyntax = fs.existsSync(operand); - - if (isEntrySyntax) { - commandToRun = defaultCommandToRun; - commandOperands = operands; - - await loadCommandByName(commandToRun); - } else { - this.logger.error(`Unknown command or entry '${operand}'`); - - const levenshtein = require("fastest-levenshtein"); - const found = knownCommands.find( - (commandOptions) => - levenshtein.distance(operand, getCommandName(commandOptions.name)) < 3, - ); - - if (found) { - this.logger.error( - `Did you mean '${getCommandName(found.name)}' (alias '${ - Array.isArray(found.alias) ? found.alias.join(", ") : found.alias - }')?`, - ); - } - - this.logger.error("Run 'webpack --help' to see available commands and options"); - process.exit(2); + if (Array.isArray(commandOptions.alias)) { + command.aliases(commandOptions.alias); } - } - - await this.program.parseAsync([commandToRun, ...commandOperands, ...unknown], { - from: "user", - }); - }); - - await this.program.parseAsync(args, parseOptions); - } - - async loadConfig(options) { - const interpret = require("interpret"); - const loadConfigByPath = async (configPath, argv = {}) => { - const ext = path.extname(configPath); - const interpreted = Object.keys(interpret.jsVariants).find((variant) => variant === ext); - - if (interpreted) { - const rechoir = require("rechoir"); - - try { - rechoir.prepare(interpret.extensions, configPath); - } catch (error) { - if (error.failures) { - this.logger.error(`Unable load '${configPath}'`); - this.logger.error(error.message); - error.failures.forEach((failure) => { - this.logger.error(failure.error.message); - }); - this.logger.error("Please install one of them"); - process.exit(2); - } - - this.logger.error(error); - process.exit(2); + else { + command.alias(commandOptions.alias); } - } - - let options; - - try { - options = await this.tryRequireThenImport(configPath, false); - } catch (error) { - this.logger.error(`Failed to load '${configPath}' config`); - - if (this.isValidationError(error)) { - this.logger.error(error.message); - } else { - this.logger.error(error); + if (commandOptions.pkg) { + command.pkg = commandOptions.pkg; } - - process.exit(2); - } - - if (Array.isArray(options)) { - await Promise.all( - options.map(async (_, i) => { - if (typeof options[i].then === "function") { - options[i] = await options[i]; - } - - // `Promise` may return `Function` - if (typeof options[i] === "function") { - // when config is a function, pass the env from args to the config function - options[i] = await options[i](argv.env, argv); - } - }), - ); - } else { - if (typeof options.then === "function") { - options = await options; + else { + command.pkg = "webpack-cli"; } - - // `Promise` may return `Function` - if (typeof options === "function") { - // when config is a function, pass the env from args to the config function - options = await options(argv.env, argv); + const { forHelp } = this.program; + let allDependenciesInstalled = true; + if (commandOptions.dependencies && commandOptions.dependencies.length > 0) { + for (const dependency of commandOptions.dependencies) { + const isPkgExist = this.checkPackageExists(dependency); + if (isPkgExist) { + continue; + } + else if (!isPkgExist && forHelp) { + allDependenciesInstalled = false; + continue; + } + let skipInstallation = false; + // Allow to use `./path/to/webpack.js` outside `node_modules` + if (dependency === WEBPACK_PACKAGE && fs.existsSync(WEBPACK_PACKAGE)) { + skipInstallation = true; + } + // Allow to use `./path/to/webpack-dev-server.js` outside `node_modules` + if (dependency === WEBPACK_DEV_SERVER_PACKAGE && fs.existsSync(WEBPACK_PACKAGE)) { + skipInstallation = true; + } + if (skipInstallation) { + continue; + } + await this.doInstall(dependency, { + preMessage: () => { + this.logger.error(`For using '${this.colors.green(commandOptions.name.split(" ")[0])}' command you need to install: '${this.colors.green(dependency)}' package.`); + }, + }); + } } - } - - const isObject = (value) => typeof value === "object" && value !== null; - - if (!isObject(options) && !Array.isArray(options)) { - this.logger.error(`Invalid configuration in '${configPath}'`); - - process.exit(2); - } - - return { options, path: configPath }; - }; - - const config = { options: {}, path: new WeakMap() }; - - if (options.config && options.config.length > 0) { - const loadedConfigs = await Promise.all( - options.config.map((configPath) => - loadConfigByPath(path.resolve(configPath), options.argv), - ), - ); - - config.options = []; - - loadedConfigs.forEach((loadedConfig) => { - const isArray = Array.isArray(loadedConfig.options); - - // TODO we should run webpack multiple times when the `--config` options have multiple values with `--merge`, need to solve for the next major release - if (config.options.length === 0) { - config.options = loadedConfig.options; - } else { - if (!Array.isArray(config.options)) { - config.options = [config.options]; - } - - if (isArray) { - loadedConfig.options.forEach((item) => { - config.options.push(item); + if (options) { + if (typeof options === "function") { + if (forHelp && !allDependenciesInstalled && commandOptions.dependencies) { + command.description(`${commandOptions.description} To see all available options you need to install ${commandOptions.dependencies + .map((dependency) => `'${dependency}'`) + .join(", ")}.`); + options = []; + } + else { + options = await options(); + } + } + options.forEach((optionForCommand) => { + this.makeOption(command, optionForCommand); }); - } else { - config.options.push(loadedConfig.options); - } } - - if (isArray) { - loadedConfig.options.forEach((options) => { - config.path.set(options, loadedConfig.path); - }); - } else { - config.path.set(loadedConfig.options, loadedConfig.path); + command.action(action); + return command; + } + makeOption(command, option) { + let mainOption; + let negativeOption; + if (option.configs) { + let needNegativeOption = false; + let negatedDescription; + const mainOptionType = new Set(); + option.configs.forEach((config) => { + switch (config.type) { + case "reset": + mainOptionType.add(Boolean); + break; + case "boolean": + if (!needNegativeOption) { + needNegativeOption = true; + negatedDescription = config.negatedDescription; + } + mainOptionType.add(Boolean); + break; + case "number": + mainOptionType.add(Number); + break; + case "string": + case "path": + case "RegExp": + mainOptionType.add(String); + break; + case "enum": { + let hasFalseEnum = false; + const enumTypes = (config.values || []).map((value) => { + switch (typeof value) { + case "string": + mainOptionType.add(String); + break; + case "number": + mainOptionType.add(Number); + break; + case "boolean": + if (!hasFalseEnum && value === false) { + hasFalseEnum = true; + break; + } + mainOptionType.add(Boolean); + break; + } + }); + if (!needNegativeOption) { + needNegativeOption = hasFalseEnum; + negatedDescription = config.negatedDescription; + } + return enumTypes; + } + } + }); + mainOption = { + flags: option.alias ? `-${option.alias}, --${option.name}` : `--${option.name}`, + description: option.description || "", + type: mainOptionType, + multiple: option.multiple, + defaultValue: option.defaultValue, + }; + if (needNegativeOption) { + negativeOption = { + flags: `--no-${option.name}`, + description: negatedDescription || option.negatedDescription || `Negative '${option.name}' option.`, + }; + } + } + else { + mainOption = { + flags: option.alias ? `-${option.alias}, --${option.name}` : `--${option.name}`, + // TODO `describe` used by `webpack-dev-server@3` + description: option.description || option.describe || "", + type: option.type + ? new Set(Array.isArray(option.type) ? option.type : [option.type]) + : new Set([Boolean]), + multiple: option.multiple, + defaultValue: option.defaultValue, + }; + if (option.negative) { + negativeOption = { + flags: `--no-${option.name}`, + description: option.negatedDescription + ? option.negatedDescription + : `Negative '${option.name}' option.`, + }; + } } - }); - - config.options = config.options.length === 1 ? config.options[0] : config.options; - } else { - // Order defines the priority, in decreasing order - const defaultConfigFiles = [ - "webpack.config", - ".webpack/webpack.config", - ".webpack/webpackfile", - ] - .map((filename) => - // Since .cjs is not available on interpret side add it manually to default config extension list - [...Object.keys(interpret.extensions), ".cjs"].map((ext) => ({ - path: path.resolve(filename + ext), - ext: ext, - module: interpret.extensions[ext], - })), - ) - .reduce((accumulator, currentValue) => accumulator.concat(currentValue), []); - - let foundDefaultConfigFile; - - for (const defaultConfigFile of defaultConfigFiles) { - if (!fs.existsSync(defaultConfigFile.path)) { - continue; + if (mainOption.type.size > 1 && mainOption.type.has(Boolean)) { + mainOption.flags = `${mainOption.flags} [value${mainOption.multiple ? "..." : ""}]`; } - - foundDefaultConfigFile = defaultConfigFile; - break; - } - - if (foundDefaultConfigFile) { - const loadedConfig = await loadConfigByPath(foundDefaultConfigFile.path, options.argv); - - config.options = loadedConfig.options; - - if (Array.isArray(config.options)) { - config.options.forEach((item) => { - config.path.set(item, loadedConfig.path); - }); - } else { - config.path.set(loadedConfig.options, loadedConfig.path); + else if (mainOption.type.size > 0 && !mainOption.type.has(Boolean)) { + mainOption.flags = `${mainOption.flags} `; } - } - } - - if (options.configName) { - const notFoundConfigNames = []; - - config.options = options.configName.map((configName) => { - let found; - - if (Array.isArray(config.options)) { - found = config.options.find((options) => options.name === configName); - } else { - found = config.options.name === configName ? config.options : undefined; + if (mainOption.type.size === 1) { + if (mainOption.type.has(Number)) { + let skipDefault = true; + const optionForCommand = new Option(mainOption.flags, mainOption.description) + .argParser((value, prev = []) => { + if (mainOption.defaultValue && mainOption.multiple && skipDefault) { + prev = []; + skipDefault = false; + } + return mainOption.multiple + ? [].concat(prev).concat(Number(value)) + : Number(value); + }) + .default(mainOption.defaultValue); + optionForCommand.helpLevel = option.helpLevel; + command.addOption(optionForCommand); + } + else if (mainOption.type.has(String)) { + let skipDefault = true; + const optionForCommand = new Option(mainOption.flags, mainOption.description) + .argParser((value, prev = []) => { + if (mainOption.defaultValue && mainOption.multiple && skipDefault) { + prev = []; + skipDefault = false; + } + return mainOption.multiple ? [].concat(prev).concat(value) : value; + }) + .default(mainOption.defaultValue); + optionForCommand.helpLevel = option.helpLevel; + command.addOption(optionForCommand); + } + else if (mainOption.type.has(Boolean)) { + const optionForCommand = new Option(mainOption.flags, mainOption.description).default(mainOption.defaultValue); + optionForCommand.helpLevel = option.helpLevel; + command.addOption(optionForCommand); + } + else { + const optionForCommand = new Option(mainOption.flags, mainOption.description) + .argParser(Array.from(mainOption.type)[0]) + .default(mainOption.defaultValue); + optionForCommand.helpLevel = option.helpLevel; + command.addOption(optionForCommand); + } } - - if (!found) { - notFoundConfigNames.push(configName); + else if (mainOption.type.size > 1) { + let skipDefault = true; + const optionForCommand = new Option(mainOption.flags, mainOption.description, mainOption.defaultValue) + .argParser((value, prev = []) => { + if (mainOption.defaultValue && mainOption.multiple && skipDefault) { + prev = []; + skipDefault = false; + } + if (mainOption.type.has(Number)) { + const numberValue = Number(value); + if (!isNaN(numberValue)) { + return mainOption.multiple + ? [].concat(prev).concat(numberValue) + : numberValue; + } + } + if (mainOption.type.has(String)) { + return mainOption.multiple ? [].concat(prev).concat(value) : value; + } + return value; + }) + .default(mainOption.defaultValue); + optionForCommand.helpLevel = option.helpLevel; + command.addOption(optionForCommand); + } + else if (mainOption.type.size === 0 && negativeOption) { + const optionForCommand = new Option(mainOption.flags, mainOption.description); + // Hide stub option + optionForCommand.hideHelp(); + optionForCommand.helpLevel = option.helpLevel; + command.addOption(optionForCommand); + } + if (negativeOption) { + const optionForCommand = new Option(negativeOption.flags, negativeOption.description); + optionForCommand.helpLevel = option.helpLevel; + command.addOption(optionForCommand); } - - return found; - }); - - if (notFoundConfigNames.length > 0) { - this.logger.error( - notFoundConfigNames - .map((configName) => `Configuration with the name "${configName}" was not found.`) - .join(" "), - ); - process.exit(2); - } - } - - if (options.merge) { - const merge = await this.tryRequireThenImport("webpack-merge"); - - // we can only merge when there are multiple configurations - // either by passing multiple configs by flags or passing a - // single config exporting an array - if (!Array.isArray(config.options) || config.options.length <= 1) { - this.logger.error("At least two configurations are required for merge."); - process.exit(2); - } - - const mergedConfigPaths = []; - - config.options = config.options.reduce((accumulator, options) => { - const configPath = config.path.get(options); - const mergedOptions = merge(accumulator, options); - - mergedConfigPaths.push(configPath); - - return mergedOptions; - }, {}); - config.path.set(config.options, mergedConfigPaths); } - - return config; - } - - async buildConfig(config, options) { - const runFunctionOnEachConfig = (options, fn) => { - if (Array.isArray(options)) { - for (let item of options) { - item = fn(item); + getBuiltInOptions() { + if (this.builtInOptionsCache) { + return this.builtInOptionsCache; } - } else { - options = fn(options); - } - - return options; - }; - - if (options.analyze) { - if (!this.checkPackageExists("webpack-bundle-analyzer")) { - await this.doInstall("webpack-bundle-analyzer", { - preMessage: () => { - this.logger.error( - `It looks like ${this.colors.yellow("webpack-bundle-analyzer")} is not installed.`, - ); - }, + const minimumHelpFlags = [ + "config", + "config-name", + "merge", + "env", + "mode", + "watch", + "watch-options-stdin", + "stats", + "devtool", + "entry", + "target", + "progress", + "json", + "name", + "output-path", + "node-env", + ]; + const builtInFlags = [ + // For configs + { + name: "config", + alias: "c", + configs: [ + { + type: "string", + }, + ], + multiple: true, + description: "Provide path to a webpack configuration file e.g. ./webpack.config.js.", + }, + { + name: "config-name", + configs: [ + { + type: "string", + }, + ], + multiple: true, + description: "Name of the configuration to use.", + }, + { + name: "merge", + alias: "m", + configs: [ + { + type: "enum", + values: [true], + }, + ], + description: "Merge two or more configurations using 'webpack-merge'.", + }, + // Complex configs + { + name: "env", + type: (value, previous = {}) => { + // for https://github.com/webpack/webpack-cli/issues/2642 + if (value.endsWith("=")) { + value.concat('""'); + } + // This ensures we're only splitting by the first `=` + const [allKeys, val] = value.split(/=(.+)/, 2); + const splitKeys = allKeys.split(/\.(?!$)/); + let prevRef = previous; + splitKeys.forEach((someKey, index) => { + if (!prevRef[someKey]) { + prevRef[someKey] = {}; + } + if (typeof prevRef[someKey] === "string") { + prevRef[someKey] = {}; + } + if (index === splitKeys.length - 1) { + if (typeof val === "string") { + prevRef[someKey] = val; + } + else { + prevRef[someKey] = true; + } + } + prevRef = prevRef[someKey]; + }); + return previous; + }, + multiple: true, + description: "Environment passed to the configuration when it is a function.", + }, + { + name: "node-env", + configs: [ + { + type: "string", + }, + ], + multiple: false, + description: "Sets process.env.NODE_ENV to the specified value.", + }, + // Adding more plugins + { + name: "hot", + alias: "h", + configs: [ + { + type: "string", + }, + { + type: "boolean", + }, + ], + negative: true, + description: "Enables Hot Module Replacement", + negatedDescription: "Disables Hot Module Replacement.", + }, + { + name: "analyze", + configs: [ + { + type: "enum", + values: [true], + }, + ], + multiple: false, + description: "It invokes webpack-bundle-analyzer plugin to get bundle information.", + }, + { + name: "progress", + configs: [ + { + type: "string", + }, + { + type: "enum", + values: [true], + }, + ], + description: "Print compilation progress during build.", + }, + { + name: "prefetch", + configs: [ + { + type: "string", + }, + ], + description: "Prefetch this request.", + }, + // Output options + { + name: "json", + configs: [ + { + type: "string", + }, + { + type: "enum", + values: [true], + }, + ], + alias: "j", + description: "Prints result as JSON or store it in a file.", + }, + // For webpack@4 + { + name: "entry", + configs: [ + { + type: "string", + }, + ], + multiple: true, + description: "The entry point(s) of your application e.g. ./src/main.js.", + }, + { + name: "output-path", + alias: "o", + configs: [ + { + type: "string", + }, + ], + description: "Output location of the file generated by webpack e.g. ./dist/.", + }, + { + name: "target", + alias: "t", + configs: [ + { + type: "string", + }, + ], + multiple: this.webpack.cli !== undefined, + description: "Sets the build target e.g. node.", + }, + { + name: "devtool", + configs: [ + { + type: "string", + }, + { + type: "enum", + values: [false], + }, + ], + negative: true, + alias: "d", + description: "Determine source maps to use.", + negatedDescription: "Do not generate source maps.", + }, + { + name: "mode", + configs: [ + { + type: "string", + }, + ], + description: "Defines the mode to pass to webpack.", + }, + { + name: "name", + configs: [ + { + type: "string", + }, + ], + description: "Name of the configuration. Used when loading multiple configurations.", + }, + { + name: "stats", + configs: [ + { + type: "string", + }, + { + type: "boolean", + }, + ], + negative: true, + description: "It instructs webpack on how to treat the stats e.g. verbose.", + negatedDescription: "Disable stats output.", + }, + { + name: "watch", + configs: [ + { + type: "boolean", + }, + ], + negative: true, + alias: "w", + description: "Watch for files changes.", + negatedDescription: "Do not watch for file changes.", + }, + { + name: "watch-options-stdin", + configs: [ + { + type: "boolean", + }, + ], + negative: true, + description: "Stop watching when stdin stream has ended.", + negatedDescription: "Do not stop watching when stdin stream has ended.", + }, + ]; + // Extract all the flags being exported from core. + // A list of cli flags generated by core can be found here https://github.com/webpack/webpack/blob/master/test/__snapshots__/Cli.test.js.snap + const coreFlags = this.webpack.cli + ? Object.entries(this.webpack.cli.getArguments()).map(([flag, meta]) => { + const inBuiltIn = builtInFlags.find((builtInFlag) => builtInFlag.name === flag); + if (inBuiltIn) { + return Object.assign(Object.assign(Object.assign(Object.assign({}, meta), { + // @ts-expect-error this might be overwritten + name: flag, group: "core" }), inBuiltIn), { configs: meta.configs || [] }); + } + return Object.assign(Object.assign({}, meta), { name: flag, group: "core" }); + }) + : []; + const options = [] + .concat(builtInFlags.filter((builtInFlag) => !coreFlags.find((coreFlag) => builtInFlag.name === coreFlag.name))) + .concat(coreFlags) + .map((option) => { + option.helpLevel = minimumHelpFlags.includes(option.name) + ? "minimum" + : "verbose"; + return option; }); - - this.logger.success( - `${this.colors.yellow("webpack-bundle-analyzer")} was installed successfully.`, - ); - } + this.builtInOptionsCache = options; + return options; } - - if (typeof options.progress === "string" && options.progress !== "profile") { - this.logger.error( - `'${options.progress}' is an invalid value for the --progress option. Only 'profile' is allowed.`, - ); - process.exit(2); + async loadWebpack(handleError = true) { + return this.tryRequireThenImport(WEBPACK_PACKAGE, handleError); } - - if (typeof options.hot === "string" && options.hot !== "only") { - this.logger.error( - `'${options.hot}' is an invalid value for the --hot option. Use 'only' instead.`, - ); - process.exit(2); + async run(args, parseOptions) { + // Built-in internal commands + const buildCommandOptions = { + name: "build [entries...]", + alias: ["bundle", "b"], + description: "Run webpack (default command, can be omitted).", + usage: "[entries...] [options]", + dependencies: [WEBPACK_PACKAGE], + }; + const watchCommandOptions = { + name: "watch [entries...]", + alias: "w", + description: "Run webpack and watch for files changes.", + usage: "[entries...] [options]", + dependencies: [WEBPACK_PACKAGE], + }; + const versionCommandOptions = { + name: "version [commands...]", + alias: "v", + description: "Output the version number of 'webpack', 'webpack-cli' and 'webpack-dev-server' and commands.", + }; + const helpCommandOptions = { + name: "help [command] [option]", + alias: "h", + description: "Display help for commands and options.", + }; + // Built-in external commands + const externalBuiltInCommandsInfo = [ + { + name: "serve [entries...]", + alias: ["server", "s"], + pkg: "@webpack-cli/serve", + }, + { + name: "info", + alias: "i", + pkg: "@webpack-cli/info", + }, + { + name: "init", + alias: ["create", "new", "c", "n"], + pkg: "@webpack-cli/generators", + }, + { + name: "loader", + alias: "l", + pkg: "@webpack-cli/generators", + }, + { + name: "plugin", + alias: "p", + pkg: "@webpack-cli/generators", + }, + { + name: "migrate", + alias: "m", + pkg: "@webpack-cli/migrate", + }, + { + name: "configtest [config-path]", + alias: "t", + pkg: "@webpack-cli/configtest", + }, + ]; + const knownCommands = [ + buildCommandOptions, + watchCommandOptions, + versionCommandOptions, + helpCommandOptions, + ...externalBuiltInCommandsInfo, + ]; + const getCommandName = (name) => name.split(" ")[0]; + const isKnownCommand = (name) => knownCommands.find((command) => getCommandName(command.name) === name || + (Array.isArray(command.alias) ? command.alias.includes(name) : command.alias === name)); + const isCommand = (input, commandOptions) => { + const longName = getCommandName(commandOptions.name); + if (input === longName) { + return true; + } + if (commandOptions.alias) { + if (Array.isArray(commandOptions.alias)) { + return commandOptions.alias.includes(input); + } + else { + return commandOptions.alias === input; + } + } + return false; + }; + const findCommandByName = (name) => this.program.commands.find((command) => name === command.name() || command.aliases().includes(name)); + const isOption = (value) => value.startsWith("-"); + const isGlobalOption = (value) => value === "--color" || + value === "--no-color" || + value === "-v" || + value === "--version" || + value === "-h" || + value === "--help"; + const loadCommandByName = async (commandName, allowToInstall = false) => { + const isBuildCommandUsed = isCommand(commandName, buildCommandOptions); + const isWatchCommandUsed = isCommand(commandName, watchCommandOptions); + if (isBuildCommandUsed || isWatchCommandUsed) { + await this.makeCommand(isBuildCommandUsed ? buildCommandOptions : watchCommandOptions, async () => { + this.webpack = await this.loadWebpack(); + return isWatchCommandUsed + ? this.getBuiltInOptions().filter((option) => option.name !== "watch") + : this.getBuiltInOptions(); + }, async (entries, options) => { + if (entries.length > 0) { + options.entry = [...entries, ...(options.entry || [])]; + } + await this.runWebpack(options, isWatchCommandUsed); + }); + } + else if (isCommand(commandName, helpCommandOptions)) { + // Stub for the `help` command + // eslint-disable-next-line @typescript-eslint/no-empty-function + this.makeCommand(helpCommandOptions, [], () => { }); + } + else if (isCommand(commandName, versionCommandOptions)) { + // Stub for the `version` command + // eslint-disable-next-line @typescript-eslint/no-empty-function + this.makeCommand(versionCommandOptions, [], () => { }); + } + else { + const builtInExternalCommandInfo = externalBuiltInCommandsInfo.find((externalBuiltInCommandInfo) => getCommandName(externalBuiltInCommandInfo.name) === commandName || + (Array.isArray(externalBuiltInCommandInfo.alias) + ? externalBuiltInCommandInfo.alias.includes(commandName) + : externalBuiltInCommandInfo.alias === commandName)); + let pkg; + if (builtInExternalCommandInfo) { + ({ pkg } = builtInExternalCommandInfo); + } + else { + pkg = commandName; + } + if (pkg !== "webpack-cli" && !this.checkPackageExists(pkg)) { + if (!allowToInstall) { + return; + } + pkg = await this.doInstall(pkg, { + preMessage: () => { + this.logger.error(`For using this command you need to install: '${this.colors.green(pkg)}' package.`); + }, + }); + } + let loadedCommand; + try { + loadedCommand = await this.tryRequireThenImport(pkg, false); + } + catch (error) { + // Ignore, command is not installed + return; + } + let command; + try { + command = new loadedCommand(); + await command.apply(this); + } + catch (error) { + this.logger.error(`Unable to load '${pkg}' command`); + this.logger.error(error); + process.exit(2); + } + } + }; + // Register own exit + this.program.exitOverride(async (error) => { + if (error.exitCode === 0) { + process.exit(0); + } + if (error.code === "executeSubCommandAsync") { + process.exit(2); + } + if (error.code === "commander.help") { + process.exit(0); + } + if (error.code === "commander.unknownOption") { + let name = error.message.match(/'(.+)'/); + if (name) { + name = name[1].substr(2); + if (name.includes("=")) { + name = name.split("=")[0]; + } + const { operands } = this.program.parseOptions(this.program.args); + const operand = typeof operands[0] !== "undefined" + ? operands[0] + : getCommandName(buildCommandOptions.name); + if (operand) { + const command = findCommandByName(operand); + if (!command) { + this.logger.error(`Can't find and load command '${operand}'`); + this.logger.error("Run 'webpack --help' to see available commands and options"); + process.exit(2); + } + const levenshtein = require("fastest-levenshtein"); + command.options.forEach((option) => { + var _a; + if (!option.hidden && levenshtein.distance(name, (_a = option.long) === null || _a === void 0 ? void 0 : _a.slice(2)) < 3) { + this.logger.error(`Did you mean '--${option.name()}'?`); + } + }); + } + } + } + // Codes: + // - commander.unknownCommand + // - commander.missingArgument + // - commander.missingMandatoryOptionValue + // - commander.optionMissingArgument + this.logger.error("Run 'webpack --help' to see available commands and options"); + process.exit(2); + }); + // Default `--color` and `--no-color` options + // eslint-disable-next-line @typescript-eslint/no-this-alias + const cli = this; + this.program.option("--color", "Enable colors on console."); + this.program.on("option:color", function () { + // @ts-expect-error shadowing 'this' is intended + const { color } = this.opts(); + cli.isColorSupportChanged = color; + cli.colors = cli.createColors(color); + }); + this.program.option("--no-color", "Disable colors on console."); + this.program.on("option:no-color", function () { + // @ts-expect-error shadowing 'this' is intended + const { color } = this.opts(); + cli.isColorSupportChanged = color; + cli.colors = cli.createColors(color); + }); + // Make `-v, --version` options + // Make `version|v [commands...]` command + const outputVersion = async (options) => { + // Filter `bundle`, `watch`, `version` and `help` commands + const possibleCommandNames = options.filter((option) => !isCommand(option, buildCommandOptions) && + !isCommand(option, watchCommandOptions) && + !isCommand(option, versionCommandOptions) && + !isCommand(option, helpCommandOptions)); + possibleCommandNames.forEach((possibleCommandName) => { + if (!isOption(possibleCommandName)) { + return; + } + this.logger.error(`Unknown option '${possibleCommandName}'`); + this.logger.error("Run 'webpack --help' to see available commands and options"); + process.exit(2); + }); + if (possibleCommandNames.length > 0) { + await Promise.all(possibleCommandNames.map((possibleCommand) => loadCommandByName(possibleCommand))); + for (const possibleCommandName of possibleCommandNames) { + const foundCommand = findCommandByName(possibleCommandName); + if (!foundCommand) { + this.logger.error(`Unknown command '${possibleCommandName}'`); + this.logger.error("Run 'webpack --help' to see available commands and options"); + process.exit(2); + } + try { + const { name, version } = this.loadJSONFile(`${foundCommand.pkg}/package.json`); + this.logger.raw(`${name} ${version}`); + } + catch (e) { + this.logger.error(`Error: External package '${foundCommand.pkg}' not found`); + process.exit(2); + } + } + } + let webpack; + try { + webpack = await this.loadWebpack(false); + } + catch (_error) { + // Nothing + } + this.logger.raw(`webpack: ${webpack ? webpack.version : "not installed"}`); + const pkgJSON = this.loadJSONFile("../package.json"); + this.logger.raw(`webpack-cli: ${pkgJSON.version}`); + let devServer; + try { + devServer = await this.loadJSONFile("webpack-dev-server/package.json", false); + } + catch (_error) { + // Nothing + } + this.logger.raw(`webpack-dev-server ${devServer ? devServer.version : "not installed"}`); + process.exit(0); + }; + this.program.option("-v, --version", "Output the version number of 'webpack', 'webpack-cli' and 'webpack-dev-server' and commands."); + const outputHelp = async (options, isVerbose, isHelpCommandSyntax, program) => { + const { bold } = this.colors; + const outputIncorrectUsageOfHelp = () => { + this.logger.error("Incorrect use of help"); + this.logger.error("Please use: 'webpack help [command] [option]' | 'webpack [command] --help'"); + this.logger.error("Run 'webpack --help' to see available commands and options"); + process.exit(2); + }; + const isGlobalHelp = options.length === 0; + const isCommandHelp = options.length === 1 && !isOption(options[0]); + if (isGlobalHelp || isCommandHelp) { + program.configureHelp({ + sortSubcommands: true, + // Support multiple aliases + commandUsage: (command) => { + let parentCmdNames = ""; + for (let parentCmd = command.parent; parentCmd; parentCmd = parentCmd.parent) { + parentCmdNames = `${parentCmd.name()} ${parentCmdNames}`; + } + if (isGlobalHelp) { + return `${parentCmdNames}${command.usage()}\n${bold("Alternative usage to run commands:")} ${parentCmdNames}[command] [options]`; + } + return `${parentCmdNames}${command.name()}|${command + .aliases() + .join("|")} ${command.usage()}`; + }, + // Support multiple aliases + subcommandTerm: (command) => { + const humanReadableArgumentName = (argument) => { + const nameOutput = argument.name + (argument.variadic === true ? "..." : ""); + return argument.required ? "<" + nameOutput + ">" : "[" + nameOutput + "]"; + }; + const args = command._args + .map((arg) => humanReadableArgumentName(arg)) + .join(" "); + return `${command.name()}|${command.aliases().join("|")}${args ? ` ${args}` : ""}${command.options.length > 0 ? " [options]" : ""}`; + }, + visibleOptions: function visibleOptions(command) { + return command.options.filter((option) => { + if (option.hidden) { + return false; + } + switch (option.helpLevel) { + case "verbose": + return isVerbose; + case "minimum": + default: + return true; + } + }); + }, + padWidth(command, helper) { + return Math.max(helper.longestArgumentTermLength(command, helper), helper.longestOptionTermLength(command, helper), + // For global options + helper.longestOptionTermLength(program, helper), helper.longestSubcommandTermLength(isGlobalHelp ? program : command, helper)); + }, + formatHelp: (command, helper) => { + const termWidth = helper.padWidth(command, helper); + const helpWidth = helper.helpWidth || process.env.WEBPACK_CLI_HELP_WIDTH || 80; + const itemIndentWidth = 2; + const itemSeparatorWidth = 2; // between term and description + const formatItem = (term, description) => { + if (description) { + const fullText = `${term.padEnd(termWidth + itemSeparatorWidth)}${description}`; + return helper.wrap(fullText, helpWidth - itemIndentWidth, termWidth + itemSeparatorWidth); + } + return term; + }; + const formatList = (textArray) => textArray.join("\n").replace(/^/gm, " ".repeat(itemIndentWidth)); + // Usage + let output = [`${bold("Usage:")} ${helper.commandUsage(command)}`, ""]; + // Description + const commandDescription = isGlobalHelp + ? "The build tool for modern web applications." + : helper.commandDescription(command); + if (commandDescription.length > 0) { + output = output.concat([commandDescription, ""]); + } + // Arguments + const argumentList = helper + .visibleArguments(command) + .map((argument) => formatItem(argument.term, argument.description)); + if (argumentList.length > 0) { + output = output.concat([bold("Arguments:"), formatList(argumentList), ""]); + } + // Options + const optionList = helper + .visibleOptions(command) + .map((option) => formatItem(helper.optionTerm(option), helper.optionDescription(option))); + if (optionList.length > 0) { + output = output.concat([bold("Options:"), formatList(optionList), ""]); + } + // Global options + const globalOptionList = program.options.map((option) => formatItem(helper.optionTerm(option), helper.optionDescription(option))); + if (globalOptionList.length > 0) { + output = output.concat([bold("Global options:"), formatList(globalOptionList), ""]); + } + // Commands + const commandList = helper + .visibleCommands(isGlobalHelp ? program : command) + .map((command) => formatItem(helper.subcommandTerm(command), helper.subcommandDescription(command))); + if (commandList.length > 0) { + output = output.concat([bold("Commands:"), formatList(commandList), ""]); + } + return output.join("\n"); + }, + }); + if (isGlobalHelp) { + await Promise.all(knownCommands.map((knownCommand) => { + return loadCommandByName(getCommandName(knownCommand.name)); + })); + const buildCommand = findCommandByName(getCommandName(buildCommandOptions.name)); + buildCommand && this.logger.raw(buildCommand.helpInformation()); + } + else { + const name = options[0]; + await loadCommandByName(name); + const command = findCommandByName(name); + if (!command) { + const builtInCommandUsed = externalBuiltInCommandsInfo.find((command) => command.name.includes(name) || name === command.alias); + if (typeof builtInCommandUsed !== "undefined") { + this.logger.error(`For using '${name}' command you need to install '${builtInCommandUsed.pkg}' package.`); + } + else { + this.logger.error(`Can't find and load command '${name}'`); + this.logger.error("Run 'webpack --help' to see available commands and options."); + } + process.exit(2); + } + this.logger.raw(command.helpInformation()); + } + } + else if (isHelpCommandSyntax) { + let isCommandSpecified = false; + let commandName = getCommandName(buildCommandOptions.name); + let optionName = ""; + if (options.length === 1) { + optionName = options[0]; + } + else if (options.length === 2) { + isCommandSpecified = true; + commandName = options[0]; + optionName = options[1]; + if (isOption(commandName)) { + outputIncorrectUsageOfHelp(); + } + } + else { + outputIncorrectUsageOfHelp(); + } + await loadCommandByName(commandName); + const command = isGlobalOption(optionName) ? program : findCommandByName(commandName); + if (!command) { + this.logger.error(`Can't find and load command '${commandName}'`); + this.logger.error("Run 'webpack --help' to see available commands and options"); + process.exit(2); + } + const option = command.options.find((option) => option.short === optionName || option.long === optionName); + if (!option) { + this.logger.error(`Unknown option '${optionName}'`); + this.logger.error("Run 'webpack --help' to see available commands and options"); + process.exit(2); + } + const nameOutput = option.flags.replace(/^.+[[<]/, "").replace(/(\.\.\.)?[\]>].*$/, "") + + (option.variadic === true ? "..." : ""); + const value = option.required + ? "<" + nameOutput + ">" + : option.optional + ? "[" + nameOutput + "]" + : ""; + this.logger.raw(`${bold("Usage")}: webpack${isCommandSpecified ? ` ${commandName}` : ""} ${option.long}${value ? ` ${value}` : ""}`); + if (option.short) { + this.logger.raw(`${bold("Short:")} webpack${isCommandSpecified ? ` ${commandName}` : ""} ${option.short}${value ? ` ${value}` : ""}`); + } + if (option.description) { + this.logger.raw(`${bold("Description:")} ${option.description}`); + } + if (!option.negate && option.defaultValue) { + this.logger.raw(`${bold("Default value:")} ${JSON.stringify(option.defaultValue)}`); + } + const flag = this.getBuiltInOptions().find((flag) => option.long === `--${flag.name}`); + if (flag && flag.configs) { + const possibleValues = flag.configs.reduce((accumulator, currentValue) => { + if (currentValue.values) { + return accumulator.concat(currentValue.values); + } + else { + return accumulator; + } + }, []); + if (possibleValues.length > 0) { + this.logger.raw(`${bold("Possible values:")} ${JSON.stringify(possibleValues.join(" | "))}`); + } + } + this.logger.raw(""); + // TODO implement this after refactor cli arguments + // logger.raw('Documentation: https://webpack.js.org/option/name/'); + } + else { + outputIncorrectUsageOfHelp(); + } + this.logger.raw("To see list of all supported commands and options run 'webpack --help=verbose'.\n"); + this.logger.raw(`${bold("Webpack documentation:")} https://webpack.js.org/.`); + this.logger.raw(`${bold("CLI documentation:")} https://webpack.js.org/api/cli/.`); + this.logger.raw(`${bold("Made with ♥ by the webpack team")}.`); + process.exit(0); + }; + this.program.helpOption(false); + this.program.addHelpCommand(false); + this.program.option("-h, --help [verbose]", "Display help for commands and options."); + let isInternalActionCalled = false; + // Default action + this.program.usage("[options]"); + this.program.allowUnknownOption(true); + this.program.action(async (options, program) => { + if (!isInternalActionCalled) { + isInternalActionCalled = true; + } + else { + this.logger.error("No commands found to run"); + process.exit(2); + } + // Command and options + const { operands, unknown } = this.program.parseOptions(program.args); + const defaultCommandToRun = getCommandName(buildCommandOptions.name); + const hasOperand = typeof operands[0] !== "undefined"; + const operand = hasOperand ? operands[0] : defaultCommandToRun; + const isHelpOption = typeof options.help !== "undefined"; + const isHelpCommandSyntax = isCommand(operand, helpCommandOptions); + if (isHelpOption || isHelpCommandSyntax) { + let isVerbose = false; + if (isHelpOption) { + if (typeof options.help === "string") { + if (options.help !== "verbose") { + this.logger.error("Unknown value for '--help' option, please use '--help=verbose'"); + process.exit(2); + } + isVerbose = true; + } + } + this.program.forHelp = true; + const optionsForHelp = [] + .concat(isHelpOption && hasOperand ? [operand] : []) + // Syntax `webpack help [command]` + .concat(operands.slice(1)) + // Syntax `webpack help [option]` + .concat(unknown) + .concat(isHelpCommandSyntax && typeof options.color !== "undefined" + ? [options.color ? "--color" : "--no-color"] + : []) + .concat(isHelpCommandSyntax && typeof options.version !== "undefined" ? ["--version"] : []); + await outputHelp(optionsForHelp, isVerbose, isHelpCommandSyntax, program); + } + const isVersionOption = typeof options.version !== "undefined"; + const isVersionCommandSyntax = isCommand(operand, versionCommandOptions); + if (isVersionOption || isVersionCommandSyntax) { + const optionsForVersion = [] + .concat(isVersionOption ? [operand] : []) + .concat(operands.slice(1)) + .concat(unknown); + await outputVersion(optionsForVersion); + } + let commandToRun = operand; + let commandOperands = operands.slice(1); + if (isKnownCommand(commandToRun)) { + await loadCommandByName(commandToRun, true); + } + else { + const isEntrySyntax = fs.existsSync(operand); + if (isEntrySyntax) { + commandToRun = defaultCommandToRun; + commandOperands = operands; + await loadCommandByName(commandToRun); + } + else { + this.logger.error(`Unknown command or entry '${operand}'`); + const levenshtein = require("fastest-levenshtein"); + const found = knownCommands.find((commandOptions) => levenshtein.distance(operand, getCommandName(commandOptions.name)) < 3); + if (found) { + this.logger.error(`Did you mean '${getCommandName(found.name)}' (alias '${Array.isArray(found.alias) ? found.alias.join(", ") : found.alias}')?`); + } + this.logger.error("Run 'webpack --help' to see available commands and options"); + process.exit(2); + } + } + await this.program.parseAsync([commandToRun, ...commandOperands, ...unknown], { + from: "user", + }); + }); + await this.program.parseAsync(args, parseOptions); } - - const CLIPlugin = await this.tryRequireThenImport("./plugins/CLIPlugin"); - - const internalBuildConfig = (item) => { - // Output warnings - if ( - item.watch && - options.argv && - options.argv.env && - (options.argv.env["WEBPACK_WATCH"] || options.argv.env["WEBPACK_SERVE"]) - ) { - this.logger.warn( - `No need to use the '${ - options.argv.env["WEBPACK_WATCH"] ? "watch" : "serve" - }' command together with '{ watch: true }' configuration, it does not make sense.`, - ); - - if (options.argv.env["WEBPACK_SERVE"]) { - item.watch = false; - } - } - - // Apply options - if (this.webpack.cli) { - const args = this.getBuiltInOptions() - .filter((flag) => flag.group === "core") - .reduce((accumulator, flag) => { - accumulator[flag.name] = flag; - - return accumulator; - }, {}); - - const values = Object.keys(options).reduce((accumulator, name) => { - if (name === "argv") { - return accumulator; - } - - const kebabName = this.toKebabCase(name); - - if (args[kebabName]) { - accumulator[kebabName] = options[name]; - } - - return accumulator; - }, {}); - - const problems = this.webpack.cli.processArguments(args, item, values); - - if (problems) { - const groupBy = (xs, key) => { - return xs.reduce((rv, x) => { - (rv[x[key]] = rv[x[key]] || []).push(x); - - return rv; - }, {}); - }; - const problemsByPath = groupBy(problems, "path"); - - for (const path in problemsByPath) { - const problems = problemsByPath[path]; - - problems.forEach((problem) => { - this.logger.error( - `${this.capitalizeFirstLetter(problem.type.replace(/-/g, " "))}${ - problem.value ? ` '${problem.value}'` : "" - } for the '--${problem.argument}' option${ - problem.index ? ` by index '${problem.index}'` : "" - }`, - ); - - if (problem.expected) { - this.logger.error(`Expected: '${problem.expected}'`); - } + async loadConfig(options) { + const interpret = require("interpret"); + const loadConfigByPath = async (configPath, argv = {}) => { + var _a; + const ext = path.extname(configPath); + const interpreted = Object.keys(interpret.jsVariants).find((variant) => variant === ext); + if (interpreted) { + const rechoir = require("rechoir"); + try { + rechoir.prepare(interpret.extensions, configPath); + } + catch (error) { + if ((_a = error) === null || _a === void 0 ? void 0 : _a.failures) { + this.logger.error(`Unable load '${configPath}'`); + this.logger.error(error.message); + error.failures.forEach((failure) => { + this.logger.error(failure.error.message); + }); + this.logger.error("Please install one of them"); + process.exit(2); + } + this.logger.error(error); + process.exit(2); + } + } + let options; + try { + options = await this.tryRequireThenImport(configPath, false); + // @ts-expect-error error type assertion + } + catch (error) { + this.logger.error(`Failed to load '${configPath}' config`); + if (this.isValidationError(error)) { + this.logger.error(error.message); + } + else { + this.logger.error(error); + } + process.exit(2); + } + if (Array.isArray(options)) { + // reassign the value to assert type + const optionsArray = options; + await Promise.all(optionsArray.map(async (_, i) => { + if (this.isPromise(optionsArray[i])) { + optionsArray[i] = await optionsArray[i]; + } + // `Promise` may return `Function` + if (this.isFunction(optionsArray[i])) { + // when config is a function, pass the env from args to the config function + optionsArray[i] = await optionsArray[i](argv.env, argv); + } + })); + options = optionsArray; + } + else { + if (this.isPromise(options)) { + options = await options; + } + // `Promise` may return `Function` + if (this.isFunction(options)) { + // when config is a function, pass the env from args to the config function + options = await options(argv.env, argv); + } + } + const isObject = (value) => typeof value === "object" && value !== null; + if (!isObject(options) && !Array.isArray(options)) { + this.logger.error(`Invalid configuration in '${configPath}'`); + process.exit(2); + } + return { options, path: configPath }; + }; + const config = { + options: {}, + path: new WeakMap(), + }; + if (options.config && options.config.length > 0) { + const loadedConfigs = await Promise.all(options.config.map((configPath) => loadConfigByPath(path.resolve(configPath), options.argv))); + config.options = []; + loadedConfigs.forEach((loadedConfig) => { + const isArray = Array.isArray(loadedConfig.options); + // TODO we should run webpack multiple times when the `--config` options have multiple values with `--merge`, need to solve for the next major release + if (config.options.length === 0) { + config.options = loadedConfig.options; + } + else { + if (!Array.isArray(config.options)) { + config.options = [config.options]; + } + if (isArray) { + loadedConfig.options.forEach((item) => { + config.options.push(item); + }); + } + else { + config.options.push(loadedConfig.options); + } + } + if (isArray) { + loadedConfig.options.forEach((options) => { + config.path.set(options, loadedConfig.path); + }); + } + else { + config.path.set(loadedConfig.options, loadedConfig.path); + } }); - } - - process.exit(2); + config.options = config.options.length === 1 ? config.options[0] : config.options; } - - // Setup default cache options - if (item.cache && item.cache.type === "filesystem") { - const configPath = config.path.get(item); - - if (configPath) { - if (!item.cache.buildDependencies) { - item.cache.buildDependencies = {}; - } - - if (!item.cache.buildDependencies.defaultConfig) { - item.cache.buildDependencies.defaultConfig = []; - } - - if (Array.isArray(configPath)) { - configPath.forEach((oneOfConfigPath) => { - item.cache.buildDependencies.defaultConfig.push(oneOfConfigPath); - }); - } else { - item.cache.buildDependencies.defaultConfig.push(configPath); - } - } + else { + // Order defines the priority, in decreasing order + const defaultConfigFiles = [ + "webpack.config", + ".webpack/webpack.config", + ".webpack/webpackfile", + ] + .map((filename) => + // Since .cjs is not available on interpret side add it manually to default config extension list + [...Object.keys(interpret.extensions), ".cjs"].map((ext) => ({ + path: path.resolve(filename + ext), + ext: ext, + module: interpret.extensions[ext], + }))) + .reduce((accumulator, currentValue) => accumulator.concat(currentValue), []); + let foundDefaultConfigFile; + for (const defaultConfigFile of defaultConfigFiles) { + if (!fs.existsSync(defaultConfigFile.path)) { + continue; + } + foundDefaultConfigFile = defaultConfigFile; + break; + } + if (foundDefaultConfigFile) { + const loadedConfig = await loadConfigByPath(foundDefaultConfigFile.path, options.argv); + config.options = loadedConfig.options; + if (Array.isArray(config.options)) { + config.options.forEach((item) => { + config.path.set(item, loadedConfig.path); + }); + } + else { + config.path.set(loadedConfig.options, loadedConfig.path); + } + } } - } - - // Setup legacy logic for webpack@4 - // TODO respect `--entry-reset` in th next major release - // TODO drop in the next major release - if (options.entry) { - item.entry = options.entry; - } - - if (options.outputPath) { - item.output = { ...item.output, ...{ path: path.resolve(options.outputPath) } }; - } - - if (options.target) { - item.target = options.target; - } - - if (typeof options.devtool !== "undefined") { - item.devtool = options.devtool; - } - - if (options.name) { - item.name = options.name; - } - - if (typeof options.stats !== "undefined") { - item.stats = options.stats; - } - - if (typeof options.watch !== "undefined") { - item.watch = options.watch; - } - - if (typeof options.watchOptionsStdin !== "undefined") { - item.watchOptions = { ...item.watchOptions, ...{ stdin: options.watchOptionsStdin } }; - } - - if (options.mode) { - item.mode = options.mode; - } - - // Respect `process.env.NODE_ENV` - if ( - !item.mode && - process.env && - process.env.NODE_ENV && - (process.env.NODE_ENV === "development" || - process.env.NODE_ENV === "production" || - process.env.NODE_ENV === "none") - ) { - item.mode = process.env.NODE_ENV; - } - - // Setup stats - // TODO remove after drop webpack@4 - const statsForWebpack4 = this.webpack.Stats && this.webpack.Stats.presetToOptions; - - if (statsForWebpack4) { - if (typeof item.stats === "undefined") { - item.stats = {}; - } else if (typeof item.stats === "boolean") { - item.stats = this.webpack.Stats.presetToOptions(item.stats); - } else if ( - typeof item.stats === "string" && - (item.stats === "none" || - item.stats === "verbose" || - item.stats === "detailed" || - item.stats === "normal" || - item.stats === "minimal" || - item.stats === "errors-only" || - item.stats === "errors-warnings") - ) { - item.stats = this.webpack.Stats.presetToOptions(item.stats); + if (options.configName) { + const notFoundConfigNames = []; + config.options = options.configName.map((configName) => { + let found; + if (Array.isArray(config.options)) { + found = config.options.find((options) => options.name === configName); + } + else { + found = config.options.name === configName ? config.options : undefined; + } + if (!found) { + notFoundConfigNames.push(configName); + } + return found; + }); + if (notFoundConfigNames.length > 0) { + this.logger.error(notFoundConfigNames + .map((configName) => `Configuration with the name "${configName}" was not found.`) + .join(" ")); + process.exit(2); + } } - } else { - if (typeof item.stats === "undefined") { - item.stats = { preset: "normal" }; - } else if (typeof item.stats === "boolean") { - item.stats = item.stats ? { preset: "normal" } : { preset: "none" }; - } else if (typeof item.stats === "string") { - item.stats = { preset: item.stats }; + if (options.merge) { + const merge = await this.tryRequireThenImport("webpack-merge"); + // we can only merge when there are multiple configurations + // either by passing multiple configs by flags or passing a + // single config exporting an array + if (!Array.isArray(config.options) || config.options.length <= 1) { + this.logger.error("At least two configurations are required for merge."); + process.exit(2); + } + const mergedConfigPaths = []; + config.options = config.options.reduce((accumulator, options) => { + const configPath = config.path.get(options); + const mergedOptions = merge(accumulator, options); + mergedConfigPaths.push(configPath); + return mergedOptions; + }, {}); + config.path.set(config.options, mergedConfigPaths); } - } - - let colors; - - // From arguments - if (typeof this.isColorSupportChanged !== "undefined") { - colors = Boolean(this.isColorSupportChanged); - } - // From stats - else if (typeof item.stats.colors !== "undefined") { - colors = item.stats.colors; - } - // Default - else { - colors = Boolean(this.colors.isColorSupported); - } - - // TODO remove after drop webpack v4 - if (typeof item.stats === "object" && item.stats !== null) { - item.stats.colors = colors; - } - - // Apply CLI plugin - if (!item.plugins) { - item.plugins = []; - } - - item.plugins.unshift( - new CLIPlugin({ - configPath: config.path.get(item), - helpfulOutput: !options.json, - hot: options.hot, - progress: options.progress, - prefetch: options.prefetch, - analyze: options.analyze, - }), - ); - - return options; - }; - - runFunctionOnEachConfig(config.options, internalBuildConfig); - - return config; - } - - isValidationError(error) { - // https://github.com/webpack/webpack/blob/master/lib/index.js#L267 - // https://github.com/webpack/webpack/blob/v4.44.2/lib/webpack.js#L90 - const ValidationError = - this.webpack.ValidationError || this.webpack.WebpackOptionsValidationError; - - return error instanceof ValidationError || error.name === "ValidationError"; - } - - async createCompiler(options, callback) { - if (typeof options.nodeEnv === "string") { - process.env.NODE_ENV = options.nodeEnv; + return config; } - - let config = await this.loadConfig(options); - config = await this.buildConfig(config, options); - - let compiler; - - try { - compiler = this.webpack( - config.options, - callback - ? (error, stats) => { - if (error && this.isValidationError(error)) { - this.logger.error(error.message); - process.exit(2); - } - - callback(error, stats); - } - : callback, - ); - } catch (error) { - if (this.isValidationError(error)) { - this.logger.error(error.message); - } else { - this.logger.error(error); - } - - process.exit(2); + async buildConfig(config, options) { + const runFunctionOnEachConfig = (options, fn) => { + if (Array.isArray(options)) { + for (let item of options) { + item = fn(item); + } + } + else { + options = fn(options); + } + return options; + }; + if (options.analyze) { + if (!this.checkPackageExists("webpack-bundle-analyzer")) { + await this.doInstall("webpack-bundle-analyzer", { + preMessage: () => { + this.logger.error(`It looks like ${this.colors.yellow("webpack-bundle-analyzer")} is not installed.`); + }, + }); + this.logger.success(`${this.colors.yellow("webpack-bundle-analyzer")} was installed successfully.`); + } + } + if (typeof options.progress === "string" && options.progress !== "profile") { + this.logger.error(`'${options.progress}' is an invalid value for the --progress option. Only 'profile' is allowed.`); + process.exit(2); + } + if (typeof options.hot === "string" && options.hot !== "only") { + this.logger.error(`'${options.hot}' is an invalid value for the --hot option. Use 'only' instead.`); + process.exit(2); + } + const CLIPlugin = await this.tryRequireThenImport("./plugins/CLIPlugin"); + const internalBuildConfig = (item) => { + // Output warnings + if (item.watch && + options.argv && + options.argv.env && + (options.argv.env["WEBPACK_WATCH"] || options.argv.env["WEBPACK_SERVE"])) { + this.logger.warn(`No need to use the '${options.argv.env["WEBPACK_WATCH"] ? "watch" : "serve"}' command together with '{ watch: true }' configuration, it does not make sense.`); + if (options.argv.env["WEBPACK_SERVE"]) { + item.watch = false; + } + } + // Apply options + if (this.webpack.cli) { + const args = this.getBuiltInOptions() + .filter((flag) => flag.group === "core") + .reduce((accumulator, flag) => { + accumulator[flag.name] = flag; + return accumulator; + }, {}); + const values = Object.keys(options).reduce((accumulator, name) => { + if (name === "argv") { + return accumulator; + } + const kebabName = this.toKebabCase(name); + if (args[kebabName]) { + accumulator[kebabName] = options[name]; + } + return accumulator; + }, {}); + const problems = this.webpack.cli.processArguments(args, item, values); + if (problems) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const groupBy = (xs, key) => { + return xs.reduce((rv, x) => { + (rv[x[key]] = rv[x[key]] || []).push(x); + return rv; + }, {}); + }; + const problemsByPath = groupBy(problems, "path"); + for (const path in problemsByPath) { + const problems = problemsByPath[path]; + problems.forEach((problem) => { + this.logger.error(`${this.capitalizeFirstLetter(problem.type.replace(/-/g, " "))}${problem.value ? ` '${problem.value}'` : ""} for the '--${problem.argument}' option${problem.index ? ` by index '${problem.index}'` : ""}`); + if (problem.expected) { + this.logger.error(`Expected: '${problem.expected}'`); + } + }); + } + process.exit(2); + } + const isFileSystemCacheOptions = (config) => { + return (Boolean(config.cache) && config.cache.type === "filesystem"); + }; + // Setup default cache options + if (isFileSystemCacheOptions(item)) { + const configPath = config.path.get(item); + if (configPath) { + if (!item.cache.buildDependencies) { + item.cache.buildDependencies = {}; + } + if (!item.cache.buildDependencies.defaultConfig) { + item.cache.buildDependencies.defaultConfig = []; + } + if (Array.isArray(configPath)) { + configPath.forEach((oneOfConfigPath) => { + item.cache.buildDependencies.defaultConfig.push(oneOfConfigPath); + }); + } + else { + item.cache.buildDependencies.defaultConfig.push(configPath); + } + } + } + } + // Setup legacy logic for webpack@4 + // TODO respect `--entry-reset` in th next major release + // TODO drop in the next major release + if (options.entry) { + item.entry = options.entry; + } + if (options.outputPath) { + item.output = Object.assign(Object.assign({}, item.output), { path: path.resolve(options.outputPath) }); + } + if (options.target) { + item.target = options.target; + } + if (typeof options.devtool !== "undefined") { + item.devtool = options.devtool; + } + if (options.name) { + item.name = options.name; + } + if (typeof options.stats !== "undefined") { + item.stats = options.stats; + } + if (typeof options.watch !== "undefined") { + item.watch = options.watch; + } + if (typeof options.watchOptionsStdin !== "undefined") { + item.watchOptions = Object.assign(Object.assign({}, item.watchOptions), { stdin: options.watchOptionsStdin }); + } + if (options.mode) { + item.mode = options.mode; + } + // Respect `process.env.NODE_ENV` + if (!item.mode && + process.env && + process.env.NODE_ENV && + (process.env.NODE_ENV === "development" || + process.env.NODE_ENV === "production" || + process.env.NODE_ENV === "none")) { + item.mode = process.env.NODE_ENV; + } + // Setup stats + // TODO remove after drop webpack@4 + const statsForWebpack4 = this.webpack.Stats && + this.webpack.Stats.presetToOptions; + if (statsForWebpack4) { + if (typeof item.stats === "undefined") { + item.stats = {}; + } + else if (typeof item.stats === "boolean") { + item.stats = this.webpack.Stats.presetToOptions(item.stats); + } + else if (typeof item.stats === "string" && + (item.stats === "none" || + item.stats === "verbose" || + item.stats === "detailed" || + item.stats === "normal" || + item.stats === "minimal" || + item.stats === "errors-only" || + item.stats === "errors-warnings")) { + item.stats = this.webpack.Stats.presetToOptions(item.stats); + } + } + else { + if (typeof item.stats === "undefined") { + item.stats = { preset: "normal" }; + } + else if (typeof item.stats === "boolean") { + item.stats = item.stats ? { preset: "normal" } : { preset: "none" }; + } + else if (typeof item.stats === "string") { + item.stats = { preset: item.stats }; + } + } + let colors; + // From arguments + if (typeof this.isColorSupportChanged !== "undefined") { + colors = Boolean(this.isColorSupportChanged); + } + // From stats + else if (typeof item.stats.colors !== "undefined") { + colors = item.stats.colors; + } + // Default + else { + colors = Boolean(this.colors.isColorSupported); + } + // TODO remove after drop webpack v4 + if (typeof item.stats === "object" && item.stats !== null) { + item.stats.colors = colors; + } + // Apply CLI plugin + if (!item.plugins) { + item.plugins = []; + } + item.plugins.unshift(new CLIPlugin({ + configPath: config.path.get(item), + helpfulOutput: !options.json, + hot: options.hot, + progress: options.progress, + prefetch: options.prefetch, + analyze: options.analyze, + })); + return options; + }; + runFunctionOnEachConfig(config.options, internalBuildConfig); + return config; } - - // TODO webpack@4 return Watching and MultiWatching instead Compiler and MultiCompiler, remove this after drop webpack@4 - if (compiler && compiler.compiler) { - compiler = compiler.compiler; + isValidationError(error) { + // https://github.com/webpack/webpack/blob/master/lib/index.js#L267 + // https://github.com/webpack/webpack/blob/v4.44.2/lib/webpack.js#L90 + const ValidationError = this.webpack.ValidationError || this.webpack.WebpackOptionsValidationError; + return error instanceof ValidationError || error.name === "ValidationError"; } - - return compiler; - } - - needWatchStdin(compiler) { - if (compiler.compilers) { - return compiler.compilers.some( - (compiler) => compiler.options.watchOptions && compiler.options.watchOptions.stdin, - ); + async createCompiler(options, callback) { + if (typeof options.nodeEnv === "string") { + process.env.NODE_ENV = options.nodeEnv; + } + let config = await this.loadConfig(options); + config = await this.buildConfig(config, options); + let compiler; + try { + compiler = this.webpack(config.options, callback + ? (error, stats) => { + if (error && this.isValidationError(error)) { + this.logger.error(error.message); + process.exit(2); + } + callback(error, stats); + } + : callback); + // @ts-expect-error error type assertion + } + catch (error) { + if (this.isValidationError(error)) { + this.logger.error(error.message); + } + else { + this.logger.error(error); + } + process.exit(2); + } + // TODO webpack@4 return Watching and MultiWatching instead Compiler and MultiCompiler, remove this after drop webpack@4 + if (compiler && compiler.compiler) { + compiler = compiler.compiler; + } + return compiler; } - - return compiler.options.watchOptions && compiler.options.watchOptions.stdin; - } - - async runWebpack(options, isWatchCommand) { - // eslint-disable-next-line prefer-const - let compiler; - let createJsonStringifyStream; - - if (options.json) { - const jsonExt = await this.tryRequireThenImport("@discoveryjs/json-ext"); - - createJsonStringifyStream = jsonExt.stringifyStream; + needWatchStdin(compiler) { + if (this.isMultipleCompiler(compiler)) { + return Boolean(compiler.compilers.some((compiler) => compiler.options.watchOptions && compiler.options.watchOptions.stdin)); + } + return Boolean(compiler.options.watchOptions && compiler.options.watchOptions.stdin); } - - const callback = (error, stats) => { - if (error) { - this.logger.error(error); - process.exit(2); - } - - if (stats.hasErrors()) { - process.exitCode = 1; - } - - if (!compiler) { - return; - } - - const statsOptions = compiler.compilers - ? { - children: compiler.compilers.map((compiler) => - compiler.options ? compiler.options.stats : undefined, - ), - } - : compiler.options - ? compiler.options.stats - : undefined; - - // TODO webpack@4 doesn't support `{ children: [{ colors: true }, { colors: true }] }` for stats - const statsForWebpack4 = this.webpack.Stats && this.webpack.Stats.presetToOptions; - - if (compiler.compilers && statsForWebpack4) { - statsOptions.colors = statsOptions.children.some((child) => child.colors); - } - - if (options.json && createJsonStringifyStream) { - const handleWriteError = (error) => { - this.logger.error(error); - process.exit(2); + async runWebpack(options, isWatchCommand) { + // eslint-disable-next-line prefer-const + let compiler; + let createJsonStringifyStream; + if (options.json) { + const jsonExt = await this.tryRequireThenImport("@discoveryjs/json-ext"); + createJsonStringifyStream = jsonExt.stringifyStream; + } + const callback = (error, stats) => { + if (error) { + this.logger.error(error); + process.exit(2); + } + if (stats && stats.hasErrors()) { + process.exitCode = 1; + } + if (!compiler || !stats) { + return; + } + const statsOptions = this.isMultipleCompiler(compiler) + ? { + children: compiler.compilers.map((compiler) => compiler.options ? compiler.options.stats : undefined), + } + : compiler.options + ? compiler.options.stats + : undefined; + // TODO webpack@4 doesn't support `{ children: [{ colors: true }, { colors: true }] }` for stats + const statsForWebpack4 = this.webpack.Stats && + this.webpack.Stats.presetToOptions; + if (this.isMultipleCompiler(compiler) && statsForWebpack4) { + statsOptions.colors = statsOptions.children.some((child) => child.colors); + } + if (options.json && createJsonStringifyStream) { + const handleWriteError = (error) => { + this.logger.error(error); + process.exit(2); + }; + if (options.json === true) { + createJsonStringifyStream(stats.toJson(statsOptions)) + .on("error", handleWriteError) + .pipe(process.stdout) + .on("error", handleWriteError) + .on("close", () => process.stdout.write("\n")); + } + else { + createJsonStringifyStream(stats.toJson(statsOptions)) + .on("error", handleWriteError) + .pipe(fs.createWriteStream(options.json)) + .on("error", handleWriteError) + // Use stderr to logging + .on("close", () => { + process.stderr.write(`[webpack-cli] ${this.colors.green(`stats are successfully stored as json to ${options.json}`)}\n`); + }); + } + } + else { + const printedStats = stats.toString(statsOptions); + // Avoid extra empty line when `stats: 'none'` + if (printedStats) { + this.logger.raw(printedStats); + } + } }; - - if (options.json === true) { - createJsonStringifyStream(stats.toJson(statsOptions)) - .on("error", handleWriteError) - .pipe(process.stdout) - .on("error", handleWriteError) - .on("close", () => process.stdout.write("\n")); - } else { - createJsonStringifyStream(stats.toJson(statsOptions)) - .on("error", handleWriteError) - .pipe(fs.createWriteStream(options.json)) - .on("error", handleWriteError) - // Use stderr to logging - .on("close", () => { - process.stderr.write( - `[webpack-cli] ${this.colors.green( - `stats are successfully stored as json to ${options.json}`, - )}\n`, - ); - }); + const env = isWatchCommand || options.watch + ? Object.assign({ WEBPACK_WATCH: true }, options.env) : Object.assign({ WEBPACK_BUNDLE: true, WEBPACK_BUILD: true }, options.env); + options.argv = Object.assign(Object.assign({}, options), { env }); + if (isWatchCommand) { + options.watch = true; + } + compiler = await this.createCompiler(options, callback); + if (!compiler) { + return; } - } else { - const printedStats = stats.toString(statsOptions); - - // Avoid extra empty line when `stats: 'none'` - if (printedStats) { - this.logger.raw(printedStats); + const isWatch = (compiler) => Boolean(this.isMultipleCompiler(compiler) + ? compiler.compilers.some((compiler) => compiler.options.watch) + : compiler.options.watch); + if (isWatch(compiler) && this.needWatchStdin(compiler)) { + process.stdin.on("end", () => { + process.exit(0); + }); + process.stdin.resume(); } - } - }; - - const env = - isWatchCommand || options.watch - ? { WEBPACK_WATCH: true, ...options.env } - : { WEBPACK_BUNDLE: true, WEBPACK_BUILD: true, ...options.env }; - - options.argv = { ...options, env }; - - if (isWatchCommand) { - options.watch = true; - } - - compiler = await this.createCompiler(options, callback); - - if (!compiler) { - return; - } - - const isWatch = (compiler) => - compiler.compilers - ? compiler.compilers.some((compiler) => compiler.options.watch) - : compiler.options.watch; - - if (isWatch(compiler) && this.needWatchStdin(compiler)) { - process.stdin.on("end", () => { - process.exit(0); - }); - process.stdin.resume(); } - } } - module.exports = WebpackCLI; diff --git a/packages/webpack-cli/src/bootstrap.ts b/packages/webpack-cli/src/bootstrap.ts new file mode 100644 index 00000000000..347c65e250c --- /dev/null +++ b/packages/webpack-cli/src/bootstrap.ts @@ -0,0 +1,18 @@ +import { IWebpackCLI } from "./types"; + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const WebpackCLI = require("./webpack-cli"); + +const runCLI = async (args: Parameters[0]) => { + // Create a new instance of the CLI object + const cli: IWebpackCLI = new WebpackCLI(); + + try { + await cli.run(args); + } catch (error) { + cli.logger.error(error); + process.exit(2); + } +}; + +module.exports = runCLI; diff --git a/packages/webpack-cli/src/index.ts b/packages/webpack-cli/src/index.ts new file mode 100644 index 00000000000..5f4a8cd0a6c --- /dev/null +++ b/packages/webpack-cli/src/index.ts @@ -0,0 +1,9 @@ +import { IWebpackCLI } from "./types"; +export * from "./types"; + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const CLI: IWebpackCLI = require("./webpack-cli"); + +module.exports = CLI; +// TODO remove after drop `@webpack-cli/migrate` +module.exports.utils = { logger: console }; diff --git a/packages/webpack-cli/src/plugins/CLIPlugin.ts b/packages/webpack-cli/src/plugins/CLIPlugin.ts new file mode 100644 index 00000000000..215049c655e --- /dev/null +++ b/packages/webpack-cli/src/plugins/CLIPlugin.ts @@ -0,0 +1,141 @@ +import { Compiler } from "webpack"; +import { CLIPluginOptions } from "../types"; + +export class CLIPlugin { + logger!: ReturnType; + options: CLIPluginOptions; + + constructor(options: CLIPluginOptions) { + this.options = options; + } + + setupHotPlugin(compiler: Compiler) { + const { HotModuleReplacementPlugin } = compiler.webpack || require("webpack"); + const hotModuleReplacementPlugin = Boolean( + compiler.options.plugins.find((plugin) => plugin instanceof HotModuleReplacementPlugin), + ); + + if (!hotModuleReplacementPlugin) { + new HotModuleReplacementPlugin().apply(compiler); + } + } + + setupPrefetchPlugin(compiler: Compiler) { + const { PrefetchPlugin } = compiler.webpack || require("webpack"); + + new PrefetchPlugin(null, this.options.prefetch).apply(compiler); + } + + async setupBundleAnalyzerPlugin(compiler: Compiler) { + // eslint-disable-next-line node/no-extraneous-require,@typescript-eslint/no-var-requires + const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer"); + const bundleAnalyzerPlugin = Boolean( + compiler.options.plugins.find((plugin) => plugin instanceof BundleAnalyzerPlugin), + ); + + if (!bundleAnalyzerPlugin) { + new BundleAnalyzerPlugin().apply(compiler); + } + } + + setupProgressPlugin(compiler: Compiler) { + const { ProgressPlugin } = compiler.webpack || require("webpack"); + const progressPlugin = Boolean( + compiler.options.plugins.find((plugin) => plugin instanceof ProgressPlugin), + ); + + if (!progressPlugin) { + new ProgressPlugin({ + profile: this.options.progress === "profile", + }).apply(compiler); + } + } + + setupHelpfulOutput(compiler: Compiler) { + const pluginName = "webpack-cli"; + const getCompilationName = () => (compiler.name ? `'${compiler.name}'` : ""); + const logCompilation = (message: string) => { + if (process.env.WEBPACK_CLI_START_FINISH_FORCE_LOG) { + process.stderr.write(message); + } else { + this.logger.log(message); + } + }; + + const { configPath } = this.options; + + compiler.hooks.run.tap(pluginName, () => { + const name = getCompilationName(); + + logCompilation(`Compiler${name ? ` ${name}` : ""} starting... `); + + if (configPath) { + this.logger.log(`Compiler${name ? ` ${name}` : ""} is using config: '${configPath}'`); + } + }); + + compiler.hooks.watchRun.tap(pluginName, (compiler) => { + const { bail, watch } = compiler.options; + + if (bail && watch) { + this.logger.warn( + 'You are using "bail" with "watch". "bail" will still exit webpack when the first error is found.', + ); + } + + const name = getCompilationName(); + + logCompilation(`Compiler${name ? ` ${name}` : ""} starting... `); + + if (configPath) { + this.logger.log(`Compiler${name ? ` ${name}` : ""} is using config: '${configPath}'`); + } + }); + + compiler.hooks.invalid.tap(pluginName, (filename, changeTime) => { + const date = new Date(changeTime * 1000); + + this.logger.log(`File '${filename}' was modified`); + this.logger.log(`Changed time is ${date} (timestamp is ${changeTime})`); + }); + + ((compiler as Partial).webpack ? compiler.hooks.afterDone : compiler.hooks.done).tap( + pluginName, + () => { + const name = getCompilationName(); + + logCompilation(`Compiler${name ? ` ${name}` : ""} finished`); + + process.nextTick(() => { + if (compiler.watchMode) { + this.logger.log(`Compiler${name ? `${name}` : ""} is watching files for updates...`); + } + }); + }, + ); + } + + apply(compiler: Compiler) { + this.logger = compiler.getInfrastructureLogger("webpack-cli"); + + if (this.options.progress) { + this.setupProgressPlugin(compiler); + } + + if (this.options.hot) { + this.setupHotPlugin(compiler); + } + + if (this.options.prefetch) { + this.setupPrefetchPlugin(compiler); + } + + if (this.options.analyze) { + this.setupBundleAnalyzerPlugin(compiler); + } + + this.setupHelpfulOutput(compiler); + } +} + +module.exports = CLIPlugin; diff --git a/packages/webpack-cli/src/types.ts b/packages/webpack-cli/src/types.ts new file mode 100644 index 00000000000..3931e96987a --- /dev/null +++ b/packages/webpack-cli/src/types.ts @@ -0,0 +1,356 @@ +import webpack, { + EntryOptions, + Stats, + Configuration, + WebpackError, + StatsOptions, + WebpackOptionsNormalized, + Compiler, + MultiCompiler, + Problem, + Argument, + AssetEmittedInfo, + FileCacheOptions, +} from "webpack"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore extraneous import is intended +// eslint-disable-next-line node/no-extraneous-import +import { ClientConfiguration, Configuration as DevServerConfig } from "webpack-dev-server"; + +import { Colorette } from "colorette"; +import { Command, CommandOptions, OptionConstructor, ParseOptions } from "commander"; +import { prepare } from "rechoir"; +import { stringifyStream } from "@discoveryjs/json-ext"; + +/** + * Webpack CLI + */ + +interface IWebpackCLI { + colors: WebpackCLIColors; + logger: WebpackCLILogger; + isColorSupportChanged: boolean | undefined; + webpack: typeof webpack; + builtInOptionsCache: WebpackCLIBuiltInOption[] | undefined; + program: WebpackCLICommand; + isMultipleCompiler(compiler: WebpackCompiler): compiler is MultiCompiler; + isPromise(value: Promise): value is Promise; + isFunction(value: unknown): value is CallableFunction; + getLogger(): WebpackCLILogger; + createColors(useColors?: boolean): WebpackCLIColors; + toKebabCase: StringFormatter; + capitalizeFirstLetter: StringFormatter; + checkPackageExists(packageName: string): boolean; + getAvailablePackageManagers(): PackageManager[]; + getDefaultPackageManager(): PackageManager | undefined; + doInstall(packageName: string, options?: PackageInstallOptions): Promise; + loadJSONFile(path: Path, handleError: boolean): Promise; + tryRequireThenImport(module: ModuleName, handleError: boolean): Promise; + makeCommand( + commandOptions: WebpackCLIOptions, + options: WebpackCLICommandOptions, + action: CommandAction, + ): Promise; + makeOption(command: WebpackCLICommand, option: WebpackCLIBuiltInOption): void; + run( + args: Parameters[0], + parseOptions?: ParseOptions, + ): Promise; + getBuiltInOptions(): WebpackCLIBuiltInOption[]; + loadWebpack(handleError?: boolean): Promise; + loadConfig(options: Partial): Promise; + buildConfig( + config: WebpackCLIConfig, + options: WebpackDevServerOptions, + ): Promise; + isValidationError(error: Error): error is WebpackError; + createCompiler( + options: Partial, + callback?: Callback<[Error | undefined, WebpackCLIStats | undefined]>, + ): Promise; + needWatchStdin(compiler: Compiler | MultiCompiler): boolean; + runWebpack(options: WebpackRunOptions, isWatchCommand: boolean): Promise; +} + +interface WebpackCLIColors extends Colorette { + isColorSupported: boolean; +} + +interface WebpackCLILogger { + error: LogHandler; + warn: LogHandler; + info: LogHandler; + success: LogHandler; + log: LogHandler; + raw: LogHandler; +} + +interface WebpackCLICommandOption extends CommanderOption { + helpLevel?: "minimum" | "verbose"; +} + +interface WebpackCLIConfig { + options: WebpackConfiguration | WebpackConfiguration[]; + path: WeakMap; +} + +interface WebpackCLICommand extends Command { + pkg: string | undefined; + forHelp: boolean | undefined; + options: WebpackCLICommandOption[]; + _args: WebpackCLICommandOption[]; +} + +interface WebpackCLIStats extends Stats { + presetToOptions?: (item: string | boolean) => StatsOptions; +} + +type WebpackCLIMainOption = Pick< + WebpackCLIBuiltInOption, + "description" | "defaultValue" | "multiple" +> & { + flags: string; + type: Set; +}; + +interface WebpackCLIOptions extends CommandOptions { + name: string; + alias: string | string[]; + description?: string; + usage?: string; + dependencies?: string[]; + pkg?: string; + argsDescription?: { [argName: string]: string }; +} + +type WebpackCLICommandOptions = + | WebpackCLIBuiltInOption[] + | (() => Promise); + +interface WebpackCLIBuiltInFlag { + name: string; + alias?: string; + type?: ( + value: string, + previous: Record, + ) => Record; + configs?: Partial[]; + negative?: boolean; + multiple?: boolean; + description: string; + describe?: string; + negatedDescription?: string; + defaultValue?: string; +} + +interface WebpackCLIBuiltInOption extends WebpackCLIBuiltInFlag { + hidden?: boolean; + group?: "core"; + helpLevel?: "minimum" | "verbose"; +} + +type WebpackCLIExternalCommandInfo = Pick & { + pkg: string; +}; + +/** + * Webpack dev server + */ + +type WebpackDevServerOptions = DevServerConfig & + WebpackConfiguration & + ClientConfiguration & + AssetEmittedInfo & + WebpackOptionsNormalized & + FileCacheOptions & + Argv & { + nodeEnv?: "string"; + watchOptionsStdin?: boolean; + progress?: boolean | "profile" | undefined; + analyze?: boolean; + prefetch?: string; + json?: boolean; + entry: EntryOptions; + merge?: boolean; + config: string[]; + configName?: string[]; + argv: Argv; + }; + +type Callback = (...args: T) => void; + +/** + * Webpack + */ + +type WebpackConfiguration = Configuration; +type ConfigOptions = PotentialPromise; +type CallableOption = (env: Env | undefined, argv: Argv) => WebpackConfiguration; +type WebpackCompiler = Compiler | MultiCompiler; + +type FlagType = boolean | "enum" | "string" | "path" | "number" | "boolean" | "RegExp" | "reset"; + +type FlagConfig = { + negatedDescription: string; + type: FlagType; + values: FlagType[]; +}; + +type FileSystemCacheOptions = WebpackConfiguration & { + cache: FileCacheOptions & { defaultConfig: string[] }; +}; + +type ProcessedArguments = Record; + +type MultipleCompilerStatsOptions = StatsOptions & { children: StatsOptions[] }; +type CommandAction = Parameters[0]; + +interface WebpackRunOptions extends WebpackOptionsNormalized { + json?: boolean; + argv?: Argv; + env: Env; +} + +/** + * Package management + */ + +type PackageManager = "pnpm" | "yarn" | "npm"; +interface PackageInstallOptions { + preMessage?: () => void; +} +interface BasicPackageJsonContent { + name: string; + version: string; + description: string; + license: string; +} + +/** + * Webpack V4 + */ + +type WebpackV4LegacyStats = Required; +interface WebpackV4Compiler extends Compiler { + compiler: Compiler; +} + +/** + * Plugins and util types + */ + +interface CLIPluginOptions { + configPath?: string; + helpfulOutput: boolean; + hot?: boolean | "only"; + progress?: boolean | "profile"; + prefetch?: string; + analyze?: boolean; +} + +type BasicPrimitive = string | boolean | number; +type Instantiable = { + new (...args: ConstructorParameters): InstanceType; +}; +type PotentialPromise = T | Promise; +type ModuleName = string; +type Path = string; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type LogHandler = (value: any) => void; +type StringFormatter = (value: string) => string; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +interface Argv extends Record { + env?: Env; +} + +interface Env { + WEBPACK_BUNDLE?: boolean; + WEBPACK_BUILD?: boolean; + WEBPACK_WATCH?: boolean; + WEBPACK_SERVE?: boolean; + WEBPACK_PACKAGE?: string; + WEBPACK_DEV_SERVER_PACKAGE?: string; +} + +type DynamicImport = (url: string) => Promise<{ default: T }>; + +interface ImportLoaderError extends Error { + code?: string; +} + +/** + * External libraries types + */ + +type CommanderOption = InstanceType; + +interface Rechoir { + prepare: typeof prepare; +} + +interface JsonExt { + stringifyStream: typeof stringifyStream; +} + +interface RechoirError extends Error { + failures: RechoirError[]; + error: Error; +} + +interface PromptOptions { + message: string; + defaultResponse: string; + stream: NodeJS.WritableStream; +} + +export { + IWebpackCLI, + WebpackCLICommandOption, + WebpackCLIBuiltInOption, + WebpackCLIBuiltInFlag, + WebpackCLIColors, + WebpackCLIStats, + WebpackCLIConfig, + WebpackCLIExternalCommandInfo, + WebpackCLIOptions, + WebpackCLICommand, + WebpackCLICommandOptions, + WebpackCLIMainOption, + WebpackCLILogger, + WebpackV4LegacyStats, + WebpackDevServerOptions, + WebpackRunOptions, + WebpackV4Compiler, + WebpackCompiler, + WebpackConfiguration, + Argv, + Argument, + BasicPrimitive, + BasicPackageJsonContent, + CallableOption, + Callback, + CLIPluginOptions, + CommandAction, + CommanderOption, + CommandOptions, + ConfigOptions, + DynamicImport, + FileSystemCacheOptions, + FlagConfig, + ImportLoaderError, + Instantiable, + JsonExt, + ModuleName, + MultipleCompilerStatsOptions, + PackageInstallOptions, + PackageManager, + Path, + ProcessedArguments, + PromptOptions, + Problem, + PotentialPromise, + Rechoir, + RechoirError, +}; diff --git a/packages/webpack-cli/src/utils/dynamic-import-loader.ts b/packages/webpack-cli/src/utils/dynamic-import-loader.ts new file mode 100644 index 00000000000..8e9eabaca84 --- /dev/null +++ b/packages/webpack-cli/src/utils/dynamic-import-loader.ts @@ -0,0 +1,15 @@ +import { DynamicImport } from "../types"; + +function dynamicImportLoader(): DynamicImport | null { + let importESM; + + try { + importESM = new Function("id", "return import(id);"); + } catch (e) { + importESM = null; + } + + return importESM as DynamicImport; +} + +module.exports = dynamicImportLoader; diff --git a/packages/webpack-cli/src/webpack-cli.ts b/packages/webpack-cli/src/webpack-cli.ts new file mode 100644 index 00000000000..27b1adb51af --- /dev/null +++ b/packages/webpack-cli/src/webpack-cli.ts @@ -0,0 +1,2515 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +import { + IWebpackCLI, + WebpackCLICommandOption, + WebpackCLIBuiltInOption, + WebpackCLIBuiltInFlag, + WebpackCLIColors, + WebpackCLIStats, + WebpackCLIConfig, + WebpackCLIExternalCommandInfo, + WebpackCLIOptions, + WebpackCLICommand, + WebpackCLICommandOptions, + WebpackCLIMainOption, + WebpackCLILogger, + WebpackV4LegacyStats, + WebpackDevServerOptions, + WebpackRunOptions, + WebpackV4Compiler, + WebpackCompiler, + WebpackConfiguration, + Argv, + BasicPrimitive, + BasicPackageJsonContent, + CallableOption, + Callback, + CLIPluginOptions, + CommandAction, + ConfigOptions, + DynamicImport, + FileSystemCacheOptions, + FlagConfig, + ImportLoaderError, + Instantiable, + JsonExt, + ModuleName, + MultipleCompilerStatsOptions, + PackageInstallOptions, + PackageManager, + Path, + ProcessedArguments, + PromptOptions, + PotentialPromise, + Rechoir, + RechoirError, + Argument, + Problem, +} from "./types"; + +import webpackMerge from "webpack-merge"; +import webpack from "webpack"; +import { Compiler, MultiCompiler, WebpackError, StatsOptions } from "webpack"; +import { stringifyStream } from "@discoveryjs/json-ext"; +import { Help, ParseOptions } from "commander"; + +import { CLIPlugin as CLIPluginClass } from "./plugins/CLIPlugin"; + +const fs = require("fs"); +const path = require("path"); +const { pathToFileURL } = require("url"); +const util = require("util"); + +const { program, Option } = require("commander"); + +const WEBPACK_PACKAGE = process.env.WEBPACK_PACKAGE || "webpack"; +const WEBPACK_DEV_SERVER_PACKAGE = process.env.WEBPACK_DEV_SERVER_PACKAGE || "webpack-dev-server"; + +class WebpackCLI implements IWebpackCLI { + colors: WebpackCLIColors; + logger: WebpackCLILogger; + isColorSupportChanged: boolean | undefined; + builtInOptionsCache: WebpackCLIBuiltInOption[] | undefined; + webpack!: typeof webpack; + program: WebpackCLICommand; + constructor() { + this.colors = this.createColors(); + this.logger = this.getLogger(); + + // Initialize program + this.program = program; + this.program.name("webpack"); + this.program.configureOutput({ + writeErr: this.logger.error, + outputError: (str, write) => + write(`Error: ${this.capitalizeFirstLetter(str.replace(/^error:/, "").trim())}`), + }); + } + + isMultipleCompiler(compiler: WebpackCompiler): compiler is MultiCompiler { + return (compiler as MultiCompiler).compilers as unknown as boolean; + } + isPromise(value: Promise): value is Promise { + return typeof (value as unknown as Promise).then === "function"; + } + isFunction(value: unknown): value is CallableFunction { + return typeof value === "function"; + } + + capitalizeFirstLetter(str: string | unknown): string { + if (typeof str !== "string") { + return ""; + } + + return str.charAt(0).toUpperCase() + str.slice(1); + } + + toKebabCase(str: string): string { + return str.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase(); + } + + createColors(useColor?: boolean): WebpackCLIColors { + const { createColors, isColorSupported } = require("colorette"); + + let shouldUseColor; + + if (useColor) { + shouldUseColor = useColor; + } else { + shouldUseColor = isColorSupported; + } + + return { ...createColors({ useColor: shouldUseColor }), isColorSupported: shouldUseColor }; + } + + getLogger(): WebpackCLILogger { + return { + error: (val) => console.error(`[webpack-cli] ${this.colors.red(util.format(val))}`), + warn: (val) => console.warn(`[webpack-cli] ${this.colors.yellow(val)}`), + info: (val) => console.info(`[webpack-cli] ${this.colors.cyan(val)}`), + success: (val) => console.log(`[webpack-cli] ${this.colors.green(val)}`), + log: (val) => console.log(`[webpack-cli] ${val}`), + raw: (val) => console.log(val), + }; + } + + checkPackageExists(packageName: string): boolean { + if (process.versions.pnp) { + return true; + } + + let dir = __dirname; + + do { + try { + if (fs.statSync(path.join(dir, "node_modules", packageName)).isDirectory()) { + return true; + } + } catch (_error) { + // Nothing + } + } while (dir !== (dir = path.dirname(dir))); + + return false; + } + + getAvailablePackageManagers(): PackageManager[] { + const { sync } = require("execa"); + const installers: PackageManager[] = ["npm", "yarn", "pnpm"]; + const hasPackageManagerInstalled = (packageManager: PackageManager) => { + try { + sync(packageManager, ["--version"]); + + return packageManager; + } catch (err) { + return false; + } + }; + const availableInstallers = installers.filter((installer) => + hasPackageManagerInstalled(installer), + ); + + if (!availableInstallers.length) { + this.logger.error("No package manager found."); + + process.exit(2); + } + + return availableInstallers; + } + + getDefaultPackageManager(): PackageManager | undefined { + const { sync } = require("execa"); + const hasLocalNpm = fs.existsSync(path.resolve(process.cwd(), "package-lock.json")); + + if (hasLocalNpm) { + return "npm"; + } + + const hasLocalYarn = fs.existsSync(path.resolve(process.cwd(), "yarn.lock")); + + if (hasLocalYarn) { + return "yarn"; + } + + const hasLocalPnpm = fs.existsSync(path.resolve(process.cwd(), "pnpm-lock.yaml")); + + if (hasLocalPnpm) { + return "pnpm"; + } + + try { + // the sync function below will fail if npm is not installed, + // an error will be thrown + if (sync("npm", ["--version"])) { + return "npm"; + } + } catch (e) { + // Nothing + } + + try { + // the sync function below will fail if yarn is not installed, + // an error will be thrown + if (sync("yarn", ["--version"])) { + return "yarn"; + } + } catch (e) { + // Nothing + } + + try { + // the sync function below will fail if pnpm is not installed, + // an error will be thrown + if (sync("pnpm", ["--version"])) { + return "pnpm"; + } + } catch (e) { + this.logger.error("No package manager found."); + + process.exit(2); + } + } + + async doInstall(packageName: string, options: PackageInstallOptions = {}): Promise { + const packageManager = this.getDefaultPackageManager(); + + if (!packageManager) { + this.logger.error("Can't find package manager"); + + process.exit(2); + } + + if (options.preMessage) { + options.preMessage(); + } + + // yarn uses 'add' command, rest npm and pnpm both use 'install' + const commandToBeRun = `${packageManager} ${[ + packageManager === "yarn" ? "add" : "install", + "-D", + packageName, + ].join(" ")}`; + + const prompt = ({ message, defaultResponse, stream }: PromptOptions) => { + const readline = require("readline"); + const rl = readline.createInterface({ + input: process.stdin, + output: stream, + }); + + return new Promise((resolve) => { + rl.question(`${message} `, (answer: string) => { + // Close the stream + rl.close(); + + const response = (answer || defaultResponse).toLowerCase(); + + // Resolve with the input response + if (response === "y" || response === "yes") { + resolve(true); + } else { + resolve(false); + } + }); + }); + }; + + let needInstall; + + try { + needInstall = await prompt({ + message: `[webpack-cli] Would you like to install '${this.colors.green( + packageName, + )}' package? (That will run '${this.colors.green(commandToBeRun)}') (${this.colors.yellow( + "Y/n", + )})`, + defaultResponse: "Y", + stream: process.stderr, + }); + } catch (error) { + this.logger.error(error); + + process.exit(error as number); + } + + if (needInstall) { + const execa = require("execa"); + + try { + await execa(commandToBeRun, [], { stdio: "inherit", shell: true }); + } catch (error) { + this.logger.error(error); + + process.exit(2); + } + + return packageName; + } + + process.exit(2); + } + + async tryRequireThenImport(module: ModuleName, handleError = true): Promise { + let result; + + try { + result = require(module); + } catch (error) { + const dynamicImportLoader: null | DynamicImport = + require("./utils/dynamic-import-loader")(); + if ( + ((error as ImportLoaderError).code === "ERR_REQUIRE_ESM" || + process.env.WEBPACK_CLI_FORCE_LOAD_ESM_CONFIG) && + pathToFileURL && + dynamicImportLoader + ) { + const urlForConfig = pathToFileURL(module); + + result = await dynamicImportLoader(urlForConfig); + result = result.default; + + return result; + } + + if (handleError) { + this.logger.error(error); + process.exit(2); + } else { + throw error; + } + } + + // For babel/typescript + if (result && typeof result === "object" && "default" in result) { + result = result.default || {}; + } + + return result || {}; + } + + loadJSONFile(pathToFile: Path, handleError = true): T { + let result; + + try { + result = require(pathToFile); + } catch (error) { + if (handleError) { + this.logger.error(error); + process.exit(2); + } else { + throw error; + } + } + + return result; + } + + async makeCommand( + commandOptions: WebpackCLIOptions, + options: WebpackCLICommandOptions, + action: CommandAction, + ): Promise { + const alreadyLoaded = this.program.commands.find( + (command) => + command.name() === commandOptions.name.split(" ")[0] || + command.aliases().includes(commandOptions.alias as string), + ); + + if (alreadyLoaded) { + return; + } + + const command = this.program.command(commandOptions.name, { + noHelp: commandOptions.noHelp, + hidden: commandOptions.hidden, + isDefault: commandOptions.isDefault, + }) as WebpackCLICommand; + + if (commandOptions.description) { + command.description(commandOptions.description, commandOptions.argsDescription); + } + + if (commandOptions.usage) { + command.usage(commandOptions.usage); + } + + if (Array.isArray(commandOptions.alias)) { + command.aliases(commandOptions.alias); + } else { + command.alias(commandOptions.alias as string); + } + + if (commandOptions.pkg) { + command.pkg = commandOptions.pkg; + } else { + command.pkg = "webpack-cli"; + } + + const { forHelp } = this.program; + + let allDependenciesInstalled = true; + + if (commandOptions.dependencies && commandOptions.dependencies.length > 0) { + for (const dependency of commandOptions.dependencies) { + const isPkgExist = this.checkPackageExists(dependency); + + if (isPkgExist) { + continue; + } else if (!isPkgExist && forHelp) { + allDependenciesInstalled = false; + continue; + } + + let skipInstallation = false; + + // Allow to use `./path/to/webpack.js` outside `node_modules` + if (dependency === WEBPACK_PACKAGE && fs.existsSync(WEBPACK_PACKAGE)) { + skipInstallation = true; + } + + // Allow to use `./path/to/webpack-dev-server.js` outside `node_modules` + if (dependency === WEBPACK_DEV_SERVER_PACKAGE && fs.existsSync(WEBPACK_PACKAGE)) { + skipInstallation = true; + } + + if (skipInstallation) { + continue; + } + + await this.doInstall(dependency, { + preMessage: () => { + this.logger.error( + `For using '${this.colors.green( + commandOptions.name.split(" ")[0], + )}' command you need to install: '${this.colors.green(dependency)}' package.`, + ); + }, + }); + } + } + + if (options) { + if (typeof options === "function") { + if (forHelp && !allDependenciesInstalled && commandOptions.dependencies) { + command.description( + `${ + commandOptions.description + } To see all available options you need to install ${commandOptions.dependencies + .map((dependency) => `'${dependency}'`) + .join(", ")}.`, + ); + options = []; + } else { + options = await options(); + } + } + + options.forEach((optionForCommand) => { + this.makeOption(command, optionForCommand); + }); + } + + command.action(action); + + return command; + } + + makeOption(command: WebpackCLICommand, option: WebpackCLIBuiltInOption) { + let mainOption: WebpackCLIMainOption; + let negativeOption; + + if (option.configs) { + let needNegativeOption = false; + let negatedDescription; + const mainOptionType: WebpackCLIMainOption["type"] = new Set(); + + option.configs.forEach((config) => { + switch (config.type) { + case "reset": + mainOptionType.add(Boolean); + break; + case "boolean": + if (!needNegativeOption) { + needNegativeOption = true; + negatedDescription = config.negatedDescription; + } + + mainOptionType.add(Boolean); + break; + case "number": + mainOptionType.add(Number); + break; + case "string": + case "path": + case "RegExp": + mainOptionType.add(String); + break; + case "enum": { + let hasFalseEnum = false; + + const enumTypes = (config.values || []).map((value) => { + switch (typeof value) { + case "string": + mainOptionType.add(String); + break; + case "number": + mainOptionType.add(Number); + break; + case "boolean": + if (!hasFalseEnum && value === false) { + hasFalseEnum = true; + break; + } + + mainOptionType.add(Boolean); + break; + } + }); + + if (!needNegativeOption) { + needNegativeOption = hasFalseEnum; + negatedDescription = config.negatedDescription; + } + + return enumTypes; + } + } + }); + + mainOption = { + flags: option.alias ? `-${option.alias}, --${option.name}` : `--${option.name}`, + description: option.description || "", + type: mainOptionType, + multiple: option.multiple, + defaultValue: option.defaultValue, + }; + + if (needNegativeOption) { + negativeOption = { + flags: `--no-${option.name}`, + description: + negatedDescription || option.negatedDescription || `Negative '${option.name}' option.`, + }; + } + } else { + mainOption = { + flags: option.alias ? `-${option.alias}, --${option.name}` : `--${option.name}`, + // TODO `describe` used by `webpack-dev-server@3` + description: option.description || option.describe || "", + type: option.type + ? new Set(Array.isArray(option.type) ? option.type : [option.type]) + : new Set([Boolean]), + multiple: option.multiple, + defaultValue: option.defaultValue, + }; + + if (option.negative) { + negativeOption = { + flags: `--no-${option.name}`, + description: option.negatedDescription + ? option.negatedDescription + : `Negative '${option.name}' option.`, + }; + } + } + + if (mainOption.type.size > 1 && mainOption.type.has(Boolean)) { + mainOption.flags = `${mainOption.flags} [value${mainOption.multiple ? "..." : ""}]`; + } else if (mainOption.type.size > 0 && !mainOption.type.has(Boolean)) { + mainOption.flags = `${mainOption.flags} `; + } + + if (mainOption.type.size === 1) { + if (mainOption.type.has(Number)) { + let skipDefault = true; + + const optionForCommand: WebpackCLICommandOption = new Option( + mainOption.flags, + mainOption.description, + ) + .argParser((value: string, prev = []) => { + if (mainOption.defaultValue && mainOption.multiple && skipDefault) { + prev = []; + skipDefault = false; + } + + return mainOption.multiple + ? ([] as number[]).concat(prev).concat(Number(value)) + : Number(value); + }) + .default(mainOption.defaultValue); + + optionForCommand.helpLevel = option.helpLevel; + + command.addOption(optionForCommand); + } else if (mainOption.type.has(String)) { + let skipDefault = true; + + const optionForCommand: WebpackCLICommandOption = new Option( + mainOption.flags, + mainOption.description, + ) + .argParser((value: string, prev = []) => { + if (mainOption.defaultValue && mainOption.multiple && skipDefault) { + prev = []; + skipDefault = false; + } + + return mainOption.multiple ? ([] as string[]).concat(prev).concat(value) : value; + }) + .default(mainOption.defaultValue); + + optionForCommand.helpLevel = option.helpLevel; + + command.addOption(optionForCommand); + } else if (mainOption.type.has(Boolean)) { + const optionForCommand = new Option(mainOption.flags, mainOption.description).default( + mainOption.defaultValue, + ); + + optionForCommand.helpLevel = option.helpLevel; + + command.addOption(optionForCommand); + } else { + const optionForCommand = new Option(mainOption.flags, mainOption.description) + .argParser(Array.from(mainOption.type)[0]) + .default(mainOption.defaultValue); + + optionForCommand.helpLevel = option.helpLevel; + + command.addOption(optionForCommand); + } + } else if (mainOption.type.size > 1) { + let skipDefault = true; + + const optionForCommand = new Option( + mainOption.flags, + mainOption.description, + mainOption.defaultValue, + ) + .argParser((value: string, prev = []) => { + if (mainOption.defaultValue && mainOption.multiple && skipDefault) { + prev = []; + skipDefault = false; + } + + if (mainOption.type.has(Number)) { + const numberValue = Number(value); + + if (!isNaN(numberValue)) { + return mainOption.multiple + ? ([] as number[]).concat(prev).concat(numberValue) + : numberValue; + } + } + + if (mainOption.type.has(String)) { + return mainOption.multiple ? ([] as string[]).concat(prev).concat(value) : value; + } + + return value; + }) + .default(mainOption.defaultValue); + + optionForCommand.helpLevel = option.helpLevel; + + command.addOption(optionForCommand); + } else if (mainOption.type.size === 0 && negativeOption) { + const optionForCommand = new Option(mainOption.flags, mainOption.description); + + // Hide stub option + optionForCommand.hideHelp(); + optionForCommand.helpLevel = option.helpLevel; + + command.addOption(optionForCommand); + } + + if (negativeOption) { + const optionForCommand = new Option(negativeOption.flags, negativeOption.description); + + optionForCommand.helpLevel = option.helpLevel; + + command.addOption(optionForCommand); + } + } + + getBuiltInOptions(): WebpackCLIBuiltInOption[] { + if (this.builtInOptionsCache) { + return this.builtInOptionsCache; + } + + const minimumHelpFlags = [ + "config", + "config-name", + "merge", + "env", + "mode", + "watch", + "watch-options-stdin", + "stats", + "devtool", + "entry", + "target", + "progress", + "json", + "name", + "output-path", + "node-env", + ]; + + const builtInFlags: WebpackCLIBuiltInFlag[] = [ + // For configs + { + name: "config", + alias: "c", + configs: [ + { + type: "string", + }, + ], + multiple: true, + description: "Provide path to a webpack configuration file e.g. ./webpack.config.js.", + }, + { + name: "config-name", + configs: [ + { + type: "string", + }, + ], + multiple: true, + description: "Name of the configuration to use.", + }, + { + name: "merge", + alias: "m", + configs: [ + { + type: "enum", + values: [true], + }, + ], + description: "Merge two or more configurations using 'webpack-merge'.", + }, + // Complex configs + { + name: "env", + type: ( + value: string, + previous: Record = {}, + ): Record => { + // for https://github.com/webpack/webpack-cli/issues/2642 + if (value.endsWith("=")) { + value.concat('""'); + } + + // This ensures we're only splitting by the first `=` + const [allKeys, val] = value.split(/=(.+)/, 2); + const splitKeys = allKeys.split(/\.(?!$)/); + + let prevRef = previous; + + splitKeys.forEach((someKey, index) => { + if (!prevRef[someKey]) { + prevRef[someKey] = {}; + } + + if (typeof prevRef[someKey] === "string") { + prevRef[someKey] = {}; + } + + if (index === splitKeys.length - 1) { + if (typeof val === "string") { + prevRef[someKey] = val; + } else { + prevRef[someKey] = true; + } + } + + prevRef = prevRef[someKey] as Record; + }); + + return previous; + }, + multiple: true, + description: "Environment passed to the configuration when it is a function.", + }, + { + name: "node-env", + configs: [ + { + type: "string", + }, + ], + multiple: false, + description: "Sets process.env.NODE_ENV to the specified value.", + }, + + // Adding more plugins + { + name: "hot", + alias: "h", + configs: [ + { + type: "string", + }, + { + type: "boolean", + }, + ], + negative: true, + description: "Enables Hot Module Replacement", + negatedDescription: "Disables Hot Module Replacement.", + }, + { + name: "analyze", + configs: [ + { + type: "enum", + values: [true], + }, + ], + multiple: false, + description: "It invokes webpack-bundle-analyzer plugin to get bundle information.", + }, + { + name: "progress", + configs: [ + { + type: "string", + }, + { + type: "enum", + values: [true], + }, + ], + description: "Print compilation progress during build.", + }, + { + name: "prefetch", + configs: [ + { + type: "string", + }, + ], + description: "Prefetch this request.", + }, + + // Output options + { + name: "json", + configs: [ + { + type: "string", + }, + { + type: "enum", + values: [true], + }, + ], + alias: "j", + description: "Prints result as JSON or store it in a file.", + }, + + // For webpack@4 + { + name: "entry", + configs: [ + { + type: "string", + }, + ], + multiple: true, + description: "The entry point(s) of your application e.g. ./src/main.js.", + }, + { + name: "output-path", + alias: "o", + configs: [ + { + type: "string", + }, + ], + description: "Output location of the file generated by webpack e.g. ./dist/.", + }, + { + name: "target", + alias: "t", + configs: [ + { + type: "string", + }, + ], + multiple: this.webpack.cli !== undefined, + description: "Sets the build target e.g. node.", + }, + { + name: "devtool", + configs: [ + { + type: "string", + }, + { + type: "enum", + values: [false], + }, + ], + negative: true, + alias: "d", + description: "Determine source maps to use.", + negatedDescription: "Do not generate source maps.", + }, + { + name: "mode", + configs: [ + { + type: "string", + }, + ], + description: "Defines the mode to pass to webpack.", + }, + { + name: "name", + configs: [ + { + type: "string", + }, + ], + description: "Name of the configuration. Used when loading multiple configurations.", + }, + { + name: "stats", + configs: [ + { + type: "string", + }, + { + type: "boolean", + }, + ], + negative: true, + description: "It instructs webpack on how to treat the stats e.g. verbose.", + negatedDescription: "Disable stats output.", + }, + { + name: "watch", + configs: [ + { + type: "boolean", + }, + ], + negative: true, + alias: "w", + description: "Watch for files changes.", + negatedDescription: "Do not watch for file changes.", + }, + { + name: "watch-options-stdin", + configs: [ + { + type: "boolean", + }, + ], + negative: true, + description: "Stop watching when stdin stream has ended.", + negatedDescription: "Do not stop watching when stdin stream has ended.", + }, + ]; + + // Extract all the flags being exported from core. + // A list of cli flags generated by core can be found here https://github.com/webpack/webpack/blob/master/test/__snapshots__/Cli.test.js.snap + const coreFlags = this.webpack.cli + ? Object.entries(this.webpack.cli.getArguments()).map(([flag, meta]) => { + const inBuiltIn = builtInFlags.find((builtInFlag) => builtInFlag.name === flag); + + if (inBuiltIn) { + return { + ...meta, + // @ts-expect-error this might be overwritten + name: flag, + group: "core", + ...inBuiltIn, + configs: meta.configs || [], + }; + } + + return { ...meta, name: flag, group: "core" }; + }) + : []; + + const options: WebpackCLIBuiltInOption[] = ([] as WebpackCLIBuiltInFlag[]) + .concat( + builtInFlags.filter( + (builtInFlag) => !coreFlags.find((coreFlag) => builtInFlag.name === coreFlag.name), + ), + ) + .concat(coreFlags) + .map((option): WebpackCLIBuiltInOption => { + (option as WebpackCLIBuiltInOption).helpLevel = minimumHelpFlags.includes(option.name) + ? "minimum" + : "verbose"; + return option as WebpackCLIBuiltInOption; + }); + + this.builtInOptionsCache = options; + + return options; + } + + async loadWebpack(handleError = true) { + return this.tryRequireThenImport(WEBPACK_PACKAGE, handleError); + } + + async run(args: Parameters[0], parseOptions: ParseOptions) { + // Built-in internal commands + const buildCommandOptions = { + name: "build [entries...]", + alias: ["bundle", "b"], + description: "Run webpack (default command, can be omitted).", + usage: "[entries...] [options]", + dependencies: [WEBPACK_PACKAGE], + }; + const watchCommandOptions = { + name: "watch [entries...]", + alias: "w", + description: "Run webpack and watch for files changes.", + usage: "[entries...] [options]", + dependencies: [WEBPACK_PACKAGE], + }; + const versionCommandOptions = { + name: "version [commands...]", + alias: "v", + description: + "Output the version number of 'webpack', 'webpack-cli' and 'webpack-dev-server' and commands.", + }; + const helpCommandOptions = { + name: "help [command] [option]", + alias: "h", + description: "Display help for commands and options.", + }; + // Built-in external commands + const externalBuiltInCommandsInfo: WebpackCLIExternalCommandInfo[] = [ + { + name: "serve [entries...]", + alias: ["server", "s"], + pkg: "@webpack-cli/serve", + }, + { + name: "info", + alias: "i", + pkg: "@webpack-cli/info", + }, + { + name: "init", + alias: ["create", "new", "c", "n"], + pkg: "@webpack-cli/generators", + }, + { + name: "loader", + alias: "l", + pkg: "@webpack-cli/generators", + }, + { + name: "plugin", + alias: "p", + pkg: "@webpack-cli/generators", + }, + { + name: "migrate", + alias: "m", + pkg: "@webpack-cli/migrate", + }, + { + name: "configtest [config-path]", + alias: "t", + pkg: "@webpack-cli/configtest", + }, + ]; + + const knownCommands = [ + buildCommandOptions, + watchCommandOptions, + versionCommandOptions, + helpCommandOptions, + ...externalBuiltInCommandsInfo, + ]; + const getCommandName = (name: string) => name.split(" ")[0]; + const isKnownCommand = (name: string) => + knownCommands.find( + (command) => + getCommandName(command.name) === name || + (Array.isArray(command.alias) ? command.alias.includes(name) : command.alias === name), + ); + const isCommand = (input: string, commandOptions: WebpackCLIOptions) => { + const longName = getCommandName(commandOptions.name); + + if (input === longName) { + return true; + } + + if (commandOptions.alias) { + if (Array.isArray(commandOptions.alias)) { + return commandOptions.alias.includes(input); + } else { + return commandOptions.alias === input; + } + } + + return false; + }; + const findCommandByName = (name: string) => + this.program.commands.find( + (command) => name === command.name() || command.aliases().includes(name), + ); + const isOption = (value: string): boolean => value.startsWith("-"); + const isGlobalOption = (value: string) => + value === "--color" || + value === "--no-color" || + value === "-v" || + value === "--version" || + value === "-h" || + value === "--help"; + + const loadCommandByName = async ( + commandName: WebpackCLIExternalCommandInfo["name"], + allowToInstall = false, + ) => { + const isBuildCommandUsed = isCommand(commandName, buildCommandOptions); + const isWatchCommandUsed = isCommand(commandName, watchCommandOptions); + + if (isBuildCommandUsed || isWatchCommandUsed) { + await this.makeCommand( + isBuildCommandUsed ? buildCommandOptions : watchCommandOptions, + async () => { + this.webpack = await this.loadWebpack(); + + return isWatchCommandUsed + ? this.getBuiltInOptions().filter((option) => option.name !== "watch") + : this.getBuiltInOptions(); + }, + async (entries, options) => { + if (entries.length > 0) { + options.entry = [...entries, ...(options.entry || [])]; + } + + await this.runWebpack(options, isWatchCommandUsed); + }, + ); + } else if (isCommand(commandName, helpCommandOptions)) { + // Stub for the `help` command + // eslint-disable-next-line @typescript-eslint/no-empty-function + this.makeCommand(helpCommandOptions, [], () => {}); + } else if (isCommand(commandName, versionCommandOptions)) { + // Stub for the `version` command + // eslint-disable-next-line @typescript-eslint/no-empty-function + this.makeCommand(versionCommandOptions, [], () => {}); + } else { + const builtInExternalCommandInfo = externalBuiltInCommandsInfo.find( + (externalBuiltInCommandInfo) => + getCommandName(externalBuiltInCommandInfo.name) === commandName || + (Array.isArray(externalBuiltInCommandInfo.alias) + ? externalBuiltInCommandInfo.alias.includes(commandName) + : externalBuiltInCommandInfo.alias === commandName), + ); + + let pkg: string; + + if (builtInExternalCommandInfo) { + ({ pkg } = builtInExternalCommandInfo); + } else { + pkg = commandName; + } + + if (pkg !== "webpack-cli" && !this.checkPackageExists(pkg)) { + if (!allowToInstall) { + return; + } + + pkg = await this.doInstall(pkg, { + preMessage: () => { + this.logger.error( + `For using this command you need to install: '${this.colors.green(pkg)}' package.`, + ); + }, + }); + } + + let loadedCommand; + + try { + loadedCommand = await this.tryRequireThenImport void>>(pkg, false); + } catch (error) { + // Ignore, command is not installed + + return; + } + + let command; + + try { + command = new loadedCommand(); + + await command.apply(this); + } catch (error) { + this.logger.error(`Unable to load '${pkg}' command`); + this.logger.error(error); + process.exit(2); + } + } + }; + + // Register own exit + this.program.exitOverride(async (error) => { + if (error.exitCode === 0) { + process.exit(0); + } + + if (error.code === "executeSubCommandAsync") { + process.exit(2); + } + + if (error.code === "commander.help") { + process.exit(0); + } + + if (error.code === "commander.unknownOption") { + let name = error.message.match(/'(.+)'/) as string | null; + + if (name) { + name = name[1].substr(2); + + if (name.includes("=")) { + name = name.split("=")[0]; + } + + const { operands } = this.program.parseOptions(this.program.args); + const operand = + typeof operands[0] !== "undefined" + ? operands[0] + : getCommandName(buildCommandOptions.name); + + if (operand) { + const command = findCommandByName(operand); + + if (!command) { + this.logger.error(`Can't find and load command '${operand}'`); + this.logger.error("Run 'webpack --help' to see available commands and options"); + process.exit(2); + } + + const levenshtein = require("fastest-levenshtein"); + + (command as WebpackCLICommand).options.forEach((option) => { + if (!option.hidden && levenshtein.distance(name, option.long?.slice(2)) < 3) { + this.logger.error(`Did you mean '--${option.name()}'?`); + } + }); + } + } + } + + // Codes: + // - commander.unknownCommand + // - commander.missingArgument + // - commander.missingMandatoryOptionValue + // - commander.optionMissingArgument + + this.logger.error("Run 'webpack --help' to see available commands and options"); + process.exit(2); + }); + + // Default `--color` and `--no-color` options + // eslint-disable-next-line @typescript-eslint/no-this-alias + const cli: IWebpackCLI = this; + this.program.option("--color", "Enable colors on console."); + this.program.on("option:color", function () { + // @ts-expect-error shadowing 'this' is intended + const { color } = this.opts(); + + cli.isColorSupportChanged = color; + cli.colors = cli.createColors(color); + }); + this.program.option("--no-color", "Disable colors on console."); + this.program.on("option:no-color", function () { + // @ts-expect-error shadowing 'this' is intended + const { color } = this.opts(); + + cli.isColorSupportChanged = color; + cli.colors = cli.createColors(color); + }); + + // Make `-v, --version` options + // Make `version|v [commands...]` command + const outputVersion = async (options: string[]) => { + // Filter `bundle`, `watch`, `version` and `help` commands + const possibleCommandNames = options.filter( + (option) => + !isCommand(option, buildCommandOptions) && + !isCommand(option, watchCommandOptions) && + !isCommand(option, versionCommandOptions) && + !isCommand(option, helpCommandOptions), + ); + + possibleCommandNames.forEach((possibleCommandName) => { + if (!isOption(possibleCommandName)) { + return; + } + + this.logger.error(`Unknown option '${possibleCommandName}'`); + this.logger.error("Run 'webpack --help' to see available commands and options"); + process.exit(2); + }); + + if (possibleCommandNames.length > 0) { + await Promise.all( + possibleCommandNames.map((possibleCommand) => loadCommandByName(possibleCommand)), + ); + + for (const possibleCommandName of possibleCommandNames) { + const foundCommand = findCommandByName(possibleCommandName) as WebpackCLICommand; + + if (!foundCommand) { + this.logger.error(`Unknown command '${possibleCommandName}'`); + this.logger.error("Run 'webpack --help' to see available commands and options"); + process.exit(2); + } + + try { + const { name, version } = this.loadJSONFile( + `${foundCommand.pkg}/package.json`, + ); + + this.logger.raw(`${name} ${version}`); + } catch (e) { + this.logger.error(`Error: External package '${foundCommand.pkg}' not found`); + process.exit(2); + } + } + } + + let webpack; + + try { + webpack = await this.loadWebpack(false); + } catch (_error) { + // Nothing + } + + this.logger.raw(`webpack: ${webpack ? webpack.version : "not installed"}`); + + const pkgJSON = this.loadJSONFile("../package.json"); + + this.logger.raw(`webpack-cli: ${pkgJSON.version}`); + + let devServer; + + try { + devServer = await this.loadJSONFile( + "webpack-dev-server/package.json", + false, + ); + } catch (_error) { + // Nothing + } + + this.logger.raw(`webpack-dev-server ${devServer ? devServer.version : "not installed"}`); + + process.exit(0); + }; + this.program.option( + "-v, --version", + "Output the version number of 'webpack', 'webpack-cli' and 'webpack-dev-server' and commands.", + ); + + const outputHelp = async ( + options: string[], + isVerbose: boolean, + isHelpCommandSyntax: boolean, + program: WebpackCLICommand, + ) => { + const { bold } = this.colors; + const outputIncorrectUsageOfHelp = () => { + this.logger.error("Incorrect use of help"); + this.logger.error( + "Please use: 'webpack help [command] [option]' | 'webpack [command] --help'", + ); + this.logger.error("Run 'webpack --help' to see available commands and options"); + process.exit(2); + }; + + const isGlobalHelp = options.length === 0; + const isCommandHelp = options.length === 1 && !isOption(options[0]); + + if (isGlobalHelp || isCommandHelp) { + program.configureHelp({ + sortSubcommands: true, + // Support multiple aliases + commandUsage: (command: WebpackCLICommand) => { + let parentCmdNames = ""; + + for (let parentCmd = command.parent; parentCmd; parentCmd = parentCmd.parent) { + parentCmdNames = `${parentCmd.name()} ${parentCmdNames}`; + } + + if (isGlobalHelp) { + return `${parentCmdNames}${command.usage()}\n${bold( + "Alternative usage to run commands:", + )} ${parentCmdNames}[command] [options]`; + } + + return `${parentCmdNames}${command.name()}|${command + .aliases() + .join("|")} ${command.usage()}`; + }, + // Support multiple aliases + subcommandTerm: (command: WebpackCLICommand) => { + const humanReadableArgumentName = (argument: WebpackCLICommandOption) => { + const nameOutput = argument.name + (argument.variadic === true ? "..." : ""); + + return argument.required ? "<" + nameOutput + ">" : "[" + nameOutput + "]"; + }; + const args = command._args + .map((arg: WebpackCLICommandOption) => humanReadableArgumentName(arg)) + .join(" "); + + return `${command.name()}|${command.aliases().join("|")}${args ? ` ${args}` : ""}${ + command.options.length > 0 ? " [options]" : "" + }`; + }, + visibleOptions: function visibleOptions( + command: WebpackCLICommand, + ): WebpackCLICommandOption[] { + return command.options.filter((option: WebpackCLICommandOption) => { + if (option.hidden) { + return false; + } + + switch (option.helpLevel) { + case "verbose": + return isVerbose; + case "minimum": + default: + return true; + } + }); + }, + padWidth(command: WebpackCLICommand, helper: Help) { + return Math.max( + helper.longestArgumentTermLength(command, helper), + helper.longestOptionTermLength(command, helper), + // For global options + helper.longestOptionTermLength(program, helper), + helper.longestSubcommandTermLength(isGlobalHelp ? program : command, helper), + ); + }, + formatHelp: (command: WebpackCLICommand, helper: Help) => { + const termWidth = helper.padWidth(command, helper); + const helpWidth = + helper.helpWidth || (process.env.WEBPACK_CLI_HELP_WIDTH as unknown as number) || 80; + const itemIndentWidth = 2; + const itemSeparatorWidth = 2; // between term and description + + const formatItem = (term: string, description: string) => { + if (description) { + const fullText = `${term.padEnd(termWidth + itemSeparatorWidth)}${description}`; + + return helper.wrap( + fullText, + helpWidth - itemIndentWidth, + termWidth + itemSeparatorWidth, + ); + } + + return term; + }; + + const formatList = (textArray: string[]) => + textArray.join("\n").replace(/^/gm, " ".repeat(itemIndentWidth)); + + // Usage + let output = [`${bold("Usage:")} ${helper.commandUsage(command)}`, ""]; + + // Description + const commandDescription = isGlobalHelp + ? "The build tool for modern web applications." + : helper.commandDescription(command); + + if (commandDescription.length > 0) { + output = output.concat([commandDescription, ""]); + } + + // Arguments + const argumentList = helper + .visibleArguments(command) + .map((argument) => formatItem(argument.term, argument.description)); + + if (argumentList.length > 0) { + output = output.concat([bold("Arguments:"), formatList(argumentList), ""]); + } + + // Options + const optionList = helper + .visibleOptions(command) + .map((option) => + formatItem(helper.optionTerm(option), helper.optionDescription(option)), + ); + + if (optionList.length > 0) { + output = output.concat([bold("Options:"), formatList(optionList), ""]); + } + + // Global options + const globalOptionList = program.options.map((option: WebpackCLICommandOption) => + formatItem(helper.optionTerm(option), helper.optionDescription(option)), + ); + + if (globalOptionList.length > 0) { + output = output.concat([bold("Global options:"), formatList(globalOptionList), ""]); + } + + // Commands + const commandList = helper + .visibleCommands(isGlobalHelp ? program : command) + .map((command) => + formatItem(helper.subcommandTerm(command), helper.subcommandDescription(command)), + ); + + if (commandList.length > 0) { + output = output.concat([bold("Commands:"), formatList(commandList), ""]); + } + + return output.join("\n"); + }, + }); + + if (isGlobalHelp) { + await Promise.all( + knownCommands.map((knownCommand) => { + return loadCommandByName(getCommandName(knownCommand.name)); + }), + ); + + const buildCommand = findCommandByName(getCommandName(buildCommandOptions.name)); + + buildCommand && this.logger.raw(buildCommand.helpInformation()); + } else { + const name = options[0]; + + await loadCommandByName(name); + + const command = findCommandByName(name); + + if (!command) { + const builtInCommandUsed = externalBuiltInCommandsInfo.find( + (command) => command.name.includes(name) || name === command.alias, + ); + if (typeof builtInCommandUsed !== "undefined") { + this.logger.error( + `For using '${name}' command you need to install '${builtInCommandUsed.pkg}' package.`, + ); + } else { + this.logger.error(`Can't find and load command '${name}'`); + this.logger.error("Run 'webpack --help' to see available commands and options."); + } + process.exit(2); + } + + this.logger.raw(command.helpInformation()); + } + } else if (isHelpCommandSyntax) { + let isCommandSpecified = false; + let commandName = getCommandName(buildCommandOptions.name); + let optionName = ""; + + if (options.length === 1) { + optionName = options[0]; + } else if (options.length === 2) { + isCommandSpecified = true; + commandName = options[0]; + optionName = options[1]; + + if (isOption(commandName)) { + outputIncorrectUsageOfHelp(); + } + } else { + outputIncorrectUsageOfHelp(); + } + + await loadCommandByName(commandName); + + const command = isGlobalOption(optionName) ? program : findCommandByName(commandName); + + if (!command) { + this.logger.error(`Can't find and load command '${commandName}'`); + this.logger.error("Run 'webpack --help' to see available commands and options"); + process.exit(2); + } + + const option = (command as WebpackCLICommand).options.find( + (option) => option.short === optionName || option.long === optionName, + ); + + if (!option) { + this.logger.error(`Unknown option '${optionName}'`); + this.logger.error("Run 'webpack --help' to see available commands and options"); + process.exit(2); + } + + const nameOutput = + option.flags.replace(/^.+[[<]/, "").replace(/(\.\.\.)?[\]>].*$/, "") + + (option.variadic === true ? "..." : ""); + const value = option.required + ? "<" + nameOutput + ">" + : option.optional + ? "[" + nameOutput + "]" + : ""; + + this.logger.raw( + `${bold("Usage")}: webpack${isCommandSpecified ? ` ${commandName}` : ""} ${option.long}${ + value ? ` ${value}` : "" + }`, + ); + + if (option.short) { + this.logger.raw( + `${bold("Short:")} webpack${isCommandSpecified ? ` ${commandName}` : ""} ${ + option.short + }${value ? ` ${value}` : ""}`, + ); + } + + if (option.description) { + this.logger.raw(`${bold("Description:")} ${option.description}`); + } + + if (!option.negate && option.defaultValue) { + this.logger.raw(`${bold("Default value:")} ${JSON.stringify(option.defaultValue)}`); + } + + const flag = this.getBuiltInOptions().find((flag) => option.long === `--${flag.name}`); + + if (flag && flag.configs) { + const possibleValues = flag.configs.reduce((accumulator, currentValue) => { + if (currentValue.values) { + return accumulator.concat(currentValue.values); + } else { + return accumulator; + } + }, []); + + if (possibleValues.length > 0) { + this.logger.raw( + `${bold("Possible values:")} ${JSON.stringify(possibleValues.join(" | "))}`, + ); + } + } + + this.logger.raw(""); + + // TODO implement this after refactor cli arguments + // logger.raw('Documentation: https://webpack.js.org/option/name/'); + } else { + outputIncorrectUsageOfHelp(); + } + + this.logger.raw( + "To see list of all supported commands and options run 'webpack --help=verbose'.\n", + ); + this.logger.raw(`${bold("Webpack documentation:")} https://webpack.js.org/.`); + this.logger.raw(`${bold("CLI documentation:")} https://webpack.js.org/api/cli/.`); + this.logger.raw(`${bold("Made with ♥ by the webpack team")}.`); + process.exit(0); + }; + this.program.helpOption(false); + this.program.addHelpCommand(false); + this.program.option("-h, --help [verbose]", "Display help for commands and options."); + + let isInternalActionCalled = false; + + // Default action + this.program.usage("[options]"); + this.program.allowUnknownOption(true); + this.program.action(async (options, program: WebpackCLICommand) => { + if (!isInternalActionCalled) { + isInternalActionCalled = true; + } else { + this.logger.error("No commands found to run"); + process.exit(2); + } + + // Command and options + const { operands, unknown } = this.program.parseOptions(program.args); + const defaultCommandToRun = getCommandName(buildCommandOptions.name); + const hasOperand = typeof operands[0] !== "undefined"; + const operand = hasOperand ? operands[0] : defaultCommandToRun; + const isHelpOption = typeof options.help !== "undefined"; + const isHelpCommandSyntax = isCommand(operand, helpCommandOptions); + + if (isHelpOption || isHelpCommandSyntax) { + let isVerbose = false; + + if (isHelpOption) { + if (typeof options.help === "string") { + if (options.help !== "verbose") { + this.logger.error("Unknown value for '--help' option, please use '--help=verbose'"); + process.exit(2); + } + + isVerbose = true; + } + } + + this.program.forHelp = true; + + const optionsForHelp = ([] as string[]) + .concat(isHelpOption && hasOperand ? [operand] : []) + // Syntax `webpack help [command]` + .concat(operands.slice(1)) + // Syntax `webpack help [option]` + .concat(unknown) + .concat( + isHelpCommandSyntax && typeof options.color !== "undefined" + ? [options.color ? "--color" : "--no-color"] + : [], + ) + .concat( + isHelpCommandSyntax && typeof options.version !== "undefined" ? ["--version"] : [], + ); + + await outputHelp(optionsForHelp, isVerbose, isHelpCommandSyntax, program); + } + + const isVersionOption = typeof options.version !== "undefined"; + const isVersionCommandSyntax = isCommand(operand, versionCommandOptions); + + if (isVersionOption || isVersionCommandSyntax) { + const optionsForVersion = ([] as string[]) + .concat(isVersionOption ? [operand] : []) + .concat(operands.slice(1)) + .concat(unknown); + + await outputVersion(optionsForVersion); + } + + let commandToRun = operand; + let commandOperands = operands.slice(1); + + if (isKnownCommand(commandToRun)) { + await loadCommandByName(commandToRun, true); + } else { + const isEntrySyntax = fs.existsSync(operand); + + if (isEntrySyntax) { + commandToRun = defaultCommandToRun; + commandOperands = operands; + + await loadCommandByName(commandToRun); + } else { + this.logger.error(`Unknown command or entry '${operand}'`); + + const levenshtein = require("fastest-levenshtein"); + const found = knownCommands.find( + (commandOptions) => + levenshtein.distance(operand, getCommandName(commandOptions.name)) < 3, + ); + + if (found) { + this.logger.error( + `Did you mean '${getCommandName(found.name)}' (alias '${ + Array.isArray(found.alias) ? found.alias.join(", ") : found.alias + }')?`, + ); + } + + this.logger.error("Run 'webpack --help' to see available commands and options"); + process.exit(2); + } + } + + await this.program.parseAsync([commandToRun, ...commandOperands, ...unknown], { + from: "user", + }); + }); + + await this.program.parseAsync(args, parseOptions); + } + + async loadConfig(options: Partial) { + const interpret = require("interpret"); + const loadConfigByPath = async (configPath: string, argv: Argv = {}) => { + const ext = path.extname(configPath); + const interpreted = Object.keys(interpret.jsVariants).find((variant) => variant === ext); + + if (interpreted) { + const rechoir: Rechoir = require("rechoir"); + + try { + rechoir.prepare(interpret.extensions, configPath); + } catch (error) { + if ((error as RechoirError)?.failures) { + this.logger.error(`Unable load '${configPath}'`); + this.logger.error((error as RechoirError).message); + (error as RechoirError).failures.forEach((failure) => { + this.logger.error(failure.error.message); + }); + this.logger.error("Please install one of them"); + process.exit(2); + } + + this.logger.error(error); + process.exit(2); + } + } + + let options: ConfigOptions | ConfigOptions[]; + + type LoadConfigOption = PotentialPromise; + + try { + options = await this.tryRequireThenImport( + configPath, + false, + ); + // @ts-expect-error error type assertion + } catch (error: Error) { + this.logger.error(`Failed to load '${configPath}' config`); + + if (this.isValidationError(error)) { + this.logger.error(error.message); + } else { + this.logger.error(error); + } + + process.exit(2); + } + + if (Array.isArray(options)) { + // reassign the value to assert type + const optionsArray: ConfigOptions[] = options; + await Promise.all( + optionsArray.map(async (_, i) => { + if ( + this.isPromise( + optionsArray[i] as Promise, + ) + ) { + optionsArray[i] = await optionsArray[i]; + } + // `Promise` may return `Function` + if (this.isFunction(optionsArray[i])) { + // when config is a function, pass the env from args to the config function + optionsArray[i] = await (optionsArray[i] as CallableOption)(argv.env, argv); + } + }), + ); + options = optionsArray; + } else { + if (this.isPromise(options as Promise)) { + options = await options; + } + + // `Promise` may return `Function` + if (this.isFunction(options)) { + // when config is a function, pass the env from args to the config function + options = await options(argv.env, argv); + } + } + + const isObject = (value: unknown): value is object => + typeof value === "object" && value !== null; + + if (!isObject(options) && !Array.isArray(options)) { + this.logger.error(`Invalid configuration in '${configPath}'`); + + process.exit(2); + } + + return { options, path: configPath }; + }; + + const config: WebpackCLIConfig = { + options: {} as WebpackConfiguration, + path: new WeakMap(), + }; + + if (options.config && options.config.length > 0) { + const loadedConfigs = await Promise.all( + options.config.map((configPath: string) => + loadConfigByPath(path.resolve(configPath), options.argv), + ), + ); + + config.options = []; + + loadedConfigs.forEach((loadedConfig) => { + const isArray = Array.isArray(loadedConfig.options); + + // TODO we should run webpack multiple times when the `--config` options have multiple values with `--merge`, need to solve for the next major release + if ((config.options as ConfigOptions[]).length === 0) { + config.options = loadedConfig.options as WebpackConfiguration; + } else { + if (!Array.isArray(config.options)) { + config.options = [config.options]; + } + + if (isArray) { + (loadedConfig.options as ConfigOptions[]).forEach((item) => { + (config.options as ConfigOptions[]).push(item); + }); + } else { + config.options.push(loadedConfig.options as WebpackConfiguration); + } + } + + if (isArray) { + (loadedConfig.options as ConfigOptions[]).forEach((options) => { + config.path.set(options, loadedConfig.path); + }); + } else { + config.path.set(loadedConfig.options, loadedConfig.path); + } + }); + + config.options = config.options.length === 1 ? config.options[0] : config.options; + } else { + // Order defines the priority, in decreasing order + const defaultConfigFiles = [ + "webpack.config", + ".webpack/webpack.config", + ".webpack/webpackfile", + ] + .map((filename) => + // Since .cjs is not available on interpret side add it manually to default config extension list + [...Object.keys(interpret.extensions), ".cjs"].map((ext) => ({ + path: path.resolve(filename + ext), + ext: ext, + module: interpret.extensions[ext], + })), + ) + .reduce((accumulator, currentValue) => accumulator.concat(currentValue), []); + + let foundDefaultConfigFile; + + for (const defaultConfigFile of defaultConfigFiles) { + if (!fs.existsSync(defaultConfigFile.path)) { + continue; + } + + foundDefaultConfigFile = defaultConfigFile; + break; + } + + if (foundDefaultConfigFile) { + const loadedConfig = await loadConfigByPath(foundDefaultConfigFile.path, options.argv); + + config.options = loadedConfig.options as WebpackConfiguration[]; + + if (Array.isArray(config.options)) { + config.options.forEach((item) => { + config.path.set(item, loadedConfig.path); + }); + } else { + config.path.set(loadedConfig.options, loadedConfig.path); + } + } + } + + if (options.configName) { + const notFoundConfigNames: string[] = []; + + config.options = options.configName.map((configName: string) => { + let found; + + if (Array.isArray(config.options)) { + found = config.options.find((options) => options.name === configName); + } else { + found = config.options.name === configName ? config.options : undefined; + } + + if (!found) { + notFoundConfigNames.push(configName); + } + + return found; + }) as WebpackConfiguration[]; + + if (notFoundConfigNames.length > 0) { + this.logger.error( + notFoundConfigNames + .map((configName) => `Configuration with the name "${configName}" was not found.`) + .join(" "), + ); + process.exit(2); + } + } + + if (options.merge) { + const merge = await this.tryRequireThenImport("webpack-merge"); + + // we can only merge when there are multiple configurations + // either by passing multiple configs by flags or passing a + // single config exporting an array + if (!Array.isArray(config.options) || config.options.length <= 1) { + this.logger.error("At least two configurations are required for merge."); + process.exit(2); + } + + const mergedConfigPaths: string[] = []; + + config.options = config.options.reduce((accumulator: object, options) => { + const configPath = config.path.get(options); + const mergedOptions = merge(accumulator, options); + + mergedConfigPaths.push(configPath as string); + + return mergedOptions; + }, {}); + config.path.set(config.options, mergedConfigPaths as unknown as string); + } + + return config; + } + + async buildConfig( + config: WebpackCLIConfig, + options: Partial, + ): Promise { + const runFunctionOnEachConfig = ( + options: ConfigOptions | ConfigOptions[], + fn: CallableFunction, + ) => { + if (Array.isArray(options)) { + for (let item of options) { + item = fn(item); + } + } else { + options = fn(options); + } + + return options; + }; + + if (options.analyze) { + if (!this.checkPackageExists("webpack-bundle-analyzer")) { + await this.doInstall("webpack-bundle-analyzer", { + preMessage: () => { + this.logger.error( + `It looks like ${this.colors.yellow("webpack-bundle-analyzer")} is not installed.`, + ); + }, + }); + + this.logger.success( + `${this.colors.yellow("webpack-bundle-analyzer")} was installed successfully.`, + ); + } + } + + if (typeof options.progress === "string" && options.progress !== "profile") { + this.logger.error( + `'${options.progress}' is an invalid value for the --progress option. Only 'profile' is allowed.`, + ); + process.exit(2); + } + + if (typeof options.hot === "string" && options.hot !== "only") { + this.logger.error( + `'${options.hot}' is an invalid value for the --hot option. Use 'only' instead.`, + ); + process.exit(2); + } + + const CLIPlugin = await this.tryRequireThenImport< + Instantiable + >("./plugins/CLIPlugin"); + + const internalBuildConfig = (item: WebpackConfiguration) => { + // Output warnings + if ( + item.watch && + options.argv && + options.argv.env && + (options.argv.env["WEBPACK_WATCH"] || options.argv.env["WEBPACK_SERVE"]) + ) { + this.logger.warn( + `No need to use the '${ + options.argv.env["WEBPACK_WATCH"] ? "watch" : "serve" + }' command together with '{ watch: true }' configuration, it does not make sense.`, + ); + + if (options.argv.env["WEBPACK_SERVE"]) { + item.watch = false; + } + } + + // Apply options + if (this.webpack.cli) { + const args: Record = this.getBuiltInOptions() + .filter((flag) => flag.group === "core") + .reduce((accumulator: Record, flag) => { + accumulator[flag.name] = flag as unknown as Argument; + return accumulator; + }, {}); + + const values: ProcessedArguments = Object.keys(options).reduce( + (accumulator: ProcessedArguments, name) => { + if (name === "argv") { + return accumulator; + } + + const kebabName = this.toKebabCase(name); + + if (args[kebabName]) { + accumulator[kebabName] = options[name as keyof typeof options as string]; + } + + return accumulator; + }, + {}, + ); + + const problems: Problem[] | null = this.webpack.cli.processArguments(args, item, values); + + if (problems) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const groupBy = (xs: Record[], key: string) => { + return xs.reduce((rv, x) => { + (rv[x[key]] = rv[x[key]] || []).push(x); + + return rv; + }, {}); + }; + const problemsByPath = groupBy(problems, "path"); + + for (const path in problemsByPath) { + const problems = problemsByPath[path]; + + problems.forEach((problem: Problem) => { + this.logger.error( + `${this.capitalizeFirstLetter(problem.type.replace(/-/g, " "))}${ + problem.value ? ` '${problem.value}'` : "" + } for the '--${problem.argument}' option${ + problem.index ? ` by index '${problem.index}'` : "" + }`, + ); + + if (problem.expected) { + this.logger.error(`Expected: '${problem.expected}'`); + } + }); + } + + process.exit(2); + } + + const isFileSystemCacheOptions = ( + config: WebpackConfiguration, + ): config is FileSystemCacheOptions => { + return ( + Boolean(config.cache) && (config as FileSystemCacheOptions).cache.type === "filesystem" + ); + }; + + // Setup default cache options + if (isFileSystemCacheOptions(item)) { + const configPath = config.path.get(item); + + if (configPath) { + if (!item.cache.buildDependencies) { + item.cache.buildDependencies = {}; + } + + if (!item.cache.buildDependencies.defaultConfig) { + item.cache.buildDependencies.defaultConfig = []; + } + + if (Array.isArray(configPath)) { + configPath.forEach((oneOfConfigPath) => { + ( + item.cache.buildDependencies as NonNullable< + FileSystemCacheOptions["cache"]["buildDependencies"] + > + ).defaultConfig.push(oneOfConfigPath); + }); + } else { + item.cache.buildDependencies.defaultConfig.push(configPath); + } + } + } + } + + // Setup legacy logic for webpack@4 + // TODO respect `--entry-reset` in th next major release + // TODO drop in the next major release + if (options.entry) { + item.entry = options.entry; + } + + if (options.outputPath) { + item.output = { ...item.output, ...{ path: path.resolve(options.outputPath) } }; + } + + if (options.target) { + item.target = options.target; + } + + if (typeof options.devtool !== "undefined") { + item.devtool = options.devtool; + } + + if (options.name) { + item.name = options.name; + } + + if (typeof options.stats !== "undefined") { + item.stats = options.stats; + } + + if (typeof options.watch !== "undefined") { + item.watch = options.watch; + } + + if (typeof options.watchOptionsStdin !== "undefined") { + item.watchOptions = { ...item.watchOptions, ...{ stdin: options.watchOptionsStdin } }; + } + + if (options.mode) { + item.mode = options.mode; + } + + // Respect `process.env.NODE_ENV` + if ( + !item.mode && + process.env && + process.env.NODE_ENV && + (process.env.NODE_ENV === "development" || + process.env.NODE_ENV === "production" || + process.env.NODE_ENV === "none") + ) { + item.mode = process.env.NODE_ENV; + } + + // Setup stats + // TODO remove after drop webpack@4 + const statsForWebpack4 = + this.webpack.Stats && + (this.webpack.Stats as unknown as Partial).presetToOptions; + + if (statsForWebpack4) { + if (typeof item.stats === "undefined") { + item.stats = {}; + } else if (typeof item.stats === "boolean") { + item.stats = (this.webpack.Stats as unknown as WebpackV4LegacyStats).presetToOptions( + item.stats, + ); + } else if ( + typeof item.stats === "string" && + (item.stats === "none" || + item.stats === "verbose" || + item.stats === "detailed" || + item.stats === "normal" || + item.stats === "minimal" || + item.stats === "errors-only" || + item.stats === "errors-warnings") + ) { + item.stats = (this.webpack.Stats as unknown as WebpackV4LegacyStats).presetToOptions( + item.stats, + ); + } + } else { + if (typeof item.stats === "undefined") { + item.stats = { preset: "normal" }; + } else if (typeof item.stats === "boolean") { + item.stats = item.stats ? { preset: "normal" } : { preset: "none" }; + } else if (typeof item.stats === "string") { + item.stats = { preset: item.stats }; + } + } + + let colors; + + // From arguments + if (typeof this.isColorSupportChanged !== "undefined") { + colors = Boolean(this.isColorSupportChanged); + } + // From stats + else if (typeof (item.stats as StatsOptions).colors !== "undefined") { + colors = (item.stats as StatsOptions).colors; + } + // Default + else { + colors = Boolean(this.colors.isColorSupported); + } + + // TODO remove after drop webpack v4 + if (typeof item.stats === "object" && item.stats !== null) { + item.stats.colors = colors; + } + + // Apply CLI plugin + if (!item.plugins) { + item.plugins = []; + } + + item.plugins.unshift( + new CLIPlugin({ + configPath: config.path.get(item), + helpfulOutput: !options.json, + hot: options.hot, + progress: options.progress, + prefetch: options.prefetch, + analyze: options.analyze, + }), + ); + + return options; + }; + + runFunctionOnEachConfig(config.options, internalBuildConfig); + + return config; + } + + isValidationError(error: Error): error is WebpackError { + // https://github.com/webpack/webpack/blob/master/lib/index.js#L267 + // https://github.com/webpack/webpack/blob/v4.44.2/lib/webpack.js#L90 + const ValidationError = + this.webpack.ValidationError || this.webpack.WebpackOptionsValidationError; + + return error instanceof ValidationError || error.name === "ValidationError"; + } + + async createCompiler( + options: Partial, + callback?: Callback<[Error | undefined, WebpackCLIStats | undefined]>, + ): Promise { + if (typeof options.nodeEnv === "string") { + process.env.NODE_ENV = options.nodeEnv; + } + + let config = await this.loadConfig(options); + config = await this.buildConfig(config, options); + + let compiler: WebpackCompiler; + try { + compiler = this.webpack( + config.options as WebpackConfiguration, + callback + ? (error, stats) => { + if (error && this.isValidationError(error)) { + this.logger.error(error.message); + process.exit(2); + } + + callback(error, stats); + } + : callback, + ); + // @ts-expect-error error type assertion + } catch (error: Error) { + if (this.isValidationError(error)) { + this.logger.error(error.message); + } else { + this.logger.error(error); + } + + process.exit(2); + } + + // TODO webpack@4 return Watching and MultiWatching instead Compiler and MultiCompiler, remove this after drop webpack@4 + if (compiler && (compiler as WebpackV4Compiler).compiler) { + compiler = (compiler as WebpackV4Compiler).compiler; + } + + return compiler; + } + + needWatchStdin(compiler: Compiler | MultiCompiler): boolean { + if (this.isMultipleCompiler(compiler)) { + return Boolean( + (compiler as MultiCompiler).compilers.some( + (compiler: Compiler) => + compiler.options.watchOptions && compiler.options.watchOptions.stdin, + ), + ); + } + + return Boolean(compiler.options.watchOptions && compiler.options.watchOptions.stdin); + } + + async runWebpack(options: WebpackRunOptions, isWatchCommand: boolean): Promise { + // eslint-disable-next-line prefer-const + let compiler: Compiler | MultiCompiler; + let createJsonStringifyStream: typeof stringifyStream; + + if (options.json) { + const jsonExt = await this.tryRequireThenImport("@discoveryjs/json-ext"); + + createJsonStringifyStream = jsonExt.stringifyStream; + } + + const callback = (error: Error | undefined, stats: WebpackCLIStats | undefined): void => { + if (error) { + this.logger.error(error); + process.exit(2); + } + + if (stats && stats.hasErrors()) { + process.exitCode = 1; + } + + if (!compiler || !stats) { + return; + } + + const statsOptions = this.isMultipleCompiler(compiler) + ? { + children: compiler.compilers.map((compiler) => + compiler.options ? compiler.options.stats : undefined, + ), + } + : compiler.options + ? compiler.options.stats + : undefined; + + // TODO webpack@4 doesn't support `{ children: [{ colors: true }, { colors: true }] }` for stats + const statsForWebpack4 = + this.webpack.Stats && + (this.webpack.Stats as unknown as WebpackV4LegacyStats).presetToOptions; + + if (this.isMultipleCompiler(compiler) && statsForWebpack4) { + (statsOptions as StatsOptions).colors = ( + statsOptions as MultipleCompilerStatsOptions + ).children.some((child) => child.colors); + } + + if (options.json && createJsonStringifyStream) { + const handleWriteError = (error: WebpackError) => { + this.logger.error(error); + process.exit(2); + }; + + if (options.json === true) { + createJsonStringifyStream(stats.toJson(statsOptions as StatsOptions)) + .on("error", handleWriteError) + .pipe(process.stdout) + .on("error", handleWriteError) + .on("close", () => process.stdout.write("\n")); + } else { + createJsonStringifyStream(stats.toJson(statsOptions as StatsOptions)) + .on("error", handleWriteError) + .pipe(fs.createWriteStream(options.json)) + .on("error", handleWriteError) + // Use stderr to logging + .on("close", () => { + process.stderr.write( + `[webpack-cli] ${this.colors.green( + `stats are successfully stored as json to ${options.json}`, + )}\n`, + ); + }); + } + } else { + const printedStats = stats.toString(statsOptions); + // Avoid extra empty line when `stats: 'none'` + if (printedStats) { + this.logger.raw(printedStats); + } + } + }; + + const env = + isWatchCommand || options.watch + ? { WEBPACK_WATCH: true, ...options.env } + : { WEBPACK_BUNDLE: true, WEBPACK_BUILD: true, ...options.env }; + + options.argv = { ...options, env }; + + if (isWatchCommand) { + options.watch = true; + } + + compiler = await this.createCompiler(options as WebpackDevServerOptions, callback); + + if (!compiler) { + return; + } + + const isWatch = (compiler: WebpackCompiler): boolean => + Boolean( + this.isMultipleCompiler(compiler) + ? compiler.compilers.some((compiler) => compiler.options.watch) + : compiler.options.watch, + ); + + if (isWatch(compiler) && this.needWatchStdin(compiler)) { + process.stdin.on("end", () => { + process.exit(0); + }); + process.stdin.resume(); + } + } +} + +module.exports = WebpackCLI; diff --git a/packages/webpack-cli/tsconfig.json b/packages/webpack-cli/tsconfig.json new file mode 100644 index 00000000000..99cbaf6e6c3 --- /dev/null +++ b/packages/webpack-cli/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./lib", + "rootDir": "./src", + "typeRoots": ["./src/types.ts"] + }, + + "include": ["./src"] +} diff --git a/test/api/CLI.test.js b/test/api/CLI.test.js index faf32be9355..aaffdf26137 100644 --- a/test/api/CLI.test.js +++ b/test/api/CLI.test.js @@ -1,3 +1,4 @@ +// eslint-disable-next-line node/no-unpublished-require const CLI = require("../../packages/webpack-cli/lib/webpack-cli"); describe("CLI API", () => { diff --git a/test/api/capitalizeFirstLetter.test.js b/test/api/capitalizeFirstLetter.test.js index c00a6e45691..5643125789a 100755 --- a/test/api/capitalizeFirstLetter.test.js +++ b/test/api/capitalizeFirstLetter.test.js @@ -1,3 +1,4 @@ +// eslint-disable-next-line node/no-unpublished-require const CLI = require("../../packages/webpack-cli/lib/webpack-cli"); describe("capitalizeFirstLetter", () => { diff --git a/test/api/do-install.test.js b/test/api/do-install.test.js index 5df7cfc0fc8..fab8ac17c3a 100644 --- a/test/api/do-install.test.js +++ b/test/api/do-install.test.js @@ -1,5 +1,6 @@ "use strict"; +// eslint-disable-next-line node/no-unpublished-require const CLI = require("../../packages/webpack-cli/lib/webpack-cli"); // eslint-disable-next-line node/no-unpublished-require diff --git a/test/api/generators/helpers.test.js b/test/api/generators/helpers.test.js index 4dc1b8c87b8..9b5d59bfc70 100644 --- a/test/api/generators/helpers.test.js +++ b/test/api/generators/helpers.test.js @@ -1,4 +1,5 @@ const path = require("path"); +// eslint-disable-next-line node/no-unpublished-require const CLI = require("../../../packages/webpack-cli/lib/webpack-cli"); const utilsDirectory = { diff --git a/test/api/get-default-package-manager.test.js b/test/api/get-default-package-manager.test.js index b1c55bd4a8a..f2866227381 100644 --- a/test/api/get-default-package-manager.test.js +++ b/test/api/get-default-package-manager.test.js @@ -1,5 +1,6 @@ const fs = require("fs"); const path = require("path"); +// eslint-disable-next-line node/no-unpublished-require const CLI = require("../../packages/webpack-cli/lib/webpack-cli"); const syncMock = jest.fn(() => { diff --git a/test/api/resolveConfig/resolveConfig.test.js b/test/api/resolveConfig/resolveConfig.test.js index 60ca2cdf574..364849f3a47 100644 --- a/test/api/resolveConfig/resolveConfig.test.js +++ b/test/api/resolveConfig/resolveConfig.test.js @@ -1,4 +1,5 @@ const { resolve } = require("path"); +// eslint-disable-next-line node/no-unpublished-require const WebpackCLI = require("../../../packages/webpack-cli/lib/webpack-cli"); const config1 = require("./webpack.config1.cjs"); const config2 = require("./webpack.config2.cjs"); diff --git a/tsconfig.json b/tsconfig.json index c732c457e71..8ba5108e005 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -35,6 +35,9 @@ }, { "path": "packages/create-webpack-app" + }, + { + "path": "packages/webpack-cli" } ] } diff --git a/yarn.lock b/yarn.lock index b05b836d6ab..e126e3e415f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -427,83 +427,84 @@ integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== "@commitlint/cli@^16.0.1": - version "16.1.0" - resolved "https://registry.yarnpkg.com/@commitlint/cli/-/cli-16.1.0.tgz#022ad86008374b02974c9f3faf86affb785f4574" - integrity sha512-x5L1knvA3isRWBRVQx+Q6D45pA9139a2aZQYpxkljMG0dj4UHZkCnsYWpnGalxPxASI7nrI0KedKfS2YeQ55cQ== - dependencies: - "@commitlint/format" "^16.0.0" - "@commitlint/lint" "^16.0.0" - "@commitlint/load" "^16.1.0" - "@commitlint/read" "^16.0.0" - "@commitlint/types" "^16.0.0" + version "16.2.1" + resolved "https://registry.yarnpkg.com/@commitlint/cli/-/cli-16.2.1.tgz#ca4e557829a2755f0e1f0cd69b56b83ce2510173" + integrity sha512-zfKf+B9osuiDbxGMJ7bWFv7XFCW8wlQYPtCffNp7Ukdb7mdrep5R9e03vPUZysnwp8NX6hg05kPEvnD/wRIGWw== + dependencies: + "@commitlint/format" "^16.2.1" + "@commitlint/lint" "^16.2.1" + "@commitlint/load" "^16.2.1" + "@commitlint/read" "^16.2.1" + "@commitlint/types" "^16.2.1" lodash "^4.17.19" resolve-from "5.0.0" resolve-global "1.0.0" yargs "^17.0.0" "@commitlint/config-conventional@^16.0.0": - version "16.0.0" - resolved "https://registry.yarnpkg.com/@commitlint/config-conventional/-/config-conventional-16.0.0.tgz#f42d9e1959416b5e691c8b5248fc2402adb1fc03" - integrity sha512-mN7J8KlKFn0kROd+q9PB01sfDx/8K/R25yITspL1No8PB4oj9M1p77xWjP80hPydqZG9OvQq+anXK3ZWeR7s3g== + version "16.2.1" + resolved "https://registry.yarnpkg.com/@commitlint/config-conventional/-/config-conventional-16.2.1.tgz#2cf47b505fb259777c063538c8498d8fd9b47779" + integrity sha512-cP9gArx7gnaj4IqmtCIcHdRjTYdRUi6lmGE+lOzGGjGe45qGOS8nyQQNvkNy2Ey2VqoSWuXXkD8zCUh6EHf1Ww== dependencies: conventional-changelog-conventionalcommits "^4.3.1" -"@commitlint/config-validator@^16.1.0": - version "16.1.0" - resolved "https://registry.yarnpkg.com/@commitlint/config-validator/-/config-validator-16.1.0.tgz#410979f713ed55cbb85504d46295c1fd2419dc4d" - integrity sha512-2cHeZPNTuf1JWbMqyA46MkExor5HMSgv8JrdmzEakUbJHUreh35/wN00FJf57qGs134exQW2thiSQ1IJUsVx2Q== +"@commitlint/config-validator@^16.2.1": + version "16.2.1" + resolved "https://registry.yarnpkg.com/@commitlint/config-validator/-/config-validator-16.2.1.tgz#794e769afd4756e4cf1bfd823b6612932e39c56d" + integrity sha512-hogSe0WGg7CKmp4IfNbdNES3Rq3UEI4XRPB8JL4EPgo/ORq5nrGTVzxJh78omibNuB8Ho4501Czb1Er1MoDWpw== dependencies: - "@commitlint/types" "^16.0.0" + "@commitlint/types" "^16.2.1" ajv "^6.12.6" -"@commitlint/ensure@^16.0.0": - version "16.0.0" - resolved "https://registry.yarnpkg.com/@commitlint/ensure/-/ensure-16.0.0.tgz#fdac1e60a944a1993deb33b5e8454c559abe9866" - integrity sha512-WdMySU8DCTaq3JPf0tZFCKIUhqxaL54mjduNhu8v4D2AMUVIIQKYMGyvXn94k8begeW6iJkTf9cXBArayskE7Q== +"@commitlint/ensure@^16.2.1": + version "16.2.1" + resolved "https://registry.yarnpkg.com/@commitlint/ensure/-/ensure-16.2.1.tgz#0fc538173f95c1eb2694eeedb79cab478347f16f" + integrity sha512-/h+lBTgf1r5fhbDNHOViLuej38i3rZqTQnBTk+xEg+ehOwQDXUuissQ5GsYXXqI5uGy+261ew++sT4EA3uBJ+A== dependencies: - "@commitlint/types" "^16.0.0" + "@commitlint/types" "^16.2.1" lodash "^4.17.19" -"@commitlint/execute-rule@^16.0.0": - version "16.0.0" - resolved "https://registry.yarnpkg.com/@commitlint/execute-rule/-/execute-rule-16.0.0.tgz#824e11ba5b208c214a474ae52a51780d32d31ebc" - integrity sha512-8edcCibmBb386x5JTHSPHINwA5L0xPkHQFY8TAuDEt5QyRZY/o5DF8OPHSa5Hx2xJvGaxxuIz4UtAT6IiRDYkw== +"@commitlint/execute-rule@^16.2.1": + version "16.2.1" + resolved "https://registry.yarnpkg.com/@commitlint/execute-rule/-/execute-rule-16.2.1.tgz#60be73be4b9af97a41546e7ce59fdd33787c65f8" + integrity sha512-oSls82fmUTLM6cl5V3epdVo4gHhbmBFvCvQGHBRdQ50H/690Uq1Dyd7hXMuKITCIdcnr9umyDkr8r5C6HZDF3g== -"@commitlint/format@^16.0.0": - version "16.0.0" - resolved "https://registry.yarnpkg.com/@commitlint/format/-/format-16.0.0.tgz#6a6fb2c1e6460aff63cc6eca30a7807a96b0ce73" - integrity sha512-9yp5NCquXL1jVMKL0ZkRwJf/UHdebvCcMvICuZV00NQGYSAL89O398nhqrqxlbjBhM5EZVq0VGcV5+7r3D4zAA== +"@commitlint/format@^16.2.1": + version "16.2.1" + resolved "https://registry.yarnpkg.com/@commitlint/format/-/format-16.2.1.tgz#6e673f710c799be78e68b2682323e04f75080d07" + integrity sha512-Yyio9bdHWmNDRlEJrxHKglamIk3d6hC0NkEUW6Ti6ipEh2g0BAhy8Od6t4vLhdZRa1I2n+gY13foy+tUgk0i1Q== dependencies: - "@commitlint/types" "^16.0.0" + "@commitlint/types" "^16.2.1" chalk "^4.0.0" -"@commitlint/is-ignored@^16.0.0": - version "16.0.0" - resolved "https://registry.yarnpkg.com/@commitlint/is-ignored/-/is-ignored-16.0.0.tgz#5ab4c4a9c7444c1a8540f50a0f1a907dfd78eb70" - integrity sha512-gmAQcwIGC/R/Lp0CEb2b5bfGC7MT5rPe09N8kOGjO/NcdNmfFSZMquwrvNJsq9hnAP0skRdHIsqwlkENkN4Lag== +"@commitlint/is-ignored@^16.2.1": + version "16.2.1" + resolved "https://registry.yarnpkg.com/@commitlint/is-ignored/-/is-ignored-16.2.1.tgz#cc688ec73a3d204b90f8086821a08814da461e5e" + integrity sha512-exl8HRzTIfb1YvDJp2b2HU5z1BT+9tmgxR2XF0YEzkMiCIuEKh+XLeocPr1VcvAKXv3Cmv5X/OfNRp+i+/HIhQ== dependencies: - "@commitlint/types" "^16.0.0" + "@commitlint/types" "^16.2.1" semver "7.3.5" -"@commitlint/lint@^16.0.0": - version "16.0.0" - resolved "https://registry.yarnpkg.com/@commitlint/lint/-/lint-16.0.0.tgz#87151a935941073027907fd4752a2e3c83cebbfe" - integrity sha512-HNl15bRC0h+pLzbMzQC3tM0j1aESXsLYhElqKnXcf5mnCBkBkHzu6WwJW8rZbfxX+YwJmNljN62cPhmdBo8x0A== - dependencies: - "@commitlint/is-ignored" "^16.0.0" - "@commitlint/parse" "^16.0.0" - "@commitlint/rules" "^16.0.0" - "@commitlint/types" "^16.0.0" - -"@commitlint/load@^16.1.0": - version "16.1.0" - resolved "https://registry.yarnpkg.com/@commitlint/load/-/load-16.1.0.tgz#7a884072ab915611080c5e99a1f1d999c05f4360" - integrity sha512-MtlEhKjP8jAF85jjX4mw8DUUwCxKsCgAc865hhpnwxjrfBcmGP7Up2AFE/M3ZMGDmSl1X1TMybQk/zohj8Cqdg== - dependencies: - "@commitlint/config-validator" "^16.1.0" - "@commitlint/execute-rule" "^16.0.0" - "@commitlint/resolve-extends" "^16.1.0" - "@commitlint/types" "^16.0.0" +"@commitlint/lint@^16.2.1": + version "16.2.1" + resolved "https://registry.yarnpkg.com/@commitlint/lint/-/lint-16.2.1.tgz#c773f082cd4f69cb7807b805b691d2a52c732f97" + integrity sha512-fNINQ3X2ZqsCkNB3Z0Z8ElmhewqrS3gy2wgBTx97BkcjOWiyPAGwDJ752hwrsUnWAVBRztgw826n37xPzxsOgg== + dependencies: + "@commitlint/is-ignored" "^16.2.1" + "@commitlint/parse" "^16.2.1" + "@commitlint/rules" "^16.2.1" + "@commitlint/types" "^16.2.1" + +"@commitlint/load@^16.2.1": + version "16.2.1" + resolved "https://registry.yarnpkg.com/@commitlint/load/-/load-16.2.1.tgz#301bda1bff66b3e40a85819f854eda72538d8e24" + integrity sha512-oSpz0jTyVI/A1AIImxJINTLDOMB8YF7lWGm+Jg5wVWM0r7ucpuhyViVvpSRTgvL0z09oIxlctyFGWUQQpI42uw== + dependencies: + "@commitlint/config-validator" "^16.2.1" + "@commitlint/execute-rule" "^16.2.1" + "@commitlint/resolve-extends" "^16.2.1" + "@commitlint/types" "^16.2.1" + "@types/node" ">=12" chalk "^4.0.0" cosmiconfig "^7.0.0" cosmiconfig-typescript-loader "^1.0.0" @@ -511,69 +512,69 @@ resolve-from "^5.0.0" typescript "^4.4.3" -"@commitlint/message@^16.0.0": - version "16.0.0" - resolved "https://registry.yarnpkg.com/@commitlint/message/-/message-16.0.0.tgz#4a467341fc6bc49e5a3ead005dd6aa36fa856b87" - integrity sha512-CmK2074SH1Ws6kFMEKOKH/7hMekGVbOD6vb4alCOo2+33ZSLUIX8iNkDYyrw38Jwg6yWUhLjyQLUxREeV+QIUA== +"@commitlint/message@^16.2.1": + version "16.2.1" + resolved "https://registry.yarnpkg.com/@commitlint/message/-/message-16.2.1.tgz#bc6a0fa446a746ac2ca78cf372e4cec48daf620d" + integrity sha512-2eWX/47rftViYg7a3axYDdrgwKv32mxbycBJT6OQY/MJM7SUfYNYYvbMFOQFaA4xIVZt7t2Alyqslbl6blVwWw== -"@commitlint/parse@^16.0.0": - version "16.0.0" - resolved "https://registry.yarnpkg.com/@commitlint/parse/-/parse-16.0.0.tgz#5ce05af14edff806effc702ba910fcb32fcb192a" - integrity sha512-F9EjFlMw4MYgBEqoRrWZZKQBzdiJzPBI0qFDFqwUvfQsMmXEREZ242T4R5bFwLINWaALFLHEIa/FXEPa6QxCag== +"@commitlint/parse@^16.2.1": + version "16.2.1" + resolved "https://registry.yarnpkg.com/@commitlint/parse/-/parse-16.2.1.tgz#50b359cb711ec566d2ee236a8e4c6baca07b77c0" + integrity sha512-2NP2dDQNL378VZYioLrgGVZhWdnJO4nAxQl5LXwYb08nEcN+cgxHN1dJV8OLJ5uxlGJtDeR8UZZ1mnQ1gSAD/g== dependencies: - "@commitlint/types" "^16.0.0" + "@commitlint/types" "^16.2.1" conventional-changelog-angular "^5.0.11" conventional-commits-parser "^3.2.2" -"@commitlint/read@^16.0.0": - version "16.0.0" - resolved "https://registry.yarnpkg.com/@commitlint/read/-/read-16.0.0.tgz#92fab45d4e0e4d7d049427306500270b3e459221" - integrity sha512-H4T2zsfmYQK9B+JtoQaCXWBHUhgIJyOzWZjSfuIV9Ce69/OgHoffNpLZPF2lX6yKuDrS1SQFhI/kUCjVc/e4ew== +"@commitlint/read@^16.2.1": + version "16.2.1" + resolved "https://registry.yarnpkg.com/@commitlint/read/-/read-16.2.1.tgz#e0539205d77cdb6879b560f95e5fb251e0c6f562" + integrity sha512-tViXGuaxLTrw2r7PiYMQOFA2fueZxnnt0lkOWqKyxT+n2XdEMGYcI9ID5ndJKXnfPGPppD0w/IItKsIXlZ+alw== dependencies: - "@commitlint/top-level" "^16.0.0" - "@commitlint/types" "^16.0.0" + "@commitlint/top-level" "^16.2.1" + "@commitlint/types" "^16.2.1" fs-extra "^10.0.0" git-raw-commits "^2.0.0" -"@commitlint/resolve-extends@^16.1.0": - version "16.1.0" - resolved "https://registry.yarnpkg.com/@commitlint/resolve-extends/-/resolve-extends-16.1.0.tgz#4b199197c45ddb436b59ef319662de6870f68fd5" - integrity sha512-8182s6AFoUFX6+FT1PgQDt15nO2ogdR/EN8SYVAdhNXw1rLz8kT5saB/ICw567GuRAUgFTUMGCXy3ctMOXPEDg== +"@commitlint/resolve-extends@^16.2.1": + version "16.2.1" + resolved "https://registry.yarnpkg.com/@commitlint/resolve-extends/-/resolve-extends-16.2.1.tgz#2f7833a5a3a7aa79f508e59fcb0f1d33c45ed360" + integrity sha512-NbbCMPKTFf2J805kwfP9EO+vV+XvnaHRcBy6ud5dF35dxMsvdJqke54W3XazXF1ZAxC4a3LBy4i/GNVBAthsEg== dependencies: - "@commitlint/config-validator" "^16.1.0" - "@commitlint/types" "^16.0.0" + "@commitlint/config-validator" "^16.2.1" + "@commitlint/types" "^16.2.1" import-fresh "^3.0.0" lodash "^4.17.19" resolve-from "^5.0.0" resolve-global "^1.0.0" -"@commitlint/rules@^16.0.0": - version "16.0.0" - resolved "https://registry.yarnpkg.com/@commitlint/rules/-/rules-16.0.0.tgz#79d28c3678d2d1f7f1cdbedaedb30b01a86ee75b" - integrity sha512-AOl0y2SBTdJ1bvIv8nwHvQKRT/jC1xb09C5VZwzHoT8sE8F54KDeEzPCwHQFgUcWdGLyS10kkOTAH2MyA8EIlg== +"@commitlint/rules@^16.2.1": + version "16.2.1" + resolved "https://registry.yarnpkg.com/@commitlint/rules/-/rules-16.2.1.tgz#7264aa1c754e1c212aeceb27e5eb380cfa7bb233" + integrity sha512-ZFezJXQaBBso+BOTre/+1dGCuCzlWVaeLiVRGypI53qVgPMzQqZhkCcrxBFeqB87qeyzr4A4EoG++IvITwwpIw== dependencies: - "@commitlint/ensure" "^16.0.0" - "@commitlint/message" "^16.0.0" - "@commitlint/to-lines" "^16.0.0" - "@commitlint/types" "^16.0.0" + "@commitlint/ensure" "^16.2.1" + "@commitlint/message" "^16.2.1" + "@commitlint/to-lines" "^16.2.1" + "@commitlint/types" "^16.2.1" execa "^5.0.0" -"@commitlint/to-lines@^16.0.0": - version "16.0.0" - resolved "https://registry.yarnpkg.com/@commitlint/to-lines/-/to-lines-16.0.0.tgz#799980a89072302445baf595e20092fb86f0a58a" - integrity sha512-iN/qU38TCKU7uKOg6RXLpD49wNiuI0TqMqybHbjefUeP/Jmzxa8ishryj0uLyVdrAl1ZjGeD1ukXGMTtvqz8iA== +"@commitlint/to-lines@^16.2.1": + version "16.2.1" + resolved "https://registry.yarnpkg.com/@commitlint/to-lines/-/to-lines-16.2.1.tgz#42d000f34dc0406f514991e86237fdab5e8affd0" + integrity sha512-9/VjpYj5j1QeY3eiog1zQWY6axsdWAc0AonUUfyZ7B0MVcRI0R56YsHAfzF6uK/g/WwPZaoe4Lb1QCyDVnpVaQ== -"@commitlint/top-level@^16.0.0": - version "16.0.0" - resolved "https://registry.yarnpkg.com/@commitlint/top-level/-/top-level-16.0.0.tgz#7c2efc33cc37df839b3de558c0bc2eaddb64efe6" - integrity sha512-/Jt6NLxyFkpjL5O0jxurZPCHURZAm7cQCqikgPCwqPAH0TLgwqdHjnYipl8J+AGnAMGDip4FNLoYrtgIpZGBYw== +"@commitlint/top-level@^16.2.1": + version "16.2.1" + resolved "https://registry.yarnpkg.com/@commitlint/top-level/-/top-level-16.2.1.tgz#bdaa53ab3d8970e0288879f1a342a8c2dfe01583" + integrity sha512-lS6GSieHW9y6ePL73ied71Z9bOKyK+Ib9hTkRsB8oZFAyQZcyRwq2w6nIa6Fngir1QW51oKzzaXfJL94qwImyw== dependencies: find-up "^5.0.0" -"@commitlint/types@^16.0.0": - version "16.0.0" - resolved "https://registry.yarnpkg.com/@commitlint/types/-/types-16.0.0.tgz#3c133f106d36132756c464071a7f2290966727a3" - integrity sha512-+0FvYOAS39bJ4aKjnYn/7FD4DfWkmQ6G/06I4F0Gvu4KS5twirEg8mIcLhmeRDOOKn4Tp8PwpLwBiSA6npEMQA== +"@commitlint/types@^16.2.1": + version "16.2.1" + resolved "https://registry.yarnpkg.com/@commitlint/types/-/types-16.2.1.tgz#f25d373b88b01e51fc3fa44488101361945a61bd" + integrity sha512-7/z7pA7BM0i8XvMSBynO7xsB3mVQPUZbVn6zMIlp/a091XJ3qAXRXc+HwLYhiIdzzS5fuxxNIHZMGHVD4HJxdA== dependencies: chalk "^4.0.0" @@ -759,14 +760,14 @@ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz#d5e0706cf8c6acd8c6032f8d54070af261bbbb2f" integrity sha512-ws57AidsDvREKrZKYffXddNkyaF14iHNHm8VQnZH6t99E8gczjNN0GpvcGny0imC80yQ0tHz1xVUKk/KFQSUyA== -"@eslint/eslintrc@^1.0.5": - version "1.0.5" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.0.5.tgz#33f1b838dbf1f923bfa517e008362b78ddbbf318" - integrity sha512-BLxsnmK3KyPunz5wmCCpqy0YelEoxxGmH73Is+Z74oOTMtExcjkr3dDR6quwrjh1YspA8DH9gnX1o069KiS9AQ== +"@eslint/eslintrc@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.1.0.tgz#583d12dbec5d4f22f333f9669f7d0b7c7815b4d3" + integrity sha512-C1DfL7XX4nPqGd6jcP01W9pVM1HYCuUkFk1432D7F0v3JSlUIeOYn9oCoi3eoLZ+iwBSb29BMFxxny0YrrEZqg== dependencies: ajv "^6.12.4" debug "^4.3.2" - espree "^9.2.0" + espree "^9.3.1" globals "^13.9.0" ignore "^4.0.6" import-fresh "^3.2.1" @@ -826,109 +827,121 @@ jest-util "^27.4.2" slash "^3.0.0" -"@jest/core@^27.4.7": - version "27.4.7" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.4.7.tgz#84eabdf42a25f1fa138272ed229bcf0a1b5e6913" - integrity sha512-n181PurSJkVMS+kClIFSX/LLvw9ExSb+4IMtD6YnfxZVerw9ANYtW0bPrm0MJu2pfe9SY9FJ9FtQ+MdZkrZwjg== +"@jest/console@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-27.5.1.tgz#260fe7239602fe5130a94f1aa386eff54b014bba" + integrity sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg== dependencies: - "@jest/console" "^27.4.6" - "@jest/reporters" "^27.4.6" - "@jest/test-result" "^27.4.6" - "@jest/transform" "^27.4.6" - "@jest/types" "^27.4.2" + "@jest/types" "^27.5.1" + "@types/node" "*" + chalk "^4.0.0" + jest-message-util "^27.5.1" + jest-util "^27.5.1" + slash "^3.0.0" + +"@jest/core@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.5.1.tgz#267ac5f704e09dc52de2922cbf3af9edcd64b626" + integrity sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ== + dependencies: + "@jest/console" "^27.5.1" + "@jest/reporters" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" emittery "^0.8.1" exit "^0.1.2" - graceful-fs "^4.2.4" - jest-changed-files "^27.4.2" - jest-config "^27.4.7" - jest-haste-map "^27.4.6" - jest-message-util "^27.4.6" - jest-regex-util "^27.4.0" - jest-resolve "^27.4.6" - jest-resolve-dependencies "^27.4.6" - jest-runner "^27.4.6" - jest-runtime "^27.4.6" - jest-snapshot "^27.4.6" - jest-util "^27.4.2" - jest-validate "^27.4.6" - jest-watcher "^27.4.6" + graceful-fs "^4.2.9" + jest-changed-files "^27.5.1" + jest-config "^27.5.1" + jest-haste-map "^27.5.1" + jest-message-util "^27.5.1" + jest-regex-util "^27.5.1" + jest-resolve "^27.5.1" + jest-resolve-dependencies "^27.5.1" + jest-runner "^27.5.1" + jest-runtime "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" + jest-validate "^27.5.1" + jest-watcher "^27.5.1" micromatch "^4.0.4" rimraf "^3.0.0" slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/environment@^27.4.6": - version "27.4.6" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.4.6.tgz#1e92885d64f48c8454df35ed9779fbcf31c56d8b" - integrity sha512-E6t+RXPfATEEGVidr84WngLNWZ8ffCPky8RqqRK6u1Bn0LK92INe0MDttyPl/JOzaq92BmDzOeuqk09TvM22Sg== +"@jest/environment@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.5.1.tgz#d7425820511fe7158abbecc010140c3fd3be9c74" + integrity sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA== dependencies: - "@jest/fake-timers" "^27.4.6" - "@jest/types" "^27.4.2" + "@jest/fake-timers" "^27.5.1" + "@jest/types" "^27.5.1" "@types/node" "*" - jest-mock "^27.4.6" + jest-mock "^27.5.1" -"@jest/fake-timers@^27.4.6": - version "27.4.6" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.4.6.tgz#e026ae1671316dbd04a56945be2fa251204324e8" - integrity sha512-mfaethuYF8scV8ntPpiVGIHQgS0XIALbpY2jt2l7wb/bvq4Q5pDLk4EP4D7SAvYT1QrPOPVZAtbdGAOOyIgs7A== +"@jest/fake-timers@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.5.1.tgz#76979745ce0579c8a94a4678af7a748eda8ada74" + integrity sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ== dependencies: - "@jest/types" "^27.4.2" + "@jest/types" "^27.5.1" "@sinonjs/fake-timers" "^8.0.1" "@types/node" "*" - jest-message-util "^27.4.6" - jest-mock "^27.4.6" - jest-util "^27.4.2" + jest-message-util "^27.5.1" + jest-mock "^27.5.1" + jest-util "^27.5.1" -"@jest/globals@^27.4.6": - version "27.4.6" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.4.6.tgz#3f09bed64b0fd7f5f996920258bd4be8f52f060a" - integrity sha512-kAiwMGZ7UxrgPzu8Yv9uvWmXXxsy0GciNejlHvfPIfWkSxChzv6bgTS3YqBkGuHcis+ouMFI2696n2t+XYIeFw== +"@jest/globals@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.5.1.tgz#7ac06ce57ab966566c7963431cef458434601b2b" + integrity sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q== dependencies: - "@jest/environment" "^27.4.6" - "@jest/types" "^27.4.2" - expect "^27.4.6" + "@jest/environment" "^27.5.1" + "@jest/types" "^27.5.1" + expect "^27.5.1" -"@jest/reporters@^27.4.6": - version "27.4.6" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.4.6.tgz#b53dec3a93baf9b00826abf95b932de919d6d8dd" - integrity sha512-+Zo9gV81R14+PSq4wzee4GC2mhAN9i9a7qgJWL90Gpx7fHYkWpTBvwWNZUXvJByYR9tAVBdc8VxDWqfJyIUrIQ== +"@jest/reporters@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.5.1.tgz#ceda7be96170b03c923c37987b64015812ffec04" + integrity sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^27.4.6" - "@jest/test-result" "^27.4.6" - "@jest/transform" "^27.4.6" - "@jest/types" "^27.4.2" + "@jest/console" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" "@types/node" "*" chalk "^4.0.0" collect-v8-coverage "^1.0.0" exit "^0.1.2" glob "^7.1.2" - graceful-fs "^4.2.4" + graceful-fs "^4.2.9" istanbul-lib-coverage "^3.0.0" istanbul-lib-instrument "^5.1.0" istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^4.0.0" istanbul-reports "^3.1.3" - jest-haste-map "^27.4.6" - jest-resolve "^27.4.6" - jest-util "^27.4.2" - jest-worker "^27.4.6" + jest-haste-map "^27.5.1" + jest-resolve "^27.5.1" + jest-util "^27.5.1" + jest-worker "^27.5.1" slash "^3.0.0" source-map "^0.6.0" string-length "^4.0.1" terminal-link "^2.0.0" v8-to-istanbul "^8.1.0" -"@jest/source-map@^27.4.0": - version "27.4.0" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-27.4.0.tgz#2f0385d0d884fb3e2554e8f71f8fa957af9a74b6" - integrity sha512-Ntjx9jzP26Bvhbm93z/AKcPRj/9wrkI88/gK60glXDx1q+IeI0rf7Lw2c89Ch6ofonB0On/iRDreQuQ6te9pgQ== +"@jest/source-map@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-27.5.1.tgz#6608391e465add4205eae073b55e7f279e04e8cf" + integrity sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg== dependencies: callsites "^3.0.0" - graceful-fs "^4.2.4" + graceful-fs "^4.2.9" source-map "^0.6.0" "@jest/test-result@^27.4.6": @@ -941,31 +954,41 @@ "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-sequencer@^27.4.6": - version "27.4.6" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.4.6.tgz#447339b8a3d7b5436f50934df30854e442a9d904" - integrity sha512-3GL+nsf6E1PsyNsJuvPyIz+DwFuCtBdtvPpm/LMXVkBJbdFvQYCDpccYT56qq5BGniXWlE81n2qk1sdXfZebnw== +"@jest/test-result@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.5.1.tgz#56a6585fa80f7cdab72b8c5fc2e871d03832f5bb" + integrity sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag== dependencies: - "@jest/test-result" "^27.4.6" - graceful-fs "^4.2.4" - jest-haste-map "^27.4.6" - jest-runtime "^27.4.6" + "@jest/console" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" -"@jest/transform@^27.4.6": - version "27.4.6" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.4.6.tgz#153621940b1ed500305eacdb31105d415dc30231" - integrity sha512-9MsufmJC8t5JTpWEQJ0OcOOAXaH5ioaIX6uHVBLBMoCZPfKKQF+EqP8kACAvCZ0Y1h2Zr3uOccg8re+Dr5jxyw== +"@jest/test-sequencer@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz#4057e0e9cea4439e544c6353c6affe58d095745b" + integrity sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ== + dependencies: + "@jest/test-result" "^27.5.1" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-runtime "^27.5.1" + +"@jest/transform@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.5.1.tgz#6c3501dcc00c4c08915f292a600ece5ecfe1f409" + integrity sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw== dependencies: "@babel/core" "^7.1.0" - "@jest/types" "^27.4.2" + "@jest/types" "^27.5.1" babel-plugin-istanbul "^6.1.1" chalk "^4.0.0" convert-source-map "^1.4.0" fast-json-stable-stringify "^2.0.0" - graceful-fs "^4.2.4" - jest-haste-map "^27.4.6" - jest-regex-util "^27.4.0" - jest-util "^27.4.2" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-regex-util "^27.5.1" + jest-util "^27.5.1" micromatch "^4.0.4" pirates "^4.0.4" slash "^3.0.0" @@ -983,6 +1006,17 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" +"@jest/types@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.5.1.tgz#3c79ec4a8ba61c170bf937bcf9e98a9df175ec80" + integrity sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^16.0.0" + chalk "^4.0.0" + "@lerna/add@4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@lerna/add/-/add-4.0.0.tgz#c36f57d132502a57b9e7058d1548b7a565ef183f" @@ -2017,7 +2051,7 @@ resolved "https://registry.yarnpkg.com/@types/envinfo/-/envinfo-7.8.1.tgz#1915df82c16d637e92146645c70db9360eb099c6" integrity sha512-pTyshpmGxqB9lRwG75v2YR0oqKYpCrklOYlZWQ88z/JB0fimT8EVmYekuIwpU3IxPZDHSXCqXKzkCrtAcKY25g== -"@types/eslint-scope@^3.7.0": +"@types/eslint-scope@^3.7.3": version "3.7.3" resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.3.tgz#125b88504b61e3c8bc6f870882003253005c3224" integrity sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g== @@ -2033,10 +2067,10 @@ "@types/estree" "*" "@types/json-schema" "*" -"@types/estree@*", "@types/estree@^0.0.50": - version "0.0.50" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.50.tgz#1e0caa9364d3fccd2931c3ed96fdbeaa5d4cca83" - integrity sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw== +"@types/estree@*", "@types/estree@^0.0.51": + version "0.0.51" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40" + integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ== "@types/expect@^1.20.4": version "1.20.4" @@ -2052,7 +2086,7 @@ "@types/qs" "*" "@types/range-parser" "*" -"@types/express@*": +"@types/express@*", "@types/express@^4.17.13": version "4.17.13" resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.13.tgz#a76e2995728999bab51a33fabce1d705a3709034" integrity sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA== @@ -2097,6 +2131,13 @@ "@types/through" "*" rxjs "^7.2.0" +"@types/interpret@*": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@types/interpret/-/interpret-1.1.1.tgz#b1bf85b0420e2414b989ce237658ad20dc03719b" + integrity sha512-HZ4d0m2Ebl8DmrOdYZHgYyipj/8Ftq1/ssB/oQR7fqfUrwtTP7IW3BDi2V445nhPBLzZjEkApaPVp83moSCXlA== + dependencies: + "@types/node" "*" + "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.4" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" @@ -2176,10 +2217,10 @@ resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.31.tgz#31b7ca6407128a3d2bbc27fe2d21b345397f6197" integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA== -"@types/node@*", "@types/node@^17.0.12": - version "17.0.13" - resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.13.tgz#5ed7ed7c662948335fcad6c412bb42d99ea754e3" - integrity sha512-Y86MAxASe25hNzlDbsviXl8jQHb0RDvKt4c40ZJQ1Don0AAL0STLZSs4N+6gLEO55pedy7r2cLwS+ZDxPm/2Bw== +"@types/node@*", "@types/node@>=12", "@types/node@^17.0.12": + version "17.0.19" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.19.tgz#726171367f404bfbe8512ba608a09ebad810c7e6" + integrity sha512-PfeQhvcMR4cPFVuYfBN4ifG7p9c+Dlh3yUZR6k+5yQK7wX3gDgVxBly4/WkBRs9x4dmcy1TVl08SY67wwtEvmA== "@types/normalize-package-data@^2.4.0": version "2.4.1" @@ -2206,6 +2247,13 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== +"@types/rechoir@^0.6.1": + version "0.6.1" + resolved "https://registry.yarnpkg.com/@types/rechoir/-/rechoir-0.6.1.tgz#e7589df255d2638db48b0dfc3294c87dc9db19ba" + integrity sha512-HbMQqyZC8W9NxE3R89rW+hFwFXeIdmCT7x91NQjzB4+0CI42K/CJfRak5/jAQ7L5qi1cGcQQdo+GI9pqqUhbKQ== + dependencies: + "@types/interpret" "*" + "@types/responselike@*", "@types/responselike@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" @@ -2298,9 +2346,9 @@ rxjs "^6.4.0" "@types/yeoman-generator@*", "@types/yeoman-generator@^5.2.8": - version "5.2.8" - resolved "https://registry.yarnpkg.com/@types/yeoman-generator/-/yeoman-generator-5.2.8.tgz#d67290fa3d091a0ed52e74da322cb1b437baafb5" - integrity sha512-eZX8PFTw8S888iv+JHw8KB0j+z2wexgFVieuIssHTD1H6ygfUnXy7Z36yhRYxO6DUG9CkuZicxIm9iDRdmdFmQ== + version "5.2.9" + resolved "https://registry.yarnpkg.com/@types/yeoman-generator/-/yeoman-generator-5.2.9.tgz#70be1011b1ed6ddb0982a17b7de80825052822be" + integrity sha512-nLcJvIoq83s/FBjTgSjtwPEUk6U8nVzVZ4fBOh9Untdgu00MjjQOFORob8kzlTSJIh0MC9mPnNuV2ErD/NlabQ== dependencies: "@types/debug" "*" "@types/ejs" "*" @@ -2310,13 +2358,13 @@ rxjs "^6.4.0" "@typescript-eslint/eslint-plugin@^5.10.1": - version "5.10.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.10.1.tgz#870195d0f2146b36d11fc71131b75aba52354c69" - integrity sha512-xN3CYqFlyE/qOcy978/L0xLR2HlcAGIyIK5sMOasxaaAPfQRj/MmMV6OC3I7NZO84oEUdWCOju34Z9W8E0pFDQ== + version "5.12.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.12.1.tgz#b2cd3e288f250ce8332d5035a2ff65aba3374ac4" + integrity sha512-M499lqa8rnNK7mUv74lSFFttuUsubIRdAbHcVaP93oFcKkEmHmLqy2n7jM9C8DVmFMYK61ExrZU6dLYhQZmUpw== dependencies: - "@typescript-eslint/scope-manager" "5.10.1" - "@typescript-eslint/type-utils" "5.10.1" - "@typescript-eslint/utils" "5.10.1" + "@typescript-eslint/scope-manager" "5.12.1" + "@typescript-eslint/type-utils" "5.12.1" + "@typescript-eslint/utils" "5.12.1" debug "^4.3.2" functional-red-black-tree "^1.0.1" ignore "^5.1.8" @@ -2325,68 +2373,102 @@ tsutils "^3.21.0" "@typescript-eslint/parser@^5.10.1": - version "5.10.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.10.1.tgz#4ce9633cc33fc70bc13786cb793c1a76fe5ad6bd" - integrity sha512-GReo3tjNBwR5RnRO0K2wDIDN31cM3MmDtgyQ85oAxAmC5K3j/g85IjP+cDfcqDsDDBf1HNKQAD0WqOYL8jXqUA== + version "5.12.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.12.0.tgz#0ca669861813df99ce54916f66f524c625ed2434" + integrity sha512-MfSwg9JMBojMUoGjUmX+D2stoQj1CBYTCP0qnnVtu9A+YQXVKNtLjasYh+jozOcrb/wau8TCfWOkQTiOAruBog== dependencies: - "@typescript-eslint/scope-manager" "5.10.1" - "@typescript-eslint/types" "5.10.1" - "@typescript-eslint/typescript-estree" "5.10.1" + "@typescript-eslint/scope-manager" "5.12.0" + "@typescript-eslint/types" "5.12.0" + "@typescript-eslint/typescript-estree" "5.12.0" debug "^4.3.2" -"@typescript-eslint/scope-manager@5.10.1": - version "5.10.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.10.1.tgz#f0539c73804d2423506db2475352a4dec36cd809" - integrity sha512-Lyvi559Gvpn94k7+ElXNMEnXu/iundV5uFmCUNnftbFrUbAJ1WBoaGgkbOBm07jVZa682oaBU37ao/NGGX4ZDg== +"@typescript-eslint/scope-manager@5.12.0": + version "5.12.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.12.0.tgz#59619e6e5e2b1ce6cb3948b56014d3a24da83f5e" + integrity sha512-GAMobtIJI8FGf1sLlUWNUm2IOkIjvn7laFWyRx7CLrv6nLBI7su+B7lbStqVlK5NdLvHRFiJo2HhiDF7Ki01WQ== dependencies: - "@typescript-eslint/types" "5.10.1" - "@typescript-eslint/visitor-keys" "5.10.1" + "@typescript-eslint/types" "5.12.0" + "@typescript-eslint/visitor-keys" "5.12.0" -"@typescript-eslint/type-utils@5.10.1": - version "5.10.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.10.1.tgz#5e526c00142585e40ab1503e83f1ff608c367405" - integrity sha512-AfVJkV8uck/UIoDqhu+ptEdBoQATON9GXnhOpPLzkQRJcSChkvD//qsz9JVffl2goxX+ybs5klvacE9vmrQyCw== +"@typescript-eslint/scope-manager@5.12.1": + version "5.12.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.12.1.tgz#58734fd45d2d1dec49641aacc075fba5f0968817" + integrity sha512-J0Wrh5xS6XNkd4TkOosxdpObzlYfXjAFIm9QxYLCPOcHVv1FyyFCPom66uIh8uBr0sZCrtS+n19tzufhwab8ZQ== dependencies: - "@typescript-eslint/utils" "5.10.1" + "@typescript-eslint/types" "5.12.1" + "@typescript-eslint/visitor-keys" "5.12.1" + +"@typescript-eslint/type-utils@5.12.1": + version "5.12.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.12.1.tgz#8d58c6a0bb176b5e9a91581cda1a7f91a114d3f0" + integrity sha512-Gh8feEhsNLeCz6aYqynh61Vsdy+tiNNkQtc+bN3IvQvRqHkXGUhYkUi+ePKzP0Mb42se7FDb+y2SypTbpbR/Sg== + dependencies: + "@typescript-eslint/utils" "5.12.1" debug "^4.3.2" tsutils "^3.21.0" -"@typescript-eslint/types@5.10.1": - version "5.10.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.10.1.tgz#dca9bd4cb8c067fc85304a31f38ec4766ba2d1ea" - integrity sha512-ZvxQ2QMy49bIIBpTqFiOenucqUyjTQ0WNLhBM6X1fh1NNlYAC6Kxsx8bRTY3jdYsYg44a0Z/uEgQkohbR0H87Q== +"@typescript-eslint/types@5.12.0": + version "5.12.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.12.0.tgz#5b4030a28222ee01e851836562c07769eecda0b8" + integrity sha512-JowqbwPf93nvf8fZn5XrPGFBdIK8+yx5UEGs2QFAYFI8IWYfrzz+6zqlurGr2ctShMaJxqwsqmra3WXWjH1nRQ== + +"@typescript-eslint/types@5.12.1": + version "5.12.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.12.1.tgz#46a36a28ff4d946821b58fe5a73c81dc2e12aa89" + integrity sha512-hfcbq4qVOHV1YRdhkDldhV9NpmmAu2vp6wuFODL71Y0Ixak+FLeEU4rnPxgmZMnGreGEghlEucs9UZn5KOfHJA== + +"@typescript-eslint/typescript-estree@5.12.0": + version "5.12.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.12.0.tgz#cabf545fd592722f0e2b4104711e63bf89525cd2" + integrity sha512-Dd9gVeOqt38QHR0BEA8oRaT65WYqPYbIc5tRFQPkfLquVEFPD1HAtbZT98TLBkEcCkvwDYOAvuSvAD9DnQhMfQ== + dependencies: + "@typescript-eslint/types" "5.12.0" + "@typescript-eslint/visitor-keys" "5.12.0" + debug "^4.3.2" + globby "^11.0.4" + is-glob "^4.0.3" + semver "^7.3.5" + tsutils "^3.21.0" -"@typescript-eslint/typescript-estree@5.10.1": - version "5.10.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.10.1.tgz#b268e67be0553f8790ba3fe87113282977adda15" - integrity sha512-PwIGnH7jIueXv4opcwEbVGDATjGPO1dx9RkUl5LlHDSe+FXxPwFL5W/qYd5/NHr7f6lo/vvTrAzd0KlQtRusJQ== +"@typescript-eslint/typescript-estree@5.12.1": + version "5.12.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.12.1.tgz#6a9425b9c305bcbc38e2d1d9a24c08e15e02b722" + integrity sha512-ahOdkIY9Mgbza7L9sIi205Pe1inCkZWAHE1TV1bpxlU4RZNPtXaDZfiiFWcL9jdxvW1hDYZJXrFm+vlMkXRbBw== dependencies: - "@typescript-eslint/types" "5.10.1" - "@typescript-eslint/visitor-keys" "5.10.1" + "@typescript-eslint/types" "5.12.1" + "@typescript-eslint/visitor-keys" "5.12.1" debug "^4.3.2" globby "^11.0.4" is-glob "^4.0.3" semver "^7.3.5" tsutils "^3.21.0" -"@typescript-eslint/utils@5.10.1": - version "5.10.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.10.1.tgz#fa682a33af47080ba2c4368ee0ad2128213a1196" - integrity sha512-RRmlITiUbLuTRtn/gcPRi4202niF+q7ylFLCKu4c+O/PcpRvZ/nAUwQ2G00bZgpWkhrNLNnvhZLbDn8Ml0qsQw== +"@typescript-eslint/utils@5.12.1": + version "5.12.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.12.1.tgz#447c24a05d9c33f9c6c64cb48f251f2371eef920" + integrity sha512-Qq9FIuU0EVEsi8fS6pG+uurbhNTtoYr4fq8tKjBupsK5Bgbk2I32UGm0Sh+WOyjOPgo/5URbxxSNV6HYsxV4MQ== dependencies: "@types/json-schema" "^7.0.9" - "@typescript-eslint/scope-manager" "5.10.1" - "@typescript-eslint/types" "5.10.1" - "@typescript-eslint/typescript-estree" "5.10.1" + "@typescript-eslint/scope-manager" "5.12.1" + "@typescript-eslint/types" "5.12.1" + "@typescript-eslint/typescript-estree" "5.12.1" eslint-scope "^5.1.1" eslint-utils "^3.0.0" -"@typescript-eslint/visitor-keys@5.10.1": - version "5.10.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.10.1.tgz#29102de692f59d7d34ecc457ed59ab5fc558010b" - integrity sha512-NjQ0Xinhy9IL979tpoTRuLKxMc0zJC7QVSdeerXs2/QvOy2yRkzX5dRb10X5woNUdJgU8G3nYRDlI33sq1K4YQ== +"@typescript-eslint/visitor-keys@5.12.0": + version "5.12.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.12.0.tgz#1ac9352ed140b07ba144ebf371b743fdf537ec16" + integrity sha512-cFwTlgnMV6TgezQynx2c/4/tx9Tufbuo9LPzmWqyRC3QC4qTGkAG1C6pBr0/4I10PAI/FlYunI3vJjIcu+ZHMg== + dependencies: + "@typescript-eslint/types" "5.12.0" + eslint-visitor-keys "^3.0.0" + +"@typescript-eslint/visitor-keys@5.12.1": + version "5.12.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.12.1.tgz#f722da106c8f9695ae5640574225e45af3e52ec3" + integrity sha512-l1KSLfupuwrXx6wc0AuOmC7Ko5g14ZOQ86wJJqRbdLbXLK02pK/DPiDDqCc7BqqiiA04/eAA6ayL0bgOrAkH7A== dependencies: - "@typescript-eslint/types" "5.10.1" + "@typescript-eslint/types" "5.12.1" eslint-visitor-keys "^3.0.0" "@webassemblyjs/ast@1.11.1": @@ -2967,18 +3049,18 @@ babel-core@^7.0.0-bridge.0: resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-7.0.0-bridge.0.tgz#95a492ddd90f9b4e9a4a1da14eb335b87b634ece" integrity sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg== -babel-jest@^27.4.6: - version "27.4.6" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.4.6.tgz#4d024e69e241cdf4f396e453a07100f44f7ce314" - integrity sha512-qZL0JT0HS1L+lOuH+xC2DVASR3nunZi/ozGhpgauJHgmI7f8rudxf6hUjEHympdQ/J64CdKmPkgfJ+A3U6QCrg== +babel-jest@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.5.1.tgz#a1bf8d61928edfefd21da27eb86a695bfd691444" + integrity sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg== dependencies: - "@jest/transform" "^27.4.6" - "@jest/types" "^27.4.2" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" "@types/babel__core" "^7.1.14" babel-plugin-istanbul "^6.1.1" - babel-preset-jest "^27.4.0" + babel-preset-jest "^27.5.1" chalk "^4.0.0" - graceful-fs "^4.2.4" + graceful-fs "^4.2.9" slash "^3.0.0" babel-plugin-dynamic-import-node@^2.3.3: @@ -2999,10 +3081,10 @@ babel-plugin-istanbul@^6.1.1: istanbul-lib-instrument "^5.0.4" test-exclude "^6.0.0" -babel-plugin-jest-hoist@^27.4.0: - version "27.4.0" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.4.0.tgz#d7831fc0f93573788d80dee7e682482da4c730d6" - integrity sha512-Jcu7qS4OX5kTWBc45Hz7BMmgXuJqRnhatqpUhnzGC3OBYpOmf2tv6jFNwZpwM7wU7MUuv2r9IPS/ZlYOuburVw== +babel-plugin-jest-hoist@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz#9be98ecf28c331eb9f5df9c72d6f89deb8181c2e" + integrity sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ== dependencies: "@babel/template" "^7.3.3" "@babel/types" "^7.3.3" @@ -3027,12 +3109,12 @@ babel-preset-current-node-syntax@^1.0.0: "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-syntax-top-level-await" "^7.8.3" -babel-preset-jest@^27.4.0: - version "27.4.0" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-27.4.0.tgz#70d0e676a282ccb200fbabd7f415db5fdf393bca" - integrity sha512-NK4jGYpnBvNxcGo7/ZpZJr51jCGT+3bwwpVIDY2oNfTxJJldRtB4VAcYdgp1loDE50ODuTu+yBjpMAswv5tlpg== +babel-preset-jest@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz#91f10f58034cb7989cb4f962b69fa6eef6a6bc81" + integrity sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag== dependencies: - babel-plugin-jest-hoist "^27.4.0" + babel-plugin-jest-hoist "^27.5.1" babel-preset-current-node-syntax "^1.0.0" balanced-match@^1.0.0: @@ -3366,7 +3448,7 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== -chokidar@^3.5.2: +chokidar@^3.5.3: version "3.5.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== @@ -4343,6 +4425,11 @@ diff-sequences@^27.4.0: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.4.0.tgz#d783920ad8d06ec718a060d00196dfef25b132a5" integrity sha512-YqiQzkrsmHMH5uuh8OdQFU9/ZpADnwzml8z0O5HvRNda+5UZsaX/xN+AAxfR2hWq1Y7HZnAzO9J5lJXOuDz2Ww== +diff-sequences@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327" + integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ== + diff@^3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" @@ -4530,9 +4617,9 @@ end-of-stream@^1.1.0: once "^1.4.0" enhanced-resolve@^5.8.3: - version "5.8.3" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz#6d552d465cce0423f5b3d718511ea53826a7b2f0" - integrity sha512-EGAbGvH7j7Xt2nc0E7D99La1OiEs8LnyimkRgwExpUMScN6O+3x9tIWs7PLQZVNx4YD+00skHXPXi1yQHpAmZA== + version "5.9.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.9.0.tgz#49ac24953ac8452ed8fed2ef1340fc8e043667ee" + integrity sha512-weDYmzbBygL7HzGGS26M3hGQx68vehdEg6VUmqSOaFzXExFqlnKuSvsEJCVGQHScS8CQMbrAqftT+AzzHNt/YA== dependencies: graceful-fs "^4.2.4" tapable "^2.2.0" @@ -4654,9 +4741,9 @@ escodegen@^2.0.0: source-map "~0.6.1" eslint-config-prettier@^8.2.0: - version "8.3.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz#f7471b20b6fe8a9a9254cc684454202886a2dd7a" - integrity sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew== + version "8.4.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.4.0.tgz#8e6d17c7436649e98c4c2189868562921ef563de" + integrity sha512-CFotdUcMY18nGRo5KGsnNxpznzhkopOcOo0InID+sgQssPrzjvsyKZPvOgymTFeHrFuC3Tzdf2YndhXtULK9Iw== eslint-plugin-es@^3.0.0: version "3.0.1" @@ -4686,10 +4773,10 @@ eslint-scope@5.1.1, eslint-scope@^5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" -eslint-scope@^7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.0.tgz#c1f6ea30ac583031f203d65c73e723b01298f153" - integrity sha512-aWwkhnS0qAXqNOgKOK0dJ2nvzEbhEvpy8OlJ9kZ0FeZnA6zpjv1/Vei+puGFFX7zkPCkHHXb7IDX3A+7yPrRWg== +eslint-scope@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642" + integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== dependencies: esrecurse "^4.3.0" estraverse "^5.2.0" @@ -4718,17 +4805,17 @@ eslint-visitor-keys@^2.0.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== -eslint-visitor-keys@^3.0.0, eslint-visitor-keys@^3.1.0, eslint-visitor-keys@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.2.0.tgz#6fbb166a6798ee5991358bc2daa1ba76cc1254a1" - integrity sha512-IOzT0X126zn7ALX0dwFiUQEdsfzrm4+ISsQS8nukaJXwEyYKRSnEIIDULYg1mCtGp7UUXgfGl7BIolXREQK+XQ== +eslint-visitor-keys@^3.0.0, eslint-visitor-keys@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" + integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== eslint@^8.6.0: - version "8.7.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.7.0.tgz#22e036842ee5b7cf87b03fe237731675b4d3633c" - integrity sha512-ifHYzkBGrzS2iDU7KjhCAVMGCvF6M3Xfs8X8b37cgrUlDt6bWRTpRh6T/gtSXv1HJ/BUGgmjvNvOEGu85Iif7w== + version "8.9.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.9.0.tgz#a2a8227a99599adc4342fd9b854cb8d8d6412fdb" + integrity sha512-PB09IGwv4F4b0/atrbcMFboF/giawbBLVC7fyDamk5Wtey4Jh2K+rYaBhCAbUyEI4QzB1ly09Uglc9iCtFaG2Q== dependencies: - "@eslint/eslintrc" "^1.0.5" + "@eslint/eslintrc" "^1.1.0" "@humanwhocodes/config-array" "^0.9.2" ajv "^6.10.0" chalk "^4.0.0" @@ -4736,10 +4823,10 @@ eslint@^8.6.0: debug "^4.3.2" doctrine "^3.0.0" escape-string-regexp "^4.0.0" - eslint-scope "^7.1.0" + eslint-scope "^7.1.1" eslint-utils "^3.0.0" - eslint-visitor-keys "^3.2.0" - espree "^9.3.0" + eslint-visitor-keys "^3.3.0" + espree "^9.3.1" esquery "^1.4.0" esutils "^2.0.2" fast-deep-equal "^3.1.3" @@ -4764,14 +4851,14 @@ eslint@^8.6.0: text-table "^0.2.0" v8-compile-cache "^2.0.3" -espree@^9.2.0, espree@^9.3.0: - version "9.3.0" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.0.tgz#c1240d79183b72aaee6ccfa5a90bc9111df085a8" - integrity sha512-d/5nCsb0JcqsSEeQzFZ8DH1RmxPcglRWh24EFTlUEmCKoehXGdpsx0RkHDubqUI8LSAIKMQp4r9SzQ3n+sm4HQ== +espree@^9.3.1: + version "9.3.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.1.tgz#8793b4bc27ea4c778c19908e0719e7b8f4115bcd" + integrity sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ== dependencies: acorn "^8.7.0" acorn-jsx "^5.3.1" - eslint-visitor-keys "^3.1.0" + eslint-visitor-keys "^3.3.0" esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0: version "4.0.1" @@ -4877,15 +4964,15 @@ expand-tilde@^2.0.0, expand-tilde@^2.0.2: dependencies: homedir-polyfill "^1.0.1" -expect@^27.4.6: - version "27.4.6" - resolved "https://registry.yarnpkg.com/expect/-/expect-27.4.6.tgz#f335e128b0335b6ceb4fcab67ece7cbd14c942e6" - integrity sha512-1M/0kAALIaj5LaG66sFJTbRsWTADnylly82cu4bspI0nl+pgP4E6Bh/aqdHlTUjul06K7xQnnrAoqfxVU0+/ag== +expect@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/expect/-/expect-27.5.1.tgz#83ce59f1e5bdf5f9d2b94b61d2050db48f3fef74" + integrity sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw== dependencies: - "@jest/types" "^27.4.2" - jest-get-type "^27.4.0" - jest-matcher-utils "^27.4.6" - jest-message-util "^27.4.6" + "@jest/types" "^27.5.1" + jest-get-type "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" express@^4.17.1: version "4.17.2" @@ -5202,9 +5289,9 @@ flow-parser@0.*: integrity sha512-H1Fu8EM/F6MtOpHYpsFXPyySatowrXMWENxRmmKAfirfBr8kjHrms3YDuv82Nhn0xWaXV7Hhynp2tEaZsLhHLw== follow-redirects@^1.0.0, follow-redirects@^1.14.0: - version "1.14.7" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.7.tgz#2004c02eb9436eee9a21446a6477debf17e81685" - integrity sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ== + version "1.14.8" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.8.tgz#016996fb9a11a100566398b1c6839337d7bfa8fc" + integrity sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA== for-in@^1.0.2: version "1.0.2" @@ -6664,87 +6751,89 @@ jake@^10.6.1: filelist "^1.0.1" minimatch "^3.0.4" -jest-changed-files@^27.4.2: - version "27.4.2" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.4.2.tgz#da2547ea47c6e6a5f6ed336151bd2075736eb4a5" - integrity sha512-/9x8MjekuzUQoPjDHbBiXbNEBauhrPU2ct7m8TfCg69ywt1y/N+yYwGh3gCpnqUS3klYWDU/lSNgv+JhoD2k1A== +jest-changed-files@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.5.1.tgz#a348aed00ec9bf671cc58a66fcbe7c3dfd6a68f5" + integrity sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw== dependencies: - "@jest/types" "^27.4.2" + "@jest/types" "^27.5.1" execa "^5.0.0" throat "^6.0.1" -jest-circus@^27.4.6: - version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.4.6.tgz#d3af34c0eb742a967b1919fbb351430727bcea6c" - integrity sha512-UA7AI5HZrW4wRM72Ro80uRR2Fg+7nR0GESbSI/2M+ambbzVuA63mn5T1p3Z/wlhntzGpIG1xx78GP2YIkf6PhQ== +jest-circus@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.5.1.tgz#37a5a4459b7bf4406e53d637b49d22c65d125ecc" + integrity sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw== dependencies: - "@jest/environment" "^27.4.6" - "@jest/test-result" "^27.4.6" - "@jest/types" "^27.4.2" + "@jest/environment" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" "@types/node" "*" chalk "^4.0.0" co "^4.6.0" dedent "^0.7.0" - expect "^27.4.6" + expect "^27.5.1" is-generator-fn "^2.0.0" - jest-each "^27.4.6" - jest-matcher-utils "^27.4.6" - jest-message-util "^27.4.6" - jest-runtime "^27.4.6" - jest-snapshot "^27.4.6" - jest-util "^27.4.2" - pretty-format "^27.4.6" + jest-each "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" + jest-runtime "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" + pretty-format "^27.5.1" slash "^3.0.0" stack-utils "^2.0.3" throat "^6.0.1" -jest-cli@^27.4.7: - version "27.4.7" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.4.7.tgz#d00e759e55d77b3bcfea0715f527c394ca314e5a" - integrity sha512-zREYhvjjqe1KsGV15mdnxjThKNDgza1fhDT+iUsXWLCq3sxe9w5xnvyctcYVT5PcdLSjv7Y5dCwTS3FCF1tiuw== +jest-cli@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.5.1.tgz#278794a6e6458ea8029547e6c6cbf673bd30b145" + integrity sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw== dependencies: - "@jest/core" "^27.4.7" - "@jest/test-result" "^27.4.6" - "@jest/types" "^27.4.2" + "@jest/core" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" chalk "^4.0.0" exit "^0.1.2" - graceful-fs "^4.2.4" + graceful-fs "^4.2.9" import-local "^3.0.2" - jest-config "^27.4.7" - jest-util "^27.4.2" - jest-validate "^27.4.6" + jest-config "^27.5.1" + jest-util "^27.5.1" + jest-validate "^27.5.1" prompts "^2.0.1" yargs "^16.2.0" -jest-config@^27.4.7: - version "27.4.7" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.4.7.tgz#4f084b2acbd172c8b43aa4cdffe75d89378d3972" - integrity sha512-xz/o/KJJEedHMrIY9v2ParIoYSrSVY6IVeE4z5Z3i101GoA5XgfbJz+1C8EYPsv7u7f39dS8F9v46BHDhn0vlw== +jest-config@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.5.1.tgz#5c387de33dca3f99ad6357ddeccd91bf3a0e4a41" + integrity sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA== dependencies: "@babel/core" "^7.8.0" - "@jest/test-sequencer" "^27.4.6" - "@jest/types" "^27.4.2" - babel-jest "^27.4.6" + "@jest/test-sequencer" "^27.5.1" + "@jest/types" "^27.5.1" + babel-jest "^27.5.1" chalk "^4.0.0" ci-info "^3.2.0" deepmerge "^4.2.2" glob "^7.1.1" - graceful-fs "^4.2.4" - jest-circus "^27.4.6" - jest-environment-jsdom "^27.4.6" - jest-environment-node "^27.4.6" - jest-get-type "^27.4.0" - jest-jasmine2 "^27.4.6" - jest-regex-util "^27.4.0" - jest-resolve "^27.4.6" - jest-runner "^27.4.6" - jest-util "^27.4.2" - jest-validate "^27.4.6" + graceful-fs "^4.2.9" + jest-circus "^27.5.1" + jest-environment-jsdom "^27.5.1" + jest-environment-node "^27.5.1" + jest-get-type "^27.5.1" + jest-jasmine2 "^27.5.1" + jest-regex-util "^27.5.1" + jest-resolve "^27.5.1" + jest-runner "^27.5.1" + jest-util "^27.5.1" + jest-validate "^27.5.1" micromatch "^4.0.4" - pretty-format "^27.4.6" + parse-json "^5.2.0" + pretty-format "^27.5.1" slash "^3.0.0" + strip-json-comments "^3.1.1" -jest-diff@^27.0.0, jest-diff@^27.4.6: +jest-diff@^27.0.0: version "27.4.6" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.4.6.tgz#93815774d2012a2cbb6cf23f84d48c7a2618f98d" integrity sha512-zjaB0sh0Lb13VyPsd92V7HkqF6yKRH9vm33rwBt7rPYrpQvS1nCvlIy2pICbKta+ZjWngYLNn4cCK4nyZkjS/w== @@ -6754,114 +6843,129 @@ jest-diff@^27.0.0, jest-diff@^27.4.6: jest-get-type "^27.4.0" pretty-format "^27.4.6" -jest-docblock@^27.4.0: - version "27.4.0" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.4.0.tgz#06c78035ca93cbbb84faf8fce64deae79a59f69f" - integrity sha512-7TBazUdCKGV7svZ+gh7C8esAnweJoG+SvcF6Cjqj4l17zA2q1cMwx2JObSioubk317H+cjcHgP+7fTs60paulg== +jest-diff@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.5.1.tgz#a07f5011ac9e6643cf8a95a462b7b1ecf6680def" + integrity sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw== dependencies: - detect-newline "^3.0.0" + chalk "^4.0.0" + diff-sequences "^27.5.1" + jest-get-type "^27.5.1" + pretty-format "^27.5.1" -jest-each@^27.4.6: - version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.4.6.tgz#e7e8561be61d8cc6dbf04296688747ab186c40ff" - integrity sha512-n6QDq8y2Hsmn22tRkgAk+z6MCX7MeVlAzxmZDshfS2jLcaBlyhpF3tZSJLR+kXmh23GEvS0ojMR8i6ZeRvpQcA== +jest-docblock@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.5.1.tgz#14092f364a42c6108d42c33c8cf30e058e25f6c0" + integrity sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ== dependencies: - "@jest/types" "^27.4.2" - chalk "^4.0.0" - jest-get-type "^27.4.0" - jest-util "^27.4.2" - pretty-format "^27.4.6" + detect-newline "^3.0.0" -jest-environment-jsdom@^27.4.6: - version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.4.6.tgz#c23a394eb445b33621dfae9c09e4c8021dea7b36" - integrity sha512-o3dx5p/kHPbUlRvSNjypEcEtgs6LmvESMzgRFQE6c+Prwl2JLA4RZ7qAnxc5VM8kutsGRTB15jXeeSbJsKN9iA== +jest-each@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.5.1.tgz#5bc87016f45ed9507fed6e4702a5b468a5b2c44e" + integrity sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ== dependencies: - "@jest/environment" "^27.4.6" - "@jest/fake-timers" "^27.4.6" - "@jest/types" "^27.4.2" + "@jest/types" "^27.5.1" + chalk "^4.0.0" + jest-get-type "^27.5.1" + jest-util "^27.5.1" + pretty-format "^27.5.1" + +jest-environment-jsdom@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz#ea9ccd1fc610209655a77898f86b2b559516a546" + integrity sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw== + dependencies: + "@jest/environment" "^27.5.1" + "@jest/fake-timers" "^27.5.1" + "@jest/types" "^27.5.1" "@types/node" "*" - jest-mock "^27.4.6" - jest-util "^27.4.2" + jest-mock "^27.5.1" + jest-util "^27.5.1" jsdom "^16.6.0" -jest-environment-node@^27.4.6: - version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.4.6.tgz#ee8cd4ef458a0ef09d087c8cd52ca5856df90242" - integrity sha512-yfHlZ9m+kzTKZV0hVfhVu6GuDxKAYeFHrfulmy7Jxwsq4V7+ZK7f+c0XP/tbVDMQW7E4neG2u147hFkuVz0MlQ== +jest-environment-node@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.5.1.tgz#dedc2cfe52fab6b8f5714b4808aefa85357a365e" + integrity sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw== dependencies: - "@jest/environment" "^27.4.6" - "@jest/fake-timers" "^27.4.6" - "@jest/types" "^27.4.2" + "@jest/environment" "^27.5.1" + "@jest/fake-timers" "^27.5.1" + "@jest/types" "^27.5.1" "@types/node" "*" - jest-mock "^27.4.6" - jest-util "^27.4.2" + jest-mock "^27.5.1" + jest-util "^27.5.1" jest-get-type@^27.4.0: version "27.4.0" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.4.0.tgz#7503d2663fffa431638337b3998d39c5e928e9b5" integrity sha512-tk9o+ld5TWq41DkK14L4wox4s2D9MtTpKaAVzXfr5CUKm5ZK2ExcaFE0qls2W71zE/6R2TxxrK9w2r6svAFDBQ== -jest-haste-map@^27.4.6: - version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.4.6.tgz#c60b5233a34ca0520f325b7e2cc0a0140ad0862a" - integrity sha512-0tNpgxg7BKurZeFkIOvGCkbmOHbLFf4LUQOxrQSMjvrQaQe3l6E8x6jYC1NuWkGo5WDdbr8FEzUxV2+LWNawKQ== +jest-get-type@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1" + integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw== + +jest-haste-map@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.5.1.tgz#9fd8bd7e7b4fa502d9c6164c5640512b4e811e7f" + integrity sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng== dependencies: - "@jest/types" "^27.4.2" + "@jest/types" "^27.5.1" "@types/graceful-fs" "^4.1.2" "@types/node" "*" anymatch "^3.0.3" fb-watchman "^2.0.0" - graceful-fs "^4.2.4" - jest-regex-util "^27.4.0" - jest-serializer "^27.4.0" - jest-util "^27.4.2" - jest-worker "^27.4.6" + graceful-fs "^4.2.9" + jest-regex-util "^27.5.1" + jest-serializer "^27.5.1" + jest-util "^27.5.1" + jest-worker "^27.5.1" micromatch "^4.0.4" walker "^1.0.7" optionalDependencies: fsevents "^2.3.2" -jest-jasmine2@^27.4.6: - version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.4.6.tgz#109e8bc036cb455950ae28a018f983f2abe50127" - integrity sha512-uAGNXF644I/whzhsf7/qf74gqy9OuhvJ0XYp8SDecX2ooGeaPnmJMjXjKt0mqh1Rl5dtRGxJgNrHlBQIBfS5Nw== +jest-jasmine2@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz#a037b0034ef49a9f3d71c4375a796f3b230d1ac4" + integrity sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ== dependencies: - "@jest/environment" "^27.4.6" - "@jest/source-map" "^27.4.0" - "@jest/test-result" "^27.4.6" - "@jest/types" "^27.4.2" + "@jest/environment" "^27.5.1" + "@jest/source-map" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" "@types/node" "*" chalk "^4.0.0" co "^4.6.0" - expect "^27.4.6" + expect "^27.5.1" is-generator-fn "^2.0.0" - jest-each "^27.4.6" - jest-matcher-utils "^27.4.6" - jest-message-util "^27.4.6" - jest-runtime "^27.4.6" - jest-snapshot "^27.4.6" - jest-util "^27.4.2" - pretty-format "^27.4.6" + jest-each "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" + jest-runtime "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" + pretty-format "^27.5.1" throat "^6.0.1" -jest-leak-detector@^27.4.6: - version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-27.4.6.tgz#ed9bc3ce514b4c582637088d9faf58a33bd59bf4" - integrity sha512-kkaGixDf9R7CjHm2pOzfTxZTQQQ2gHTIWKY/JZSiYTc90bZp8kSZnUMS3uLAfwTZwc0tcMRoEX74e14LG1WapA== +jest-leak-detector@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz#6ec9d54c3579dd6e3e66d70e3498adf80fde3fb8" + integrity sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ== dependencies: - jest-get-type "^27.4.0" - pretty-format "^27.4.6" + jest-get-type "^27.5.1" + pretty-format "^27.5.1" -jest-matcher-utils@^27.4.6: - version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.4.6.tgz#53ca7f7b58170638590e946f5363b988775509b8" - integrity sha512-XD4PKT3Wn1LQnRAq7ZsTI0VRuEc9OrCPFiO1XL7bftTGmfNF0DcEwMHRgqiu7NGf8ZoZDREpGrCniDkjt79WbA== +jest-matcher-utils@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab" + integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw== dependencies: chalk "^4.0.0" - jest-diff "^27.4.6" - jest-get-type "^27.4.0" - pretty-format "^27.4.6" + jest-diff "^27.5.1" + jest-get-type "^27.5.1" + pretty-format "^27.5.1" jest-message-util@^27.4.6: version "27.4.6" @@ -6878,12 +6982,27 @@ jest-message-util@^27.4.6: slash "^3.0.0" stack-utils "^2.0.3" -jest-mock@^27.4.6: - version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.4.6.tgz#77d1ba87fbd33ccb8ef1f061697e7341b7635195" - integrity sha512-kvojdYRkst8iVSZ1EJ+vc1RRD9llueBjKzXzeCytH3dMM7zvPV/ULcfI2nr0v0VUgm3Bjt3hBCQvOeaBz+ZTHw== +jest-message-util@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.5.1.tgz#bdda72806da10d9ed6425e12afff38cd1458b6cf" + integrity sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g== dependencies: - "@jest/types" "^27.4.2" + "@babel/code-frame" "^7.12.13" + "@jest/types" "^27.5.1" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^27.5.1" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-mock@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.5.1.tgz#19948336d49ef4d9c52021d34ac7b5f36ff967d6" + integrity sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og== + dependencies: + "@jest/types" "^27.5.1" "@types/node" "*" jest-pnp-resolver@^1.2.2: @@ -6891,126 +7010,130 @@ jest-pnp-resolver@^1.2.2: resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== -jest-regex-util@^27.0.0, jest-regex-util@^27.4.0: +jest-regex-util@^27.0.0: version "27.4.0" resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.4.0.tgz#e4c45b52653128843d07ad94aec34393ea14fbca" integrity sha512-WeCpMpNnqJYMQoOjm1nTtsgbR4XHAk1u00qDoNBQoykM280+/TmgA5Qh5giC1ecy6a5d4hbSsHzpBtu5yvlbEg== -jest-resolve-dependencies@^27.4.6: - version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.4.6.tgz#fc50ee56a67d2c2183063f6a500cc4042b5e2327" - integrity sha512-W85uJZcFXEVZ7+MZqIPCscdjuctruNGXUZ3OHSXOfXR9ITgbUKeHj+uGcies+0SsvI5GtUfTw4dY7u9qjTvQOw== +jest-regex-util@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.5.1.tgz#4da143f7e9fd1e542d4aa69617b38e4a78365b95" + integrity sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg== + +jest-resolve-dependencies@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz#d811ecc8305e731cc86dd79741ee98fed06f1da8" + integrity sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg== dependencies: - "@jest/types" "^27.4.2" - jest-regex-util "^27.4.0" - jest-snapshot "^27.4.6" + "@jest/types" "^27.5.1" + jest-regex-util "^27.5.1" + jest-snapshot "^27.5.1" -jest-resolve@^27.4.6: - version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.4.6.tgz#2ec3110655e86d5bfcfa992e404e22f96b0b5977" - integrity sha512-SFfITVApqtirbITKFAO7jOVN45UgFzcRdQanOFzjnbd+CACDoyeX7206JyU92l4cRr73+Qy/TlW51+4vHGt+zw== +jest-resolve@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.5.1.tgz#a2f1c5a0796ec18fe9eb1536ac3814c23617b384" + integrity sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw== dependencies: - "@jest/types" "^27.4.2" + "@jest/types" "^27.5.1" chalk "^4.0.0" - graceful-fs "^4.2.4" - jest-haste-map "^27.4.6" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" jest-pnp-resolver "^1.2.2" - jest-util "^27.4.2" - jest-validate "^27.4.6" + jest-util "^27.5.1" + jest-validate "^27.5.1" resolve "^1.20.0" resolve.exports "^1.1.0" slash "^3.0.0" -jest-runner@^27.4.6: - version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.4.6.tgz#1d390d276ec417e9b4d0d081783584cbc3e24773" - integrity sha512-IDeFt2SG4DzqalYBZRgbbPmpwV3X0DcntjezPBERvnhwKGWTW7C5pbbA5lVkmvgteeNfdd/23gwqv3aiilpYPg== +jest-runner@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.5.1.tgz#071b27c1fa30d90540805c5645a0ec167c7b62e5" + integrity sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ== dependencies: - "@jest/console" "^27.4.6" - "@jest/environment" "^27.4.6" - "@jest/test-result" "^27.4.6" - "@jest/transform" "^27.4.6" - "@jest/types" "^27.4.2" + "@jest/console" "^27.5.1" + "@jest/environment" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" "@types/node" "*" chalk "^4.0.0" emittery "^0.8.1" - exit "^0.1.2" - graceful-fs "^4.2.4" - jest-docblock "^27.4.0" - jest-environment-jsdom "^27.4.6" - jest-environment-node "^27.4.6" - jest-haste-map "^27.4.6" - jest-leak-detector "^27.4.6" - jest-message-util "^27.4.6" - jest-resolve "^27.4.6" - jest-runtime "^27.4.6" - jest-util "^27.4.2" - jest-worker "^27.4.6" + graceful-fs "^4.2.9" + jest-docblock "^27.5.1" + jest-environment-jsdom "^27.5.1" + jest-environment-node "^27.5.1" + jest-haste-map "^27.5.1" + jest-leak-detector "^27.5.1" + jest-message-util "^27.5.1" + jest-resolve "^27.5.1" + jest-runtime "^27.5.1" + jest-util "^27.5.1" + jest-worker "^27.5.1" source-map-support "^0.5.6" throat "^6.0.1" -jest-runtime@^27.4.6: - version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.4.6.tgz#83ae923818e3ea04463b22f3597f017bb5a1cffa" - integrity sha512-eXYeoR/MbIpVDrjqy5d6cGCFOYBFFDeKaNWqTp0h6E74dK0zLHzASQXJpl5a2/40euBmKnprNLJ0Kh0LCndnWQ== - dependencies: - "@jest/environment" "^27.4.6" - "@jest/fake-timers" "^27.4.6" - "@jest/globals" "^27.4.6" - "@jest/source-map" "^27.4.0" - "@jest/test-result" "^27.4.6" - "@jest/transform" "^27.4.6" - "@jest/types" "^27.4.2" +jest-runtime@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.5.1.tgz#4896003d7a334f7e8e4a53ba93fb9bcd3db0a1af" + integrity sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A== + dependencies: + "@jest/environment" "^27.5.1" + "@jest/fake-timers" "^27.5.1" + "@jest/globals" "^27.5.1" + "@jest/source-map" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" chalk "^4.0.0" cjs-module-lexer "^1.0.0" collect-v8-coverage "^1.0.0" execa "^5.0.0" glob "^7.1.3" - graceful-fs "^4.2.4" - jest-haste-map "^27.4.6" - jest-message-util "^27.4.6" - jest-mock "^27.4.6" - jest-regex-util "^27.4.0" - jest-resolve "^27.4.6" - jest-snapshot "^27.4.6" - jest-util "^27.4.2" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-message-util "^27.5.1" + jest-mock "^27.5.1" + jest-regex-util "^27.5.1" + jest-resolve "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" slash "^3.0.0" strip-bom "^4.0.0" -jest-serializer@^27.4.0: - version "27.4.0" - resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-27.4.0.tgz#34866586e1cae2388b7d12ffa2c7819edef5958a" - integrity sha512-RDhpcn5f1JYTX2pvJAGDcnsNTnsV9bjYPU8xcV+xPwOXnUPOQwf4ZEuiU6G9H1UztH+OapMgu/ckEVwO87PwnQ== +jest-serializer@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-27.5.1.tgz#81438410a30ea66fd57ff730835123dea1fb1f64" + integrity sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w== dependencies: "@types/node" "*" - graceful-fs "^4.2.4" + graceful-fs "^4.2.9" -jest-snapshot@^27.4.6: - version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.4.6.tgz#e2a3b4fff8bdce3033f2373b2e525d8b6871f616" - integrity sha512-fafUCDLQfzuNP9IRcEqaFAMzEe7u5BF7mude51wyWv7VRex60WznZIC7DfKTgSIlJa8aFzYmXclmN328aqSDmQ== +jest-snapshot@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.5.1.tgz#b668d50d23d38054a51b42c4039cab59ae6eb6a1" + integrity sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA== dependencies: "@babel/core" "^7.7.2" "@babel/generator" "^7.7.2" "@babel/plugin-syntax-typescript" "^7.7.2" "@babel/traverse" "^7.7.2" "@babel/types" "^7.0.0" - "@jest/transform" "^27.4.6" - "@jest/types" "^27.4.2" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" "@types/babel__traverse" "^7.0.4" "@types/prettier" "^2.1.5" babel-preset-current-node-syntax "^1.0.0" chalk "^4.0.0" - expect "^27.4.6" - graceful-fs "^4.2.4" - jest-diff "^27.4.6" - jest-get-type "^27.4.0" - jest-haste-map "^27.4.6" - jest-matcher-utils "^27.4.6" - jest-message-util "^27.4.6" - jest-util "^27.4.2" + expect "^27.5.1" + graceful-fs "^4.2.9" + jest-diff "^27.5.1" + jest-get-type "^27.5.1" + jest-haste-map "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" + jest-util "^27.5.1" natural-compare "^1.4.0" - pretty-format "^27.4.6" + pretty-format "^27.5.1" semver "^7.3.2" jest-util@^27.0.0, jest-util@^27.4.2: @@ -7025,17 +7148,29 @@ jest-util@^27.0.0, jest-util@^27.4.2: graceful-fs "^4.2.4" picomatch "^2.2.3" -jest-validate@^27.4.6: - version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.4.6.tgz#efc000acc4697b6cf4fa68c7f3f324c92d0c4f1f" - integrity sha512-872mEmCPVlBqbA5dToC57vA3yJaMRfIdpCoD3cyHWJOMx+SJwLNw0I71EkWs41oza/Er9Zno9XuTkRYCPDUJXQ== +jest-util@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.5.1.tgz#3ba9771e8e31a0b85da48fe0b0891fb86c01c2f9" + integrity sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw== dependencies: - "@jest/types" "^27.4.2" + "@jest/types" "^27.5.1" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + +jest-validate@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.5.1.tgz#9197d54dc0bdb52260b8db40b46ae668e04df067" + integrity sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ== + dependencies: + "@jest/types" "^27.5.1" camelcase "^6.2.0" chalk "^4.0.0" - jest-get-type "^27.4.0" + jest-get-type "^27.5.1" leven "^3.1.0" - pretty-format "^27.4.6" + pretty-format "^27.5.1" jest-watch-typeahead@^1.0.0: version "1.0.0" @@ -7050,7 +7185,7 @@ jest-watch-typeahead@^1.0.0: string-length "^5.0.1" strip-ansi "^7.0.1" -jest-watcher@^27.0.0, jest-watcher@^27.4.6: +jest-watcher@^27.0.0: version "27.4.6" resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-27.4.6.tgz#673679ebeffdd3f94338c24f399b85efc932272d" integrity sha512-yKQ20OMBiCDigbD0quhQKLkBO+ObGN79MO4nT7YaCuQ5SM+dkBNWE8cZX0FjU6czwMvWw6StWbe+Wv4jJPJ+fw== @@ -7063,7 +7198,20 @@ jest-watcher@^27.0.0, jest-watcher@^27.4.6: jest-util "^27.4.2" string-length "^4.0.1" -jest-worker@^27.4.1, jest-worker@^27.4.6: +jest-watcher@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-27.5.1.tgz#71bd85fb9bde3a2c2ec4dc353437971c43c642a2" + integrity sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw== + dependencies: + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + jest-util "^27.5.1" + string-length "^4.0.1" + +jest-worker@^27.4.1: version "27.4.6" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.4.6.tgz#5d2d93db419566cb680752ca0792780e71b3273e" integrity sha512-gHWJF/6Xi5CTG5QCvROr6GcmpIqNYpDJyc8A1h/DyXqH1tD6SnRCM0d3U5msV31D2LB/U+E0M+W4oyvKV44oNw== @@ -7072,14 +7220,23 @@ jest-worker@^27.4.1, jest-worker@^27.4.6: merge-stream "^2.0.0" supports-color "^8.0.0" +jest-worker@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + jest@^27.0.3: - version "27.4.7" - resolved "https://registry.yarnpkg.com/jest/-/jest-27.4.7.tgz#87f74b9026a1592f2da05b4d258e57505f28eca4" - integrity sha512-8heYvsx7nV/m8m24Vk26Y87g73Ba6ueUd0MWed/NXMhSZIm62U/llVbS0PJe1SHunbyXjJ/BqG1z9bFjGUIvTg== + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest/-/jest-27.5.1.tgz#dadf33ba70a779be7a6fc33015843b51494f63fc" + integrity sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ== dependencies: - "@jest/core" "^27.4.7" + "@jest/core" "^27.5.1" import-local "^3.0.2" - jest-cli "^27.4.7" + jest-cli "^27.5.1" js-tokens@^4.0.0: version "4.0.0" @@ -7360,9 +7517,9 @@ lines-and-columns@^1.1.6: integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== lint-staged@^12.1.7: - version "12.3.2" - resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-12.3.2.tgz#c87fe59dca475b7d1cb56863c5faa03c145e1446" - integrity sha512-gtw4Cbj01SuVSfAOXC6ivd/7VKHTj51yj5xV8TgktFmYNMsZzXuSd5/brqJEA93v63wL7R6iDlunMANOechC0A== + version "12.3.4" + resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-12.3.4.tgz#4b1ff8c394c3e6da436aaec5afd4db18b5dac360" + integrity sha512-yv/iK4WwZ7/v0GtVkNb3R82pdL9M+ScpIbJLJNyCXkJ1FGaXvRCOg/SeL59SZtPpqZhE7BD6kPKFLIDUhDx2/w== dependencies: cli-truncate "^3.1.0" colorette "^2.0.16" @@ -7726,7 +7883,7 @@ mem-fs@^1.1.0: vinyl "^2.0.1" vinyl-file "^3.0.0" -memfs@^3.2.2: +memfs@^3.4.1: version "3.4.1" resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.4.1.tgz#b78092f466a0dce054d63d39275b24c71d3f1305" integrity sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw== @@ -8750,7 +8907,7 @@ parse-json@^4.0.0: error-ex "^1.3.1" json-parse-better-errors "^1.0.1" -parse-json@^5.0.0: +parse-json@^5.0.0, parse-json@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== @@ -8954,6 +9111,15 @@ pretty-format@^27.0.0, pretty-format@^27.4.6: ansi-styles "^5.0.0" react-is "^17.0.1" +pretty-format@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" + integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== + dependencies: + ansi-regex "^5.0.1" + ansi-styles "^5.0.0" + react-is "^17.0.1" + process-nextick-args@^2.0.0, process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" @@ -11020,30 +11186,31 @@ webpack-bundle-analyzer@^4.3.0: sirv "^1.0.7" ws "^7.3.1" -webpack-dev-middleware@^5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-5.3.0.tgz#8fc02dba6e72e1d373eca361623d84610f27be7c" - integrity sha512-MouJz+rXAm9B1OTOYaJnn6rtD/lWZPy2ufQCH3BPs8Rloh/Du6Jze4p7AeLYHkVi0giJnYLaSGDC7S+GM9arhg== +webpack-dev-middleware@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-5.3.1.tgz#aa079a8dedd7e58bfeab358a9af7dab304cee57f" + integrity sha512-81EujCKkyles2wphtdrnPg/QqegC/AtqNH//mQkBYSMqwFVCQrxM6ktB2O/SPlZy7LqeEfTbV3cZARGQz6umhg== dependencies: colorette "^2.0.10" - memfs "^3.2.2" + memfs "^3.4.1" mime-types "^2.1.31" range-parser "^1.2.1" schema-utils "^4.0.0" -webpack-dev-server@^4.7.2: - version "4.7.3" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.7.3.tgz#4e995b141ff51fa499906eebc7906f6925d0beaa" - integrity sha512-mlxq2AsIw2ag016nixkzUkdyOE8ST2GTy34uKSABp1c4nhjZvH90D5ZRR+UOLSsG4Z3TFahAi72a3ymRtfRm+Q== +webpack-dev-server@^4.7.4: + version "4.7.4" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.7.4.tgz#d0ef7da78224578384e795ac228d8efb63d5f945" + integrity sha512-nfdsb02Zi2qzkNmgtZjkrMOcXnYZ6FLKcQwpxT7MvmHKc+oTtDsBju8j+NMyAygZ9GW1jMEUpy3itHtqgEhe1A== dependencies: "@types/bonjour" "^3.5.9" "@types/connect-history-api-fallback" "^1.3.5" + "@types/express" "^4.17.13" "@types/serve-index" "^1.9.1" "@types/sockjs" "^0.3.33" "@types/ws" "^8.2.2" ansi-html-community "^0.0.8" bonjour "^3.5.0" - chokidar "^3.5.2" + chokidar "^3.5.3" colorette "^2.0.10" compression "^1.7.4" connect-history-api-fallback "^1.6.0" @@ -11063,8 +11230,8 @@ webpack-dev-server@^4.7.2: sockjs "^0.3.21" spdy "^4.0.2" strip-ansi "^7.0.0" - webpack-dev-middleware "^5.3.0" - ws "^8.1.0" + webpack-dev-middleware "^5.3.1" + ws "^8.4.2" webpack-merge@^5.7.3: version "5.8.0" @@ -11080,12 +11247,12 @@ webpack-sources@^3.2.3: integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== webpack@^5.67.0: - version "5.67.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.67.0.tgz#cb43ca2aad5f7cc81c4cd36b626e6b819805dbfd" - integrity sha512-LjFbfMh89xBDpUMgA1W9Ur6Rn/gnr2Cq1jjHFPo4v6a79/ypznSYbAyPgGhwsxBtMIaEmDD1oJoA7BEYw/Fbrw== + version "5.69.1" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.69.1.tgz#8cfd92c192c6a52c99ab00529b5a0d33aa848dc5" + integrity sha512-+VyvOSJXZMT2V5vLzOnDuMz5GxEqLk7hKWQ56YxPW/PQRUuKimPqmEIJOx8jHYeyo65pKbapbW464mvsKbaj4A== dependencies: - "@types/eslint-scope" "^3.7.0" - "@types/estree" "^0.0.50" + "@types/eslint-scope" "^3.7.3" + "@types/estree" "^0.0.51" "@webassemblyjs/ast" "1.11.1" "@webassemblyjs/wasm-edit" "1.11.1" "@webassemblyjs/wasm-parser" "1.11.1" @@ -11301,7 +11468,7 @@ ws@^7.3.1, ws@^7.4.6: resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.6.tgz#e59fc509fb15ddfb65487ee9765c5a51dec5fe7b" integrity sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA== -ws@^8.1.0: +ws@^8.4.2: version "8.4.2" resolved "https://registry.yarnpkg.com/ws/-/ws-8.4.2.tgz#18e749868d8439f2268368829042894b6907aa0b" integrity sha512-Kbk4Nxyq7/ZWqr/tarI9yIt/+iNNFOjBXEWgTb4ydaNHBNGgvf2QHbS9fdfsndfjFlFwEd4Al+mw83YkaD10ZA== From 1036a22c78dc1f47b8ed5326faf176668f8c38f7 Mon Sep 17 00:00:00 2001 From: Rishabh Chawla Date: Fri, 25 Feb 2022 11:59:46 +0530 Subject: [PATCH 14/15] chore: types --- packages/create-webpack-app/bin/index.js | 0 packages/create-webpack-app/src/generator.ts | 10 +++--- packages/create-webpack-app/src/handlers.ts | 2 +- packages/create-webpack-app/src/helpers.ts | 28 +++++++-------- packages/create-webpack-app/src/index.ts | 2 +- .../create-webpack-app/src/scaffold-utils.ts | 4 +-- .../create-webpack-app/src/types/index.ts | 8 ++--- yarn.lock | 36 +++++++------------ 8 files changed, 40 insertions(+), 50 deletions(-) mode change 100644 => 100755 packages/create-webpack-app/bin/index.js diff --git a/packages/create-webpack-app/bin/index.js b/packages/create-webpack-app/bin/index.js old mode 100644 new mode 100755 diff --git a/packages/create-webpack-app/src/generator.ts b/packages/create-webpack-app/src/generator.ts index 7d97caa84c4..b9908569911 100644 --- a/packages/create-webpack-app/src/generator.ts +++ b/packages/create-webpack-app/src/generator.ts @@ -17,17 +17,17 @@ import handlers from "./handlers"; */ export default class Generator extends CustomGenerator { public answers: Record; - public configurationPath: string; + public configurationPath!: string; public force: boolean; public generationPath: string; - public packageManager: string; + public packageManager!: string; public resolvedGenerationPath: string; public supportedTemplates: string[]; public template: string; // eslint-disable-next-line @typescript-eslint/no-explicit-any public cli: any; - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any + // eslint-disable-next-line @typescript-eslint/no-explicit-any public constructor(args: any, opts: any) { super(args, opts); @@ -59,7 +59,7 @@ export default class Generator extends CustomGenerator { } } - this.template = await getTemplate.call(this); + this.template = await getTemplate(this); await handlers[this.template].questions(this, Question); @@ -83,7 +83,7 @@ export default class Generator extends CustomGenerator { } public async installPlugins(): Promise { - this.packageManager = await getInstaller.call(this); + this.packageManager = await getInstaller(this); const opts: { dev?: boolean; diff --git a/packages/create-webpack-app/src/handlers.ts b/packages/create-webpack-app/src/handlers.ts index 9d3a213686f..bbec8ff339a 100644 --- a/packages/create-webpack-app/src/handlers.ts +++ b/packages/create-webpack-app/src/handlers.ts @@ -2,4 +2,4 @@ import * as defaultHandler from "./handlers/default"; export default { default: defaultHandler, -}; +} as Record; diff --git a/packages/create-webpack-app/src/helpers.ts b/packages/create-webpack-app/src/helpers.ts index d63b2187988..596bac8af37 100644 --- a/packages/create-webpack-app/src/helpers.ts +++ b/packages/create-webpack-app/src/helpers.ts @@ -1,3 +1,4 @@ +import Generator from "./generator"; import { List } from "./scaffold-utils"; const regex = /[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g; @@ -8,7 +9,7 @@ const regex = /[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+ * @returns output string */ export function toKebabCase(str: string): string { - return str.match(regex).join("-").toLowerCase(); + return (str.match(regex) as string[]).join("-").toLowerCase(); } /** @@ -17,44 +18,43 @@ export function toKebabCase(str: string): string { * @returns {string} output string */ export function toUpperCamelCase(str: string): string { - return str - .match(regex) + return (str.match(regex) as string[]) .map((x) => x.slice(0, 1).toUpperCase() + x.slice(1).toLowerCase()) .join(""); } -export async function getInstaller(): Promise { - const installers = this.cli.getAvailablePackageManagers(); +export async function getInstaller(self: Generator): Promise { + const installers = self.cli.getAvailablePackageManagers(); if (installers.length === 1) { return installers[0]; } // Prompt for the package manager of choice - const defaultPackager = this.cli.getDefaultPackageManager(); + const defaultPackager = self.cli.getDefaultPackageManager(); const { packager } = await List( - this, + self, "packager", "Pick a package manager:", installers, defaultPackager, - this.force, + self.force, ); return packager; } -export async function getTemplate(): Promise { - if (this.supportedTemplates.includes(this.template)) { - return this.template; +export async function getTemplate(self: Generator): Promise { + if (self.supportedTemplates.includes(self.template)) { + return self.template; } - this.cli.logger.warn(`⚠ ${this.template} is not a valid template, please select one from below`); + self.cli.logger.warn(`⚠ ${self.template} is not a valid template, please select one from below`); const { selectedTemplate } = await List( - this, + self, "selectedTemplate", "Select a valid template from below:", - this.supportedTemplates, + self.supportedTemplates, "default", false, ); diff --git a/packages/create-webpack-app/src/index.ts b/packages/create-webpack-app/src/index.ts index a0894434fe4..848763a45de 100644 --- a/packages/create-webpack-app/src/index.ts +++ b/packages/create-webpack-app/src/index.ts @@ -57,7 +57,7 @@ export default function (): void { }); const generatorName = "create-webpack-app-generator"; - env.registerStub(Generator, generatorName); + env.registerStub(Generator as never, generatorName); env.run(generatorName, { cli: { colors, logger }, options }, () => { logger.success("Project has been initialised with webpack!"); diff --git a/packages/create-webpack-app/src/scaffold-utils.ts b/packages/create-webpack-app/src/scaffold-utils.ts index 5c07b15513f..ec9749e0068 100644 --- a/packages/create-webpack-app/src/scaffold-utils.ts +++ b/packages/create-webpack-app/src/scaffold-utils.ts @@ -10,7 +10,7 @@ export function List( name: string, message: string, choices: string[], - defaultChoice?: string, + defaultChoice: string, skip = false, ): CustomGeneratorStringPrompt { if (skip) { @@ -24,7 +24,7 @@ export function Input( self: Generator, name: string, message: string, - defaultChoice?: string, + defaultChoice: string, skip = false, ): CustomGeneratorStringPrompt { if (skip) { diff --git a/packages/create-webpack-app/src/types/index.ts b/packages/create-webpack-app/src/types/index.ts index 791abe1ae2f..de1cfb94aa6 100644 --- a/packages/create-webpack-app/src/types/index.ts +++ b/packages/create-webpack-app/src/types/index.ts @@ -1,8 +1,8 @@ import Generator from "yeoman-generator"; export class CustomGenerator extends Generator { - public force: boolean; - public dependencies: string[]; - public answers: Record; - public configurationPath: string; + public force!: boolean; + public dependencies!: string[]; + public answers!: Record; + public configurationPath!: string; } diff --git a/yarn.lock b/yarn.lock index d2e6c72e2d8..7ceec13f5b6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2357,6 +2357,18 @@ "@types/yeoman-environment" "*" rxjs "^6.4.0" +"@types/yeoman-generator@^4.11.3": + version "4.11.4" + resolved "https://registry.yarnpkg.com/@types/yeoman-generator/-/yeoman-generator-4.11.4.tgz#bc0ff86a5d28cb2c6456ecb3dcef1364ffbc764e" + integrity sha512-JB0rxFS8oskkKLALii9y3Tb6DQaLQ/bxBU6nUIAh9e/47T1PvVODfMlj1Hkxrw/rzNhgGOzkG/xiOHL5EsqCag== + dependencies: + "@types/debug" "*" + "@types/ejs" "*" + "@types/inquirer" "*" + "@types/mem-fs-editor" "*" + "@types/yeoman-environment" "*" + rxjs "^6.4.0" + "@typescript-eslint/eslint-plugin@^5.10.1": version "5.12.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.12.1.tgz#b2cd3e288f250ce8332d5035a2ff65aba3374ac4" @@ -4368,11 +4380,6 @@ detect-indent@^6.0.0: resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== -detect-newline@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" - integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== - detect-node@^2.0.4: version "2.1.0" resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" @@ -4391,11 +4398,6 @@ diff-sequences@^27.5.1: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327" integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ== -diff-sequences@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327" - integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ== - diff@^3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" @@ -6814,10 +6816,7 @@ jest-docblock@^27.5.1: resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.5.1.tgz#14092f364a42c6108d42c33c8cf30e058e25f6c0" integrity sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ== dependencies: - chalk "^4.0.0" - diff-sequences "^27.5.1" - jest-get-type "^27.5.1" - pretty-format "^27.5.1" + detect-newline "^3.0.0" jest-each@^27.5.1: version "27.5.1" @@ -9065,15 +9064,6 @@ pretty-format@^27.0.0, pretty-format@^27.4.6, pretty-format@^27.5.1: ansi-styles "^5.0.0" react-is "^17.0.1" -pretty-format@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" - integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== - dependencies: - ansi-regex "^5.0.1" - ansi-styles "^5.0.0" - react-is "^17.0.1" - process-nextick-args@^2.0.0, process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" From 681bf69372650dee3b39b019373a1e13c5ff3af2 Mon Sep 17 00:00:00 2001 From: Rishabh Chawla Date: Fri, 25 Feb 2022 12:41:17 +0530 Subject: [PATCH 15/15] chore: types --- packages/create-webpack-app/src/handlers/default.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/create-webpack-app/src/handlers/default.ts b/packages/create-webpack-app/src/handlers/default.ts index 0c04fb8bf3a..e4251ceaa46 100644 --- a/packages/create-webpack-app/src/handlers/default.ts +++ b/packages/create-webpack-app/src/handlers/default.ts @@ -36,7 +36,7 @@ export async function questions( break; } - // Configure devServer configuraion + // Configure devServer configuration const { devServer } = await Question.Confirm( self, "devServer",