From f910fe3c217897d1cdefa2dfaa731caacb3cb69b Mon Sep 17 00:00:00 2001 From: Uzair Date: Mon, 13 May 2024 22:29:25 +0530 Subject: [PATCH 01/81] feat: init new package and installed deps - init create-webpack-app package - installed deps - plop - minimist --- packages/create-webpack-app/README.md | 11 ++++++ packages/create-webpack-app/package.json | 45 ++++++++++++++++++++++++ tsconfig.json | 3 ++ 3 files changed, 59 insertions(+) create mode 100644 packages/create-webpack-app/README.md create mode 100644 packages/create-webpack-app/package.json diff --git a/packages/create-webpack-app/README.md b/packages/create-webpack-app/README.md new file mode 100644 index 00000000000..26d10e07bcb --- /dev/null +++ b/packages/create-webpack-app/README.md @@ -0,0 +1,11 @@ +# `create-webpack-app` + +> TODO: description + +## Usage + +```js +const createWebpackApp = require("create-webpack-app"); + +// TODO: DEMONSTRATE API +``` diff --git a/packages/create-webpack-app/package.json b/packages/create-webpack-app/package.json new file mode 100644 index 00000000000..99205217f63 --- /dev/null +++ b/packages/create-webpack-app/package.json @@ -0,0 +1,45 @@ +{ + "name": "create-webpack-app", + "version": "1.0.0", + "description": "CLI for scaffolding webpack projects using default config, framework templates, loader or plugins templates", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/webpack/create-webpack-app.git" + }, + "homepage": "https://github.com/webpack/webpack-cli/tree/master/packages/create-webpack-app", + "bugs": "https://github.com/webpack/webpack-cli/issues", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "bin": { + "create-webpack-app": "./bin/index.js" + }, + "main": "./lib/index.js", + "engines": { + "node": ">=14.15.0" + }, + "keywords": [ + "webpack", + "cli", + "scaffolding", + "module", + "bundler", + "web", + "frameworks" + ], + "files": [ + "bin", + "lib", + "!**/*__tests__" + ], + "dependencies": { + "minimist": "^1.2.8", + "node-plop": "^0.32.0", + "plop": "^4.0.1" + }, + "devDependencies": {}, + "peerDependencies": {}, + "peerDependenciesMeta": {} +} diff --git a/tsconfig.json b/tsconfig.json index 796ecc1c96e..7a8f5bdf817 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -35,6 +35,9 @@ }, { "path": "packages/webpack-cli" + }, + { + "path": "packages/create-webpack-app" } ] } From c175c9d80e649e04568d1d7ea18edc0c34853889 Mon Sep 17 00:00:00 2001 From: Uzair Date: Mon, 13 May 2024 22:33:40 +0530 Subject: [PATCH 02/81] feat: add template directory from prototype added template files which containe handlebar templates and other common files --- .../create-webpack-app/templates/base-less.hbs | 10 ++++++++++ .../create-webpack-app/templates/base-sass.hbs | 9 +++++++++ packages/create-webpack-app/templates/base.hbs | 3 +++ .../templates/default/.babelrc | 11 +++++++++++ .../templates/default/README.md | 15 +++++++++++++++ .../templates/default/index.js | 1 + .../templates/default/package.json.hbs | 9 +++++++++ .../templates/default/postcss.config.js | 5 +++++ .../templates/default/src/index.ts | 2 ++ .../templates/default/template.html.hbs | 18 ++++++++++++++++++ .../templates/default/tsconfig.json | 10 ++++++++++ .../templates/default/webpack.config.js.hbs | 18 ++++++++++++++++++ .../templates/webpack.config.hbs | 4 ++++ 13 files changed, 115 insertions(+) create mode 100644 packages/create-webpack-app/templates/base-less.hbs create mode 100644 packages/create-webpack-app/templates/base-sass.hbs create mode 100644 packages/create-webpack-app/templates/base.hbs 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.hbs create mode 100644 packages/create-webpack-app/templates/default/postcss.config.js create mode 100644 packages/create-webpack-app/templates/default/src/index.ts create mode 100644 packages/create-webpack-app/templates/default/template.html.hbs create mode 100644 packages/create-webpack-app/templates/default/tsconfig.json create mode 100644 packages/create-webpack-app/templates/default/webpack.config.js.hbs create mode 100644 packages/create-webpack-app/templates/webpack.config.hbs diff --git a/packages/create-webpack-app/templates/base-less.hbs b/packages/create-webpack-app/templates/base-less.hbs new file mode 100644 index 00000000000..4829b625843 --- /dev/null +++ b/packages/create-webpack-app/templates/base-less.hbs @@ -0,0 +1,10 @@ +{{#projectName}} + /src /public /package.json /README.md /webpack.config.js /styles /styles.less +{{/projectName}} + +{{#if selectedLess}} + // Add loader configuration for Less in webpack.config.js module.exports = { // ... existing + configuration from base.hbs (replace with actual configuration) module: { rules: [ // ... existing + rules from base.hbs { test: /\.less$/, use: [ 'style-loader', 'css-loader', 'less-loader', ], }, + ], }, }; +{{/if}} \ No newline at end of file diff --git a/packages/create-webpack-app/templates/base-sass.hbs b/packages/create-webpack-app/templates/base-sass.hbs new file mode 100644 index 00000000000..b14b52e0f08 --- /dev/null +++ b/packages/create-webpack-app/templates/base-sass.hbs @@ -0,0 +1,9 @@ +{{#projectName}} + /src /public /package.json /README.md /webpack.config.js /styles /styles.scss +{{/projectName}} +{{#if selectedSass}} + // Add loader configuration for Sass in webpack.config.js module.exports = { // ... existing + configuration from base.hbs (replace with actual configuration) module: { rules: [ // ... existing + rules from base.hbs { test: /\.scss$/, use: [ 'style-loader', 'css-loader', 'sass-loader', ], }, + ], }, }; +{{/if}} \ No newline at end of file diff --git a/packages/create-webpack-app/templates/base.hbs b/packages/create-webpack-app/templates/base.hbs new file mode 100644 index 00000000000..f400e5f52d1 --- /dev/null +++ b/packages/create-webpack-app/templates/base.hbs @@ -0,0 +1,3 @@ +{{#projectName}} + /src /public /package.json /README.md /webpack.config.js +{{/projectName}} \ No newline at end of file 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.hbs b/packages/create-webpack-app/templates/default/package.json.hbs new file mode 100644 index 00000000000..f2e913213d9 --- /dev/null +++ b/packages/create-webpack-app/templates/default/package.json.hbs @@ -0,0 +1,9 @@ +{ "version": "1.0.0", "description": +{{description}}, "name": +{{projectName}}, "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}} + "serve": "webpack serve" +{{/if}} +} } \ No newline at end of file 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/src/index.ts b/packages/create-webpack-app/templates/default/src/index.ts new file mode 100644 index 00000000000..8dfab679d73 --- /dev/null +++ b/packages/create-webpack-app/templates/default/src/index.ts @@ -0,0 +1,2 @@ +const helloWorld = "Hello World!"; +console.log(helloWorld); diff --git a/packages/create-webpack-app/templates/default/template.html.hbs b/packages/create-webpack-app/templates/default/template.html.hbs new file mode 100644 index 00000000000..0acfbcce6d0 --- /dev/null +++ b/packages/create-webpack-app/templates/default/template.html.hbs @@ -0,0 +1,18 @@ + + + + Webpack App + + +

Hello world!

+

Tip: Check your console

+ + {{#if workboxWebpackPlugin}} + + {{/if}} + \ No newline at end of file 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.config.js.hbs b/packages/create-webpack-app/templates/default/webpack.config.js.hbs new file mode 100644 index 00000000000..a8b1b40554d --- /dev/null +++ b/packages/create-webpack-app/templates/default/webpack.config.js.hbs @@ -0,0 +1,18 @@ +{{#each plugins}} + const + {{this}} + = require('{{this}}'); +{{/each}} + +module.exports = { // ... existing base configuration (replace with your actual configuration) +entry: './src/index.js', output: { path: path.resolve(__dirname, 'dist'), }, modules: { rules: [ // +Add loaders here +{{#each loaders}} + { test: /\.{{this}}$/, use: [ 'style-loader', 'css-loader', '{{this}}-loader', ], }, +{{/each}} +], }, plugins: [ // Add plugins here +{{#each plugins}} + new + {{this}}(), +{{/each}} +], }; \ No newline at end of file diff --git a/packages/create-webpack-app/templates/webpack.config.hbs b/packages/create-webpack-app/templates/webpack.config.hbs new file mode 100644 index 00000000000..1e1675b5d90 --- /dev/null +++ b/packages/create-webpack-app/templates/webpack.config.hbs @@ -0,0 +1,4 @@ +module.exports = { // ... existing base configuration (replace with your actual configuration) +entry: './src/index.js', output: { path: path.resolve(__dirname, 'dist'), }, modules: { rules: [ // +Add loaders here { test: /\.less$/, use: [ 'style-loader', 'css-loader', 'less-loader', ], }, ], }, +plugins: [ // Add plugins here ], }; \ No newline at end of file From 12fbc202efabdd6d8310d4601f444c843cb37633 Mon Sep 17 00:00:00 2001 From: Uzair Date: Wed, 15 May 2024 19:17:16 +0530 Subject: [PATCH 03/81] build: typescript init init .tsconfig --- packages/create-webpack-app/src/index.ts | 1 + packages/create-webpack-app/tsconfig.json | 14 ++++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 packages/create-webpack-app/src/index.ts create mode 100644 packages/create-webpack-app/tsconfig.json diff --git a/packages/create-webpack-app/src/index.ts b/packages/create-webpack-app/src/index.ts new file mode 100644 index 00000000000..2e533d1536d --- /dev/null +++ b/packages/create-webpack-app/src/index.ts @@ -0,0 +1 @@ +console.log("Typescript initialized!"); diff --git a/packages/create-webpack-app/tsconfig.json b/packages/create-webpack-app/tsconfig.json new file mode 100644 index 00000000000..991ba583366 --- /dev/null +++ b/packages/create-webpack-app/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../../tsconfig.json", + "exclude": ["src/utils/__tests__"], + "compilerOptions": { + "outDir": "lib", + "rootDir": "src" + }, + "include": ["src"], + "references": [ + { + "path": "../webpack-cli" + } + ] +} From 92c2f1951dbe9c78e951519f0899eb7a08e31de7 Mon Sep 17 00:00:00 2001 From: Uzair Date: Wed, 15 May 2024 19:18:17 +0530 Subject: [PATCH 04/81] build: set type: module in package.json --- packages/create-webpack-app/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/create-webpack-app/package.json b/packages/create-webpack-app/package.json index 99205217f63..00f48c72912 100644 --- a/packages/create-webpack-app/package.json +++ b/packages/create-webpack-app/package.json @@ -7,6 +7,7 @@ "type": "git", "url": "https://github.com/webpack/create-webpack-app.git" }, + "type": "module", "homepage": "https://github.com/webpack/webpack-cli/tree/master/packages/create-webpack-app", "bugs": "https://github.com/webpack/webpack-cli/issues", "funding": { From 488f3dfe7c812e5d458b42200d7cbda4ac57904d Mon Sep 17 00:00:00 2001 From: Uzair Date: Thu, 16 May 2024 20:29:30 +0530 Subject: [PATCH 05/81] feat: change template to align more with generators package change the internal structure of template files --- .eslintignore | 3 + .../templates/base-less.hbs | 10 -- .../templates/base-sass.hbs | 9 -- .../create-webpack-app/templates/base.hbs | 3 - .../templates/default/package.json.hbs | 9 -- .../templates/default/src/index.ts | 2 - .../templates/default/webpack.config.js.hbs | 18 --- .../templates/{ => init}/default/.babelrc | 0 .../templates/{ => init}/default/README.md | 4 +- .../templates/{ => init}/default/index.js | 0 .../templates/init/default/package.json.hbs | 8 ++ .../{ => init}/default/postcss.config.js | 0 .../templates/init/default/src/index.ts | 1 + .../{ => init}/default/template.html.hbs | 0 .../{ => init}/default/tsconfig.json | 0 .../init/default/webpack.configjs.hbs | 75 +++++++++++++ .../templates/init/react/index.d.ts.tpl | 8 ++ .../templates/init/react/index.html.tpl | 12 ++ .../templates/init/react/package.json.tpl | 12 ++ .../templates/init/react/src/App.js.tpl | 13 +++ .../templates/init/react/src/App.tsx.tpl | 13 +++ .../init/react/src/assets/webpack.png.tpl | Bin 0 -> 2337 bytes .../templates/init/react/src/index.js.tpl | 12 ++ .../templates/init/react/src/index.tsx.tpl | 12 ++ .../init/react/src/styles/global.css.tpl | 18 +++ .../init/react/src/styles/global.less.tpl | 18 +++ .../init/react/src/styles/global.scss.tpl | 18 +++ .../init/react/src/styles/global.styl.tpl | 18 +++ .../templates/init/react/tsconfig.json.tpl | 12 ++ .../init/react/webpack.config.js.tpl | 105 ++++++++++++++++++ .../templates/webpack.config.hbs | 4 - 31 files changed, 360 insertions(+), 57 deletions(-) delete mode 100644 packages/create-webpack-app/templates/base-less.hbs delete mode 100644 packages/create-webpack-app/templates/base-sass.hbs delete mode 100644 packages/create-webpack-app/templates/base.hbs delete mode 100644 packages/create-webpack-app/templates/default/package.json.hbs delete mode 100644 packages/create-webpack-app/templates/default/src/index.ts delete mode 100644 packages/create-webpack-app/templates/default/webpack.config.js.hbs rename packages/create-webpack-app/templates/{ => init}/default/.babelrc (100%) rename packages/create-webpack-app/templates/{ => init}/default/README.md (91%) rename packages/create-webpack-app/templates/{ => init}/default/index.js (100%) create mode 100644 packages/create-webpack-app/templates/init/default/package.json.hbs rename packages/create-webpack-app/templates/{ => init}/default/postcss.config.js (100%) create mode 100644 packages/create-webpack-app/templates/init/default/src/index.ts rename packages/create-webpack-app/templates/{ => init}/default/template.html.hbs (100%) rename packages/create-webpack-app/templates/{ => init}/default/tsconfig.json (100%) create mode 100644 packages/create-webpack-app/templates/init/default/webpack.configjs.hbs create mode 100644 packages/create-webpack-app/templates/init/react/index.d.ts.tpl create mode 100644 packages/create-webpack-app/templates/init/react/index.html.tpl create mode 100644 packages/create-webpack-app/templates/init/react/package.json.tpl create mode 100644 packages/create-webpack-app/templates/init/react/src/App.js.tpl create mode 100644 packages/create-webpack-app/templates/init/react/src/App.tsx.tpl create mode 100644 packages/create-webpack-app/templates/init/react/src/assets/webpack.png.tpl create mode 100644 packages/create-webpack-app/templates/init/react/src/index.js.tpl create mode 100644 packages/create-webpack-app/templates/init/react/src/index.tsx.tpl create mode 100644 packages/create-webpack-app/templates/init/react/src/styles/global.css.tpl create mode 100644 packages/create-webpack-app/templates/init/react/src/styles/global.less.tpl create mode 100644 packages/create-webpack-app/templates/init/react/src/styles/global.scss.tpl create mode 100644 packages/create-webpack-app/templates/init/react/src/styles/global.styl.tpl create mode 100644 packages/create-webpack-app/templates/init/react/tsconfig.json.tpl create mode 100644 packages/create-webpack-app/templates/init/react/webpack.config.js.tpl delete mode 100644 packages/create-webpack-app/templates/webpack.config.hbs diff --git a/.eslintignore b/.eslintignore index 91718ffa7a2..75873a14a25 100644 --- a/.eslintignore +++ b/.eslintignore @@ -11,3 +11,6 @@ test/build/config/error-commonjs/syntax-error.js test/build/config/error-mjs/syntax-error.mjs test/build/config/error-array/webpack.config.js test/configtest/with-config-path/syntax-error.config.js + +packages/create-webpack-app + diff --git a/packages/create-webpack-app/templates/base-less.hbs b/packages/create-webpack-app/templates/base-less.hbs deleted file mode 100644 index 4829b625843..00000000000 --- a/packages/create-webpack-app/templates/base-less.hbs +++ /dev/null @@ -1,10 +0,0 @@ -{{#projectName}} - /src /public /package.json /README.md /webpack.config.js /styles /styles.less -{{/projectName}} - -{{#if selectedLess}} - // Add loader configuration for Less in webpack.config.js module.exports = { // ... existing - configuration from base.hbs (replace with actual configuration) module: { rules: [ // ... existing - rules from base.hbs { test: /\.less$/, use: [ 'style-loader', 'css-loader', 'less-loader', ], }, - ], }, }; -{{/if}} \ No newline at end of file diff --git a/packages/create-webpack-app/templates/base-sass.hbs b/packages/create-webpack-app/templates/base-sass.hbs deleted file mode 100644 index b14b52e0f08..00000000000 --- a/packages/create-webpack-app/templates/base-sass.hbs +++ /dev/null @@ -1,9 +0,0 @@ -{{#projectName}} - /src /public /package.json /README.md /webpack.config.js /styles /styles.scss -{{/projectName}} -{{#if selectedSass}} - // Add loader configuration for Sass in webpack.config.js module.exports = { // ... existing - configuration from base.hbs (replace with actual configuration) module: { rules: [ // ... existing - rules from base.hbs { test: /\.scss$/, use: [ 'style-loader', 'css-loader', 'sass-loader', ], }, - ], }, }; -{{/if}} \ No newline at end of file diff --git a/packages/create-webpack-app/templates/base.hbs b/packages/create-webpack-app/templates/base.hbs deleted file mode 100644 index f400e5f52d1..00000000000 --- a/packages/create-webpack-app/templates/base.hbs +++ /dev/null @@ -1,3 +0,0 @@ -{{#projectName}} - /src /public /package.json /README.md /webpack.config.js -{{/projectName}} \ No newline at end of file diff --git a/packages/create-webpack-app/templates/default/package.json.hbs b/packages/create-webpack-app/templates/default/package.json.hbs deleted file mode 100644 index f2e913213d9..00000000000 --- a/packages/create-webpack-app/templates/default/package.json.hbs +++ /dev/null @@ -1,9 +0,0 @@ -{ "version": "1.0.0", "description": -{{description}}, "name": -{{projectName}}, "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}} - "serve": "webpack serve" -{{/if}} -} } \ No newline at end of file diff --git a/packages/create-webpack-app/templates/default/src/index.ts b/packages/create-webpack-app/templates/default/src/index.ts deleted file mode 100644 index 8dfab679d73..00000000000 --- a/packages/create-webpack-app/templates/default/src/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -const helloWorld = "Hello World!"; -console.log(helloWorld); diff --git a/packages/create-webpack-app/templates/default/webpack.config.js.hbs b/packages/create-webpack-app/templates/default/webpack.config.js.hbs deleted file mode 100644 index a8b1b40554d..00000000000 --- a/packages/create-webpack-app/templates/default/webpack.config.js.hbs +++ /dev/null @@ -1,18 +0,0 @@ -{{#each plugins}} - const - {{this}} - = require('{{this}}'); -{{/each}} - -module.exports = { // ... existing base configuration (replace with your actual configuration) -entry: './src/index.js', output: { path: path.resolve(__dirname, 'dist'), }, modules: { rules: [ // -Add loaders here -{{#each loaders}} - { test: /\.{{this}}$/, use: [ 'style-loader', 'css-loader', '{{this}}-loader', ], }, -{{/each}} -], }, plugins: [ // Add plugins here -{{#each plugins}} - new - {{this}}(), -{{/each}} -], }; \ No newline at end of file diff --git a/packages/create-webpack-app/templates/default/.babelrc b/packages/create-webpack-app/templates/init/default/.babelrc similarity index 100% rename from packages/create-webpack-app/templates/default/.babelrc rename to packages/create-webpack-app/templates/init/default/.babelrc diff --git a/packages/create-webpack-app/templates/default/README.md b/packages/create-webpack-app/templates/init/default/README.md similarity index 91% rename from packages/create-webpack-app/templates/default/README.md rename to packages/create-webpack-app/templates/init/default/README.md index fa32788f72a..0b9aa64bb33 100644 --- a/packages/create-webpack-app/templates/default/README.md +++ b/packages/create-webpack-app/templates/init/default/README.md @@ -2,13 +2,13 @@ This project has been created using **webpack-cli**, you can now run -``` +```bash npm run build ``` or -``` +```bash yarn build ``` diff --git a/packages/create-webpack-app/templates/default/index.js b/packages/create-webpack-app/templates/init/default/index.js similarity index 100% rename from packages/create-webpack-app/templates/default/index.js rename to packages/create-webpack-app/templates/init/default/index.js diff --git a/packages/create-webpack-app/templates/init/default/package.json.hbs b/packages/create-webpack-app/templates/init/default/package.json.hbs new file mode 100644 index 00000000000..303efe61123 --- /dev/null +++ b/packages/create-webpack-app/templates/init/default/package.json.hbs @@ -0,0 +1,8 @@ +{ "version": "1.0.0", "description": "My webpack project", "name": "{{projectName}}", "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 devServer}} + "serve": "webpack serve" +{{/if}} +} } \ No newline at end of file diff --git a/packages/create-webpack-app/templates/default/postcss.config.js b/packages/create-webpack-app/templates/init/default/postcss.config.js similarity index 100% rename from packages/create-webpack-app/templates/default/postcss.config.js rename to packages/create-webpack-app/templates/init/default/postcss.config.js diff --git a/packages/create-webpack-app/templates/init/default/src/index.ts b/packages/create-webpack-app/templates/init/default/src/index.ts new file mode 100644 index 00000000000..184dfcc9987 --- /dev/null +++ b/packages/create-webpack-app/templates/init/default/src/index.ts @@ -0,0 +1 @@ +console.log("Hello, World!"); diff --git a/packages/create-webpack-app/templates/default/template.html.hbs b/packages/create-webpack-app/templates/init/default/template.html.hbs similarity index 100% rename from packages/create-webpack-app/templates/default/template.html.hbs rename to packages/create-webpack-app/templates/init/default/template.html.hbs diff --git a/packages/create-webpack-app/templates/default/tsconfig.json b/packages/create-webpack-app/templates/init/default/tsconfig.json similarity index 100% rename from packages/create-webpack-app/templates/default/tsconfig.json rename to packages/create-webpack-app/templates/init/default/tsconfig.json diff --git a/packages/create-webpack-app/templates/init/default/webpack.configjs.hbs b/packages/create-webpack-app/templates/init/default/webpack.configjs.hbs new file mode 100644 index 00000000000..6f598c78dbf --- /dev/null +++ b/packages/create-webpack-app/templates/init/default/webpack.configjs.hbs @@ -0,0 +1,75 @@ +// Generated using webpack-cli https://github.com/webpack/webpack-cli const path = require('path'); +{{#if (htmlWebpackPlugin)}} + const HtmlWebpackPlugin = require('html-webpack-plugin'); +{{/if}} +{{#isEqualToString extractPlugin "No"}} + const MiniCssExtractPlugin = require('mini-css-extract-plugin'); +{{/isEqualToString}} +{{#if (workboxWebpackPlugin)}} + const WorkboxWebpackPlugin = require('workbox-webpack-plugin>) +{{/if}} + +const isProduction = process.env.NODE_ENV === 'production'; +{{!-- {{#isEqualToString cssType 'none'}} --}} +{{!-- {{#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'; }} +{{!-- {{/if}} --}} +{{!-- {{/isEqualToString}} --}} + +const config = { entry: '{{entry}}', output: { path: path.resolve(__dirname, 'dist'), },{{#if + (devServer) +}} + devServer: { open: true, host: 'localhost', },{{/if}} +plugins: [{{#if (htmlWebpackPlugin)}} + new HtmlWebpackPlugin({ template: 'index.html', }), +{{/if}} +{{#isEqualToString extractPlugin "Yes"}} + new MiniCssExtractPlugin(), +{{/isEqualToString}} +// Add your plugins here // Learn more about plugins from +https://webpack.js.org/configuration/plugins/ ], module: { rules: [{{#isEqualToString + langType "ES6" +}} + { test: /\.(js|jsx)$/i, loader: 'babel-loader', },{{/isEqualToString}} +{{#isEqualToString langType "Typescript"}} + { test: /\.(ts|tsx)$/i, loader: 'ts-loader', exclude: ['/node_modules/'], },{{/isEqualToString}}{{#if + isCSS +}} + {{#unless isPostCSS}} + { test: /\.css$/i, use: [stylesHandler,'css-loader'], },{{/unless}}{{/if}}{{#isEqualToString + cssType "SASS" +}} + { test: /\.s[ac]ss$/i, use: [stylesHandler, 'css-loader', + {{#if (isPostCSS)}}'postcss-loader', {{/if}}'sass-loader'], },{{/isEqualToString}}{{#isEqualToString + cssType "LESS" +}} + { test: /\.less$/i, use: [stylesHandler, 'css-loader', + {{#if (isPostCSS)}}'postcss-loader', {{/if}}'less-loader'], },{{/isEqualToString}}{{#isEqualToString + cssType "Stylus" +}} + { test: /\.styl$/i, use: [stylesHandler, 'css-loader', + {{#if (isPostCSS)}}'postcss-loader', {{/if}}'stylus-loader'], },{{/isEqualToString}}{{#if + (isPostCSS) +}} + {{#if (isCSS)}} + { test: /\.css$/i, use: [stylesHandler, 'css-loader', 'postcss-loader'], },{{/if}}{{/if}} +{ test: /\.(eot|svg|ttf|woff|woff2|png|jpg|g#if)$/i, type: 'asset', }, +{{#if (htmlWebpackPlugin)}} + { test: /\.html$/i, use: ['html-loader'], },{{/if}} + +// Add your rules for custom modules here // Learn more about loaders from +https://webpack.js.org/loaders/ ], }, +{{#isEqualToString langType "Typescript"}} + resolve: { extensions: ['.tsx', '.ts', '.jsx', '.js', '...'], },{{/isEqualToString}} +}; module.exports = () => { if (isProduction) { config.mode = 'production'; +{{#isEqualToString extractPlugin "Only for Production"}} + config.plugins.push(new MiniCssExtractPlugin()); +{{/isEqualToString}} +{{#if (workboxWebpackPlugin)}} + config.plugins.push(new WorkboxWebpackPlugin.GenerateSW()); +{{/if}} +} else { config.mode = 'development'; } return config; }; \ No newline at end of file diff --git a/packages/create-webpack-app/templates/init/react/index.d.ts.tpl b/packages/create-webpack-app/templates/init/react/index.d.ts.tpl new file mode 100644 index 00000000000..38ee1835c96 --- /dev/null +++ b/packages/create-webpack-app/templates/init/react/index.d.ts.tpl @@ -0,0 +1,8 @@ +declare module "*.png"; +declare module "*.jpg"; +declare module "*.gif"; +declare module "*.svg"; +declare module "*.ttf"; +declare module "*.woff"; +declare module "*.woff2"; +declare module "*.eot"; diff --git a/packages/create-webpack-app/templates/init/react/index.html.tpl b/packages/create-webpack-app/templates/init/react/index.html.tpl new file mode 100644 index 00000000000..e7da43e690d --- /dev/null +++ b/packages/create-webpack-app/templates/init/react/index.html.tpl @@ -0,0 +1,12 @@ + + + + + + + Webpack + React + + +
+ + diff --git a/packages/create-webpack-app/templates/init/react/package.json.tpl b/packages/create-webpack-app/templates/init/react/package.json.tpl new file mode 100644 index 00000000000..14aebb6b9c7 --- /dev/null +++ b/packages/create-webpack-app/templates/init/react/package.json.tpl @@ -0,0 +1,12 @@ +{ + "version": "1.0.0", + "description": "My webpack project", + "name": "my-webpack-project", + "scripts": { + "build": "webpack --mode=production --node-env=production", + "build:dev": "webpack --mode=development", + "build:prod": "webpack --mode=production --node-env=production", + "watch": "webpack --watch", + "serve": "webpack serve" + } +} diff --git a/packages/create-webpack-app/templates/init/react/src/App.js.tpl b/packages/create-webpack-app/templates/init/react/src/App.js.tpl new file mode 100644 index 00000000000..1120de98950 --- /dev/null +++ b/packages/create-webpack-app/templates/init/react/src/App.js.tpl @@ -0,0 +1,13 @@ +import React from "react"; +import webpackLogo from "./assets/webpack.png"; + +function App() { + return ( +
+

Welcome to your React App!

+ webpack logo +
+ ); +} + +export default App; \ No newline at end of file diff --git a/packages/create-webpack-app/templates/init/react/src/App.tsx.tpl b/packages/create-webpack-app/templates/init/react/src/App.tsx.tpl new file mode 100644 index 00000000000..3471324c763 --- /dev/null +++ b/packages/create-webpack-app/templates/init/react/src/App.tsx.tpl @@ -0,0 +1,13 @@ +import React from "react"; +import webpackLogo from "./assets/webpack.png"; + +function App() { + return ( +
+

Welcome to your Typescript React App!

+ webpack logo +
+ ); +} + +export default App; \ No newline at end of file diff --git a/packages/create-webpack-app/templates/init/react/src/assets/webpack.png.tpl b/packages/create-webpack-app/templates/init/react/src/assets/webpack.png.tpl new file mode 100644 index 0000000000000000000000000000000000000000..b2390da4f06813863ee470dcfaab6d0f489fa0fc GIT binary patch literal 2337 zcmZ{mdpr{g8^CI*%S1(Uq7a0aC+T|(m^cdbdD}lD!Ik2-^>#}^X7k~RgVJPssUJLIGz0< zqq~@cMgssqYfDq(TS3&N%+T9;`Z6ss=kgusX2nhm`D6Y+D|x~ZC3k$fL{$glaCCQV z1a+eb?-5S69($PH))Zv{m5n!5N%?OWeBTb*Y*=$?U0TimqBbOR=38FU$RuL+Jyg*P zn5R~y6u`}Tt5a)}3zR`V>D<`8ppOzec1C?)Cex2gpYf9|-agLCE$r(0!v~|4(fv#+ z#R{!?cQXQhv7{nQk%fcb4dgcd?Vl;BauU1tz6smv%{7k|{agT34-dJtQkhvZzkX;V z9hfiyzw5_kJ2Ote&c&sao=6H*(tY`^Mn5~I6r7~1#7rxA6*X+^Oq_nSm<(x1WQ2iO z1o+lRa(1_$(hHM8X+(X+V2$inBUu?n0$D*NN6Fb~V!uDpe26gAmHty6OCyB^O;6^@ ziFtf}S1d}thSrZPCFOaR{XEbSET06)=BNkA9aNY6NM2A6&>}{iAQU(I#Ay;Y%bIYy z$iWW~T_-QZgFOGm%A8N5R-?bItMUPN)}$&@-)p`zXhBI<4dC$GtKfRUsE18Jf^)=C z9p#y=ptLd<#B%xIIUag8ze>U5EBP;9qeRlZm2vF4%40lNCha5_O`WG0*4Y~wx{>VPk*Ho}9FyBa z(ghy1_Nk}_dIQ}zX+&e8o7|02(vH#<>}Yl3XzKT(15TB*JY{zh;C`>3igirgm^iOv z#cQI>Z4|m`p?;{iCx>rmaCRIL^Q2dA2WlgQzno*eZUQZNY4@%dn{>^IR)!Kwhm(E@ z^jFiq#A|h}xBt8oRuVqsa#}2HmO7DGpGxQ&Me!*W1=?B0QG+12T7tdd z^I%1L5G-~kYoIF{#wssNQ^XP6Z(*q5YX4U|?I$5wb2IOk9+)O8=xNgvQK04-w^_BpWF7rYSuR|$gr=FfS{*z^_YWh ziw+WNkEjOIHX`zI5z2+<@l3)n%!Z_EqDZI2}Z9Z9Yl^}9DP+eb{`BIlokR?L;WFpLVGR99mOU*p4h zWrazXALX5zgsZW%zcdu?>+di{AEb(2=j+(K?<)mT>Y8+y`4YaIu$RVqa8~b)XtTvw z7rC;FjQ!Ct?LS#6y$zA#Ywd#tU$5@VNH_{y2h7jd4d1;DGO}ChjN9tBP&km-)u%acjy%9LGZbT8{z?jd9#NBEN+99LZjg%E3)Fo@r)|R&2$UhRseE z2N=(;(;bQJ)$@@I(`AL6YGkh0Uq7B6>;)rZ#`2?ZaV&TiFS=?u!uzf|F_Zu7tal&+ z7-RcCYy|eEE_mnx3$nC?0bx@$5Gv4m5gS+$VK^EBt!qY|IUF3X)Nt=#g?f7Tj;YAz zCLTtFgo&>Nza+RkCj%clv_;*dG3`y9o$W)BGEp*aV%~4sp82Zs4zXKKyZY-%tQn0* z7FJ~YMiE?R6h3trDCA`16kKz{$IHA;eAf$kvDkj6+Z|TqhRKZz@5hbFwd?Fk_*;>R zu1pOAH`B}2&`e5=tF<^XK3>i_^?kbL7n`9jxJZyd;nZIi-e*Tkto`M%sYXth*%YWx zH*whaNpY0FxhD#BJuTlZbU=hEZm^LqUi3DR15o-^l|if|5bKwbe?JUrZ*{b5`CyVS z{Rs6BNybsA$$4xiH+@xR%>8DIP2#EpIO-Nn5mFGH@Jx+oFmd|C{jSS8B(6`4OkM5w zi#tlLR@e#X)L-e6njA^aZ!^<^*AB zXnq3Ym1VnwmcpjVzbbg{31 +import "./styles/global.css";<% } if (cssType == 'SASS') { %> +import "./styles/global.scss";<% } if (cssType == 'LESS') { %> +import "./styles/global.less";<% } if (cssType == 'Stylus') { %> +import "./styles/global.styl";<% } %> + +const container = document.getElementById('root'); +const root = createRoot(container); +root.render(); diff --git a/packages/create-webpack-app/templates/init/react/src/index.tsx.tpl b/packages/create-webpack-app/templates/init/react/src/index.tsx.tpl new file mode 100644 index 00000000000..885b41bf176 --- /dev/null +++ b/packages/create-webpack-app/templates/init/react/src/index.tsx.tpl @@ -0,0 +1,12 @@ +import React from 'react'; +import { createRoot } from 'react-dom/client'; +import App from './App'; +<% if (cssType == 'CSS only') { %> +import "./styles/global.css";<% } if (cssType == 'SASS') { %> +import "./styles/global.scss";<% } if (cssType == 'LESS') { %> +import "./styles/global.less";<% } if (cssType == 'Stylus') { %> +import "./styles/global.styl";<% } %> + +const container = document.getElementById('root'); +const root = createRoot(container); +root.render(); \ No newline at end of file diff --git a/packages/create-webpack-app/templates/init/react/src/styles/global.css.tpl b/packages/create-webpack-app/templates/init/react/src/styles/global.css.tpl new file mode 100644 index 00000000000..67377414818 --- /dev/null +++ b/packages/create-webpack-app/templates/init/react/src/styles/global.css.tpl @@ -0,0 +1,18 @@ +html, +body { + padding: 0; + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, + Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; +} + +.heading { + font-weight: 300; +} + +.container { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; +} \ No newline at end of file diff --git a/packages/create-webpack-app/templates/init/react/src/styles/global.less.tpl b/packages/create-webpack-app/templates/init/react/src/styles/global.less.tpl new file mode 100644 index 00000000000..67377414818 --- /dev/null +++ b/packages/create-webpack-app/templates/init/react/src/styles/global.less.tpl @@ -0,0 +1,18 @@ +html, +body { + padding: 0; + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, + Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; +} + +.heading { + font-weight: 300; +} + +.container { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; +} \ No newline at end of file diff --git a/packages/create-webpack-app/templates/init/react/src/styles/global.scss.tpl b/packages/create-webpack-app/templates/init/react/src/styles/global.scss.tpl new file mode 100644 index 00000000000..67377414818 --- /dev/null +++ b/packages/create-webpack-app/templates/init/react/src/styles/global.scss.tpl @@ -0,0 +1,18 @@ +html, +body { + padding: 0; + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, + Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; +} + +.heading { + font-weight: 300; +} + +.container { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; +} \ No newline at end of file diff --git a/packages/create-webpack-app/templates/init/react/src/styles/global.styl.tpl b/packages/create-webpack-app/templates/init/react/src/styles/global.styl.tpl new file mode 100644 index 00000000000..67377414818 --- /dev/null +++ b/packages/create-webpack-app/templates/init/react/src/styles/global.styl.tpl @@ -0,0 +1,18 @@ +html, +body { + padding: 0; + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, + Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; +} + +.heading { + font-weight: 300; +} + +.container { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; +} \ No newline at end of file diff --git a/packages/create-webpack-app/templates/init/react/tsconfig.json.tpl b/packages/create-webpack-app/templates/init/react/tsconfig.json.tpl new file mode 100644 index 00000000000..d53bc6605bf --- /dev/null +++ b/packages/create-webpack-app/templates/init/react/tsconfig.json.tpl @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "jsx": "react", + "allowSyntheticDefaultImports": true, + "noImplicitAny": true, + "module": "es6", + "target": "es5", + "allowJs": true, + "moduleResolution": "node" + }, + "files": ["src/index.tsx", "index.d.ts"] +} diff --git a/packages/create-webpack-app/templates/init/react/webpack.config.js.tpl b/packages/create-webpack-app/templates/init/react/webpack.config.js.tpl new file mode 100644 index 00000000000..d75566497be --- /dev/null +++ b/packages/create-webpack-app/templates/init/react/webpack.config.js.tpl @@ -0,0 +1,105 @@ +// 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$/, + exclude: /node_modules/, + use: { + loader: "babel-loader", + options: { + presets: ["@babel/preset-env", "@babel/preset-react"], + }, + }, + },<% } %><% 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/ + ], + }, + resolve: { + alias: { + "@": path.resolve(__dirname, "./src/"), + },<% if (langType == "Typescript") {%> + extensions: ['.tsx', '.ts', '.jsx', '.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; +}; diff --git a/packages/create-webpack-app/templates/webpack.config.hbs b/packages/create-webpack-app/templates/webpack.config.hbs deleted file mode 100644 index 1e1675b5d90..00000000000 --- a/packages/create-webpack-app/templates/webpack.config.hbs +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { // ... existing base configuration (replace with your actual configuration) -entry: './src/index.js', output: { path: path.resolve(__dirname, 'dist'), }, modules: { rules: [ // -Add loaders here { test: /\.less$/, use: [ 'style-loader', 'css-loader', 'less-loader', ], }, ], }, -plugins: [ // Add plugins here ], }; \ No newline at end of file From a17fa634a9ce210a5bfdbfcd77d435403bca1b8b Mon Sep 17 00:00:00 2001 From: Uzair Date: Thu, 16 May 2024 20:42:11 +0530 Subject: [PATCH 06/81] feat: add core logic to run generators and also add types --- .../src/generators/default.ts | 1 + packages/create-webpack-app/src/index.ts | 1 - packages/create-webpack-app/src/plopfile.ts | 171 ++++++++++++++++++ packages/create-webpack-app/src/types.ts | 21 +++ 4 files changed, 193 insertions(+), 1 deletion(-) create mode 100644 packages/create-webpack-app/src/generators/default.ts delete mode 100644 packages/create-webpack-app/src/index.ts create mode 100644 packages/create-webpack-app/src/plopfile.ts create mode 100644 packages/create-webpack-app/src/types.ts diff --git a/packages/create-webpack-app/src/generators/default.ts b/packages/create-webpack-app/src/generators/default.ts new file mode 100644 index 00000000000..a57ddced116 --- /dev/null +++ b/packages/create-webpack-app/src/generators/default.ts @@ -0,0 +1 @@ +// refactored generator will be written here shortly diff --git a/packages/create-webpack-app/src/index.ts b/packages/create-webpack-app/src/index.ts deleted file mode 100644 index 2e533d1536d..00000000000 --- a/packages/create-webpack-app/src/index.ts +++ /dev/null @@ -1 +0,0 @@ -console.log("Typescript initialized!"); diff --git a/packages/create-webpack-app/src/plopfile.ts b/packages/create-webpack-app/src/plopfile.ts new file mode 100644 index 00000000000..bd1cec907ed --- /dev/null +++ b/packages/create-webpack-app/src/plopfile.ts @@ -0,0 +1,171 @@ +import { NodePlopAPI } from "./types"; +import { resolve } from "path"; +/* eslint-disable no-unused-vars */ + +export default function (plop: NodePlopAPI) { + const dependencies = ["webpack", "webpack-cli"]; + // Define a base generator for the project structure + plop.setGenerator("init", { + description: "Create a basic Webpack project", + prompts: [ + { + type: "input", + name: "projectName", + message: "Enter your project name:", + default: "webpack-project", + validate(input, _) { + if (!input) { + return "Project name cannot be empty"; + } + return true; + }, + }, + { + type: "input", + name: "configPath", + message: "Enter the project destination:", + default: ".", + filter: (input, answers) => { + return resolve(input, answers.projectName); + }, + }, + { + type: "list", + name: "langType", + message: "Which of the following JS solutions do you want to use?", + choices: ["none", "ES6", "Typescript"], + default: "none", + filter: (input, _) => { + switch (input) { + case "ES6": + dependencies.push("babel-loader", "@babel/core", "@babel/preset-env"); + break; + case "Typescript": + dependencies.push("typescript", "ts-loader"); + break; + } + return input; + }, + }, + { + type: "confirm", + name: "devServer", + message: "Would you like to use Webpack Dev server?", + default: true, + filter: (input, _) => { + if (input) { + dependencies.push("webpack-dev-server"); + } + return input; + }, + }, + { + type: "confirm", + name: "htmlWebpackPlugin", + message: "Do you want to simplify the creation of HTML files for your bundle?", + default: true, + filter: (input, _) => { + if (input) { + dependencies.push("html-webpack-plugin", "html-loader"); + } + return input; + }, + }, + { + type: "confirm", + name: "workboxWebpackPlugin", + message: "Do you want to add PWA support?", + default: true, + filter: (input, _) => { + if (input) { + dependencies.push("workbox-webpack-plugin"); + } + return input; + }, + }, + { + type: "list", + name: "cssType", + message: "Which of the following CSS solution do you want to use?", + choices: ["none", "CSS only", "SASS", "LESS", "Stylus"], + default: "Css only", + filter: (input, answers) => { + if (input === "none") { + answers.isCSS = false; + answers.isPostCSS = false; + answers.extractPlugin = "No"; + } else { + dependencies.push("style-loader", "css-loader"); + switch (input) { + case "CSS only": + answers.isCSS = true; + break; + case "SASS": + dependencies.push("sass-loader", "node-sass"); + break; + case "LESS": + dependencies.push("less-loader", "less"); + break; + case "Stylus": + dependencies.push("stylus-loader", "stylus"); + break; + } + } + return input; + }, + }, + { + type: "confirm", + name: "isCSS", + message: (answers) => + `Will you be using CSS styles along with ${answers.cssType}in your project?`, + when: (answers) => answers.cssType !== "CSS only", + default: true, + }, + { + type: "confirm", + name: "isPostCSS", + message: "Do you want to use PostCSS in your project?", + when: (answers) => answers.isCSS, + filter: (input, _) => { + if (input) { + dependencies.push("postcss-loader", "autoprefixer"); + } + return input; + }, + default: true, + }, + { + type: "list", + name: "extractPlugin", + message: "Do you want to extract CSS into separate files?", + choices: ["No", "Only for Production", "Yes"], + when: (answers) => answers.isCSS, + default: "No", + filter: (input, _) => { + if (input !== "No") { + dependencies.push("mini-css-extract-plugin"); + } + return input; + }, + }, + ], + actions: [ + { + type: "addMany", + destination: "{{configPath}}", + base: "../templates/init/default", + templateFiles: "../templates/init/default/**/*", + force: true, + verbose: true, + }, + ], + }); + plop.setHelper( + "isEqualToString", + function (this: typeof Function, value: string, comparison: string, options: any) { + return value === comparison ? options.fn(this) : options.inverse(this); + }, + ); +} +// module.exports = plop.generator("basicProject").run; diff --git a/packages/create-webpack-app/src/types.ts b/packages/create-webpack-app/src/types.ts new file mode 100644 index 00000000000..f2ad6693c44 --- /dev/null +++ b/packages/create-webpack-app/src/types.ts @@ -0,0 +1,21 @@ +export type { + ActionConfig, + ActionType, + AddActionConfig, + AddManyActionConfig, + AppendActionConfig, + CustomActionFunction, + ModifyActionConfig, + PlopCfg, + PlopGenerator, + NodePlopAPI, + PlopGeneratorConfig, + Actions, +} from "node-plop"; + +export type InitOptions = { template: string; force?: boolean }; +export type LoaderOptions = { template: string }; +export type PluginOptions = { template: string }; +export type InitGeneratorOptions = { generationPath: string } & InitOptions; +export type LoaderGeneratorOptions = { generationPath: string } & LoaderOptions; +export type PluginGeneratorOptions = { generationPath: string } & PluginOptions; From bb6ca9fe40b45472a6a67b17b8c16e78e3450e91 Mon Sep 17 00:00:00 2001 From: Uzair Date: Thu, 16 May 2024 21:00:20 +0530 Subject: [PATCH 07/81] chore: add scripts in package.json add build script add watch script --- packages/create-webpack-app/package.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/create-webpack-app/package.json b/packages/create-webpack-app/package.json index 00f48c72912..9904e14b5c7 100644 --- a/packages/create-webpack-app/package.json +++ b/packages/create-webpack-app/package.json @@ -18,6 +18,10 @@ "create-webpack-app": "./bin/index.js" }, "main": "./lib/index.js", + "scripts": { + "build": "tsc --build", + "watch": "tsc --watch" + }, "engines": { "node": ">=14.15.0" }, From d5e9fc389bf96d271ee1e0edd3b6ba0d51ec57fe Mon Sep 17 00:00:00 2001 From: Uzair Date: Thu, 16 May 2024 21:01:32 +0530 Subject: [PATCH 08/81] build: change module type to esm in package.json type: module is set in package.json --- packages/create-webpack-app/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/create-webpack-app/package.json b/packages/create-webpack-app/package.json index 9904e14b5c7..ed6338645ae 100644 --- a/packages/create-webpack-app/package.json +++ b/packages/create-webpack-app/package.json @@ -7,7 +7,6 @@ "type": "git", "url": "https://github.com/webpack/create-webpack-app.git" }, - "type": "module", "homepage": "https://github.com/webpack/webpack-cli/tree/master/packages/create-webpack-app", "bugs": "https://github.com/webpack/webpack-cli/issues", "funding": { @@ -17,6 +16,7 @@ "bin": { "create-webpack-app": "./bin/index.js" }, + "type": "module", "main": "./lib/index.js", "scripts": { "build": "tsc --build", From 667d50dec1647ce4969e47321e3bf7c2d0c849d9 Mon Sep 17 00:00:00 2001 From: Uzair Date: Thu, 16 May 2024 21:11:43 +0530 Subject: [PATCH 09/81] feat: add entrypoint executable which wraps the generator cli --- packages/create-webpack-app/bin/cli.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 packages/create-webpack-app/bin/cli.js diff --git a/packages/create-webpack-app/bin/cli.js b/packages/create-webpack-app/bin/cli.js new file mode 100644 index 00000000000..72c9fa973cf --- /dev/null +++ b/packages/create-webpack-app/bin/cli.js @@ -0,0 +1,19 @@ +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import minimist from "minimist"; +import { Plop, run } from "plop"; +/* cSpell:disable */ +const args = process.argv.slice(2); +const argv = minimist(args); +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +Plop.prepare( + { + cwd: argv.cwd, + configPath: path.resolve(__dirname, "../lib/plopfile.js"), + preload: argv.preload || [], + completion: argv.completion, + }, + (env) => Plop.execute(env, run), +); From 8f89b98b106dd8117fc2a3d35336af8ee426d6a9 Mon Sep 17 00:00:00 2001 From: Uzair Date: Sun, 19 May 2024 04:50:43 +0530 Subject: [PATCH 10/81] chore: add ejs and @types/ejs as dependencies add ejs for rendering logic using ejs templates in future --- packages/create-webpack-app/package.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/create-webpack-app/package.json b/packages/create-webpack-app/package.json index ed6338645ae..55feb49818d 100644 --- a/packages/create-webpack-app/package.json +++ b/packages/create-webpack-app/package.json @@ -40,11 +40,14 @@ "!**/*__tests__" ], "dependencies": { + "ejs": "^3.1.10", "minimist": "^1.2.8", "node-plop": "^0.32.0", "plop": "^4.0.1" }, - "devDependencies": {}, + "devDependencies": { + "@types/ejs": "^3.1.5" + }, "peerDependencies": {}, "peerDependenciesMeta": {} } From d2ca7f68ced6e4d7137a57bd6daa1c3a5aabc458 Mon Sep 17 00:00:00 2001 From: Uzair Date: Sun, 19 May 2024 04:53:55 +0530 Subject: [PATCH 11/81] build: change emit module of tsc to esnext for compatibility with plopfile, as it throws error if it's a commonjs file --- packages/create-webpack-app/tsconfig.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/create-webpack-app/tsconfig.json b/packages/create-webpack-app/tsconfig.json index 991ba583366..db8e7e2a7ed 100644 --- a/packages/create-webpack-app/tsconfig.json +++ b/packages/create-webpack-app/tsconfig.json @@ -4,6 +4,7 @@ "compilerOptions": { "outDir": "lib", "rootDir": "src" + "module": "esnext" }, "include": ["src"], "references": [ From acf5fa571df18695980057cc6f27590a2d6d2b8b Mon Sep 17 00:00:00 2001 From: Uzair Date: Sun, 19 May 2024 04:57:04 +0530 Subject: [PATCH 12/81] feat: add ejs template rendering support - change templates to ejs templates - implement ejs rendering logic in plopfile.ts --- packages/create-webpack-app/src/plopfile.ts | 3 + .../templates/init/default/package.json.hbs | 8 -- .../templates/init/default/package.json.tpl | 17 +++ .../templates/init/default/template.html.hbs | 18 ---- .../templates/init/default/template.html.tpl | 26 +++++ .../init/default/webpack.config.js.tpl | 101 ++++++++++++++++++ .../init/default/webpack.configjs.hbs | 75 ------------- 7 files changed, 147 insertions(+), 101 deletions(-) delete mode 100644 packages/create-webpack-app/templates/init/default/package.json.hbs create mode 100644 packages/create-webpack-app/templates/init/default/package.json.tpl delete mode 100644 packages/create-webpack-app/templates/init/default/template.html.hbs create mode 100644 packages/create-webpack-app/templates/init/default/template.html.tpl create mode 100644 packages/create-webpack-app/templates/init/default/webpack.config.js.tpl delete mode 100644 packages/create-webpack-app/templates/init/default/webpack.configjs.hbs diff --git a/packages/create-webpack-app/src/plopfile.ts b/packages/create-webpack-app/src/plopfile.ts index bd1cec907ed..d1fa7ac603e 100644 --- a/packages/create-webpack-app/src/plopfile.ts +++ b/packages/create-webpack-app/src/plopfile.ts @@ -1,5 +1,6 @@ import { NodePlopAPI } from "./types"; import { resolve } from "path"; +import ejs from "ejs"; /* eslint-disable no-unused-vars */ export default function (plop: NodePlopAPI) { @@ -156,6 +157,8 @@ export default function (plop: NodePlopAPI) { destination: "{{configPath}}", base: "../templates/init/default", templateFiles: "../templates/init/default/**/*", + transform: (content, data) => ejs.render(content, data), + stripExtensions: ["tpl"], force: true, verbose: true, }, diff --git a/packages/create-webpack-app/templates/init/default/package.json.hbs b/packages/create-webpack-app/templates/init/default/package.json.hbs deleted file mode 100644 index 303efe61123..00000000000 --- a/packages/create-webpack-app/templates/init/default/package.json.hbs +++ /dev/null @@ -1,8 +0,0 @@ -{ "version": "1.0.0", "description": "My webpack project", "name": "{{projectName}}", "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 devServer}} - "serve": "webpack serve" -{{/if}} -} } \ No newline at end of file diff --git a/packages/create-webpack-app/templates/init/default/package.json.tpl b/packages/create-webpack-app/templates/init/default/package.json.tpl new file mode 100644 index 00000000000..db35cf0f4b4 --- /dev/null +++ b/packages/create-webpack-app/templates/init/default/package.json.tpl @@ -0,0 +1,17 @@ +{ + "version": "1.0.0", + + "description": "My webpack project", + + "name": <%= projectName %>, + "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 (devServer) { %> + "serve": "webpack serve" + <% } %> + } +} diff --git a/packages/create-webpack-app/templates/init/default/template.html.hbs b/packages/create-webpack-app/templates/init/default/template.html.hbs deleted file mode 100644 index 0acfbcce6d0..00000000000 --- a/packages/create-webpack-app/templates/init/default/template.html.hbs +++ /dev/null @@ -1,18 +0,0 @@ - - - - Webpack App - - -

Hello world!

-

Tip: Check your console

- - {{#if workboxWebpackPlugin}} - - {{/if}} - \ No newline at end of file diff --git a/packages/create-webpack-app/templates/init/default/template.html.tpl b/packages/create-webpack-app/templates/init/default/template.html.tpl new file mode 100644 index 00000000000..d61cf83b04b --- /dev/null +++ b/packages/create-webpack-app/templates/init/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/init/default/webpack.config.js.tpl b/packages/create-webpack-app/templates/init/default/webpack.config.js.tpl new file mode 100644 index 00000000000..178aaf1a991 --- /dev/null +++ b/packages/create-webpack-app/templates/init/default/webpack.config.js.tpl @@ -0,0 +1,101 @@ +// 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 (cssType !== 'none') { %> +<% 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: '<%= entryPoint %>', + 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: [stylesHandler, 'css-loader', <% if (isPostCSS) { %>'postcss-loader', <% } %>'less-loader'], + },<% } %><% if (cssType == 'Stylus') { %> + { + test: /\.styl$/i, + use: [stylesHandler, 'css-loader', <% if (isPostCSS) { %>'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', + }, + %><% if (htmlWebpackPlugin) { %> + { + test: /\.html$/i, + use: ['html-loader'], + },<% } %> + + // Add your rules for custom modules here + // Learn more about loaders from https://webpack.js.org/loaders/ + ], + },<% if (langType == "Typescript") {%> + resolve: { + extensions: ['.tsx', '.ts', '.jsx', '.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; +}; diff --git a/packages/create-webpack-app/templates/init/default/webpack.configjs.hbs b/packages/create-webpack-app/templates/init/default/webpack.configjs.hbs deleted file mode 100644 index 6f598c78dbf..00000000000 --- a/packages/create-webpack-app/templates/init/default/webpack.configjs.hbs +++ /dev/null @@ -1,75 +0,0 @@ -// Generated using webpack-cli https://github.com/webpack/webpack-cli const path = require('path'); -{{#if (htmlWebpackPlugin)}} - const HtmlWebpackPlugin = require('html-webpack-plugin'); -{{/if}} -{{#isEqualToString extractPlugin "No"}} - const MiniCssExtractPlugin = require('mini-css-extract-plugin'); -{{/isEqualToString}} -{{#if (workboxWebpackPlugin)}} - const WorkboxWebpackPlugin = require('workbox-webpack-plugin>) -{{/if}} - -const isProduction = process.env.NODE_ENV === 'production'; -{{!-- {{#isEqualToString cssType 'none'}} --}} -{{!-- {{#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'; }} -{{!-- {{/if}} --}} -{{!-- {{/isEqualToString}} --}} - -const config = { entry: '{{entry}}', output: { path: path.resolve(__dirname, 'dist'), },{{#if - (devServer) -}} - devServer: { open: true, host: 'localhost', },{{/if}} -plugins: [{{#if (htmlWebpackPlugin)}} - new HtmlWebpackPlugin({ template: 'index.html', }), -{{/if}} -{{#isEqualToString extractPlugin "Yes"}} - new MiniCssExtractPlugin(), -{{/isEqualToString}} -// Add your plugins here // Learn more about plugins from -https://webpack.js.org/configuration/plugins/ ], module: { rules: [{{#isEqualToString - langType "ES6" -}} - { test: /\.(js|jsx)$/i, loader: 'babel-loader', },{{/isEqualToString}} -{{#isEqualToString langType "Typescript"}} - { test: /\.(ts|tsx)$/i, loader: 'ts-loader', exclude: ['/node_modules/'], },{{/isEqualToString}}{{#if - isCSS -}} - {{#unless isPostCSS}} - { test: /\.css$/i, use: [stylesHandler,'css-loader'], },{{/unless}}{{/if}}{{#isEqualToString - cssType "SASS" -}} - { test: /\.s[ac]ss$/i, use: [stylesHandler, 'css-loader', - {{#if (isPostCSS)}}'postcss-loader', {{/if}}'sass-loader'], },{{/isEqualToString}}{{#isEqualToString - cssType "LESS" -}} - { test: /\.less$/i, use: [stylesHandler, 'css-loader', - {{#if (isPostCSS)}}'postcss-loader', {{/if}}'less-loader'], },{{/isEqualToString}}{{#isEqualToString - cssType "Stylus" -}} - { test: /\.styl$/i, use: [stylesHandler, 'css-loader', - {{#if (isPostCSS)}}'postcss-loader', {{/if}}'stylus-loader'], },{{/isEqualToString}}{{#if - (isPostCSS) -}} - {{#if (isCSS)}} - { test: /\.css$/i, use: [stylesHandler, 'css-loader', 'postcss-loader'], },{{/if}}{{/if}} -{ test: /\.(eot|svg|ttf|woff|woff2|png|jpg|g#if)$/i, type: 'asset', }, -{{#if (htmlWebpackPlugin)}} - { test: /\.html$/i, use: ['html-loader'], },{{/if}} - -// Add your rules for custom modules here // Learn more about loaders from -https://webpack.js.org/loaders/ ], }, -{{#isEqualToString langType "Typescript"}} - resolve: { extensions: ['.tsx', '.ts', '.jsx', '.js', '...'], },{{/isEqualToString}} -}; module.exports = () => { if (isProduction) { config.mode = 'production'; -{{#isEqualToString extractPlugin "Only for Production"}} - config.plugins.push(new MiniCssExtractPlugin()); -{{/isEqualToString}} -{{#if (workboxWebpackPlugin)}} - config.plugins.push(new WorkboxWebpackPlugin.GenerateSW()); -{{/if}} -} else { config.mode = 'development'; } return config; }; \ No newline at end of file From df3969839648f00c01bc7a817b20051f12ffea8e Mon Sep 17 00:00:00 2001 From: Uzair Date: Sun, 19 May 2024 04:58:01 +0530 Subject: [PATCH 13/81] feat: add entryPoint prompt --- packages/create-webpack-app/src/plopfile.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/create-webpack-app/src/plopfile.ts b/packages/create-webpack-app/src/plopfile.ts index d1fa7ac603e..a61508ae8b7 100644 --- a/packages/create-webpack-app/src/plopfile.ts +++ b/packages/create-webpack-app/src/plopfile.ts @@ -30,6 +30,18 @@ export default function (plop: NodePlopAPI) { return resolve(input, answers.projectName); }, }, + { + type: "input", + name: "entryPoint", + message: "Enter the entry point of your application:", + default: "src/index.js", + validate(input, _) { + if (!input) { + return "Entry point cannot be empty"; + } + return true; + }, + }, { type: "list", name: "langType", From 93c2142969de3670fd2c4279cc6e654dc115df85 Mon Sep 17 00:00:00 2001 From: Uzair Date: Sun, 19 May 2024 04:59:48 +0530 Subject: [PATCH 14/81] chore: remove unused code - remove helper function - remove unnecessary comment --- packages/create-webpack-app/src/plopfile.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/packages/create-webpack-app/src/plopfile.ts b/packages/create-webpack-app/src/plopfile.ts index a61508ae8b7..20944e752dd 100644 --- a/packages/create-webpack-app/src/plopfile.ts +++ b/packages/create-webpack-app/src/plopfile.ts @@ -176,11 +176,4 @@ export default function (plop: NodePlopAPI) { }, ], }); - plop.setHelper( - "isEqualToString", - function (this: typeof Function, value: string, comparison: string, options: any) { - return value === comparison ? options.fn(this) : options.inverse(this); - }, - ); } -// module.exports = plop.generator("basicProject").run; From 85b06bcc25f1bd4bfd89dd74e8ecdba3a7c52e19 Mon Sep 17 00:00:00 2001 From: Uzair Date: Mon, 20 May 2024 02:16:52 +0530 Subject: [PATCH 15/81] chore: change eslint and cspell silencer --- packages/create-webpack-app/bin/cli.js | 2 +- packages/create-webpack-app/src/plopfile.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/create-webpack-app/bin/cli.js b/packages/create-webpack-app/bin/cli.js index 72c9fa973cf..14b8c50ba2e 100644 --- a/packages/create-webpack-app/bin/cli.js +++ b/packages/create-webpack-app/bin/cli.js @@ -2,7 +2,7 @@ import path from "node:path"; import { fileURLToPath } from "node:url"; import minimist from "minimist"; import { Plop, run } from "plop"; -/* cSpell:disable */ +// cSpell:ignore plopfile, plopfile.js const args = process.argv.slice(2); const argv = minimist(args); const __filename = fileURLToPath(import.meta.url); diff --git a/packages/create-webpack-app/src/plopfile.ts b/packages/create-webpack-app/src/plopfile.ts index 20944e752dd..d555d8eddea 100644 --- a/packages/create-webpack-app/src/plopfile.ts +++ b/packages/create-webpack-app/src/plopfile.ts @@ -1,7 +1,6 @@ import { NodePlopAPI } from "./types"; import { resolve } from "path"; import ejs from "ejs"; -/* eslint-disable no-unused-vars */ export default function (plop: NodePlopAPI) { const dependencies = ["webpack", "webpack-cli"]; From 88acfd2449938ec5d2cc10470abd0a59b41a052a Mon Sep 17 00:00:00 2001 From: Uzair Date: Mon, 20 May 2024 02:19:11 +0530 Subject: [PATCH 16/81] build: change plopfile.ts on reviews - better input validation - remove entrypoint prompt - fix path issues --- packages/create-webpack-app/src/plopfile.ts | 27 ++++++--------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/packages/create-webpack-app/src/plopfile.ts b/packages/create-webpack-app/src/plopfile.ts index d555d8eddea..3572ef4093f 100644 --- a/packages/create-webpack-app/src/plopfile.ts +++ b/packages/create-webpack-app/src/plopfile.ts @@ -14,7 +14,7 @@ export default function (plop: NodePlopAPI) { message: "Enter your project name:", default: "webpack-project", validate(input, _) { - if (!input) { + if (!input.trim()) { return "Project name cannot be empty"; } return true; @@ -22,24 +22,10 @@ export default function (plop: NodePlopAPI) { }, { type: "input", - name: "configPath", + name: "projectPath", message: "Enter the project destination:", default: ".", - filter: (input, answers) => { - return resolve(input, answers.projectName); - }, - }, - { - type: "input", - name: "entryPoint", - message: "Enter the entry point of your application:", - default: "src/index.js", - validate(input, _) { - if (!input) { - return "Entry point cannot be empty"; - } - return true; - }, + filter: (input) => resolve(input), }, { type: "list", @@ -165,10 +151,13 @@ export default function (plop: NodePlopAPI) { actions: [ { type: "addMany", - destination: "{{configPath}}", + destination: "{{projectPath}}/{{dashCase projectName}}", base: "../templates/init/default", templateFiles: "../templates/init/default/**/*", - transform: (content, data) => ejs.render(content, data), + transform: (content, data) => { + data.entryPoint = data.langType === "Typescript" ? "index.ts" : "index.js"; + return ejs.render(content, data); + }, stripExtensions: ["tpl"], force: true, verbose: true, From 55e13ec8ad4aa2c447ce7a19fd9d94998c6737a4 Mon Sep 17 00:00:00 2001 From: Uzair Date: Mon, 20 May 2024 02:20:47 +0530 Subject: [PATCH 17/81] feat: change the templates - add both index.js and index.ts - fix bud in package.json template file --- packages/create-webpack-app/templates/init/default/index.js | 1 - .../create-webpack-app/templates/init/default/package.json.tpl | 2 +- .../create-webpack-app/templates/init/default/src/index.js | 2 ++ .../create-webpack-app/templates/init/default/src/index.ts | 3 ++- 4 files changed, 5 insertions(+), 3 deletions(-) delete mode 100644 packages/create-webpack-app/templates/init/default/index.js create mode 100644 packages/create-webpack-app/templates/init/default/src/index.js diff --git a/packages/create-webpack-app/templates/init/default/index.js b/packages/create-webpack-app/templates/init/default/index.js deleted file mode 100644 index 019c0f4bc8e..00000000000 --- a/packages/create-webpack-app/templates/init/default/index.js +++ /dev/null @@ -1 +0,0 @@ -console.log("Hello World!"); diff --git a/packages/create-webpack-app/templates/init/default/package.json.tpl b/packages/create-webpack-app/templates/init/default/package.json.tpl index db35cf0f4b4..f8ede733855 100644 --- a/packages/create-webpack-app/templates/init/default/package.json.tpl +++ b/packages/create-webpack-app/templates/init/default/package.json.tpl @@ -3,7 +3,7 @@ "description": "My webpack project", - "name": <%= projectName %>, + "name": "<%= projectName %>", "scripts": { "build": "webpack --mode=production --node-env=production", diff --git a/packages/create-webpack-app/templates/init/default/src/index.js b/packages/create-webpack-app/templates/init/default/src/index.js new file mode 100644 index 00000000000..dcc0dd61af1 --- /dev/null +++ b/packages/create-webpack-app/templates/init/default/src/index.js @@ -0,0 +1,2 @@ +// delete this file if Language is Typescript +console.log("Hello World!"); diff --git a/packages/create-webpack-app/templates/init/default/src/index.ts b/packages/create-webpack-app/templates/init/default/src/index.ts index 184dfcc9987..74401839d59 100644 --- a/packages/create-webpack-app/templates/init/default/src/index.ts +++ b/packages/create-webpack-app/templates/init/default/src/index.ts @@ -1 +1,2 @@ -console.log("Hello, World!"); +// delete this file if Language is Javascript +console.log("Hello World!"); From 897e00b6388aebc5dbddc7c17daa09a6c2208293 Mon Sep 17 00:00:00 2001 From: Uzair Date: Mon, 20 May 2024 02:21:34 +0530 Subject: [PATCH 18/81] fix: fix missing comma in tsconfig --- packages/create-webpack-app/tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/create-webpack-app/tsconfig.json b/packages/create-webpack-app/tsconfig.json index db8e7e2a7ed..a41c8c48bc9 100644 --- a/packages/create-webpack-app/tsconfig.json +++ b/packages/create-webpack-app/tsconfig.json @@ -3,7 +3,7 @@ "exclude": ["src/utils/__tests__"], "compilerOptions": { "outDir": "lib", - "rootDir": "src" + "rootDir": "src", "module": "esnext" }, "include": ["src"], From f9cb74a73570a48882ca7d00787ddf01b9ec19b9 Mon Sep 17 00:00:00 2001 From: Uzair Date: Mon, 20 May 2024 02:22:54 +0530 Subject: [PATCH 19/81] chore: change package.json on reviews - fix the url - bumped the required node from 14 -> 18 - removed empty peerDeps and peerMetaDeps fields - fix cli entry point typo --- packages/create-webpack-app/package.json | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/create-webpack-app/package.json b/packages/create-webpack-app/package.json index 55feb49818d..f4ca4da2ceb 100644 --- a/packages/create-webpack-app/package.json +++ b/packages/create-webpack-app/package.json @@ -5,7 +5,7 @@ "license": "MIT", "repository": { "type": "git", - "url": "https://github.com/webpack/create-webpack-app.git" + "url": "https://github.com/webpack-cli/create-webpack-app.git" }, "homepage": "https://github.com/webpack/webpack-cli/tree/master/packages/create-webpack-app", "bugs": "https://github.com/webpack/webpack-cli/issues", @@ -14,7 +14,7 @@ "url": "https://opencollective.com/webpack" }, "bin": { - "create-webpack-app": "./bin/index.js" + "create-webpack-app": "./bin/cli.js" }, "type": "module", "main": "./lib/index.js", @@ -23,7 +23,7 @@ "watch": "tsc --watch" }, "engines": { - "node": ">=14.15.0" + "node": ">=18.12.0" }, "keywords": [ "webpack", @@ -47,7 +47,5 @@ }, "devDependencies": { "@types/ejs": "^3.1.5" - }, - "peerDependencies": {}, - "peerDependenciesMeta": {} + } } From b6baa6a93211e08daddf7bab79e969114f697395 Mon Sep 17 00:00:00 2001 From: Uzair Date: Mon, 20 May 2024 02:27:55 +0530 Subject: [PATCH 20/81] docs: change readme based on review --- packages/create-webpack-app/README.md | 42 +++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/packages/create-webpack-app/README.md b/packages/create-webpack-app/README.md index 26d10e07bcb..4fce91ebbbe 100644 --- a/packages/create-webpack-app/README.md +++ b/packages/create-webpack-app/README.md @@ -1,11 +1,41 @@ -# `create-webpack-app` + -> TODO: description +# create-webpack-app CLI -## Usage +## About -```js -const createWebpackApp = require("create-webpack-app"); +- `create-webpack-app` is a cli tool that enables developers to quickly scaffold a new webpack project. It provides a flexible set of commands for developers to increase speed when setting up a custom webpack project. webpack CLI addresses these needs by providing a set of tools to improve the setup of custom webpack configuration. +- It is also going to support several front end frameworks and libraries like React, Angular, Vue, Svelte, etc. +- Webpack Loader and Plugin scaffolding is also supported. -// TODO: DEMONSTRATE API +## How to install + +```bash +npm install --save-dev @webpack-cli/create-webpack-app +``` + +or + +```bash +yarn add @webpack-cli/create-webpack-app --dev +``` + +## Supported arguments and commands + +### Usage + +All interactions with create-webpack-app are of the form + +```bash +npx create-webpack-app [command] [options] ``` + +### Help Usage + +### Available Commands + +### Available Options From d9a0856bb235557e4bbad11b078fc9bc59e373c6 Mon Sep 17 00:00:00 2001 From: Uzair Date: Tue, 28 May 2024 02:26:27 +0530 Subject: [PATCH 21/81] fix: customACtionConfig support in plopfile.ts setGenerator config property --- packages/create-webpack-app/src/types.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/create-webpack-app/src/types.ts b/packages/create-webpack-app/src/types.ts index f2ad6693c44..a895f581116 100644 --- a/packages/create-webpack-app/src/types.ts +++ b/packages/create-webpack-app/src/types.ts @@ -1,6 +1,5 @@ export type { ActionConfig, - ActionType, AddActionConfig, AddManyActionConfig, AppendActionConfig, @@ -12,7 +11,9 @@ export type { PlopGeneratorConfig, Actions, } from "node-plop"; - +import { ActionType as ActionTypeBase, CustomActionConfig } from "node-plop"; +// extended ACtionType to include custom action config as previously it was not recognizing +export type ActionType = ActionTypeBase | CustomActionConfig; export type InitOptions = { template: string; force?: boolean }; export type LoaderOptions = { template: string }; export type PluginOptions = { template: string }; From d8ec7e122e73c7559debd5eb73307890045f186d Mon Sep 17 00:00:00 2001 From: Uzair Date: Sat, 1 Jun 2024 19:39:24 +0530 Subject: [PATCH 22/81] feat: add npmInstall custom package - installs all the packages from deps array to the project directory --- packages/create-webpack-app/src/plopfile.ts | 70 ++++++++++++++++++++- 1 file changed, 67 insertions(+), 3 deletions(-) diff --git a/packages/create-webpack-app/src/plopfile.ts b/packages/create-webpack-app/src/plopfile.ts index 3572ef4093f..a608c54d73b 100644 --- a/packages/create-webpack-app/src/plopfile.ts +++ b/packages/create-webpack-app/src/plopfile.ts @@ -1,9 +1,55 @@ import { NodePlopAPI } from "./types"; -import { resolve } from "path"; +import { resolve, join } from "path"; import ejs from "ejs"; - +import { spawn } from "child_process"; export default function (plop: NodePlopAPI) { const dependencies = ["webpack", "webpack-cli"]; + plop.setActionType("pkgInstall", (answers) => { + const options = { + cwd: `${answers.projectPath}/${answers.projectName}`, + encoding: "utf-8", + }; + if (answers.devServer) { + dependencies.push("webpack-dev-server"); + } + if (answers.htmlWebpackPlugin) { + dependencies.push("html-webpack-plugin", "html-loader"); + } + if (answers.workboxWebpackPlugin) { + dependencies.push("workbox-webpack-plugin"); + } + if (answers.isPostCSS) { + dependencies.push("postcss-loader", "autoprefixer"); + } + console.log(`Installing packages: ${dependencies.join(", ")}`); + const returnMessage = `Project ${answers.projectName} has been successfully created at ${answers.projectPath}/${answers.projectName}`; + const packageManager = answers.packageManager; + const installCommandPrefix = packageManager === "yarn" ? "add" : "install"; + + const npmInstallPackages = spawn( + `${packageManager}`, + [`${installCommandPrefix}`, ...dependencies], + options, + ); + npmInstallPackages.stdout.on("data", (data) => { + console.log(data.toString()); + }); + npmInstallPackages.stderr.on("data", (data) => { + console.error(data.toString()); + }); + npmInstallPackages.on("error", (err) => { + console.error(err); + }); + npmInstallPackages.on("close", (code) => { + if (code !== 0) { + console.error(`child process exited with code ${code}`); + return; + } else { + console.log(returnMessage); + } + }); + return "Package Installation Phase..."; // executes before the child process is completed + }); // Define a base generator for the project structure plop.setGenerator("init", { description: "Create a basic Webpack project", @@ -25,7 +71,9 @@ export default function (plop: NodePlopAPI) { name: "projectPath", message: "Enter the project destination:", default: ".", - filter: (input) => resolve(input), + filter: (input) => { + return resolve(join(process.cwd(), input)); + }, }, { type: "list", @@ -147,6 +195,19 @@ export default function (plop: NodePlopAPI) { return input; }, }, + { + type: "list", + name: "packageManager", + message: "Which package manager do you want to use?", + choices: ["npm", "yarn"], + default: "npm", + validate(input, _) { + if (!input.trim()) { + return "Package manager cannot be empty"; + } + return true; + }, + }, ], actions: [ { @@ -162,6 +223,9 @@ export default function (plop: NodePlopAPI) { force: true, verbose: true, }, + { + type: "pkgInstall", + }, ], }); } From 4c59396621fd6e40f0d79fd05c8709069503301c Mon Sep 17 00:00:00 2001 From: Uzair Date: Sat, 1 Jun 2024 19:39:59 +0530 Subject: [PATCH 23/81] chore: remove redundant code --- packages/create-webpack-app/src/plopfile.ts | 24 --------------------- 1 file changed, 24 deletions(-) diff --git a/packages/create-webpack-app/src/plopfile.ts b/packages/create-webpack-app/src/plopfile.ts index a608c54d73b..d3a6dfaf4b7 100644 --- a/packages/create-webpack-app/src/plopfile.ts +++ b/packages/create-webpack-app/src/plopfile.ts @@ -98,36 +98,18 @@ export default function (plop: NodePlopAPI) { name: "devServer", message: "Would you like to use Webpack Dev server?", default: true, - filter: (input, _) => { - if (input) { - dependencies.push("webpack-dev-server"); - } - return input; - }, }, { type: "confirm", name: "htmlWebpackPlugin", message: "Do you want to simplify the creation of HTML files for your bundle?", default: true, - filter: (input, _) => { - if (input) { - dependencies.push("html-webpack-plugin", "html-loader"); - } - return input; - }, }, { type: "confirm", name: "workboxWebpackPlugin", message: "Do you want to add PWA support?", default: true, - filter: (input, _) => { - if (input) { - dependencies.push("workbox-webpack-plugin"); - } - return input; - }, }, { type: "list", @@ -173,12 +155,6 @@ export default function (plop: NodePlopAPI) { name: "isPostCSS", message: "Do you want to use PostCSS in your project?", when: (answers) => answers.isCSS, - filter: (input, _) => { - if (input) { - dependencies.push("postcss-loader", "autoprefixer"); - } - return input; - }, default: true, }, { From 04b5f90c5dd809defca015bf747820dc12453438 Mon Sep 17 00:00:00 2001 From: Uzair Date: Sat, 1 Jun 2024 19:42:18 +0530 Subject: [PATCH 24/81] fix: typo in prompt --- packages/create-webpack-app/src/plopfile.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/create-webpack-app/src/plopfile.ts b/packages/create-webpack-app/src/plopfile.ts index d3a6dfaf4b7..9bbbe91d16c 100644 --- a/packages/create-webpack-app/src/plopfile.ts +++ b/packages/create-webpack-app/src/plopfile.ts @@ -146,7 +146,7 @@ export default function (plop: NodePlopAPI) { type: "confirm", name: "isCSS", message: (answers) => - `Will you be using CSS styles along with ${answers.cssType}in your project?`, + `Will you be using CSS styles along with ${answers.cssType} in your project?`, when: (answers) => answers.cssType !== "CSS only", default: true, }, From 1bdf9244aee2c9630ebbfbcc0afa80ce867b1ded Mon Sep 17 00:00:00 2001 From: Uzair Date: Wed, 5 Jun 2024 17:47:29 +0530 Subject: [PATCH 25/81] feat: add skip prompt functionality - single prompt to ask whether to skip - if yes then returns default answers object - if no then returns the interactive prompts interface --- packages/create-webpack-app/src/plopfile.ts | 290 +++++++++++--------- packages/create-webpack-app/src/types.ts | 2 + 2 files changed, 160 insertions(+), 132 deletions(-) diff --git a/packages/create-webpack-app/src/plopfile.ts b/packages/create-webpack-app/src/plopfile.ts index 9bbbe91d16c..d8a3ebb4420 100644 --- a/packages/create-webpack-app/src/plopfile.ts +++ b/packages/create-webpack-app/src/plopfile.ts @@ -1,4 +1,4 @@ -import { NodePlopAPI } from "./types"; +import { NodePlopAPI, Answers } from "./types"; import { resolve, join } from "path"; import ejs from "ejs"; import { spawn } from "child_process"; @@ -53,138 +53,164 @@ export default function (plop: NodePlopAPI) { // Define a base generator for the project structure plop.setGenerator("init", { description: "Create a basic Webpack project", - prompts: [ - { - type: "input", - name: "projectName", - message: "Enter your project name:", - default: "webpack-project", - validate(input, _) { - if (!input.trim()) { - return "Project name cannot be empty"; - } - return true; - }, - }, - { - type: "input", - name: "projectPath", - message: "Enter the project destination:", - default: ".", - filter: (input) => { - return resolve(join(process.cwd(), input)); - }, - }, - { - type: "list", - name: "langType", - message: "Which of the following JS solutions do you want to use?", - choices: ["none", "ES6", "Typescript"], - default: "none", - filter: (input, _) => { - switch (input) { - case "ES6": - dependencies.push("babel-loader", "@babel/core", "@babel/preset-env"); - break; - case "Typescript": - dependencies.push("typescript", "ts-loader"); - break; - } - return input; - }, - }, - { + prompts: async function (inquirer): Promise { + const { skip } = await inquirer.prompt({ type: "confirm", - name: "devServer", - message: "Would you like to use Webpack Dev server?", - default: true, - }, - { - type: "confirm", - name: "htmlWebpackPlugin", - message: "Do you want to simplify the creation of HTML files for your bundle?", - default: true, - }, - { - type: "confirm", - name: "workboxWebpackPlugin", - message: "Do you want to add PWA support?", - default: true, - }, - { - type: "list", - name: "cssType", - message: "Which of the following CSS solution do you want to use?", - choices: ["none", "CSS only", "SASS", "LESS", "Stylus"], - default: "Css only", - filter: (input, answers) => { - if (input === "none") { - answers.isCSS = false; - answers.isPostCSS = false; - answers.extractPlugin = "No"; - } else { - dependencies.push("style-loader", "css-loader"); - switch (input) { - case "CSS only": - answers.isCSS = true; - break; - case "SASS": - dependencies.push("sass-loader", "node-sass"); - break; - case "LESS": - dependencies.push("less-loader", "less"); - break; - case "Stylus": - dependencies.push("stylus-loader", "stylus"); - break; - } - } - return input; - }, - }, - { - type: "confirm", - name: "isCSS", - message: (answers) => - `Will you be using CSS styles along with ${answers.cssType} in your project?`, - when: (answers) => answers.cssType !== "CSS only", - default: true, - }, - { - type: "confirm", - name: "isPostCSS", - message: "Do you want to use PostCSS in your project?", - when: (answers) => answers.isCSS, - default: true, - }, - { - type: "list", - name: "extractPlugin", - message: "Do you want to extract CSS into separate files?", - choices: ["No", "Only for Production", "Yes"], - when: (answers) => answers.isCSS, - default: "No", - filter: (input, _) => { - if (input !== "No") { - dependencies.push("mini-css-extract-plugin"); - } - return input; - }, - }, - { - type: "list", - name: "packageManager", - message: "Which package manager do you want to use?", - choices: ["npm", "yarn"], - default: "npm", - validate(input, _) { - if (!input.trim()) { - return "Package manager cannot be empty"; - } - return true; - }, - }, - ], + name: "skip", + message: "Do you want to skip prompts and proceed with default options ?", + default: false, + }); + if (skip) { + console.log("Skipping prompts and proceeding with default options"); + return { + projectName: "webpack-project", + projectPath: resolve(join(process.cwd(), ".")), + langType: "none", + devServer: true, + htmlWebpackPlugin: true, + workboxWebpackPlugin: true, + cssType: "CSS only", + isCSS: true, + isPostCSS: true, + extractPlugin: "No", + packageManager: "npm", + }; + } else { + console.log("Please answer the following prompts to create your project"); + return await inquirer.prompt([ + { + type: "input", + name: "projectName", + message: "Enter your project name:", + default: "webpack-project", + validate(input, _) { + if (!input.trim()) { + return "Project name cannot be empty"; + } + return true; + }, + }, + { + type: "input", + name: "projectPath", + message: "Enter the project destination:", + default: ".", + filter: (input) => { + return resolve(join(process.cwd(), input)); + }, + }, + { + type: "list", + name: "langType", + message: "Which of the following JS solutions do you want to use?", + choices: ["none", "ES6", "Typescript"], + default: "none", + filter: (input, _) => { + switch (input) { + case "ES6": + dependencies.push("babel-loader", "@babel/core", "@babel/preset-env"); + break; + case "Typescript": + dependencies.push("typescript", "ts-loader"); + break; + } + return input; + }, + }, + { + type: "confirm", + name: "devServer", + message: "Would you like to use Webpack Dev server?", + default: true, + }, + { + type: "confirm", + name: "htmlWebpackPlugin", + message: "Do you want to simplify the creation of HTML files for your bundle?", + default: true, + }, + { + type: "confirm", + name: "workboxWebpackPlugin", + message: "Do you want to add PWA support?", + default: true, + }, + { + type: "list", + name: "cssType", + message: "Which of the following CSS solution do you want to use?", + choices: ["none", "CSS only", "SASS", "LESS", "Stylus"], + default: "Css only", + filter: (input, answers) => { + if (input === "none") { + answers.isCSS = false; + answers.isPostCSS = false; + answers.extractPlugin = "No"; + } else { + dependencies.push("style-loader", "css-loader"); + switch (input) { + case "CSS only": + answers.isCSS = true; + break; + case "SASS": + dependencies.push("sass-loader", "node-sass"); + break; + case "LESS": + dependencies.push("less-loader", "less"); + break; + case "Stylus": + dependencies.push("stylus-loader", "stylus"); + break; + } + } + return input; + }, + }, + { + type: "confirm", + name: "isCSS", + message: (answers) => + `Will you be using CSS styles along with ${answers.cssType} in your project?`, + when: (answers) => answers.cssType !== "CSS only", + default: true, + }, + { + type: "confirm", + name: "isPostCSS", + message: "Do you want to use PostCSS in your project?", + when: (answers) => answers.isCSS, + default: true, + }, + { + type: "list", + name: "extractPlugin", + message: "Do you want to extract CSS into separate files?", + choices: ["No", "Only for Production", "Yes"], + when: (answers) => answers.isCSS, + default: "No", + filter: (input, _) => { + if (input !== "No") { + dependencies.push("mini-css-extract-plugin"); + } + return input; + }, + }, + { + type: "list", + name: "packageManager", + message: "Which package manager do you want to use?", + choices: ["npm", "yarn"], + default: "npm", + validate(input, _) { + if (!input.trim()) { + return "Package manager cannot be empty"; + } + return true; + }, + }, + ]); + } + }, actions: [ { type: "addMany", diff --git a/packages/create-webpack-app/src/types.ts b/packages/create-webpack-app/src/types.ts index a895f581116..89c62017c7f 100644 --- a/packages/create-webpack-app/src/types.ts +++ b/packages/create-webpack-app/src/types.ts @@ -11,6 +11,8 @@ export type { PlopGeneratorConfig, Actions, } from "node-plop"; +export interface Answers extends Record {} + import { ActionType as ActionTypeBase, CustomActionConfig } from "node-plop"; // extended ACtionType to include custom action config as previously it was not recognizing export type ActionType = ActionTypeBase | CustomActionConfig; From 5e6a70bf6650ed7860aeb7c4edb2cd3f2f47f5cb Mon Sep 17 00:00:00 2001 From: Uzair Date: Sat, 8 Jun 2024 03:25:55 +0530 Subject: [PATCH 26/81] chore: install commander and remove obsolete deps --- packages/create-webpack-app/bin/cli.js | 32 +++++++++++------------- packages/create-webpack-app/package.json | 5 ++-- 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/packages/create-webpack-app/bin/cli.js b/packages/create-webpack-app/bin/cli.js index 14b8c50ba2e..a343d7c7642 100644 --- a/packages/create-webpack-app/bin/cli.js +++ b/packages/create-webpack-app/bin/cli.js @@ -1,19 +1,15 @@ -import path from "node:path"; -import { fileURLToPath } from "node:url"; -import minimist from "minimist"; -import { Plop, run } from "plop"; -// cSpell:ignore plopfile, plopfile.js -const args = process.argv.slice(2); -const argv = minimist(args); -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); +import { Command } from "commander"; +const program = new Command(); -Plop.prepare( - { - cwd: argv.cwd, - configPath: path.resolve(__dirname, "../lib/plopfile.js"), - preload: argv.preload || [], - completion: argv.completion, - }, - (env) => Plop.execute(env, run), -); +import pkg from "../package.json" with { type: "json" }; + +program.version(pkg.version, "-v, --version"); +program.helpOption("-h, --help", "Display help for command"); + +program + .option("-s, --skip", "Skip the prompt and use the default values") + .option("-f, --force", "Force the generator actions to override existing files"); + +program.parse(process.argv); + +console.log(program.opts()); diff --git a/packages/create-webpack-app/package.json b/packages/create-webpack-app/package.json index f4ca4da2ceb..b33c458cd32 100644 --- a/packages/create-webpack-app/package.json +++ b/packages/create-webpack-app/package.json @@ -40,10 +40,9 @@ "!**/*__tests__" ], "dependencies": { + "commander": "^12.1.0", "ejs": "^3.1.10", - "minimist": "^1.2.8", - "node-plop": "^0.32.0", - "plop": "^4.0.1" + "node-plop": "^0.32.0" }, "devDependencies": { "@types/ejs": "^3.1.5" From bb5e201b141a93189bb376c7bcdeef92c9383a6c Mon Sep 17 00:00:00 2001 From: Uzair Date: Sat, 8 Jun 2024 03:27:16 +0530 Subject: [PATCH 27/81] refactor: remove dynamic prompt and revert to previous state --- packages/create-webpack-app/src/plopfile.ts | 288 +++++++++----------- 1 file changed, 131 insertions(+), 157 deletions(-) diff --git a/packages/create-webpack-app/src/plopfile.ts b/packages/create-webpack-app/src/plopfile.ts index d8a3ebb4420..7974772a606 100644 --- a/packages/create-webpack-app/src/plopfile.ts +++ b/packages/create-webpack-app/src/plopfile.ts @@ -53,164 +53,138 @@ export default function (plop: NodePlopAPI) { // Define a base generator for the project structure plop.setGenerator("init", { description: "Create a basic Webpack project", - prompts: async function (inquirer): Promise { - const { skip } = await inquirer.prompt({ + prompts: [ + { + type: "input", + name: "projectName", + message: "Enter your project name:", + default: "webpack-project", + validate(input, _) { + if (!input.trim()) { + return "Project name cannot be empty"; + } + return true; + }, + }, + { + type: "input", + name: "projectPath", + message: "Enter the project destination:", + default: ".", + filter: (input) => { + return resolve(join(process.cwd(), input)); + }, + }, + { + type: "list", + name: "langType", + message: "Which of the following JS solutions do you want to use?", + choices: ["none", "ES6", "Typescript"], + default: "none", + filter: (input, _) => { + switch (input) { + case "ES6": + dependencies.push("babel-loader", "@babel/core", "@babel/preset-env"); + break; + case "Typescript": + dependencies.push("typescript", "ts-loader"); + break; + } + return input; + }, + }, + { type: "confirm", - name: "skip", - message: "Do you want to skip prompts and proceed with default options ?", - default: false, - }); - if (skip) { - console.log("Skipping prompts and proceeding with default options"); - return { - projectName: "webpack-project", - projectPath: resolve(join(process.cwd(), ".")), - langType: "none", - devServer: true, - htmlWebpackPlugin: true, - workboxWebpackPlugin: true, - cssType: "CSS only", - isCSS: true, - isPostCSS: true, - extractPlugin: "No", - packageManager: "npm", - }; - } else { - console.log("Please answer the following prompts to create your project"); - return await inquirer.prompt([ - { - type: "input", - name: "projectName", - message: "Enter your project name:", - default: "webpack-project", - validate(input, _) { - if (!input.trim()) { - return "Project name cannot be empty"; - } - return true; - }, - }, - { - type: "input", - name: "projectPath", - message: "Enter the project destination:", - default: ".", - filter: (input) => { - return resolve(join(process.cwd(), input)); - }, - }, - { - type: "list", - name: "langType", - message: "Which of the following JS solutions do you want to use?", - choices: ["none", "ES6", "Typescript"], - default: "none", - filter: (input, _) => { - switch (input) { - case "ES6": - dependencies.push("babel-loader", "@babel/core", "@babel/preset-env"); - break; - case "Typescript": - dependencies.push("typescript", "ts-loader"); - break; - } - return input; - }, - }, - { - type: "confirm", - name: "devServer", - message: "Would you like to use Webpack Dev server?", - default: true, - }, - { - type: "confirm", - name: "htmlWebpackPlugin", - message: "Do you want to simplify the creation of HTML files for your bundle?", - default: true, - }, - { - type: "confirm", - name: "workboxWebpackPlugin", - message: "Do you want to add PWA support?", - default: true, - }, - { - type: "list", - name: "cssType", - message: "Which of the following CSS solution do you want to use?", - choices: ["none", "CSS only", "SASS", "LESS", "Stylus"], - default: "Css only", - filter: (input, answers) => { - if (input === "none") { - answers.isCSS = false; - answers.isPostCSS = false; - answers.extractPlugin = "No"; - } else { - dependencies.push("style-loader", "css-loader"); - switch (input) { - case "CSS only": - answers.isCSS = true; - break; - case "SASS": - dependencies.push("sass-loader", "node-sass"); - break; - case "LESS": - dependencies.push("less-loader", "less"); - break; - case "Stylus": - dependencies.push("stylus-loader", "stylus"); - break; - } - } - return input; - }, - }, - { - type: "confirm", - name: "isCSS", - message: (answers) => - `Will you be using CSS styles along with ${answers.cssType} in your project?`, - when: (answers) => answers.cssType !== "CSS only", - default: true, - }, - { - type: "confirm", - name: "isPostCSS", - message: "Do you want to use PostCSS in your project?", - when: (answers) => answers.isCSS, - default: true, - }, - { - type: "list", - name: "extractPlugin", - message: "Do you want to extract CSS into separate files?", - choices: ["No", "Only for Production", "Yes"], - when: (answers) => answers.isCSS, - default: "No", - filter: (input, _) => { - if (input !== "No") { - dependencies.push("mini-css-extract-plugin"); - } - return input; - }, - }, - { - type: "list", - name: "packageManager", - message: "Which package manager do you want to use?", - choices: ["npm", "yarn"], - default: "npm", - validate(input, _) { - if (!input.trim()) { - return "Package manager cannot be empty"; - } - return true; - }, - }, - ]); - } - }, + name: "devServer", + message: "Would you like to use Webpack Dev server?", + default: true, + }, + { + type: "confirm", + name: "htmlWebpackPlugin", + message: "Do you want to simplify the creation of HTML files for your bundle?", + default: true, + }, + { + type: "confirm", + name: "workboxWebpackPlugin", + message: "Do you want to add PWA support?", + default: true, + }, + { + type: "list", + name: "cssType", + message: "Which of the following CSS solution do you want to use?", + choices: ["none", "CSS only", "SASS", "LESS", "Stylus"], + default: "Css only", + filter: (input, answers) => { + if (input === "none") { + answers.isCSS = false; + answers.isPostCSS = false; + answers.extractPlugin = "No"; + } else { + dependencies.push("style-loader", "css-loader"); + switch (input) { + case "CSS only": + answers.isCSS = true; + break; + case "SASS": + dependencies.push("sass-loader", "node-sass"); + break; + case "LESS": + dependencies.push("less-loader", "less"); + break; + case "Stylus": + dependencies.push("stylus-loader", "stylus"); + break; + } + } + return input; + }, + }, + { + type: "confirm", + name: "isCSS", + message: (answers) => + `Will you be using CSS styles along with ${answers.cssType} in your project?`, + when: (answers) => answers.cssType !== "CSS only", + default: true, + }, + { + type: "confirm", + name: "isPostCSS", + message: "Do you want to use PostCSS in your project?", + when: (answers) => answers.isCSS, + default: true, + }, + { + type: "list", + name: "extractPlugin", + message: "Do you want to extract CSS into separate files?", + choices: ["No", "Only for Production", "Yes"], + when: (answers) => answers.isCSS, + default: "No", + filter: (input, _) => { + if (input !== "No") { + dependencies.push("mini-css-extract-plugin"); + } + return input; + }, + }, + { + type: "list", + name: "packageManager", + message: "Which package manager do you want to use?", + choices: ["npm", "yarn"], + default: "npm", + validate(input, _) { + if (!input.trim()) { + return "Package manager cannot be empty"; + } + return true; + }, + }, + ], actions: [ { type: "addMany", From 9bf21bf9958d36ec0f5d7b45995d056bc2cd2790 Mon Sep 17 00:00:00 2001 From: Uzair Date: Sat, 8 Jun 2024 03:30:40 +0530 Subject: [PATCH 28/81] feat: create commander cli and implement skip feat created commander cli implement init command with -s -f flags to skip and -f to override --- packages/create-webpack-app/bin/cli.js | 47 +++++++++++++++++++++----- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/packages/create-webpack-app/bin/cli.js b/packages/create-webpack-app/bin/cli.js index a343d7c7642..121c219cd7d 100644 --- a/packages/create-webpack-app/bin/cli.js +++ b/packages/create-webpack-app/bin/cli.js @@ -1,15 +1,46 @@ import { Command } from "commander"; -const program = new Command(); +import { resolve, join } from "path"; +import nodePlop from "node-plop"; +// Cspell:ignore plopfile, plopfile.js -import pkg from "../package.json" with { type: "json" }; +const program = new Command(); +const plop = await nodePlop("./lib/plopfile.js"); +const defaultValues = { + init: { + projectName: "webpack-project", + projectPath: resolve(join(process.cwd(), ".")), + langType: "none", + devServer: true, + htmlWebpackPlugin: true, + workboxWebpackPlugin: true, + cssType: "CSS only", + isCSS: true, + isPostCSS: true, + extractPlugin: "No", + packageManager: "npm", + }, +}; -program.version(pkg.version, "-v, --version"); +program.version("1.0.0", "-v, --version"); program.helpOption("-h, --help", "Display help for command"); program - .option("-s, --skip", "Skip the prompt and use the default values") - .option("-f, --force", "Force the generator actions to override existing files"); - -program.parse(process.argv); + .command("init") + .description("Initialize a new Webpack project") + .option("-s, --skip", "Skip the prompt and use the default values", false) + .option("-f, --force", "Force the generator actions to override existing files", false) + .action(function (opts) { + console.log("Initializing a new Webpack project"); + const { skip, force } = opts; + const initGenerator = plop.getGenerator("init"); + if (skip) { + console.log("Skipping the prompt and using the default values"); + initGenerator.runActions(defaultValues.init); + } else { + initGenerator.runPrompts([]).then((answers) => { + initGenerator.runActions(answers); + }); + } + }); -console.log(program.opts()); +program.parse(); From a295914ac7c5873250fcf7a89e4429f24d35604a Mon Sep 17 00:00:00 2001 From: Uzair Date: Sat, 8 Jun 2024 19:38:31 +0530 Subject: [PATCH 29/81] refactor: change prompt order to have genPath as first --- packages/create-webpack-app/bin/cli.js | 2 +- packages/create-webpack-app/src/plopfile.ts | 18 +- .../create-webpack-app.test.js | 680 ++++++++++++++++++ test/create-webpack-app/test.utils.js | 426 +++++++++++ 4 files changed, 1116 insertions(+), 10 deletions(-) create mode 100644 test/create-webpack-app/create-webpack-app.test.js create mode 100644 test/create-webpack-app/test.utils.js diff --git a/packages/create-webpack-app/bin/cli.js b/packages/create-webpack-app/bin/cli.js index 121c219cd7d..f711a75961c 100644 --- a/packages/create-webpack-app/bin/cli.js +++ b/packages/create-webpack-app/bin/cli.js @@ -7,8 +7,8 @@ const program = new Command(); const plop = await nodePlop("./lib/plopfile.js"); const defaultValues = { init: { - projectName: "webpack-project", projectPath: resolve(join(process.cwd(), ".")), + projectName: "webpack-project", langType: "none", devServer: true, htmlWebpackPlugin: true, diff --git a/packages/create-webpack-app/src/plopfile.ts b/packages/create-webpack-app/src/plopfile.ts index 7974772a606..906d5e9ffd1 100644 --- a/packages/create-webpack-app/src/plopfile.ts +++ b/packages/create-webpack-app/src/plopfile.ts @@ -54,6 +54,15 @@ export default function (plop: NodePlopAPI) { plop.setGenerator("init", { description: "Create a basic Webpack project", prompts: [ + { + type: "input", + name: "projectPath", + message: "Enter the project destination:", + default: ".", + filter: (input) => { + return resolve(join(process.cwd(), input)); + }, + }, { type: "input", name: "projectName", @@ -66,15 +75,6 @@ export default function (plop: NodePlopAPI) { return true; }, }, - { - type: "input", - name: "projectPath", - message: "Enter the project destination:", - default: ".", - filter: (input) => { - return resolve(join(process.cwd(), input)); - }, - }, { type: "list", name: "langType", diff --git a/test/create-webpack-app/create-webpack-app.test.js b/test/create-webpack-app/create-webpack-app.test.js new file mode 100644 index 00000000000..edcfc5ae6f7 --- /dev/null +++ b/test/create-webpack-app/create-webpack-app.test.js @@ -0,0 +1,680 @@ +const os = require("os"); +const path = require("path"); +const { mkdirSync, existsSync, readFileSync } = require("fs"); +const { join, resolve } = require("path"); +const { + isWindows, + run, + runPromptWithAnswers, + uniqueDirectoryForTest, +} = require("../utils/test-utils"); + +jest.setTimeout(480000); + +const ENTER = "\x0D"; +const DOWN = "\x1B\x5B\x42"; + +const defaultTemplateFiles = [ + "package.json", + "package-lock.json", + "src", + "src/index.js", + "webpack.config.js", +]; + +const reactTemplateFiles = [...defaultTemplateFiles, "index.html"]; + +// Helper to read from package.json in a given path +const readFromPkgJSON = (path) => { + const pkgJSONPath = join(path, "package.json"); + + if (!existsSync(pkgJSONPath)) { + return {}; + } + + const pkgJSON = JSON.parse(readFileSync(pkgJSONPath, "utf8")); + const { devDependencies: devDeps } = pkgJSON; + + // Update devDeps versions to be x.x.x to prevent frequent snapshot updates + Object.keys(devDeps).forEach((dep) => (devDeps[dep] = "x.x.x")); + + return { ...pkgJSON, devDependencies: devDeps }; +}; + +// Helper to read from webpack.config.js in a given path +const readFromWebpackConfig = (path) => readFileSync(join(path, "webpack.config.js"), "utf8"); + +describe("init command", () => { + it("should generate default project when nothing is passed", async () => { + const assetsPath = await uniqueDirectoryForTest(); + const { stdout, stderr } = await run(assetsPath, ["init", "--force"]); + + expect(stdout).toContain("Project has been initialised with webpack!"); + expect(stderr).toContain("webpack.config.js"); + + // Test files + defaultTemplateFiles.forEach((file) => { + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + }); + + // Check if the generated package.json file content matches the snapshot + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + }); + + it("should generate project when generationPath is supplied", async () => { + const assetsPath = await uniqueDirectoryForTest(); + const { stdout, stderr } = await run(__dirname, ["init", assetsPath, "--force"]); + + expect(stdout).toContain("Project has been initialised with webpack!"); + expect(stderr).toContain("webpack.config.js"); + + // Test files + defaultTemplateFiles.forEach((file) => { + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + }); + + // Check if the generated package.json file content matches the snapshot + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + }); + + it("should generate folders if non existing generation path is given", async () => { + const assetsPath = path.resolve(os.tmpdir(), Date.now().toString()); + const { stdout, stderr } = await run(__dirname, ["init", assetsPath, "--force"]); + + expect(stdout).toContain("Project has been initialised with webpack!"); + expect(stderr).toContain(`create ${path.relative(__dirname, assetsPath)}`); + expect(stderr).toContain("webpack.config.js"); + + // Test files + defaultTemplateFiles.forEach((file) => { + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + }); + + // Check if the generated package.json file content matches the snapshot + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + }); + + it("should configure assets modules by default", async () => { + const assetsPath = path.resolve(os.tmpdir(), Date.now().toString()); + const { stdout, stderr } = await run(__dirname, ["init", assetsPath, "--force"]); + + expect(stdout).toContain("Project has been initialised with webpack!"); + expect(stderr).toContain(`create ${path.relative(__dirname, assetsPath)}`); + expect(stderr).toContain("webpack.config.js"); + + // Test files + defaultTemplateFiles.forEach((file) => { + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + }); + + // Check if the generated package.json file content matches the snapshot + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + + // Check if the generated webpack configuration matches the snapshot + expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); + }); + + it("should ask question when wrong template is supplied", async () => { + const assetsPath = await uniqueDirectoryForTest(); + const { stdout, stderr } = await runPromptWithAnswers( + assetsPath, + ["init", "--force", "--template=apple"], + [`${ENTER}`], + ); + + expect(stdout).toContain("Project has been initialised with webpack!"); + expect(stderr).toContain("apple is not a valid template, please select one from below"); + expect(stderr).toContain("webpack.config.js"); + + // Test files + defaultTemplateFiles.forEach((file) => { + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + }); + + // Check if the generated package.json file content matches the snapshot + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + }); + + it("should generate typescript project correctly", async () => { + const assetsPath = await uniqueDirectoryForTest(); + const { stdout, stderr } = await runPromptWithAnswers( + assetsPath, + ["init"], + [`${DOWN}${DOWN}${ENTER}`, `n${ENTER}`, `n${ENTER}`, `n${ENTER}`, ENTER, ENTER], + ); + + expect(stdout).toContain("Project has been initialised with webpack!"); + expect(stderr).toContain("webpack.config.js"); + expect(stderr).toContain("tsconfig.json"); + + // Test files + const files = [ + ...defaultTemplateFiles.filter((file) => file !== "src/index.js"), + "src/index.ts", + "tsconfig.json", + ]; + + files.forEach((file) => { + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + }); + + // Check if the generated package.json file content matches the snapshot + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + + // Check if the generated webpack configuration matches the snapshot + expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); + }); + + it("should generate ES6 project correctly", async () => { + const assetsPath = await uniqueDirectoryForTest(); + const { stdout, stderr } = await runPromptWithAnswers( + assetsPath, + ["init"], + [`${DOWN}${ENTER}`, `n${ENTER}`, `n${ENTER}`, `n${ENTER}`, ENTER, ENTER], + ); + + expect(stdout).toContain("Project has been initialised with webpack!"); + expect(stderr).toContain("webpack.config.js"); + expect(stderr).toContain(".babelrc"); + + // Test files + const files = [...defaultTemplateFiles, ".babelrc"]; + + files.forEach((file) => { + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + }); + + // Check if the generated package.json file content matches the snapshot + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + + // Check if the generated webpack configuration matches the snapshot + expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); + }); + + it("should use sass in project when selected", async () => { + const assetsPath = await uniqueDirectoryForTest(); + const { stdout, stderr } = await runPromptWithAnswers( + assetsPath, + ["init"], + [ + `${ENTER}`, + `n${ENTER}`, + `n${ENTER}`, + `n${ENTER}`, + `${DOWN}${DOWN}${ENTER}`, + `n${ENTER}`, + `n${ENTER}`, + `n${ENTER}`, + ENTER, + ], + ); + + expect(stdout).toContain("Project has been initialised with webpack!"); + expect(stderr).toContain("webpack.config.js"); + + // Test files + defaultTemplateFiles.forEach((file) => { + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + }); + + // Check if the generated package.json file content matches the snapshot + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + + // Check if the generated webpack configuration matches the snapshot + expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); + }); + + it("should use sass with postcss in project when selected", async () => { + const assetsPath = await uniqueDirectoryForTest(); + const { stdout, stderr } = await runPromptWithAnswers( + assetsPath, + ["init"], + [ + `${ENTER}`, + `n${ENTER}`, + `n${ENTER}`, + `n${ENTER}`, + `${DOWN}${DOWN}${ENTER}`, + `n${ENTER}`, + `y${ENTER}`, + `n${ENTER}`, + ENTER, + ], + ); + + expect(stdout).toContain("Project has been initialised with webpack!"); + expect(stderr).toContain("webpack.config.js"); + + // Test files + const files = [...defaultTemplateFiles, "postcss.config.js"]; + + files.forEach((file) => { + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + }); + + // Check if the generated package.json file content matches the snapshot + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + + // Check if the generated webpack configuration matches the snapshot + expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); + }); + + it("should use mini-css-extract-plugin when selected", async () => { + const assetsPath = await uniqueDirectoryForTest(); + const { stdout, stderr } = await runPromptWithAnswers( + assetsPath, + ["init"], + [ + `${ENTER}`, + `n${ENTER}`, + `n${ENTER}`, + `n${ENTER}`, + `${DOWN}${DOWN}${ENTER}`, + `n${ENTER}`, + `n${ENTER}`, + `y${ENTER}`, + ENTER, + ], + ); + + expect(stdout).toContain("Project has been initialised with webpack!"); + expect(stderr).toContain("webpack.config.js"); + + // Test files + defaultTemplateFiles.forEach((file) => { + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + }); + + // Check if the generated package.json file content matches the snapshot + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + + // Check if the generated webpack configuration matches the snapshot + expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); + }); + + it("should use sass and css with postcss in project when selected", async () => { + const assetsPath = await uniqueDirectoryForTest(); + const { stdout, stderr } = await runPromptWithAnswers( + assetsPath, + ["init"], + [ + `${ENTER}`, + `n${ENTER}`, + `n${ENTER}`, + `n${ENTER}`, + `${DOWN}${DOWN}${ENTER}`, + `y${ENTER}`, + `y${ENTER}`, + `n${ENTER}`, + ENTER, + ], + ); + + expect(stdout).toContain("Project has been initialised with webpack!"); + expect(stderr).toContain("webpack.config.js"); + + // Test files + const files = [...defaultTemplateFiles, "postcss.config.js"]; + + files.forEach((file) => { + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + }); + + // Check if the generated package.json file content matches the snapshot + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + + // Check if the generated webpack configuration matches the snapshot + expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); + }); + + it("should use less in project when selected", async () => { + const assetsPath = await uniqueDirectoryForTest(); + const { stdout, stderr } = await runPromptWithAnswers( + assetsPath, + ["init"], + [ + `${ENTER}`, + `n${ENTER}`, + `n${ENTER}`, + `n${ENTER}`, + `${DOWN}${DOWN}${DOWN}${ENTER}`, + `n${ENTER}`, + `n${ENTER}`, + `n${ENTER}`, + ENTER, + ], + ); + + expect(stdout).toContain("Project has been initialised with webpack!"); + expect(stderr).toContain("webpack.config.js"); + + // Test files + defaultTemplateFiles.forEach((file) => { + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + }); + + // Check if the generated package.json file content matches the snapshot + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + + // Check if the generated webpack configuration matches the snapshot + expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); + }); + + it("should use stylus in project when selected", async () => { + const assetsPath = await uniqueDirectoryForTest(); + const { stdout, stderr } = await runPromptWithAnswers( + assetsPath, + ["init"], + [ + `${ENTER}`, + `n${ENTER}`, + `n${ENTER}`, + `n${ENTER}`, + `${DOWN}${DOWN}${DOWN}${DOWN}${ENTER}`, + `n${ENTER}`, + `n${ENTER}`, + `n${ENTER}`, + ENTER, + ], + ); + + expect(stdout).toContain("Project has been initialised with webpack!"); + expect(stderr).toContain("webpack.config.js"); + + // Test files + defaultTemplateFiles.forEach((file) => { + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + }); + + // Check if the generated package.json file content matches the snapshot + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + + // Check if the generated webpack configuration matches the snapshot + expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); + }); + + it("should configure WDS as opted", async () => { + const assetsPath = await uniqueDirectoryForTest(); + const { stdout, stderr } = await runPromptWithAnswers( + assetsPath, + ["init"], + [ENTER, ENTER, `n${ENTER}`, `n${ENTER}`, ENTER, ENTER], + ); + + expect(stdout).toContain("Do you want to use webpack-dev-server?"); + expect(stdout).toContain("Project has been initialised with webpack!"); + expect(stderr).toContain("webpack.config.js"); + + // Test files + defaultTemplateFiles.forEach((file) => { + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + }); + + // Check if the generated package.json file content matches the snapshot + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + + // Check if the generated webpack configuration matches the snapshot + expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); + }); + + it("should use postcss in project when selected", async () => { + const assetsPath = await uniqueDirectoryForTest(); + const { stdout, stderr } = await runPromptWithAnswers( + assetsPath, + ["init"], + [ + `${ENTER}`, + `n${ENTER}`, + `n${ENTER}`, + `n${ENTER}`, + `${DOWN}${ENTER}`, + ENTER, + `n${ENTER}`, + ENTER, + ], + ); + + expect(stdout).toContain("Project has been initialised with webpack!"); + expect(stderr).toContain("webpack.config.js"); + + // Test files + const files = [...defaultTemplateFiles, "postcss.config.js"]; + + files.forEach((file) => { + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + }); + + // Check if the generated package.json file content matches the snapshot + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + + // Check if the generated webpack configuration matches the snapshot + expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); + }); + + it("should configure html-webpack-plugin as opted", async () => { + const assetsPath = await uniqueDirectoryForTest(); + const { stdout, stderr } = await runPromptWithAnswers( + assetsPath, + ["init"], + [ENTER, `n${ENTER}`, ENTER, `n${ENTER}`, ENTER, ENTER], + ); + + expect(stdout).toContain("Do you want to simplify the creation of HTML files for your bundle?"); + expect(stdout).toContain("Project has been initialised with webpack!"); + expect(stderr).toContain("webpack.config.js"); + + // Test files + defaultTemplateFiles.forEach((file) => { + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + }); + + // Check if the generated package.json file content matches the snapshot + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + + // Check if the generated webpack configuration matches the snapshot + expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); + }); + + it("should configure workbox-webpack-plugin as opted", async () => { + const assetsPath = await uniqueDirectoryForTest(); + const { stdout, stderr } = await runPromptWithAnswers( + assetsPath, + ["init"], + [ENTER, `n${ENTER}`, ENTER, ENTER, ENTER, ENTER], + ); + + expect(stdout).toContain("Do you want to add PWA support?"); + expect(stdout).toContain("Project has been initialised with webpack!"); + expect(stderr).toContain("webpack.config.js"); + + // Test files + defaultTemplateFiles.forEach((file) => { + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + }); + + // Check if the generated package.json file content matches the snapshot + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + + // Check if the generated webpack configuration matches the snapshot + expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); + }); + + it("should throw if the current path is not writable", async () => { + if (isWindows) { + return; + } + + const assetsPath = await uniqueDirectoryForTest(); + const projectPath = join(assetsPath, "non-writable-path"); + + mkdirSync(projectPath, 0o500); + + const { exitCode, stderr } = await run(projectPath, ["init", "my-app"], { reject: false }); + + expect(exitCode).toBe(2); + expect(stderr).toContain("Failed to initialize the project."); + }); + + it("should work with 'new' alias", async () => { + const assetsPath = await uniqueDirectoryForTest(); + const { stdout, stderr } = await run(assetsPath, ["new", "--force"]); + + expect(stdout).toContain("Project has been initialised with webpack!"); + expect(stderr).toContain("webpack.config.js"); + + // Test files + defaultTemplateFiles.forEach((file) => { + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + }); + + // Check if the generated package.json file content matches the snapshot + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + }); + + it("should work with 'create' alias", async () => { + const assetsPath = await uniqueDirectoryForTest(); + const { stdout, stderr } = await run(assetsPath, ["create", "--force"]); + + expect(stdout).toContain("Project has been initialised with webpack!"); + expect(stderr).toContain("webpack.config.js"); + + // Test files + defaultTemplateFiles.forEach((file) => { + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + }); + + // Check if the generated package.json file content matches the snapshot + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + }); + + it("should work with 'c' alias", async () => { + const assetsPath = await uniqueDirectoryForTest(); + const { stdout, stderr } = await run(assetsPath, ["c", "--force"]); + + expect(stdout).toContain("Project has been initialised with webpack!"); + expect(stderr).toContain("webpack.config.js"); + + // Test files + defaultTemplateFiles.forEach((file) => { + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + }); + + // Check if the generated package.json file content matches the snapshot + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + }); + + it("should work with 'n' alias", async () => { + const assetsPath = await uniqueDirectoryForTest(); + const { stdout, stderr } = await run(assetsPath, ["n", "--force"]); + + expect(stdout).toContain("Project has been initialised with webpack!"); + expect(stderr).toContain("webpack.config.js"); + + // Test files + defaultTemplateFiles.forEach((file) => { + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + }); + + // Check if the generated package.json file content matches the snapshot + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + }); + + it("recognizes '-t' as an alias for '--template'", async () => { + const assetsPath = await uniqueDirectoryForTest(); + const { stdout, stderr } = await run(assetsPath, ["init", "-t", "default", "--force"]); + + expect(stdout).toContain("Project has been initialised with webpack!"); + expect(stderr).toContain("webpack.config.js"); + + // Test files + defaultTemplateFiles.forEach((file) => { + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + }); + + // Check if the generated package.json file content matches the snapshot + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + }); + + it("recognizes '-f' as an alias for '--force'", async () => { + const assetsPath = await uniqueDirectoryForTest(); + const { stdout, stderr } = await run(assetsPath, ["init", "-f"]); + + expect(stdout).toContain("Project has been initialised with webpack!"); + expect(stderr).toContain("webpack.config.js"); + + // Test files + defaultTemplateFiles.forEach((file) => { + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + }); + + // Check if the generated package.json file content matches the snapshot + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + }); + + it("uses yarn as the package manager when opted", async () => { + const assetsPath = await uniqueDirectoryForTest(); + const { stdout, stderr } = await runPromptWithAnswers( + assetsPath, + ["init"], + [ENTER, `n${ENTER}`, `n${ENTER}`, `n${ENTER}`, ENTER, `${DOWN}${ENTER}`], + ); + + expect(stdout).toContain("Project has been initialised with webpack!"); + expect(stderr).toContain("webpack.config.js"); + + // Test files + const files = [ + ...defaultTemplateFiles.filter((file) => file !== "package-lock.json"), + "yarn.lock", + ]; + + files.forEach((file) => { + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + }); + + // Check if the generated package.json file content matches the snapshot + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + }); + + it("should generate react template with prompt answers", async () => { + const assetsPath = await uniqueDirectoryForTest(); + const { stdout, stderr } = await runPromptWithAnswers( + assetsPath, + ["init"], + [ENTER, `y${ENTER}`, `${DOWN}${ENTER}`, `y${ENTER}`, ENTER, ENTER], + ); + + expect(stdout).toContain("Project has been initialised with webpack!"); + expect(stderr).toContain("webpack.config.js"); + + // Test files + reactTemplateFiles.forEach((file) => { + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + }); + + // Check if the generated package.json file content matches the snapshot + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + + // Check if the generated webpack configuration matches the snapshot + expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); + }); + + it("should generate react template with --force", async () => { + const assetsPath = await uniqueDirectoryForTest(); + const { stdout, stderr } = await run(assetsPath, ["init", "--template=react", "--force"]); + + expect(stdout).toContain("Project has been initialised with webpack!"); + expect(stderr).toContain("webpack.config.js"); + + // Test files + reactTemplateFiles.forEach((file) => { + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + }); + + // Check if the generated package.json file content matches the snapshot + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + + // Check if the generated webpack configuration matches the snapshot + expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); + }); +}); diff --git a/test/create-webpack-app/test.utils.js b/test/create-webpack-app/test.utils.js new file mode 100644 index 00000000000..ab6960494eb --- /dev/null +++ b/test/create-webpack-app/test.utils.js @@ -0,0 +1,426 @@ +/* eslint-disable node/no-unpublished-require */ + +"use strict"; + +const os = require("os"); +const stripAnsi = require("strip-ansi"); +const path = require("path"); +const fs = require("fs"); +const execa = require("execa"); +const internalIp = require("internal-ip"); +const { exec } = require("child_process"); +const { node: execaNode } = execa; +const { Writable } = require("readable-stream"); +const concat = require("concat-stream"); +const { cli } = require("webpack"); + +const CREATE_WEBPACK_PATH = path.resolve(__dirname, "../../packages/create-webpack-app/bin/cli.js"); +const ENABLE_LOG_COMPILATION = process.env.ENABLE_PIPE || false; +const isWindows = process.platform === "win32"; + +const hyphenToUpperCase = (name) => { + if (!name) { + return name; + } + + return name.replace(/-([a-z])/g, function (g) { + return g[1].toUpperCase(); + }); +}; + +const processKill = (process) => { + if (isWindows) { + exec("taskkill /pid " + process.pid + " /T /F"); + } else { + process.kill(); + } +}; + +/** + * Webpack CLI test runner. + * + * @param {string} cwd The path to folder that contains test + * @param {Array} args Array of arguments + * @param {Object} options Options for tests + * @returns {Promise} + */ +const createProcess = (cwd, args, options) => { + const { nodeOptions = [] } = options; + const processExecutor = nodeOptions.length ? execaNode : execa; + + return processExecutor(CREATE_WEBPACK_PATH, args, { + cwd: path.resolve(cwd), + reject: false, + stdio: ENABLE_LOG_COMPILATION ? "inherit" : "pipe", + maxBuffer: Infinity, + env: { WEBPACK_CLI_HELP_WIDTH: 1024 }, + ...options, + }); +}; + +/** + * Run the webpack CLI for a test case. + * + * @param {string} cwd The path to folder that contains test + * @param {Array} args Array of arguments + * @param {Object} options Options for tests + * @returns {Promise} + */ +const run = async (cwd, args = [], options = {}) => { + return createProcess(cwd, args, options); +}; + +/** + * Run the webpack CLI for a test case and get process. + * + * @param {string} cwd The path to folder that contains test + * @param {Array} args Array of arguments + * @param {Object} options Options for tests + * @returns {Promise} + */ +const runAndGetProcess = (cwd, args = [], options = {}) => { + return createProcess(cwd, args, options); +}; + +/** + * Run the webpack CLI in watch mode for a test case. + * + * @param {string} cwd The path to folder that contains test + * @param {Array} args Array of arguments + * @param {Object} options Options for tests + * @returns {Object} The webpack output or Promise when nodeOptions are present + */ +const runWatch = (cwd, args = [], options = {}) => { + return new Promise((resolve, reject) => { + const process = createProcess(cwd, args, options); + const outputKillStr = options.killString || /webpack \d+\.\d+\.\d/; + const stdoutKillStr = options.stdoutKillStr; + const stderrKillStr = options.stderrKillStr; + + let isStdoutDone = false; + let isStderrDone = false; + + process.stdout.pipe( + new Writable({ + write(chunk, encoding, callback) { + const output = stripAnsi(chunk.toString("utf8")); + + if (stdoutKillStr && stdoutKillStr.test(output)) { + isStdoutDone = true; + } else if (!stdoutKillStr && outputKillStr.test(output)) { + processKill(process); + } + + if (isStdoutDone && isStderrDone) { + processKill(process); + } + + callback(); + }, + }), + ); + + process.stderr.pipe( + new Writable({ + write(chunk, encoding, callback) { + const output = stripAnsi(chunk.toString("utf8")); + + if (stderrKillStr && stderrKillStr.test(output)) { + isStderrDone = true; + } else if (!stderrKillStr && outputKillStr.test(output)) { + processKill(process); + } + + if (isStdoutDone && isStderrDone) { + processKill(process); + } + + callback(); + }, + }), + ); + + process + .then((result) => { + resolve(result); + }) + .catch((error) => { + reject(error); + }); + }); +}; + +/** + * runPromptWithAnswers + * @param {string} location location of current working directory + * @param {string[]} args CLI args to pass in + * @param {string[]} answers answers to be passed to stdout for inquirer question + */ +const runPromptWithAnswers = (location, args, answers) => { + const process = runAndGetProcess(location, args); + + process.stdin.setDefaultEncoding("utf-8"); + + const delay = 2000; + let outputTimeout; + let currentAnswer = 0; + + const writeAnswer = (output) => { + if (!answers) { + process.stdin.write(output); + processKill(process); + + return; + } + + if (currentAnswer < answers.length) { + process.stdin.write(answers[currentAnswer]); + currentAnswer++; + } + }; + + process.stdout.pipe( + new Writable({ + write(chunk, encoding, callback) { + const output = chunk.toString("utf8"); + + if (output) { + if (outputTimeout) { + clearTimeout(outputTimeout); + } + + // we must receive new stdout, then have 2 seconds + // without any stdout before writing the next answer + outputTimeout = setTimeout(() => { + writeAnswer(output); + }, delay); + } + + callback(); + }, + }), + ); + + return new Promise((resolve) => { + const obj = {}; + + let stdoutDone = false; + let stderrDone = false; + + const complete = () => { + if (outputTimeout) { + clearTimeout(outputTimeout); + } + + if (stdoutDone && stderrDone) { + processKill(process); + resolve(obj); + } + }; + + process.stdout.pipe( + concat((result) => { + stdoutDone = true; + obj.stdout = result.toString(); + + complete(); + }), + ); + + process.stderr.pipe( + concat((result) => { + stderrDone = true; + obj.stderr = result.toString(); + + complete(); + }), + ); + }); +}; + +const normalizeVersions = (output) => { + return output.replace( + /(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?/gi, + "x.x.x", + ); +}; + +const normalizeCwd = (output) => { + return output + .replace(/\\/g, "/") + .replace(new RegExp(process.cwd().replace(/\\/g, "/"), "g"), ""); +}; + +const normalizeError = (output) => { + return output + .replace(/SyntaxError: .+/, "SyntaxError: ") + .replace(/\s+at .+(}|\)|\d)/gs, "\n at stack"); +}; + +const normalizeStdout = (stdout) => { + if (typeof stdout !== "string") { + return stdout; + } + + if (stdout.length === 0) { + return stdout; + } + + let normalizedStdout = stripAnsi(stdout); + normalizedStdout = normalizeCwd(normalizedStdout); + normalizedStdout = normalizeVersions(normalizedStdout); + normalizedStdout = normalizeError(normalizedStdout); + + return normalizedStdout; +}; + +const normalizeStderr = (stderr) => { + if (typeof stderr !== "string") { + return stderr; + } + + if (stderr.length === 0) { + return stderr; + } + + let normalizedStderr = stripAnsi(stderr); + normalizedStderr = normalizeCwd(normalizedStderr); + + const networkIPv4 = internalIp.v4.sync(); + + if (networkIPv4) { + normalizedStderr = normalizedStderr.replace(new RegExp(networkIPv4, "g"), ""); + } + + const networkIPv6 = internalIp.v6.sync(); + + if (networkIPv6) { + normalizedStderr = normalizedStderr.replace(new RegExp(networkIPv6, "g"), ""); + } + + normalizedStderr = normalizedStderr.replace(/:[0-9]+\//g, ":/"); + + if (!/On Your Network \(IPv6\)/.test(stderr)) { + // Github Actions doesn't' support IPv6 on ubuntu in some cases + normalizedStderr = normalizedStderr.split("\n"); + + const ipv4MessageIndex = normalizedStderr.findIndex((item) => + /On Your Network \(IPv4\)/.test(item), + ); + + if (ipv4MessageIndex !== -1) { + normalizedStderr.splice( + ipv4MessageIndex + 1, + 0, + " [webpack-dev-server] On Your Network (IPv6): http://[]:/", + ); + } + + normalizedStderr = normalizedStderr.join("\n"); + } + + // TODO remove me after drop old Node.js versions and update deps + // Suppress warnings for Node.js version >= v22 + // [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead. + if (process.version.startsWith("v22")) { + normalizedStderr = normalizedStderr + .split("\n") + .filter((line) => { + return ( + !line.includes("DeprecationWarning: The `punycode` module is deprecated.") && + !line.includes("Use `node --trace-deprecation ...`") + ); + }) + .join("\n"); + } + + // the warning below is causing CI failure on some jobs + if (/Gracefully shutting down/.test(stderr)) { + normalizedStderr = normalizedStderr.replace( + "\n [webpack-dev-server] Gracefully shutting down. To force exit, press ^C again. Please wait...", + "", + ); + } + + normalizedStderr = normalizeVersions(normalizedStderr); + normalizedStderr = normalizeError(normalizedStderr); + + return normalizedStderr; +}; + +const getWebpackCliArguments = (startWith) => { + if (typeof startWith === "undefined") { + return cli.getArguments(); + } + + const result = {}; + + for (const [name, value] of Object.entries(cli.getArguments())) { + if (name.startsWith(startWith)) { + result[name] = value; + } + } + + return result; +}; + +const readFile = (path, options = {}) => + new Promise((resolve, reject) => { + fs.readFile(path, options, (err, stats) => { + if (err) { + reject(err); + } + resolve(stats); + }); + }); + +const readdir = (path) => + new Promise((resolve, reject) => { + fs.readdir(path, (err, stats) => { + if (err) { + reject(err); + } + resolve(stats); + }); + }); + +// cSpell:ignore Symbhas, ABCDEFGHNR, Vfgcti +const urlAlphabet = "ModuleSymbhasOwnPr-0123456789ABCDEFGHNRVfgctiUvz_KqYTJkLxpZXIjQW"; + +const uuid = (size = 21) => { + let id = ""; + let i = size; + + while (i--) { + // `| 0` is more compact and faster than `Math.floor()`. + id += urlAlphabet[(Math.random() * 64) | 0]; + } + + return id; +}; + +const uniqueDirectoryForTest = async () => { + const result = path.resolve(os.tmpdir(), uuid()); + + if (!fs.existsSync(result)) { + fs.mkdirSync(result); + } + + return result; +}; + +module.exports = { + run, + runAndGetProcess, + runWatch, + runPromptWithAnswers, + isWindows, + normalizeStderr, + normalizeStdout, + uniqueDirectoryForTest, + readFile, + readdir, + hyphenToUpperCase, + processKill, + getWebpackCliArguments, +}; From e6add830932c39181aef9612d33b6a5f959a0122 Mon Sep 17 00:00:00 2001 From: Uzair Date: Sat, 8 Jun 2024 19:41:21 +0530 Subject: [PATCH 30/81] refactor: change console output format for deps installation --- packages/create-webpack-app/src/plopfile.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/create-webpack-app/src/plopfile.ts b/packages/create-webpack-app/src/plopfile.ts index 906d5e9ffd1..3476a639a65 100644 --- a/packages/create-webpack-app/src/plopfile.ts +++ b/packages/create-webpack-app/src/plopfile.ts @@ -21,8 +21,9 @@ export default function (plop: NodePlopAPI) { if (answers.isPostCSS) { dependencies.push("postcss-loader", "autoprefixer"); } - console.log(`Installing packages: ${dependencies.join(", ")}`); + console.log(`Installing packages: ${dependencies.join("\n")}`); const returnMessage = `Project ${answers.projectName} has been successfully created at ${answers.projectPath}/${answers.projectName}`; + const packageManager = answers.packageManager; const installCommandPrefix = packageManager === "yarn" ? "add" : "install"; @@ -37,9 +38,6 @@ export default function (plop: NodePlopAPI) { npmInstallPackages.stderr.on("data", (data) => { console.error(data.toString()); }); - npmInstallPackages.on("error", (err) => { - console.error(err); - }); npmInstallPackages.on("close", (code) => { if (code !== 0) { console.error(`child process exited with code ${code}`); From 4ac8bf00891fa4a519de3e56ca6911922a47c4ca Mon Sep 17 00:00:00 2001 From: Uzair Date: Sat, 8 Jun 2024 19:41:57 +0530 Subject: [PATCH 31/81] feat: change -s flag to -f|--force and make it default --- packages/create-webpack-app/bin/cli.js | 27 +++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/packages/create-webpack-app/bin/cli.js b/packages/create-webpack-app/bin/cli.js index f711a75961c..f84deeaf1da 100644 --- a/packages/create-webpack-app/bin/cli.js +++ b/packages/create-webpack-app/bin/cli.js @@ -21,23 +21,32 @@ const defaultValues = { }, }; -program.version("1.0.0", "-v, --version"); -program.helpOption("-h, --help", "Display help for command"); +program + .version("1.0.0", "-v, --version") + .usage("[command] [options]") + .helpOption("-h, --help", "Display help for command") + .description("A CLI tool to generate a Webpack project"); program - .command("init") + .command("init", { isDefault: true }) + .aliases(["i", "c", "create", "new"]) .description("Initialize a new Webpack project") - .option("-s, --skip", "Skip the prompt and use the default values", false) - .option("-f, --force", "Force the generator actions to override existing files", false) - .action(function (opts) { + .argument("[projectPath]", "Path to create the project") + .argument("[projectName]", "Name of the project") + .option("-f, --force", "Skip the prompt and use the default values", false) + .action(function (projectName, projectPath, opts) { console.log("Initializing a new Webpack project"); - const { skip, force } = opts; + const { force } = opts; const initGenerator = plop.getGenerator("init"); - if (skip) { + const byPassValues = []; + if (projectName) byPassValues.push(projectName); + if (projectPath) byPassValues.push(projectPath); + + if (force) { console.log("Skipping the prompt and using the default values"); initGenerator.runActions(defaultValues.init); } else { - initGenerator.runPrompts([]).then((answers) => { + initGenerator.runPrompts(byPassValues).then((answers) => { initGenerator.runActions(answers); }); } From d1fb3e7299a5d2be924b75731476b0df9f3f18dd Mon Sep 17 00:00:00 2001 From: Uzair Date: Sat, 8 Jun 2024 21:51:11 +0530 Subject: [PATCH 32/81] chore: add 'uzair' as a word for cspell --- .cspell.json | 1 + 1 file changed, 1 insertion(+) diff --git a/.cspell.json b/.cspell.json index 156c2be7a33..4afe60b55e2 100644 --- a/.cspell.json +++ b/.cspell.json @@ -92,6 +92,7 @@ "testname", "Tobio", "tsbuildinfo", + "uzair", "typeahead", "wagoid", "webassembly", From 2928772482d81843283289bf9063f8ebb20d11f9 Mon Sep 17 00:00:00 2001 From: Uzair Date: Sat, 8 Jun 2024 21:55:47 +0530 Subject: [PATCH 33/81] test: create test suite for create-webpack-app package - create test cases for all the functionalities - Default project - Project with a specified generation path - TypeScript project - ES6 project - Project with Sass - Project with Sass and PostCSS - Project with Mini-CSS-Extract-Plugin - Project with Sass, CSS, and PostCSSProject with Less - Project with Stylus - Project with Webpack Dev Server (WDS) - Project with HtmlWebpackPluginProject with WorkboxWebpackPlugin - Project with a writable current path - Project with Yarn package manager --- .../create-webpack-app.test.js.snap.webpack5 | 1088 +++++++++++++++++ .../create-webpack-app.test.js | 196 +-- 2 files changed, 1186 insertions(+), 98 deletions(-) create mode 100644 test/create-webpack-app/__snapshots__/create-webpack-app.test.js.snap.webpack5 diff --git a/test/create-webpack-app/__snapshots__/create-webpack-app.test.js.snap.webpack5 b/test/create-webpack-app/__snapshots__/create-webpack-app.test.js.snap.webpack5 new file mode 100644 index 00000000000..1dd7841aa72 --- /dev/null +++ b/test/create-webpack-app/__snapshots__/create-webpack-app.test.js.snap.webpack5 @@ -0,0 +1,1088 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`init command should configure WDS as opted 1`] = ` +{ + "description": "My webpack project", + "devDependencies": { + "webpack": "x.x.x", + "webpack-cli": "x.x.x", + "webpack-dev-server": "x.x.x", + }, + "name": "my-webpack-project", + "scripts": { + "build": "webpack --mode=production --node-env=production", + "build:dev": "webpack --mode=development", + "build:prod": "webpack --mode=production --node-env=production", + "serve": "webpack serve", + "watch": "webpack --watch", + }, + "version": "1.0.0", +} +`; + +exports[`init command should configure WDS as opted 2`] = ` +"// Generated using webpack-cli https://github.com/webpack/webpack-cli + +const path = require("path"); + +const isProduction = process.env.NODE_ENV === "production"; + +const config = { + entry: "./src/index.js", + output: { + path: path.resolve(__dirname, "dist"), + }, + devServer: { + open: true, + host: "localhost", + }, + plugins: [ + // Add your plugins here + // Learn more about plugins from https://webpack.js.org/configuration/plugins/ + ], + module: { + rules: [ + { + 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/ + ], + }, +}; + +module.exports = () => { + if (isProduction) { + config.mode = "production"; + } else { + config.mode = "development"; + } + return config; +}; +" +`; + +exports[`init command should configure assets modules by default 1`] = ` +{ + "description": "My webpack project", + "devDependencies": { + "html-loader": "x.x.x", + "html-webpack-plugin": "x.x.x", + "webpack": "x.x.x", + "webpack-cli": "x.x.x", + "webpack-dev-server": "x.x.x", + "workbox-webpack-plugin": "x.x.x", + }, + "name": "my-webpack-project", + "scripts": { + "build": "webpack --mode=production --node-env=production", + "build:dev": "webpack --mode=development", + "build:prod": "webpack --mode=production --node-env=production", + "serve": "webpack serve", + "watch": "webpack --watch", + }, + "version": "1.0.0", +} +`; + +exports[`init command should configure assets modules by default 2`] = ` +"// Generated using webpack-cli https://github.com/webpack/webpack-cli + +const path = require("path"); +const HtmlWebpackPlugin = require("html-webpack-plugin"); +const WorkboxWebpackPlugin = require("workbox-webpack-plugin"); + +const isProduction = process.env.NODE_ENV === "production"; + +const config = { + entry: "./src/index.js", + output: { + path: path.resolve(__dirname, "dist"), + }, + devServer: { + open: true, + host: "localhost", + }, + plugins: [ + new HtmlWebpackPlugin({ + template: "index.html", + }), + + // Add your plugins here + // Learn more about plugins from https://webpack.js.org/configuration/plugins/ + ], + module: { + rules: [ + { + test: /\\.(eot|svg|ttf|woff|woff2|png|jpg|gif)$/i, + type: "asset", + }, + + { + test: /\\.html$/i, + use: ["html-loader"], + }, + + // Add your rules for custom modules here + // Learn more about loaders from https://webpack.js.org/loaders/ + ], + }, +}; + +module.exports = () => { + if (isProduction) { + config.mode = "production"; + + config.plugins.push(new WorkboxWebpackPlugin.GenerateSW()); + } else { + config.mode = "development"; + } + return config; +}; +" +`; + +exports[`init command should configure html-webpack-plugin as opted 1`] = ` +{ + "description": "My webpack project", + "devDependencies": { + "html-loader": "x.x.x", + "html-webpack-plugin": "x.x.x", + "webpack": "x.x.x", + "webpack-cli": "x.x.x", + }, + "name": "my-webpack-project", + "scripts": { + "build": "webpack --mode=production --node-env=production", + "build:dev": "webpack --mode=development", + "build:prod": "webpack --mode=production --node-env=production", + "watch": "webpack --watch", + }, + "version": "1.0.0", +} +`; + +exports[`init command should configure html-webpack-plugin as opted 2`] = ` +"// Generated using webpack-cli https://github.com/webpack/webpack-cli + +const path = require("path"); +const HtmlWebpackPlugin = require("html-webpack-plugin"); + +const isProduction = process.env.NODE_ENV === "production"; + +const config = { + entry: "./src/index.js", + output: { + path: path.resolve(__dirname, "dist"), + }, + plugins: [ + new HtmlWebpackPlugin({ + template: "index.html", + }), + + // Add your plugins here + // Learn more about plugins from https://webpack.js.org/configuration/plugins/ + ], + module: { + rules: [ + { + test: /\\.(eot|svg|ttf|woff|woff2|png|jpg|gif)$/i, + type: "asset", + }, + + { + test: /\\.html$/i, + use: ["html-loader"], + }, + + // Add your rules for custom modules here + // Learn more about loaders from https://webpack.js.org/loaders/ + ], + }, +}; + +module.exports = () => { + if (isProduction) { + config.mode = "production"; + } else { + config.mode = "development"; + } + return config; +}; +" +`; + +exports[`init command should configure workbox-webpack-plugin as opted 1`] = ` +{ + "description": "My webpack project", + "devDependencies": { + "html-loader": "x.x.x", + "html-webpack-plugin": "x.x.x", + "webpack": "x.x.x", + "webpack-cli": "x.x.x", + "workbox-webpack-plugin": "x.x.x", + }, + "name": "my-webpack-project", + "scripts": { + "build": "webpack --mode=production --node-env=production", + "build:dev": "webpack --mode=development", + "build:prod": "webpack --mode=production --node-env=production", + "watch": "webpack --watch", + }, + "version": "1.0.0", +} +`; + +exports[`init command should configure workbox-webpack-plugin as opted 2`] = ` +"// Generated using webpack-cli https://github.com/webpack/webpack-cli + +const path = require("path"); +const HtmlWebpackPlugin = require("html-webpack-plugin"); +const WorkboxWebpackPlugin = require("workbox-webpack-plugin"); + +const isProduction = process.env.NODE_ENV === "production"; + +const config = { + entry: "./src/index.js", + output: { + path: path.resolve(__dirname, "dist"), + }, + plugins: [ + new HtmlWebpackPlugin({ + template: "index.html", + }), + + // Add your plugins here + // Learn more about plugins from https://webpack.js.org/configuration/plugins/ + ], + module: { + rules: [ + { + test: /\\.(eot|svg|ttf|woff|woff2|png|jpg|gif)$/i, + type: "asset", + }, + + { + test: /\\.html$/i, + use: ["html-loader"], + }, + + // Add your rules for custom modules here + // Learn more about loaders from https://webpack.js.org/loaders/ + ], + }, +}; + +module.exports = () => { + if (isProduction) { + config.mode = "production"; + + config.plugins.push(new WorkboxWebpackPlugin.GenerateSW()); + } else { + config.mode = "development"; + } + return config; +}; +" +`; + +exports[`init command should generate ES6 project correctly 1`] = ` +{ + "description": "My webpack project", + "devDependencies": { + "@babel/core": "x.x.x", + "@babel/preset-env": "x.x.x", + "babel-loader": "x.x.x", + "webpack": "x.x.x", + "webpack-cli": "x.x.x", + }, + "name": "my-webpack-project", + "scripts": { + "build": "webpack --mode=production --node-env=production", + "build:dev": "webpack --mode=development", + "build:prod": "webpack --mode=production --node-env=production", + "watch": "webpack --watch", + }, + "version": "1.0.0", +} +`; + +exports[`init command should generate ES6 project correctly 2`] = ` +"// Generated using webpack-cli https://github.com/webpack/webpack-cli + +const path = require("path"); + +const isProduction = process.env.NODE_ENV === "production"; + +const config = { + entry: "./src/index.js", + output: { + path: path.resolve(__dirname, "dist"), + }, + plugins: [ + // Add your plugins here + // Learn more about plugins from https://webpack.js.org/configuration/plugins/ + ], + module: { + rules: [ + { + test: /\\.(js|jsx)$/i, + loader: "babel-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/ + ], + }, +}; + +module.exports = () => { + if (isProduction) { + config.mode = "production"; + } else { + config.mode = "development"; + } + return config; +}; +" +`; + +exports[`init command should generate default project when nothing is passed 1`] = ` +{ + "description": "My webpack project", + "devDependencies": { + "html-loader": "x.x.x", + "html-webpack-plugin": "x.x.x", + "webpack": "x.x.x", + "webpack-cli": "x.x.x", + "webpack-dev-server": "x.x.x", + "workbox-webpack-plugin": "x.x.x", + }, + "name": "my-webpack-project", + "scripts": { + "build": "webpack --mode=production --node-env=production", + "build:dev": "webpack --mode=development", + "build:prod": "webpack --mode=production --node-env=production", + "serve": "webpack serve", + "watch": "webpack --watch", + }, + "version": "1.0.0", +} +`; + +exports[`init command should generate folders if non existing generation path is given 1`] = ` +{ + "description": "My webpack project", + "devDependencies": { + "html-loader": "x.x.x", + "html-webpack-plugin": "x.x.x", + "webpack": "x.x.x", + "webpack-cli": "x.x.x", + "webpack-dev-server": "x.x.x", + "workbox-webpack-plugin": "x.x.x", + }, + "name": "my-webpack-project", + "scripts": { + "build": "webpack --mode=production --node-env=production", + "build:dev": "webpack --mode=development", + "build:prod": "webpack --mode=production --node-env=production", + "serve": "webpack serve", + "watch": "webpack --watch", + }, + "version": "1.0.0", +} +`; + +exports[`init command should generate project when generationPath is supplied 1`] = ` +{ + "description": "My webpack project", + "devDependencies": { + "html-loader": "x.x.x", + "html-webpack-plugin": "x.x.x", + "webpack": "x.x.x", + "webpack-cli": "x.x.x", + "webpack-dev-server": "x.x.x", + "workbox-webpack-plugin": "x.x.x", + }, + "name": "my-webpack-project", + "scripts": { + "build": "webpack --mode=production --node-env=production", + "build:dev": "webpack --mode=development", + "build:prod": "webpack --mode=production --node-env=production", + "serve": "webpack serve", + "watch": "webpack --watch", + }, + "version": "1.0.0", +} +`; + +exports[`init command should generate typescript project correctly 1`] = ` +{ + "description": "My webpack project", + "devDependencies": { + "ts-loader": "x.x.x", + "typescript": "x.x.x", + "webpack": "x.x.x", + "webpack-cli": "x.x.x", + }, + "name": "my-webpack-project", + "scripts": { + "build": "webpack --mode=production --node-env=production", + "build:dev": "webpack --mode=development", + "build:prod": "webpack --mode=production --node-env=production", + "watch": "webpack --watch", + }, + "version": "1.0.0", +} +`; + +exports[`init command should generate typescript project correctly 2`] = ` +"// Generated using webpack-cli https://github.com/webpack/webpack-cli + +const path = require("path"); + +const isProduction = process.env.NODE_ENV === "production"; + +const config = { + entry: "./src/index.ts", + output: { + path: path.resolve(__dirname, "dist"), + }, + plugins: [ + // Add your plugins here + // Learn more about plugins from https://webpack.js.org/configuration/plugins/ + ], + module: { + rules: [ + { + test: /\\.(ts|tsx)$/i, + loader: "ts-loader", + exclude: ["/node_modules/"], + }, + { + 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/ + ], + }, + resolve: { + extensions: [".tsx", ".ts", ".jsx", ".js", "..."], + }, +}; + +module.exports = () => { + if (isProduction) { + config.mode = "production"; + } else { + config.mode = "development"; + } + return config; +}; +" +`; + +exports[`init command should use less in project when selected 1`] = ` +{ + "description": "My webpack project", + "devDependencies": { + "css-loader": "x.x.x", + "less": "x.x.x", + "less-loader": "x.x.x", + "style-loader": "x.x.x", + "webpack": "x.x.x", + "webpack-cli": "x.x.x", + }, + "name": "my-webpack-project", + "scripts": { + "build": "webpack --mode=production --node-env=production", + "build:dev": "webpack --mode=development", + "build:prod": "webpack --mode=production --node-env=production", + "watch": "webpack --watch", + }, + "version": "1.0.0", +} +`; + +exports[`init command should use less in project when selected 2`] = ` +"// Generated using webpack-cli https://github.com/webpack/webpack-cli + +const path = require("path"); + +const isProduction = process.env.NODE_ENV === "production"; + +const stylesHandler = "style-loader"; + +const config = { + entry: "./src/index.js", + output: { + path: path.resolve(__dirname, "dist"), + }, + plugins: [ + // Add your plugins here + // Learn more about plugins from https://webpack.js.org/configuration/plugins/ + ], + module: { + rules: [ + { + test: /\\.less$/i, + use: [stylesHandler, "css-loader", "less-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/ + ], + }, +}; + +module.exports = () => { + if (isProduction) { + config.mode = "production"; + } else { + config.mode = "development"; + } + return config; +}; +" +`; + +exports[`init command should use mini-css-extract-plugin when selected 1`] = ` +{ + "description": "My webpack project", + "devDependencies": { + "css-loader": "x.x.x", + "sass": "x.x.x", + "sass-loader": "x.x.x", + "style-loader": "x.x.x", + "webpack": "x.x.x", + "webpack-cli": "x.x.x", + }, + "name": "my-webpack-project", + "scripts": { + "build": "webpack --mode=production --node-env=production", + "build:dev": "webpack --mode=development", + "build:prod": "webpack --mode=production --node-env=production", + "watch": "webpack --watch", + }, + "version": "1.0.0", +} +`; + +exports[`init command should use mini-css-extract-plugin when selected 2`] = ` +"// Generated using webpack-cli https://github.com/webpack/webpack-cli + +const path = require("path"); + +const isProduction = process.env.NODE_ENV === "production"; + +const stylesHandler = "style-loader"; + +const config = { + entry: "./src/index.js", + output: { + path: path.resolve(__dirname, "dist"), + }, + plugins: [ + // Add your plugins here + // Learn more about plugins from https://webpack.js.org/configuration/plugins/ + ], + module: { + rules: [ + { + test: /\\.s[ac]ss$/i, + use: [stylesHandler, "css-loader", "sass-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/ + ], + }, +}; + +module.exports = () => { + if (isProduction) { + config.mode = "production"; + } else { + config.mode = "development"; + } + return config; +}; +" +`; + +exports[`init command should use postcss in project when selected 1`] = ` +{ + "description": "My webpack project", + "devDependencies": { + "autoprefixer": "x.x.x", + "css-loader": "x.x.x", + "postcss": "x.x.x", + "postcss-loader": "x.x.x", + "style-loader": "x.x.x", + "webpack": "x.x.x", + "webpack-cli": "x.x.x", + }, + "name": "my-webpack-project", + "scripts": { + "build": "webpack --mode=production --node-env=production", + "build:dev": "webpack --mode=development", + "build:prod": "webpack --mode=production --node-env=production", + "watch": "webpack --watch", + }, + "version": "1.0.0", +} +`; + +exports[`init command should use postcss in project when selected 2`] = ` +"// Generated using webpack-cli https://github.com/webpack/webpack-cli + +const path = require("path"); + +const isProduction = process.env.NODE_ENV === "production"; + +const stylesHandler = "style-loader"; + +const config = { + entry: "./src/index.js", + output: { + path: path.resolve(__dirname, "dist"), + }, + plugins: [ + // Add your plugins here + // Learn more about plugins from https://webpack.js.org/configuration/plugins/ + ], + module: { + rules: [ + { + 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/ + ], + }, +}; + +module.exports = () => { + if (isProduction) { + config.mode = "production"; + } else { + config.mode = "development"; + } + return config; +}; +" +`; + +exports[`init command should use sass and css with postcss in project when selected 1`] = ` +{ + "description": "My webpack project", + "devDependencies": { + "autoprefixer": "x.x.x", + "css-loader": "x.x.x", + "postcss": "x.x.x", + "postcss-loader": "x.x.x", + "sass": "x.x.x", + "sass-loader": "x.x.x", + "style-loader": "x.x.x", + "webpack": "x.x.x", + "webpack-cli": "x.x.x", + }, + "name": "my-webpack-project", + "scripts": { + "build": "webpack --mode=production --node-env=production", + "build:dev": "webpack --mode=development", + "build:prod": "webpack --mode=production --node-env=production", + "watch": "webpack --watch", + }, + "version": "1.0.0", +} +`; + +exports[`init command should use sass and css with postcss in project when selected 2`] = ` +"// Generated using webpack-cli https://github.com/webpack/webpack-cli + +const path = require("path"); + +const isProduction = process.env.NODE_ENV === "production"; + +const stylesHandler = "style-loader"; + +const config = { + entry: "./src/index.js", + output: { + path: path.resolve(__dirname, "dist"), + }, + plugins: [ + // Add your plugins here + // Learn more about plugins from https://webpack.js.org/configuration/plugins/ + ], + module: { + rules: [ + { + test: /\\.s[ac]ss$/i, + use: [stylesHandler, "css-loader", "postcss-loader", "sass-loader"], + }, + { + 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/ + ], + }, +}; + +module.exports = () => { + if (isProduction) { + config.mode = "production"; + } else { + config.mode = "development"; + } + return config; +}; +" +`; + +exports[`init command should use sass in project when selected 1`] = ` +{ + "description": "My webpack project", + "devDependencies": { + "css-loader": "x.x.x", + "sass": "x.x.x", + "sass-loader": "x.x.x", + "style-loader": "x.x.x", + "webpack": "x.x.x", + "webpack-cli": "x.x.x", + }, + "name": "my-webpack-project", + "scripts": { + "build": "webpack --mode=production --node-env=production", + "build:dev": "webpack --mode=development", + "build:prod": "webpack --mode=production --node-env=production", + "watch": "webpack --watch", + }, + "version": "1.0.0", +} +`; + +exports[`init command should use sass in project when selected 2`] = ` +"// Generated using webpack-cli https://github.com/webpack/webpack-cli + +const path = require("path"); + +const isProduction = process.env.NODE_ENV === "production"; + +const stylesHandler = "style-loader"; + +const config = { + entry: "./src/index.js", + output: { + path: path.resolve(__dirname, "dist"), + }, + plugins: [ + // Add your plugins here + // Learn more about plugins from https://webpack.js.org/configuration/plugins/ + ], + module: { + rules: [ + { + test: /\\.s[ac]ss$/i, + use: [stylesHandler, "css-loader", "sass-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/ + ], + }, +}; + +module.exports = () => { + if (isProduction) { + config.mode = "production"; + } else { + config.mode = "development"; + } + return config; +}; +" +`; + +exports[`init command should use sass with postcss in project when selected 1`] = ` +{ + "description": "My webpack project", + "devDependencies": { + "autoprefixer": "x.x.x", + "css-loader": "x.x.x", + "postcss": "x.x.x", + "postcss-loader": "x.x.x", + "sass": "x.x.x", + "sass-loader": "x.x.x", + "style-loader": "x.x.x", + "webpack": "x.x.x", + "webpack-cli": "x.x.x", + }, + "name": "my-webpack-project", + "scripts": { + "build": "webpack --mode=production --node-env=production", + "build:dev": "webpack --mode=development", + "build:prod": "webpack --mode=production --node-env=production", + "watch": "webpack --watch", + }, + "version": "1.0.0", +} +`; + +exports[`init command should use sass with postcss in project when selected 2`] = ` +"// Generated using webpack-cli https://github.com/webpack/webpack-cli + +const path = require("path"); + +const isProduction = process.env.NODE_ENV === "production"; + +const stylesHandler = "style-loader"; + +const config = { + entry: "./src/index.js", + output: { + path: path.resolve(__dirname, "dist"), + }, + plugins: [ + // Add your plugins here + // Learn more about plugins from https://webpack.js.org/configuration/plugins/ + ], + module: { + rules: [ + { + test: /\\.s[ac]ss$/i, + use: [stylesHandler, "css-loader", "postcss-loader", "sass-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/ + ], + }, +}; + +module.exports = () => { + if (isProduction) { + config.mode = "production"; + } else { + config.mode = "development"; + } + return config; +}; +" +`; + +exports[`init command should use stylus in project when selected 1`] = ` +{ + "description": "My webpack project", + "devDependencies": { + "css-loader": "x.x.x", + "style-loader": "x.x.x", + "stylus": "x.x.x", + "stylus-loader": "x.x.x", + "webpack": "x.x.x", + "webpack-cli": "x.x.x", + }, + "name": "my-webpack-project", + "scripts": { + "build": "webpack --mode=production --node-env=production", + "build:dev": "webpack --mode=development", + "build:prod": "webpack --mode=production --node-env=production", + "watch": "webpack --watch", + }, + "version": "1.0.0", +} +`; + +exports[`init command should use stylus in project when selected 2`] = ` +"// Generated using webpack-cli https://github.com/webpack/webpack-cli + +const path = require("path"); + +const isProduction = process.env.NODE_ENV === "production"; + +const stylesHandler = "style-loader"; + +const config = { + entry: "./src/index.js", + output: { + path: path.resolve(__dirname, "dist"), + }, + plugins: [ + // Add your plugins here + // Learn more about plugins from https://webpack.js.org/configuration/plugins/ + ], + module: { + rules: [ + { + test: /\\.styl$/i, + use: [stylesHandler, "css-loader", "stylus-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/ + ], + }, +}; + +module.exports = () => { + if (isProduction) { + config.mode = "production"; + } else { + config.mode = "development"; + } + return config; +}; +" +`; + +exports[`init command should work with 'c' alias 1`] = ` +{ + "description": "My webpack project", + "devDependencies": { + "html-loader": "x.x.x", + "html-webpack-plugin": "x.x.x", + "webpack": "x.x.x", + "webpack-cli": "x.x.x", + "webpack-dev-server": "x.x.x", + "workbox-webpack-plugin": "x.x.x", + }, + "name": "my-webpack-project", + "scripts": { + "build": "webpack --mode=production --node-env=production", + "build:dev": "webpack --mode=development", + "build:prod": "webpack --mode=production --node-env=production", + "serve": "webpack serve", + "watch": "webpack --watch", + }, + "version": "1.0.0", +} +`; + +exports[`init command should work with 'create' alias 1`] = ` +{ + "description": "My webpack project", + "devDependencies": { + "html-loader": "x.x.x", + "html-webpack-plugin": "x.x.x", + "webpack": "x.x.x", + "webpack-cli": "x.x.x", + "webpack-dev-server": "x.x.x", + "workbox-webpack-plugin": "x.x.x", + }, + "name": "my-webpack-project", + "scripts": { + "build": "webpack --mode=production --node-env=production", + "build:dev": "webpack --mode=development", + "build:prod": "webpack --mode=production --node-env=production", + "serve": "webpack serve", + "watch": "webpack --watch", + }, + "version": "1.0.0", +} +`; + +exports[`init command should work with 'n' alias 1`] = ` +{ + "description": "My webpack project", + "devDependencies": { + "html-loader": "x.x.x", + "html-webpack-plugin": "x.x.x", + "webpack": "x.x.x", + "webpack-cli": "x.x.x", + "webpack-dev-server": "x.x.x", + "workbox-webpack-plugin": "x.x.x", + }, + "name": "my-webpack-project", + "scripts": { + "build": "webpack --mode=production --node-env=production", + "build:dev": "webpack --mode=development", + "build:prod": "webpack --mode=production --node-env=production", + "serve": "webpack serve", + "watch": "webpack --watch", + }, + "version": "1.0.0", +} +`; + +exports[`init command should work with 'new' alias 1`] = ` +{ + "description": "My webpack project", + "devDependencies": { + "html-loader": "x.x.x", + "html-webpack-plugin": "x.x.x", + "webpack": "x.x.x", + "webpack-cli": "x.x.x", + "webpack-dev-server": "x.x.x", + "workbox-webpack-plugin": "x.x.x", + }, + "name": "my-webpack-project", + "scripts": { + "build": "webpack --mode=production --node-env=production", + "build:dev": "webpack --mode=development", + "build:prod": "webpack --mode=production --node-env=production", + "serve": "webpack serve", + "watch": "webpack --watch", + }, + "version": "1.0.0", +} +`; + +exports[`init command uses yarn as the package manager when opted 1`] = ` +{ + "description": "My webpack project", + "devDependencies": { + "webpack": "x.x.x", + "webpack-cli": "x.x.x", + }, + "name": "my-webpack-project", + "scripts": { + "build": "webpack --mode=production --node-env=production", + "build:dev": "webpack --mode=development", + "build:prod": "webpack --mode=production --node-env=production", + "watch": "webpack --watch", + }, + "version": "1.0.0", +} +`; diff --git a/test/create-webpack-app/create-webpack-app.test.js b/test/create-webpack-app/create-webpack-app.test.js index edcfc5ae6f7..a77ec977a49 100644 --- a/test/create-webpack-app/create-webpack-app.test.js +++ b/test/create-webpack-app/create-webpack-app.test.js @@ -22,7 +22,7 @@ const defaultTemplateFiles = [ "webpack.config.js", ]; -const reactTemplateFiles = [...defaultTemplateFiles, "index.html"]; +// const reactTemplateFiles = [...defaultTemplateFiles, "index.html"]; // Helper to read from package.json in a given path const readFromPkgJSON = (path) => { @@ -93,7 +93,7 @@ describe("init command", () => { // Check if the generated package.json file content matches the snapshot expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); }); - + // it("should configure assets modules by default", async () => { const assetsPath = path.resolve(os.tmpdir(), Date.now().toString()); const { stdout, stderr } = await run(__dirname, ["init", assetsPath, "--force"]); @@ -113,28 +113,28 @@ describe("init command", () => { // Check if the generated webpack configuration matches the snapshot expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); }); - - it("should ask question when wrong template is supplied", async () => { - const assetsPath = await uniqueDirectoryForTest(); - const { stdout, stderr } = await runPromptWithAnswers( - assetsPath, - ["init", "--force", "--template=apple"], - [`${ENTER}`], - ); - - expect(stdout).toContain("Project has been initialised with webpack!"); - expect(stderr).toContain("apple is not a valid template, please select one from below"); - expect(stderr).toContain("webpack.config.js"); - - // Test files - defaultTemplateFiles.forEach((file) => { - expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); - }); - - // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); - }); - + // + // it("should ask question when wrong template is supplied", async () => { + // const assetsPath = await uniqueDirectoryForTest(); + // const { stdout, stderr } = await runPromptWithAnswers( + // assetsPath, + // ["init", "--force", "--template=apple"], + // [`${ENTER}`], + // ); + // + // expect(stdout).toContain("Project has been initialised with webpack!"); + // expect(stderr).toContain("apple is not a valid template, please select one from below"); + // expect(stderr).toContain("webpack.config.js"); + // + // // Test files + // defaultTemplateFiles.forEach((file) => { + // expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + // }); + // + // // Check if the generated package.json file content matches the snapshot + // expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + // }); + // it("should generate typescript project correctly", async () => { const assetsPath = await uniqueDirectoryForTest(); const { stdout, stderr } = await runPromptWithAnswers( @@ -164,7 +164,7 @@ describe("init command", () => { // Check if the generated webpack configuration matches the snapshot expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); }); - + // it("should generate ES6 project correctly", async () => { const assetsPath = await uniqueDirectoryForTest(); const { stdout, stderr } = await runPromptWithAnswers( @@ -579,38 +579,38 @@ describe("init command", () => { expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); }); - it("recognizes '-t' as an alias for '--template'", async () => { - const assetsPath = await uniqueDirectoryForTest(); - const { stdout, stderr } = await run(assetsPath, ["init", "-t", "default", "--force"]); - - expect(stdout).toContain("Project has been initialised with webpack!"); - expect(stderr).toContain("webpack.config.js"); - - // Test files - defaultTemplateFiles.forEach((file) => { - expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); - }); - - // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); - }); - - it("recognizes '-f' as an alias for '--force'", async () => { - const assetsPath = await uniqueDirectoryForTest(); - const { stdout, stderr } = await run(assetsPath, ["init", "-f"]); - - expect(stdout).toContain("Project has been initialised with webpack!"); - expect(stderr).toContain("webpack.config.js"); - - // Test files - defaultTemplateFiles.forEach((file) => { - expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); - }); - - // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); - }); - + // it("recognizes '-t' as an alias for '--template'", async () => { + // const assetsPath = await uniqueDirectoryForTest(); + // const { stdout, stderr } = await run(assetsPath, ["init", "-t", "default", "--force"]); + // + // expect(stdout).toContain("Project has been initialised with webpack!"); + // expect(stderr).toContain("webpack.config.js"); + // + // // Test files + // defaultTemplateFiles.forEach((file) => { + // expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + // }); + // + // // Check if the generated package.json file content matches the snapshot + // expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + // }); + // + // it("recognizes '-f' as an alias for '--force'", async () => { + // const assetsPath = await uniqueDirectoryForTest(); + // const { stdout, stderr } = await run(assetsPath, ["init", "-f"]); + // + // expect(stdout).toContain("Project has been initialised with webpack!"); + // expect(stderr).toContain("webpack.config.js"); + // + // // Test files + // defaultTemplateFiles.forEach((file) => { + // expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + // }); + // + // // Check if the generated package.json file content matches the snapshot + // expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + // }); + // it("uses yarn as the package manager when opted", async () => { const assetsPath = await uniqueDirectoryForTest(); const { stdout, stderr } = await runPromptWithAnswers( @@ -636,45 +636,45 @@ describe("init command", () => { expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); }); - it("should generate react template with prompt answers", async () => { - const assetsPath = await uniqueDirectoryForTest(); - const { stdout, stderr } = await runPromptWithAnswers( - assetsPath, - ["init"], - [ENTER, `y${ENTER}`, `${DOWN}${ENTER}`, `y${ENTER}`, ENTER, ENTER], - ); - - expect(stdout).toContain("Project has been initialised with webpack!"); - expect(stderr).toContain("webpack.config.js"); - - // Test files - reactTemplateFiles.forEach((file) => { - expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); - }); - - // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); - - // Check if the generated webpack configuration matches the snapshot - expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); - }); - - it("should generate react template with --force", async () => { - const assetsPath = await uniqueDirectoryForTest(); - const { stdout, stderr } = await run(assetsPath, ["init", "--template=react", "--force"]); - - expect(stdout).toContain("Project has been initialised with webpack!"); - expect(stderr).toContain("webpack.config.js"); - - // Test files - reactTemplateFiles.forEach((file) => { - expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); - }); - - // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); - - // Check if the generated webpack configuration matches the snapshot - expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); - }); + // it("should generate react template with prompt answers", async () => { + // const assetsPath = await uniqueDirectoryForTest(); + // const { stdout, stderr } = await runPromptWithAnswers( + // assetsPath, + // ["init"], + // [ENTER, `y${ENTER}`, `${DOWN}${ENTER}`, `y${ENTER}`, ENTER, ENTER], + // ); + // + // expect(stdout).toContain("Project has been initialised with webpack!"); + // expect(stderr).toContain("webpack.config.js"); + // + // // Test files + // reactTemplateFiles.forEach((file) => { + // expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + // }); + // + // // Check if the generated package.json file content matches the snapshot + // expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + // + // // Check if the generated webpack configuration matches the snapshot + // expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); + // }); + // + // it("should generate react template with --force", async () => { + // const assetsPath = await uniqueDirectoryForTest(); + // const { stdout, stderr } = await run(assetsPath, ["init", "--template=react", "--force"]); + // + // expect(stdout).toContain("Project has been initialised with webpack!"); + // expect(stderr).toContain("webpack.config.js"); + // + // // Test files + // reactTemplateFiles.forEach((file) => { + // expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + // }); + // + // // Check if the generated package.json file content matches the snapshot + // expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + // + // // Check if the generated webpack configuration matches the snapshot + // expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); + // }); }); From 3f66230f7a9cfb77f76389d0dd1c5fd61ebc1672 Mon Sep 17 00:00:00 2001 From: Uzair Khan Date: Sat, 15 Jun 2024 18:02:18 +0530 Subject: [PATCH 34/81] Update packages/create-webpack-app/README.md fix grammar Co-authored-by: Nitin Kumar --- packages/create-webpack-app/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/create-webpack-app/README.md b/packages/create-webpack-app/README.md index 4fce91ebbbe..87e45ea4d96 100644 --- a/packages/create-webpack-app/README.md +++ b/packages/create-webpack-app/README.md @@ -8,7 +8,7 @@ ## About -- `create-webpack-app` is a cli tool that enables developers to quickly scaffold a new webpack project. It provides a flexible set of commands for developers to increase speed when setting up a custom webpack project. webpack CLI addresses these needs by providing a set of tools to improve the setup of custom webpack configuration. +- `create-webpack-app` is a cli tool that enables developers to scaffold a new webpack project quickly. It provides developers with a flexible set of commands to increase speed when setting up a custom webpack project. webpack CLI addresses these needs by providing tools to improve the setup of custom webpack configuration. - It is also going to support several front end frameworks and libraries like React, Angular, Vue, Svelte, etc. - Webpack Loader and Plugin scaffolding is also supported. From 769e09562fc25486a637af317fd53260368489a1 Mon Sep 17 00:00:00 2001 From: Uzair Khan Date: Sat, 15 Jun 2024 18:03:31 +0530 Subject: [PATCH 35/81] Update packages/create-webpack-app/README.md Co-authored-by: Nitin Kumar --- packages/create-webpack-app/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/create-webpack-app/README.md b/packages/create-webpack-app/README.md index 87e45ea4d96..9b754adafdd 100644 --- a/packages/create-webpack-app/README.md +++ b/packages/create-webpack-app/README.md @@ -9,7 +9,7 @@ ## About - `create-webpack-app` is a cli tool that enables developers to scaffold a new webpack project quickly. It provides developers with a flexible set of commands to increase speed when setting up a custom webpack project. webpack CLI addresses these needs by providing tools to improve the setup of custom webpack configuration. -- It is also going to support several front end frameworks and libraries like React, Angular, Vue, Svelte, etc. +- It also supports several front-end frameworks and libraries like React, Angular, Vue, Svelte, etc. - Webpack Loader and Plugin scaffolding is also supported. ## How to install From cb309df2bf32eabf548d1b3f5f865a7787251c86 Mon Sep 17 00:00:00 2001 From: Uzair Date: Tue, 25 Jun 2024 20:54:03 +0530 Subject: [PATCH 36/81] refactor: migrate cli logic to ts from js - write whole command building and actions logic of commander in src/index.ts for future scalability and type safety - bin/cli.js now only contains importing this src/index.ts compiled lib/index.js file. --- packages/create-webpack-app/bin/cli.js | 56 +--------------- packages/create-webpack-app/src/index.ts | 83 ++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 54 deletions(-) create mode 100644 packages/create-webpack-app/src/index.ts diff --git a/packages/create-webpack-app/bin/cli.js b/packages/create-webpack-app/bin/cli.js index f84deeaf1da..5efc82e1b4d 100644 --- a/packages/create-webpack-app/bin/cli.js +++ b/packages/create-webpack-app/bin/cli.js @@ -1,55 +1,3 @@ -import { Command } from "commander"; -import { resolve, join } from "path"; -import nodePlop from "node-plop"; -// Cspell:ignore plopfile, plopfile.js +#!/usr/bin/env node -const program = new Command(); -const plop = await nodePlop("./lib/plopfile.js"); -const defaultValues = { - init: { - projectPath: resolve(join(process.cwd(), ".")), - projectName: "webpack-project", - langType: "none", - devServer: true, - htmlWebpackPlugin: true, - workboxWebpackPlugin: true, - cssType: "CSS only", - isCSS: true, - isPostCSS: true, - extractPlugin: "No", - packageManager: "npm", - }, -}; - -program - .version("1.0.0", "-v, --version") - .usage("[command] [options]") - .helpOption("-h, --help", "Display help for command") - .description("A CLI tool to generate a Webpack project"); - -program - .command("init", { isDefault: true }) - .aliases(["i", "c", "create", "new"]) - .description("Initialize a new Webpack project") - .argument("[projectPath]", "Path to create the project") - .argument("[projectName]", "Name of the project") - .option("-f, --force", "Skip the prompt and use the default values", false) - .action(function (projectName, projectPath, opts) { - console.log("Initializing a new Webpack project"); - const { force } = opts; - const initGenerator = plop.getGenerator("init"); - const byPassValues = []; - if (projectName) byPassValues.push(projectName); - if (projectPath) byPassValues.push(projectPath); - - if (force) { - console.log("Skipping the prompt and using the default values"); - initGenerator.runActions(defaultValues.init); - } else { - initGenerator.runPrompts(byPassValues).then((answers) => { - initGenerator.runActions(answers); - }); - } - }); - -program.parse(); +require("../lib/index.js"); diff --git a/packages/create-webpack-app/src/index.ts b/packages/create-webpack-app/src/index.ts new file mode 100644 index 00000000000..50ba5e0db58 --- /dev/null +++ b/packages/create-webpack-app/src/index.ts @@ -0,0 +1,83 @@ +// Cspell:ignore plopfile, plopfile.js +import { Command } from "commander"; +import { resolve, dirname } from "path"; +import nodePlop from "node-plop"; +import { fileURLToPath } from "url"; +import { PlopActionHooksChanges, PlopActionHooksFailures } from "./types"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +const program = new Command(); +const plop = await nodePlop(resolve(__dirname, "./plopfile.js")); +const defaultValues = { + init: { + projectPath: process.cwd(), + projectName: "webpack-project", + langType: "none", + devServer: true, + htmlWebpackPlugin: true, + workboxWebpackPlugin: true, + cssType: "CSS only", + isCSS: true, + isPostCSS: true, + extractPlugin: "No", + packageManager: "yarn", + }, +}; + +program + .version("1.0.0", "-v, --version") + .usage("[command] [options]") + .helpOption("-h, --help", "Display help for command") + .description("A CLI tool to generate a Webpack project"); + +program + .command("init", { isDefault: true }) + .aliases(["i", "c", "create", "new"]) + .description("Initialize a new Webpack project") + .argument("[projectPath]", "Path to create the project") + .argument("[projectName]", "Name of the project") + .option("-f, --force", "Skip the prompt and use the default values", false) + .action(async function (projectPath, projectName, opts) { + console.log("Initializing a new Webpack project"); + const { force } = opts; + const initGenerator = plop.getGenerator("init"); + const byPassValues = []; + if (projectPath) byPassValues.push(projectPath); + if (projectName) byPassValues.push(projectName); + try { + if (force) { + console.log("Skipping the prompt and using the default values"); + await initGenerator.runActions( + { + ...defaultValues.init, + projectName: byPassValues[1] ? byPassValues[1] : defaultValues.init.projectName, + projectPath: byPassValues[0] + ? resolve(process.cwd(), byPassValues[0]) + : defaultValues.init.projectPath, + }, + { + onSuccess: onSuccessHandler, + onFailure: onFailureHandler, + }, + ); + } else { + const answers = await initGenerator.runPrompts(byPassValues); + await initGenerator.runActions(answers, { + onSuccess: onSuccessHandler, + onFailure: onFailureHandler, + }); + } + } catch (error) { + console.log(`Error: ${error}`); + } + console.log("Project initialised with webpack!"); + }); + +const onSuccessHandler = (change: PlopActionHooksChanges) => { + console.log(`${change.path}`); +}; +const onFailureHandler = (failure: PlopActionHooksFailures) => { + console.log(`Failure in performing action ${failure.type} \n Error is: ${failure.error}`); +}; +program.parse(); From 860e5c2db070672b3b974d5763989dcfc178e53a Mon Sep 17 00:00:00 2001 From: Uzair Date: Tue, 25 Jun 2024 20:58:28 +0530 Subject: [PATCH 37/81] feat: add conditional file generation based on dynamic action function - using dynamic action function to generate files based on choices - change template structure --- packages/create-webpack-app/src/plopfile.ts | 84 +++++++++++++++---- .../babel.config.json} | 0 .../postcss.config.js | 0 .../{default => customFiles}/tsconfig.json | 0 .../{template.html.tpl => index.html.tpl} | 0 .../templates/init/default/package.json.tpl | 9 +- .../templates/init/default/src/index.js | 2 - .../templates/init/default/src/index.ts | 2 - 8 files changed, 72 insertions(+), 25 deletions(-) rename packages/create-webpack-app/templates/init/{default/.babelrc => customFiles/babel.config.json} (100%) rename packages/create-webpack-app/templates/init/{default => customFiles}/postcss.config.js (100%) rename packages/create-webpack-app/templates/init/{default => customFiles}/tsconfig.json (100%) rename packages/create-webpack-app/templates/init/default/{template.html.tpl => index.html.tpl} (100%) delete mode 100644 packages/create-webpack-app/templates/init/default/src/index.js delete mode 100644 packages/create-webpack-app/templates/init/default/src/index.ts diff --git a/packages/create-webpack-app/src/plopfile.ts b/packages/create-webpack-app/src/plopfile.ts index 3476a639a65..4802104572b 100644 --- a/packages/create-webpack-app/src/plopfile.ts +++ b/packages/create-webpack-app/src/plopfile.ts @@ -183,23 +183,75 @@ export default function (plop: NodePlopAPI) { }, }, ], - actions: [ - { - type: "addMany", - destination: "{{projectPath}}/{{dashCase projectName}}", - base: "../templates/init/default", - templateFiles: "../templates/init/default/**/*", - transform: (content, data) => { - data.entryPoint = data.langType === "Typescript" ? "index.ts" : "index.js"; - return ejs.render(content, data); + actions: function (answers: Answers) { + // setting some default values based on the answers + answers.entryPoint = answers.langType === "Typescript" ? "./src/index.ts" : "./src/index.js"; + answers.jsConfig = null; + answers.jsConfig = answers.langType === "Typescript" ? "tsconfig.json" : "babel.config.json"; + answers.cssConfig = answers.isPostCSS ? "postcss.config.js" : null; + + // adding some dependencies based on the answers + if (answers.devServer) { + dependencies.push("webpack-dev-server"); + } + if (answers.htmlWebpackPlugin) { + dependencies.push("html-webpack-plugin", "html-loader"); + } + if (answers.workboxWebpackPlugin) { + dependencies.push("workbox-webpack-plugin"); + } + if (answers.isPostCSS) { + dependencies.push("postcss-loader", "autoprefixer"); + } + + const actions: ActionType[] = [ + { + type: "addMany", + destination: "{{projectPath}}/{{dashCase projectName}}", + base: "../templates/init/default", + templateFiles: "../templates/init/default/**/*", + transform: (content: string, data: Answers) => { + return ejs.render(content, data); + }, + stripExtensions: ["tpl"], + force: true, + verbose: true, }, - stripExtensions: ["tpl"], - force: true, - verbose: true, - }, - { + { + type: "add", + path: "{{projectPath}}/{{dashCase projectName}}/{{entryPoint}}", + force: true, + transform: (data: Answers) => { + if (data.langType === "Typescript") { + return `console.log("Hello, this is the entrypoint for your TypeScript Project!");`; + } else if (data.langType === "ES6") { + return `console.log("Hello, this is the entrypoint for your ES6 Project!");`; + } else { + return `console.log("Hello, this is the entrypoint for your Project!");`; + } + }, + }, + ]; + if (answers.jsConfig) { + actions.push({ + type: "add", + templateFile: "../templates/init/customFiles/{{jsConfig}}", + path: "{{projectPath}}/{{dashCase projectName}}/{{jsConfig}}", + force: true, + }); + } + if (answers.cssConfig) { + actions.push({ + type: "add", + templateFile: "../templates/init/customFiles/{{cssConfig}}", + path: "{{projectPath}}/{{dashCase projectName}}/{{cssConfig}}", + force: true, + }); + } + actions.push({ type: "pkgInstall", - }, - ], + }); + return actions; + } as DynamicActionsFunction, }); } diff --git a/packages/create-webpack-app/templates/init/default/.babelrc b/packages/create-webpack-app/templates/init/customFiles/babel.config.json similarity index 100% rename from packages/create-webpack-app/templates/init/default/.babelrc rename to packages/create-webpack-app/templates/init/customFiles/babel.config.json diff --git a/packages/create-webpack-app/templates/init/default/postcss.config.js b/packages/create-webpack-app/templates/init/customFiles/postcss.config.js similarity index 100% rename from packages/create-webpack-app/templates/init/default/postcss.config.js rename to packages/create-webpack-app/templates/init/customFiles/postcss.config.js diff --git a/packages/create-webpack-app/templates/init/default/tsconfig.json b/packages/create-webpack-app/templates/init/customFiles/tsconfig.json similarity index 100% rename from packages/create-webpack-app/templates/init/default/tsconfig.json rename to packages/create-webpack-app/templates/init/customFiles/tsconfig.json diff --git a/packages/create-webpack-app/templates/init/default/template.html.tpl b/packages/create-webpack-app/templates/init/default/index.html.tpl similarity index 100% rename from packages/create-webpack-app/templates/init/default/template.html.tpl rename to packages/create-webpack-app/templates/init/default/index.html.tpl diff --git a/packages/create-webpack-app/templates/init/default/package.json.tpl b/packages/create-webpack-app/templates/init/default/package.json.tpl index f8ede733855..e6128170e88 100644 --- a/packages/create-webpack-app/templates/init/default/package.json.tpl +++ b/packages/create-webpack-app/templates/init/default/package.json.tpl @@ -5,11 +5,10 @@ "name": "<%= projectName %>", "scripts": { - "build": "webpack --mode=production --node-env=production", - - "build:dev": "webpack --mode=development", - "build:prod": "webpack --mode=production --node-env=production", - "watch": "webpack --watch", + "build": "webpack --mode=production --node-env=production", + "build:dev": "webpack --mode=development", + "build:prod": "webpack --mode=production --node-env=production", + "watch": "webpack --watch", <% if (devServer) { %> "serve": "webpack serve" <% } %> diff --git a/packages/create-webpack-app/templates/init/default/src/index.js b/packages/create-webpack-app/templates/init/default/src/index.js deleted file mode 100644 index dcc0dd61af1..00000000000 --- a/packages/create-webpack-app/templates/init/default/src/index.js +++ /dev/null @@ -1,2 +0,0 @@ -// delete this file if Language is Typescript -console.log("Hello World!"); diff --git a/packages/create-webpack-app/templates/init/default/src/index.ts b/packages/create-webpack-app/templates/init/default/src/index.ts deleted file mode 100644 index 74401839d59..00000000000 --- a/packages/create-webpack-app/templates/init/default/src/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -// delete this file if Language is Javascript -console.log("Hello World!"); From de92604f38ed9c44307d681a1ccae930a6248f8b Mon Sep 17 00:00:00 2001 From: Uzair Date: Tue, 25 Jun 2024 21:06:54 +0530 Subject: [PATCH 38/81] docs: change doc to align with its features --- packages/create-webpack-app/README.md | 30 ++++++++++++++++----------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/packages/create-webpack-app/README.md b/packages/create-webpack-app/README.md index 9b754adafdd..27748b48d30 100644 --- a/packages/create-webpack-app/README.md +++ b/packages/create-webpack-app/README.md @@ -12,30 +12,36 @@ - It also supports several front-end frameworks and libraries like React, Angular, Vue, Svelte, etc. - Webpack Loader and Plugin scaffolding is also supported. -## How to install +## Supported arguments and commands + +### Usage ```bash -npm install --save-dev @webpack-cli/create-webpack-app +npx create-webpack-app [command] [options] ``` -or +### CLI options + +**To generate default template** ```bash -yarn add @webpack-cli/create-webpack-app --dev +npx create-webpack-app ``` -## Supported arguments and commands +**To generate with default answers** -### Usage +```bash +npx create-webpack-app -f, --force +``` -All interactions with create-webpack-app are of the form +**To scaffold in a specified path** ```bash -npx create-webpack-app [command] [options] +npx create-webpack-app [generation-path] ``` -### Help Usage +**To scaffold in a specified path with a custom project-name** -### Available Commands - -### Available Options +```bash +npx create-webpack-app [generation-path] [project-name] +``` From b150fa83128c87d550fdae985aba2514d9e281b4 Mon Sep 17 00:00:00 2001 From: Uzair Date: Tue, 25 Jun 2024 21:08:55 +0530 Subject: [PATCH 39/81] feat: change dependency installation to be cross-compatible - using cross-spawn instead of normal spawn method --- packages/create-webpack-app/package.json | 2 + packages/create-webpack-app/src/plopfile.ts | 77 ++++++++++----------- 2 files changed, 39 insertions(+), 40 deletions(-) diff --git a/packages/create-webpack-app/package.json b/packages/create-webpack-app/package.json index b33c458cd32..b46583b79d1 100644 --- a/packages/create-webpack-app/package.json +++ b/packages/create-webpack-app/package.json @@ -41,10 +41,12 @@ ], "dependencies": { "commander": "^12.1.0", + "cross-spawn": "^7.0.3", "ejs": "^3.1.10", "node-plop": "^0.32.0" }, "devDependencies": { + "@types/cross-spawn": "^6.0.6", "@types/ejs": "^3.1.5" } } diff --git a/packages/create-webpack-app/src/plopfile.ts b/packages/create-webpack-app/src/plopfile.ts index 4802104572b..c97d192193a 100644 --- a/packages/create-webpack-app/src/plopfile.ts +++ b/packages/create-webpack-app/src/plopfile.ts @@ -1,53 +1,50 @@ -import { NodePlopAPI, Answers } from "./types"; -import { resolve, join } from "path"; +import { NodePlopAPI, Answers, ActionType } from "./types"; +import { resolve } from "path"; import ejs from "ejs"; -import { spawn } from "child_process"; +import { spawn } from "cross-spawn"; +import { DynamicActionsFunction } from "node-plop"; + export default function (plop: NodePlopAPI) { - const dependencies = ["webpack", "webpack-cli"]; + // dependencies to be installed + const dependencies: Array = ["webpack", "webpack-cli"]; + + // Define a custom action for installing packages plop.setActionType("pkgInstall", (answers) => { const options = { cwd: `${answers.projectPath}/${answers.projectName}`, - encoding: "utf-8", + encoding: "utf8", }; - if (answers.devServer) { - dependencies.push("webpack-dev-server"); - } - if (answers.htmlWebpackPlugin) { - dependencies.push("html-webpack-plugin", "html-loader"); - } - if (answers.workboxWebpackPlugin) { - dependencies.push("workbox-webpack-plugin"); - } - if (answers.isPostCSS) { - dependencies.push("postcss-loader", "autoprefixer"); - } - console.log(`Installing packages: ${dependencies.join("\n")}`); - const returnMessage = `Project ${answers.projectName} has been successfully created at ${answers.projectPath}/${answers.projectName}`; - const packageManager = answers.packageManager; - const installCommandPrefix = packageManager === "yarn" ? "add" : "install"; + // promise to complete subprocess of installing packages and return a message + const returnPromise: Promise = new Promise((resolve, reject) => { + console.log(`Installing packages:\n\t${dependencies.join("\n\t")}`); + const returnMessage = `All the dependencies have been installed `; + const packageManager = answers.packageManager; + const installCommandPrefix = packageManager === "yarn" ? "add" : "install"; + const installMode = packageManager === "yarn" ? "-D" : "--save-dev"; - const npmInstallPackages = spawn( - `${packageManager}`, - [`${installCommandPrefix}`, ...dependencies], - options, - ); - npmInstallPackages.stdout.on("data", (data) => { - console.log(data.toString()); - }); - npmInstallPackages.stderr.on("data", (data) => { - console.error(data.toString()); - }); - npmInstallPackages.on("close", (code) => { - if (code !== 0) { - console.error(`child process exited with code ${code}`); - return; - } else { - console.log(returnMessage); - } + const npmInstallPackages = spawn( + `${packageManager}`, + [`${installCommandPrefix}`, `${installMode}`, ...dependencies], + options, + ); + npmInstallPackages.stdout?.on("data", (data) => { + console.log(data.toString()); + }); + npmInstallPackages.stderr?.on("data", (data) => { + console.error(data.toString()); + }); + npmInstallPackages.on("exit", (code) => { + if (code === 0) { + resolve(returnMessage); + } else { + reject(`Error occurred while installing packages`); + } + }); }); - return "Package Installation Phase..."; // executes before the child process is completed + return returnPromise; }); + // Define a base generator for the project structure plop.setGenerator("init", { description: "Create a basic Webpack project", From f3315f41d327751789dce3da615eec8572bd328c Mon Sep 17 00:00:00 2001 From: Uzair Date: Tue, 25 Jun 2024 21:10:29 +0530 Subject: [PATCH 40/81] refactor: remove unused var --- packages/create-webpack-app/src/plopfile.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/create-webpack-app/src/plopfile.ts b/packages/create-webpack-app/src/plopfile.ts index c97d192193a..21041ef9f72 100644 --- a/packages/create-webpack-app/src/plopfile.ts +++ b/packages/create-webpack-app/src/plopfile.ts @@ -55,7 +55,7 @@ export default function (plop: NodePlopAPI) { message: "Enter the project destination:", default: ".", filter: (input) => { - return resolve(join(process.cwd(), input)); + return resolve(process.cwd(), input); }, }, { @@ -63,7 +63,7 @@ export default function (plop: NodePlopAPI) { name: "projectName", message: "Enter your project name:", default: "webpack-project", - validate(input, _) { + validate(input) { if (!input.trim()) { return "Project name cannot be empty"; } @@ -76,7 +76,7 @@ export default function (plop: NodePlopAPI) { message: "Which of the following JS solutions do you want to use?", choices: ["none", "ES6", "Typescript"], default: "none", - filter: (input, _) => { + filter: (input) => { switch (input) { case "ES6": dependencies.push("babel-loader", "@babel/core", "@babel/preset-env"); @@ -159,7 +159,7 @@ export default function (plop: NodePlopAPI) { choices: ["No", "Only for Production", "Yes"], when: (answers) => answers.isCSS, default: "No", - filter: (input, _) => { + filter: (input) => { if (input !== "No") { dependencies.push("mini-css-extract-plugin"); } @@ -171,8 +171,8 @@ export default function (plop: NodePlopAPI) { name: "packageManager", message: "Which package manager do you want to use?", choices: ["npm", "yarn"], - default: "npm", - validate(input, _) { + default: "yarn", + validate(input) { if (!input.trim()) { return "Package manager cannot be empty"; } From 19f59ea52ec0e6d01d26b8c2f68c065e3defeb1a Mon Sep 17 00:00:00 2001 From: Uzair Date: Tue, 25 Jun 2024 21:13:31 +0530 Subject: [PATCH 41/81] feat: add types --- packages/create-webpack-app/src/types.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/create-webpack-app/src/types.ts b/packages/create-webpack-app/src/types.ts index 89c62017c7f..090e1e654e7 100644 --- a/packages/create-webpack-app/src/types.ts +++ b/packages/create-webpack-app/src/types.ts @@ -1,6 +1,7 @@ export type { ActionConfig, AddActionConfig, + CustomActionConfig, AddManyActionConfig, AppendActionConfig, CustomActionFunction, @@ -11,8 +12,19 @@ export type { PlopGeneratorConfig, Actions, } from "node-plop"; -export interface Answers extends Record {} +// eslint-disable-next-line +export type Answers = Record; +export interface PlopActionHooksFailures { + type: string; + path: string; + error: string; + message: string; +} +export interface PlopActionHooksChanges { + type: string; + path: string; +} import { ActionType as ActionTypeBase, CustomActionConfig } from "node-plop"; // extended ACtionType to include custom action config as previously it was not recognizing export type ActionType = ActionTypeBase | CustomActionConfig; From 02f04cbf91cb1722bdc6ce03ca66296c5eb86b8f Mon Sep 17 00:00:00 2001 From: Uzair Date: Tue, 25 Jun 2024 21:14:18 +0530 Subject: [PATCH 42/81] chore: enable eslint for package/create-webpack-app --- .eslintignore | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.eslintignore b/.eslintignore index 75873a14a25..d2f46930b9d 100644 --- a/.eslintignore +++ b/.eslintignore @@ -11,6 +11,4 @@ test/build/config/error-commonjs/syntax-error.js test/build/config/error-mjs/syntax-error.mjs test/build/config/error-array/webpack.config.js test/configtest/with-config-path/syntax-error.config.js - -packages/create-webpack-app - +test/create-webpack-app/** From 284ab14684e07db9096a195f5cfd59b4f3d4236b Mon Sep 17 00:00:00 2001 From: Uzair Date: Tue, 25 Jun 2024 22:17:44 +0530 Subject: [PATCH 43/81] chore: change module type and fix control flow for errors --- .eslintrc.js | 6 ++++++ packages/create-webpack-app/bin/cli.js | 3 ++- packages/create-webpack-app/src/index.ts | 6 +++--- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index a17e0c5be7c..4a050da537e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -42,5 +42,11 @@ module.exports = { "node/no-unsupported-features/es-syntax": "off", }, }, + { + files: ["**/packages/create-webpack-app/**/*.js"], + parserOptions: { + sourceType: "module", + }, + }, ], }; diff --git a/packages/create-webpack-app/bin/cli.js b/packages/create-webpack-app/bin/cli.js index 5efc82e1b4d..aaefc07b7d0 100644 --- a/packages/create-webpack-app/bin/cli.js +++ b/packages/create-webpack-app/bin/cli.js @@ -1,3 +1,4 @@ #!/usr/bin/env node -require("../lib/index.js"); +//eslint-disable-next-line +import * as cli from "../lib/index.js"; diff --git a/packages/create-webpack-app/src/index.ts b/packages/create-webpack-app/src/index.ts index 50ba5e0db58..8165c4ea483 100644 --- a/packages/create-webpack-app/src/index.ts +++ b/packages/create-webpack-app/src/index.ts @@ -68,16 +68,16 @@ program onFailure: onFailureHandler, }); } + console.log("Project initialised with webpack!"); } catch (error) { - console.log(`Error: ${error}`); + console.log(`${error}`); } - console.log("Project initialised with webpack!"); }); const onSuccessHandler = (change: PlopActionHooksChanges) => { console.log(`${change.path}`); }; const onFailureHandler = (failure: PlopActionHooksFailures) => { - console.log(`Failure in performing action ${failure.type} \n Error is: ${failure.error}`); + throw new Error(failure.error); }; program.parse(); From b80f340ca36858801b259d71f8b75da7ddcec847 Mon Sep 17 00:00:00 2001 From: Uzair Date: Mon, 1 Jul 2024 14:50:52 +0530 Subject: [PATCH 44/81] feat: add logger utility module --- .../create-webpack-app/src/utils/logger.ts | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 packages/create-webpack-app/src/utils/logger.ts diff --git a/packages/create-webpack-app/src/utils/logger.ts b/packages/create-webpack-app/src/utils/logger.ts new file mode 100644 index 00000000000..abfd432e286 --- /dev/null +++ b/packages/create-webpack-app/src/utils/logger.ts @@ -0,0 +1,36 @@ +import { WebpackCLILogger } from "webpack-cli"; +import { green, yellow, Color, red, cyan, cyanBright, blue, blueBright } from "colorette"; +import { PlopActionHooksChanges, PlopActionHooksFailures } from "../types"; +import { basename } from "path"; + +const prefix: string = blueBright("create-webpack"); +const getLogger = (): WebpackCLILogger => { + return { + error: (val) => console.error(`[${prefix}] ${red(val)}`), + warn: (val) => console.warn(`[${prefix}] ${yellow(val)}`), + info: (val) => console.info(`[${prefix}] ${cyan(val)}`), + success: (val) => console.log(`[${prefix}] ${green(val)}`), + log: (val) => console.log(`[${prefix}] ${val}`), + raw: (val) => console.log(val), + }; +}; +const logger = getLogger(); +const typeDisplay: Record = { + function: yellow("-> "), + add: green("create "), + addMany: green("create "), + modify: `${blue("modify")}${green("+")}${red("- ")}`, + append: green("append_+ "), + skip: cyanBright("skip-- "), +}; +function onSuccessHandler(change: PlopActionHooksChanges): void { + change.path.split("\n").forEach((line) => { + const prefix = typeDisplay[change.type] || ""; + console.log(`\t${prefix} ${basename(line)}`); + }); +} +function onFailureHandler(failure: PlopActionHooksFailures): void { + throw new Error(failure.error); +} + +export { logger, onSuccessHandler, onFailureHandler }; From 944bb332f238947618c56e382b902c24bb3019af Mon Sep 17 00:00:00 2001 From: Uzair Date: Mon, 1 Jul 2024 14:53:27 +0530 Subject: [PATCH 45/81] refactor: refactor and implement logger --- packages/create-webpack-app/bin/cli.js | 0 packages/create-webpack-app/src/index.ts | 29 ++++++++++++------------ 2 files changed, 15 insertions(+), 14 deletions(-) mode change 100644 => 100755 packages/create-webpack-app/bin/cli.js diff --git a/packages/create-webpack-app/bin/cli.js b/packages/create-webpack-app/bin/cli.js old mode 100644 new mode 100755 diff --git a/packages/create-webpack-app/src/index.ts b/packages/create-webpack-app/src/index.ts index 8165c4ea483..b16686ba65b 100644 --- a/packages/create-webpack-app/src/index.ts +++ b/packages/create-webpack-app/src/index.ts @@ -3,7 +3,9 @@ import { Command } from "commander"; import { resolve, dirname } from "path"; import nodePlop from "node-plop"; import { fileURLToPath } from "url"; -import { PlopActionHooksChanges, PlopActionHooksFailures } from "./types"; + +// eslint-disable-next-line node/no-missing-import +import { onSuccessHandler, onFailureHandler, logger } from "./utils/logger.js"; const __dirname = dirname(fileURLToPath(import.meta.url)); @@ -21,7 +23,7 @@ const defaultValues = { isCSS: true, isPostCSS: true, extractPlugin: "No", - packageManager: "yarn", + packageManager: "npm", }, }; @@ -33,21 +35,23 @@ program program .command("init", { isDefault: true }) - .aliases(["i", "c", "create", "new"]) + .aliases(["i", "n", "c", "create", "new"]) .description("Initialize a new Webpack project") .argument("[projectPath]", "Path to create the project") .argument("[projectName]", "Name of the project") .option("-f, --force", "Skip the prompt and use the default values", false) .action(async function (projectPath, projectName, opts) { - console.log("Initializing a new Webpack project"); const { force } = opts; const initGenerator = plop.getGenerator("init"); - const byPassValues = []; + const byPassValues: Array = []; + if (projectPath) byPassValues.push(projectPath); if (projectName) byPassValues.push(projectName); try { if (force) { - console.log("Skipping the prompt and using the default values"); + logger.warn("Skipping the prompt and using the default values"); + + logger.info("Initializing a new Webpack project"); await initGenerator.runActions( { ...defaultValues.init, @@ -63,21 +67,18 @@ program ); } else { const answers = await initGenerator.runPrompts(byPassValues); + + logger.info("Initializing a new Webpack project"); await initGenerator.runActions(answers, { onSuccess: onSuccessHandler, onFailure: onFailureHandler, }); } - console.log("Project initialised with webpack!"); + logger.success("Project has been initialised with webpack!"); } catch (error) { - console.log(`${error}`); + logger.error(`Failed to initialize the project with webpack!\n ${error}`); + process.exit(2); } }); -const onSuccessHandler = (change: PlopActionHooksChanges) => { - console.log(`${change.path}`); -}; -const onFailureHandler = (failure: PlopActionHooksFailures) => { - throw new Error(failure.error); -}; program.parse(); From b82f4be04c903ed3ef3cdab38ae970ef7aea87d2 Mon Sep 17 00:00:00 2001 From: Uzair Date: Mon, 1 Jul 2024 15:00:59 +0530 Subject: [PATCH 46/81] feat: change stdio settings for pkgInstall action change stdio settings to incorporate each animations and spinners of the underlying child process --- packages/create-webpack-app/src/plopfile.ts | 28 +++++++++++++-------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/packages/create-webpack-app/src/plopfile.ts b/packages/create-webpack-app/src/plopfile.ts index 21041ef9f72..c309369dd34 100644 --- a/packages/create-webpack-app/src/plopfile.ts +++ b/packages/create-webpack-app/src/plopfile.ts @@ -3,42 +3,50 @@ import { resolve } from "path"; import ejs from "ejs"; import { spawn } from "cross-spawn"; import { DynamicActionsFunction } from "node-plop"; +import { ChildProcess, SpawnOptionsWithStdioTuple, StdioNull, StdioPipe } from "child_process"; export default function (plop: NodePlopAPI) { // dependencies to be installed const dependencies: Array = ["webpack", "webpack-cli"]; // Define a custom action for installing packages - plop.setActionType("pkgInstall", (answers) => { - const options = { - cwd: `${answers.projectPath}/${answers.projectName}`, - encoding: "utf8", + plop.setActionType("pkgInstall", (answers, config) => { + const options: SpawnOptionsWithStdioTuple< + StdioNull, + StdioNull | StdioPipe, + StdioPipe | StdioNull + > = { + cwd: config.path, + stdio: [ + "inherit", // Use parent's stdio configuration + process.stdout.isTTY ? "inherit" : "pipe", // Pipe child process' stdout to parent's stdout + process.stderr.isTTY ? "inherit" : "pipe", // Pipe child process' stderr to parent's stderr + ], }; // promise to complete subprocess of installing packages and return a message const returnPromise: Promise = new Promise((resolve, reject) => { - console.log(`Installing packages:\n\t${dependencies.join("\n\t")}`); - const returnMessage = `All the dependencies have been installed `; + const returnMessage = `Project Dependencies installed successfully`; const packageManager = answers.packageManager; const installCommandPrefix = packageManager === "yarn" ? "add" : "install"; const installMode = packageManager === "yarn" ? "-D" : "--save-dev"; - const npmInstallPackages = spawn( + const npmInstallPackages: ChildProcess = spawn( `${packageManager}`, - [`${installCommandPrefix}`, `${installMode}`, ...dependencies], + [`${installCommandPrefix}`, `${installMode}`, ...config.packages], options, ); npmInstallPackages.stdout?.on("data", (data) => { console.log(data.toString()); }); npmInstallPackages.stderr?.on("data", (data) => { - console.error(data.toString()); + console.warn(data.toString()); }); npmInstallPackages.on("exit", (code) => { if (code === 0) { resolve(returnMessage); } else { - reject(`Error occurred while installing packages`); + reject(`Error occurred while installing packages\n Exit code: ${code}`); } }); }); From b4cca8fa5532c89efd9d67f25e88671c57ac761b Mon Sep 17 00:00:00 2001 From: Uzair Date: Mon, 1 Jul 2024 15:02:55 +0530 Subject: [PATCH 47/81] fix: change conditional prompt to align with original logic --- packages/create-webpack-app/src/plopfile.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/create-webpack-app/src/plopfile.ts b/packages/create-webpack-app/src/plopfile.ts index c309369dd34..232115d4694 100644 --- a/packages/create-webpack-app/src/plopfile.ts +++ b/packages/create-webpack-app/src/plopfile.ts @@ -157,7 +157,6 @@ export default function (plop: NodePlopAPI) { type: "confirm", name: "isPostCSS", message: "Do you want to use PostCSS in your project?", - when: (answers) => answers.isCSS, default: true, }, { @@ -165,7 +164,6 @@ export default function (plop: NodePlopAPI) { name: "extractPlugin", message: "Do you want to extract CSS into separate files?", choices: ["No", "Only for Production", "Yes"], - when: (answers) => answers.isCSS, default: "No", filter: (input) => { if (input !== "No") { @@ -179,7 +177,7 @@ export default function (plop: NodePlopAPI) { name: "packageManager", message: "Which package manager do you want to use?", choices: ["npm", "yarn"], - default: "yarn", + default: "npm", validate(input) { if (!input.trim()) { return "Package manager cannot be empty"; @@ -255,6 +253,9 @@ export default function (plop: NodePlopAPI) { } actions.push({ type: "pkgInstall", + path: plop.renderString("{{projectPath}}/{{dashCase projectName}}", answers), + // Custom function don't automatically render hbs template as path hence manual rendering + packages: dependencies, }); return actions; } as DynamicActionsFunction, From e3ef415fb9f8f61f9de3ea1486e9af889ec7ae17 Mon Sep 17 00:00:00 2001 From: Uzair Date: Mon, 1 Jul 2024 15:13:14 +0530 Subject: [PATCH 48/81] test: implement test according to package made necessary changes based on the differences from the original cli and its tests keeping original tests as base --- .../create-webpack-app.test.js.snap.webpack5 | 1192 ++++++++++------- .../create-webpack-app.test.js | 464 ++++--- 2 files changed, 941 insertions(+), 715 deletions(-) diff --git a/test/create-webpack-app/__snapshots__/create-webpack-app.test.js.snap.webpack5 b/test/create-webpack-app/__snapshots__/create-webpack-app.test.js.snap.webpack5 index 1dd7841aa72..e50fdd986cb 100644 --- a/test/create-webpack-app/__snapshots__/create-webpack-app.test.js.snap.webpack5 +++ b/test/create-webpack-app/__snapshots__/create-webpack-app.test.js.snap.webpack5 @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`init command should configure WDS as opted 1`] = ` +exports[`create-webpack-app cli should configure WDS as opted 1`] = ` { "description": "My webpack project", "devDependencies": { @@ -8,7 +8,7 @@ exports[`init command should configure WDS as opted 1`] = ` "webpack-cli": "x.x.x", "webpack-dev-server": "x.x.x", }, - "name": "my-webpack-project", + "name": "webpack-project", "scripts": { "build": "webpack --mode=production --node-env=production", "build:dev": "webpack --mode=development", @@ -20,62 +20,68 @@ exports[`init command should configure WDS as opted 1`] = ` } `; -exports[`init command should configure WDS as opted 2`] = ` +exports[`create-webpack-app cli should configure WDS as opted 2`] = ` "// Generated using webpack-cli https://github.com/webpack/webpack-cli -const path = require("path"); +const path = require('path'); + +const isProduction = process.env.NODE_ENV === 'production'; -const isProduction = process.env.NODE_ENV === "production"; const config = { - entry: "./src/index.js", - output: { - path: path.resolve(__dirname, "dist"), - }, - devServer: { - open: true, - host: "localhost", - }, - plugins: [ - // Add your plugins here - // Learn more about plugins from https://webpack.js.org/configuration/plugins/ - ], - module: { - rules: [ - { - 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/ + entry: './src/index.js', + output: { + path: path.resolve(__dirname, 'dist'), + }, + devServer: { + open: true, + host: 'localhost', + }, + plugins: [ + // Add your plugins here + // Learn more about plugins from https://webpack.js.org/configuration/plugins/ ], - }, + module: { + rules: [ + { + 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/ + ], + }, }; module.exports = () => { - if (isProduction) { - config.mode = "production"; - } else { - config.mode = "development"; - } - return config; + if (isProduction) { + config.mode = 'production'; + + + } else { + config.mode = 'development'; + } + return config; }; " `; -exports[`init command should configure assets modules by default 1`] = ` +exports[`create-webpack-app cli should configure assets modules by default 1`] = ` { "description": "My webpack project", "devDependencies": { + "autoprefixer": "x.x.x", "html-loader": "x.x.x", "html-webpack-plugin": "x.x.x", + "postcss-loader": "x.x.x", "webpack": "x.x.x", "webpack-cli": "x.x.x", "webpack-dev-server": "x.x.x", "workbox-webpack-plugin": "x.x.x", }, - "name": "my-webpack-project", + "name": "webpack-project", "scripts": { "build": "webpack --mode=production --node-env=production", "build:dev": "webpack --mode=development", @@ -87,64 +93,75 @@ exports[`init command should configure assets modules by default 1`] = ` } `; -exports[`init command should configure assets modules by default 2`] = ` +exports[`create-webpack-app cli should configure assets modules by default 2`] = ` "// Generated using webpack-cli https://github.com/webpack/webpack-cli -const path = require("path"); -const HtmlWebpackPlugin = require("html-webpack-plugin"); -const WorkboxWebpackPlugin = require("workbox-webpack-plugin"); +const path = require('path'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); +const WorkboxWebpackPlugin = require('workbox-webpack-plugin'); + +const isProduction = process.env.NODE_ENV === 'production'; + + +const stylesHandler = 'style-loader'; + -const isProduction = process.env.NODE_ENV === "production"; const config = { - entry: "./src/index.js", - output: { - path: path.resolve(__dirname, "dist"), - }, - devServer: { - open: true, - host: "localhost", - }, - plugins: [ - new HtmlWebpackPlugin({ - template: "index.html", - }), - - // Add your plugins here - // Learn more about plugins from https://webpack.js.org/configuration/plugins/ - ], - module: { - rules: [ - { - test: /\\.(eot|svg|ttf|woff|woff2|png|jpg|gif)$/i, - type: "asset", - }, - - { - test: /\\.html$/i, - use: ["html-loader"], - }, - - // Add your rules for custom modules here - // Learn more about loaders from https://webpack.js.org/loaders/ + entry: './src/index.js', + output: { + path: path.resolve(__dirname, 'dist'), + }, + devServer: { + open: true, + host: 'localhost', + }, + plugins: [ + new HtmlWebpackPlugin({ + template: 'index.html', + }), + + // Add your plugins here + // Learn more about plugins from https://webpack.js.org/configuration/plugins/ ], - }, + module: { + rules: [ + { + test: /\\.css$/i, + use: [stylesHandler, 'css-loader', 'postcss-loader'], + }, + { + test: /\\.(eot|svg|ttf|woff|woff2|png|jpg|gif)$/i, + type: 'asset', + }, + + { + test: /\\.html$/i, + use: ['html-loader'], + }, + + // Add your rules for custom modules here + // Learn more about loaders from https://webpack.js.org/loaders/ + ], + }, }; module.exports = () => { - if (isProduction) { - config.mode = "production"; - - config.plugins.push(new WorkboxWebpackPlugin.GenerateSW()); - } else { - config.mode = "development"; - } - return config; + if (isProduction) { + config.mode = 'production'; + + + config.plugins.push(new WorkboxWebpackPlugin.GenerateSW()); + + } else { + config.mode = 'development'; + } + return config; }; " `; -exports[`init command should configure html-webpack-plugin as opted 1`] = ` +exports[`create-webpack-app cli should configure html-webpack-plugin as opted 1`] = ` { "description": "My webpack project", "devDependencies": { @@ -153,7 +170,7 @@ exports[`init command should configure html-webpack-plugin as opted 1`] = ` "webpack": "x.x.x", "webpack-cli": "x.x.x", }, - "name": "my-webpack-project", + "name": "webpack-project", "scripts": { "build": "webpack --mode=production --node-env=production", "build:dev": "webpack --mode=development", @@ -164,57 +181,60 @@ exports[`init command should configure html-webpack-plugin as opted 1`] = ` } `; -exports[`init command should configure html-webpack-plugin as opted 2`] = ` +exports[`create-webpack-app cli should configure html-webpack-plugin as opted 2`] = ` "// Generated using webpack-cli https://github.com/webpack/webpack-cli -const path = require("path"); -const HtmlWebpackPlugin = require("html-webpack-plugin"); +const path = require('path'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); + +const isProduction = process.env.NODE_ENV === 'production'; -const isProduction = process.env.NODE_ENV === "production"; const config = { - entry: "./src/index.js", - output: { - path: path.resolve(__dirname, "dist"), - }, - plugins: [ - new HtmlWebpackPlugin({ - template: "index.html", - }), - - // Add your plugins here - // Learn more about plugins from https://webpack.js.org/configuration/plugins/ - ], - module: { - rules: [ - { - test: /\\.(eot|svg|ttf|woff|woff2|png|jpg|gif)$/i, - type: "asset", - }, - - { - test: /\\.html$/i, - use: ["html-loader"], - }, - - // Add your rules for custom modules here - // Learn more about loaders from https://webpack.js.org/loaders/ + entry: './src/index.js', + output: { + path: path.resolve(__dirname, 'dist'), + }, + plugins: [ + new HtmlWebpackPlugin({ + template: 'index.html', + }), + + // Add your plugins here + // Learn more about plugins from https://webpack.js.org/configuration/plugins/ ], - }, + module: { + rules: [ + { + test: /\\.(eot|svg|ttf|woff|woff2|png|jpg|gif)$/i, + type: 'asset', + }, + + { + test: /\\.html$/i, + use: ['html-loader'], + }, + + // Add your rules for custom modules here + // Learn more about loaders from https://webpack.js.org/loaders/ + ], + }, }; module.exports = () => { - if (isProduction) { - config.mode = "production"; - } else { - config.mode = "development"; - } - return config; + if (isProduction) { + config.mode = 'production'; + + + } else { + config.mode = 'development'; + } + return config; }; " `; -exports[`init command should configure workbox-webpack-plugin as opted 1`] = ` +exports[`create-webpack-app cli should configure workbox-webpack-plugin as opted 1`] = ` { "description": "My webpack project", "devDependencies": { @@ -224,7 +244,7 @@ exports[`init command should configure workbox-webpack-plugin as opted 1`] = ` "webpack-cli": "x.x.x", "workbox-webpack-plugin": "x.x.x", }, - "name": "my-webpack-project", + "name": "webpack-project", "scripts": { "build": "webpack --mode=production --node-env=production", "build:dev": "webpack --mode=development", @@ -235,60 +255,63 @@ exports[`init command should configure workbox-webpack-plugin as opted 1`] = ` } `; -exports[`init command should configure workbox-webpack-plugin as opted 2`] = ` +exports[`create-webpack-app cli should configure workbox-webpack-plugin as opted 2`] = ` "// Generated using webpack-cli https://github.com/webpack/webpack-cli -const path = require("path"); -const HtmlWebpackPlugin = require("html-webpack-plugin"); -const WorkboxWebpackPlugin = require("workbox-webpack-plugin"); +const path = require('path'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); +const WorkboxWebpackPlugin = require('workbox-webpack-plugin'); + +const isProduction = process.env.NODE_ENV === 'production'; -const isProduction = process.env.NODE_ENV === "production"; const config = { - entry: "./src/index.js", - output: { - path: path.resolve(__dirname, "dist"), - }, - plugins: [ - new HtmlWebpackPlugin({ - template: "index.html", - }), - - // Add your plugins here - // Learn more about plugins from https://webpack.js.org/configuration/plugins/ - ], - module: { - rules: [ - { - test: /\\.(eot|svg|ttf|woff|woff2|png|jpg|gif)$/i, - type: "asset", - }, - - { - test: /\\.html$/i, - use: ["html-loader"], - }, - - // Add your rules for custom modules here - // Learn more about loaders from https://webpack.js.org/loaders/ + entry: './src/index.js', + output: { + path: path.resolve(__dirname, 'dist'), + }, + plugins: [ + new HtmlWebpackPlugin({ + template: 'index.html', + }), + + // Add your plugins here + // Learn more about plugins from https://webpack.js.org/configuration/plugins/ ], - }, + module: { + rules: [ + { + test: /\\.(eot|svg|ttf|woff|woff2|png|jpg|gif)$/i, + type: 'asset', + }, + + { + test: /\\.html$/i, + use: ['html-loader'], + }, + + // Add your rules for custom modules here + // Learn more about loaders from https://webpack.js.org/loaders/ + ], + }, }; module.exports = () => { - if (isProduction) { - config.mode = "production"; - - config.plugins.push(new WorkboxWebpackPlugin.GenerateSW()); - } else { - config.mode = "development"; - } - return config; + if (isProduction) { + config.mode = 'production'; + + + config.plugins.push(new WorkboxWebpackPlugin.GenerateSW()); + + } else { + config.mode = 'development'; + } + return config; }; " `; -exports[`init command should generate ES6 project correctly 1`] = ` +exports[`create-webpack-app cli should generate ES6 project correctly 1`] = ` { "description": "My webpack project", "devDependencies": { @@ -298,7 +321,7 @@ exports[`init command should generate ES6 project correctly 1`] = ` "webpack": "x.x.x", "webpack-cli": "x.x.x", }, - "name": "my-webpack-project", + "name": "webpack-project", "scripts": { "build": "webpack --mode=production --node-env=production", "build:dev": "webpack --mode=development", @@ -309,62 +332,68 @@ exports[`init command should generate ES6 project correctly 1`] = ` } `; -exports[`init command should generate ES6 project correctly 2`] = ` +exports[`create-webpack-app cli should generate ES6 project correctly 2`] = ` "// Generated using webpack-cli https://github.com/webpack/webpack-cli -const path = require("path"); +const path = require('path'); + +const isProduction = process.env.NODE_ENV === 'production'; -const isProduction = process.env.NODE_ENV === "production"; const config = { - entry: "./src/index.js", - output: { - path: path.resolve(__dirname, "dist"), - }, - plugins: [ - // Add your plugins here - // Learn more about plugins from https://webpack.js.org/configuration/plugins/ - ], - module: { - rules: [ - { - test: /\\.(js|jsx)$/i, - loader: "babel-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/ + entry: './src/index.js', + output: { + path: path.resolve(__dirname, 'dist'), + }, + plugins: [ + // Add your plugins here + // Learn more about plugins from https://webpack.js.org/configuration/plugins/ ], - }, + module: { + rules: [ + { + test: /\\.(js|jsx)$/i, + loader: 'babel-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/ + ], + }, }; module.exports = () => { - if (isProduction) { - config.mode = "production"; - } else { - config.mode = "development"; - } - return config; + if (isProduction) { + config.mode = 'production'; + + + } else { + config.mode = 'development'; + } + return config; }; " `; -exports[`init command should generate default project when nothing is passed 1`] = ` +exports[`create-webpack-app cli should generate default project when nothing is passed 1`] = ` { "description": "My webpack project", "devDependencies": { + "autoprefixer": "x.x.x", "html-loader": "x.x.x", "html-webpack-plugin": "x.x.x", + "postcss-loader": "x.x.x", "webpack": "x.x.x", "webpack-cli": "x.x.x", "webpack-dev-server": "x.x.x", "workbox-webpack-plugin": "x.x.x", }, - "name": "my-webpack-project", + "name": "webpack-project", "scripts": { "build": "webpack --mode=production --node-env=production", "build:dev": "webpack --mode=development", @@ -376,18 +405,20 @@ exports[`init command should generate default project when nothing is passed 1`] } `; -exports[`init command should generate folders if non existing generation path is given 1`] = ` +exports[`create-webpack-app cli should generate folders if non existing generation path is given 1`] = ` { "description": "My webpack project", "devDependencies": { + "autoprefixer": "x.x.x", "html-loader": "x.x.x", "html-webpack-plugin": "x.x.x", + "postcss-loader": "x.x.x", "webpack": "x.x.x", "webpack-cli": "x.x.x", "webpack-dev-server": "x.x.x", "workbox-webpack-plugin": "x.x.x", }, - "name": "my-webpack-project", + "name": "webpack-project", "scripts": { "build": "webpack --mode=production --node-env=production", "build:dev": "webpack --mode=development", @@ -399,18 +430,20 @@ exports[`init command should generate folders if non existing generation path is } `; -exports[`init command should generate project when generationPath is supplied 1`] = ` +exports[`create-webpack-app cli should generate project when generationPath is supplied 1`] = ` { "description": "My webpack project", "devDependencies": { + "autoprefixer": "x.x.x", "html-loader": "x.x.x", "html-webpack-plugin": "x.x.x", + "postcss-loader": "x.x.x", "webpack": "x.x.x", "webpack-cli": "x.x.x", "webpack-dev-server": "x.x.x", "workbox-webpack-plugin": "x.x.x", }, - "name": "my-webpack-project", + "name": "webpack-project", "scripts": { "build": "webpack --mode=production --node-env=production", "build:dev": "webpack --mode=development", @@ -422,7 +455,7 @@ exports[`init command should generate project when generationPath is supplied 1` } `; -exports[`init command should generate typescript project correctly 1`] = ` +exports[`create-webpack-app cli should generate typescript project correctly 1`] = ` { "description": "My webpack project", "devDependencies": { @@ -431,7 +464,7 @@ exports[`init command should generate typescript project correctly 1`] = ` "webpack": "x.x.x", "webpack-cli": "x.x.x", }, - "name": "my-webpack-project", + "name": "webpack-project", "scripts": { "build": "webpack --mode=production --node-env=production", "build:dev": "webpack --mode=development", @@ -442,55 +475,59 @@ exports[`init command should generate typescript project correctly 1`] = ` } `; -exports[`init command should generate typescript project correctly 2`] = ` +exports[`create-webpack-app cli should generate typescript project correctly 2`] = ` "// Generated using webpack-cli https://github.com/webpack/webpack-cli -const path = require("path"); +const path = require('path'); + +const isProduction = process.env.NODE_ENV === 'production'; -const isProduction = process.env.NODE_ENV === "production"; const config = { - entry: "./src/index.ts", - output: { - path: path.resolve(__dirname, "dist"), - }, - plugins: [ - // Add your plugins here - // Learn more about plugins from https://webpack.js.org/configuration/plugins/ - ], - module: { - rules: [ - { - test: /\\.(ts|tsx)$/i, - loader: "ts-loader", - exclude: ["/node_modules/"], - }, - { - 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/ + entry: './src/index.ts', + output: { + path: path.resolve(__dirname, 'dist'), + }, + plugins: [ + // Add your plugins here + // Learn more about plugins from https://webpack.js.org/configuration/plugins/ ], - }, - resolve: { - extensions: [".tsx", ".ts", ".jsx", ".js", "..."], - }, + module: { + rules: [ + { + test: /\\.(ts|tsx)$/i, + loader: 'ts-loader', + exclude: ['/node_modules/'], + }, + { + 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/ + ], + }, + resolve: { + extensions: ['.tsx', '.ts', '.jsx', '.js', '...'], + }, }; module.exports = () => { - if (isProduction) { - config.mode = "production"; - } else { - config.mode = "development"; - } - return config; + if (isProduction) { + config.mode = 'production'; + + + } else { + config.mode = 'development'; + } + return config; }; " `; -exports[`init command should use less in project when selected 1`] = ` +exports[`create-webpack-app cli should use less in project when selected 1`] = ` { "description": "My webpack project", "devDependencies": { @@ -501,7 +538,7 @@ exports[`init command should use less in project when selected 1`] = ` "webpack": "x.x.x", "webpack-cli": "x.x.x", }, - "name": "my-webpack-project", + "name": "webpack-project", "scripts": { "build": "webpack --mode=production --node-env=production", "build:dev": "webpack --mode=development", @@ -512,64 +549,70 @@ exports[`init command should use less in project when selected 1`] = ` } `; -exports[`init command should use less in project when selected 2`] = ` +exports[`create-webpack-app cli should use less in project when selected 2`] = ` "// Generated using webpack-cli https://github.com/webpack/webpack-cli -const path = require("path"); +const path = require('path'); + +const isProduction = process.env.NODE_ENV === 'production'; + + +const stylesHandler = 'style-loader'; -const isProduction = process.env.NODE_ENV === "production"; -const stylesHandler = "style-loader"; const config = { - entry: "./src/index.js", - output: { - path: path.resolve(__dirname, "dist"), - }, - plugins: [ - // Add your plugins here - // Learn more about plugins from https://webpack.js.org/configuration/plugins/ - ], - module: { - rules: [ - { - test: /\\.less$/i, - use: [stylesHandler, "css-loader", "less-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/ + entry: './src/index.js', + output: { + path: path.resolve(__dirname, 'dist'), + }, + plugins: [ + // Add your plugins here + // Learn more about plugins from https://webpack.js.org/configuration/plugins/ ], - }, + module: { + rules: [ + { + test: /\\.less$/i, + use: [stylesHandler, 'css-loader', 'less-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/ + ], + }, }; module.exports = () => { - if (isProduction) { - config.mode = "production"; - } else { - config.mode = "development"; - } - return config; + if (isProduction) { + config.mode = 'production'; + + + } else { + config.mode = 'development'; + } + return config; }; " `; -exports[`init command should use mini-css-extract-plugin when selected 1`] = ` +exports[`create-webpack-app cli should use mini-css-extract-plugin when selected 1`] = ` { "description": "My webpack project", "devDependencies": { "css-loader": "x.x.x", - "sass": "x.x.x", + "node-sass": "x.x.x", "sass-loader": "x.x.x", "style-loader": "x.x.x", "webpack": "x.x.x", "webpack-cli": "x.x.x", }, - "name": "my-webpack-project", + "name": "webpack-project", "scripts": { "build": "webpack --mode=production --node-env=production", "build:dev": "webpack --mode=development", @@ -580,65 +623,70 @@ exports[`init command should use mini-css-extract-plugin when selected 1`] = ` } `; -exports[`init command should use mini-css-extract-plugin when selected 2`] = ` +exports[`create-webpack-app cli should use mini-css-extract-plugin when selected 2`] = ` "// Generated using webpack-cli https://github.com/webpack/webpack-cli -const path = require("path"); +const path = require('path'); + +const isProduction = process.env.NODE_ENV === 'production'; + + +const stylesHandler = 'style-loader'; -const isProduction = process.env.NODE_ENV === "production"; -const stylesHandler = "style-loader"; const config = { - entry: "./src/index.js", - output: { - path: path.resolve(__dirname, "dist"), - }, - plugins: [ - // Add your plugins here - // Learn more about plugins from https://webpack.js.org/configuration/plugins/ - ], - module: { - rules: [ - { - test: /\\.s[ac]ss$/i, - use: [stylesHandler, "css-loader", "sass-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/ + entry: './src/index.js', + output: { + path: path.resolve(__dirname, 'dist'), + }, + plugins: [ + // Add your plugins here + // Learn more about plugins from https://webpack.js.org/configuration/plugins/ ], - }, + module: { + rules: [ + { + test: /\\.s[ac]ss$/i, + use: [stylesHandler, 'css-loader', 'sass-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/ + ], + }, }; module.exports = () => { - if (isProduction) { - config.mode = "production"; - } else { - config.mode = "development"; - } - return config; + if (isProduction) { + config.mode = 'production'; + + + } else { + config.mode = 'development'; + } + return config; }; " `; -exports[`init command should use postcss in project when selected 1`] = ` +exports[`create-webpack-app cli should use postcss in project when selected 1`] = ` { "description": "My webpack project", "devDependencies": { "autoprefixer": "x.x.x", "css-loader": "x.x.x", - "postcss": "x.x.x", "postcss-loader": "x.x.x", "style-loader": "x.x.x", "webpack": "x.x.x", "webpack-cli": "x.x.x", }, - "name": "my-webpack-project", + "name": "webpack-project", "scripts": { "build": "webpack --mode=production --node-env=production", "build:dev": "webpack --mode=development", @@ -649,67 +697,72 @@ exports[`init command should use postcss in project when selected 1`] = ` } `; -exports[`init command should use postcss in project when selected 2`] = ` +exports[`create-webpack-app cli should use postcss in project when selected 2`] = ` "// Generated using webpack-cli https://github.com/webpack/webpack-cli -const path = require("path"); +const path = require('path'); + +const isProduction = process.env.NODE_ENV === 'production'; + + +const stylesHandler = 'style-loader'; -const isProduction = process.env.NODE_ENV === "production"; -const stylesHandler = "style-loader"; const config = { - entry: "./src/index.js", - output: { - path: path.resolve(__dirname, "dist"), - }, - plugins: [ - // Add your plugins here - // Learn more about plugins from https://webpack.js.org/configuration/plugins/ - ], - module: { - rules: [ - { - 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/ + entry: './src/index.js', + output: { + path: path.resolve(__dirname, 'dist'), + }, + plugins: [ + // Add your plugins here + // Learn more about plugins from https://webpack.js.org/configuration/plugins/ ], - }, + module: { + rules: [ + { + 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/ + ], + }, }; module.exports = () => { - if (isProduction) { - config.mode = "production"; - } else { - config.mode = "development"; - } - return config; + if (isProduction) { + config.mode = 'production'; + + + } else { + config.mode = 'development'; + } + return config; }; " `; -exports[`init command should use sass and css with postcss in project when selected 1`] = ` +exports[`create-webpack-app cli should use sass and css with postcss in project when selected 1`] = ` { "description": "My webpack project", "devDependencies": { "autoprefixer": "x.x.x", "css-loader": "x.x.x", - "postcss": "x.x.x", + "node-sass": "x.x.x", "postcss-loader": "x.x.x", - "sass": "x.x.x", "sass-loader": "x.x.x", "style-loader": "x.x.x", "webpack": "x.x.x", "webpack-cli": "x.x.x", }, - "name": "my-webpack-project", + "name": "webpack-project", "scripts": { "build": "webpack --mode=production --node-env=production", "build:dev": "webpack --mode=development", @@ -720,68 +773,74 @@ exports[`init command should use sass and css with postcss in project when selec } `; -exports[`init command should use sass and css with postcss in project when selected 2`] = ` +exports[`create-webpack-app cli should use sass and css with postcss in project when selected 2`] = ` "// Generated using webpack-cli https://github.com/webpack/webpack-cli -const path = require("path"); +const path = require('path'); + +const isProduction = process.env.NODE_ENV === 'production'; + + +const stylesHandler = 'style-loader'; -const isProduction = process.env.NODE_ENV === "production"; -const stylesHandler = "style-loader"; const config = { - entry: "./src/index.js", - output: { - path: path.resolve(__dirname, "dist"), - }, - plugins: [ - // Add your plugins here - // Learn more about plugins from https://webpack.js.org/configuration/plugins/ - ], - module: { - rules: [ - { - test: /\\.s[ac]ss$/i, - use: [stylesHandler, "css-loader", "postcss-loader", "sass-loader"], - }, - { - 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/ + entry: './src/index.js', + output: { + path: path.resolve(__dirname, 'dist'), + }, + plugins: [ + // Add your plugins here + // Learn more about plugins from https://webpack.js.org/configuration/plugins/ ], - }, + module: { + rules: [ + { + test: /\\.s[ac]ss$/i, + use: [stylesHandler, 'css-loader', 'postcss-loader', 'sass-loader'], + }, + { + 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/ + ], + }, }; module.exports = () => { - if (isProduction) { - config.mode = "production"; - } else { - config.mode = "development"; - } - return config; + if (isProduction) { + config.mode = 'production'; + + + } else { + config.mode = 'development'; + } + return config; }; " `; -exports[`init command should use sass in project when selected 1`] = ` +exports[`create-webpack-app cli should use sass in project when selected 1`] = ` { "description": "My webpack project", "devDependencies": { "css-loader": "x.x.x", - "sass": "x.x.x", + "node-sass": "x.x.x", "sass-loader": "x.x.x", "style-loader": "x.x.x", "webpack": "x.x.x", "webpack-cli": "x.x.x", }, - "name": "my-webpack-project", + "name": "webpack-project", "scripts": { "build": "webpack --mode=production --node-env=production", "build:dev": "webpack --mode=development", @@ -792,67 +851,72 @@ exports[`init command should use sass in project when selected 1`] = ` } `; -exports[`init command should use sass in project when selected 2`] = ` +exports[`create-webpack-app cli should use sass in project when selected 2`] = ` "// Generated using webpack-cli https://github.com/webpack/webpack-cli -const path = require("path"); +const path = require('path'); + +const isProduction = process.env.NODE_ENV === 'production'; + + +const stylesHandler = 'style-loader'; -const isProduction = process.env.NODE_ENV === "production"; -const stylesHandler = "style-loader"; const config = { - entry: "./src/index.js", - output: { - path: path.resolve(__dirname, "dist"), - }, - plugins: [ - // Add your plugins here - // Learn more about plugins from https://webpack.js.org/configuration/plugins/ - ], - module: { - rules: [ - { - test: /\\.s[ac]ss$/i, - use: [stylesHandler, "css-loader", "sass-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/ + entry: './src/index.js', + output: { + path: path.resolve(__dirname, 'dist'), + }, + plugins: [ + // Add your plugins here + // Learn more about plugins from https://webpack.js.org/configuration/plugins/ ], - }, + module: { + rules: [ + { + test: /\\.s[ac]ss$/i, + use: [stylesHandler, 'css-loader', 'sass-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/ + ], + }, }; module.exports = () => { - if (isProduction) { - config.mode = "production"; - } else { - config.mode = "development"; - } - return config; + if (isProduction) { + config.mode = 'production'; + + + } else { + config.mode = 'development'; + } + return config; }; " `; -exports[`init command should use sass with postcss in project when selected 1`] = ` +exports[`create-webpack-app cli should use sass with postcss in project when selected 1`] = ` { "description": "My webpack project", "devDependencies": { "autoprefixer": "x.x.x", "css-loader": "x.x.x", - "postcss": "x.x.x", + "node-sass": "x.x.x", "postcss-loader": "x.x.x", - "sass": "x.x.x", "sass-loader": "x.x.x", "style-loader": "x.x.x", "webpack": "x.x.x", "webpack-cli": "x.x.x", }, - "name": "my-webpack-project", + "name": "webpack-project", "scripts": { "build": "webpack --mode=production --node-env=production", "build:dev": "webpack --mode=development", @@ -863,53 +927,59 @@ exports[`init command should use sass with postcss in project when selected 1`] } `; -exports[`init command should use sass with postcss in project when selected 2`] = ` +exports[`create-webpack-app cli should use sass with postcss in project when selected 2`] = ` "// Generated using webpack-cli https://github.com/webpack/webpack-cli -const path = require("path"); +const path = require('path'); + +const isProduction = process.env.NODE_ENV === 'production'; + + +const stylesHandler = 'style-loader'; -const isProduction = process.env.NODE_ENV === "production"; -const stylesHandler = "style-loader"; const config = { - entry: "./src/index.js", - output: { - path: path.resolve(__dirname, "dist"), - }, - plugins: [ - // Add your plugins here - // Learn more about plugins from https://webpack.js.org/configuration/plugins/ - ], - module: { - rules: [ - { - test: /\\.s[ac]ss$/i, - use: [stylesHandler, "css-loader", "postcss-loader", "sass-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/ + entry: './src/index.js', + output: { + path: path.resolve(__dirname, 'dist'), + }, + plugins: [ + // Add your plugins here + // Learn more about plugins from https://webpack.js.org/configuration/plugins/ ], - }, + module: { + rules: [ + { + test: /\\.s[ac]ss$/i, + use: [stylesHandler, 'css-loader', 'postcss-loader', 'sass-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/ + ], + }, }; module.exports = () => { - if (isProduction) { - config.mode = "production"; - } else { - config.mode = "development"; - } - return config; + if (isProduction) { + config.mode = 'production'; + + + } else { + config.mode = 'development'; + } + return config; }; " `; -exports[`init command should use stylus in project when selected 1`] = ` +exports[`create-webpack-app cli should use stylus in project when selected 1`] = ` { "description": "My webpack project", "devDependencies": { @@ -920,7 +990,7 @@ exports[`init command should use stylus in project when selected 1`] = ` "webpack": "x.x.x", "webpack-cli": "x.x.x", }, - "name": "my-webpack-project", + "name": "webpack-project", "scripts": { "build": "webpack --mode=production --node-env=production", "build:dev": "webpack --mode=development", @@ -931,64 +1001,97 @@ exports[`init command should use stylus in project when selected 1`] = ` } `; -exports[`init command should use stylus in project when selected 2`] = ` +exports[`create-webpack-app cli should use stylus in project when selected 2`] = ` "// Generated using webpack-cli https://github.com/webpack/webpack-cli -const path = require("path"); +const path = require('path'); + +const isProduction = process.env.NODE_ENV === 'production'; + + +const stylesHandler = 'style-loader'; -const isProduction = process.env.NODE_ENV === "production"; -const stylesHandler = "style-loader"; const config = { - entry: "./src/index.js", - output: { - path: path.resolve(__dirname, "dist"), - }, - plugins: [ - // Add your plugins here - // Learn more about plugins from https://webpack.js.org/configuration/plugins/ - ], - module: { - rules: [ - { - test: /\\.styl$/i, - use: [stylesHandler, "css-loader", "stylus-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/ + entry: './src/index.js', + output: { + path: path.resolve(__dirname, 'dist'), + }, + plugins: [ + // Add your plugins here + // Learn more about plugins from https://webpack.js.org/configuration/plugins/ ], - }, + module: { + rules: [ + { + test: /\\.styl$/i, + use: [stylesHandler, 'css-loader', 'stylus-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/ + ], + }, }; module.exports = () => { - if (isProduction) { - config.mode = "production"; - } else { - config.mode = "development"; - } - return config; + if (isProduction) { + config.mode = 'production'; + + + } else { + config.mode = 'development'; + } + return config; }; " `; -exports[`init command should work with 'c' alias 1`] = ` +exports[`create-webpack-app cli should work with 'c' alias 1`] = ` +{ + "description": "My webpack project", + "devDependencies": { + "autoprefixer": "x.x.x", + "html-loader": "x.x.x", + "html-webpack-plugin": "x.x.x", + "postcss-loader": "x.x.x", + "webpack": "x.x.x", + "webpack-cli": "x.x.x", + "webpack-dev-server": "x.x.x", + "workbox-webpack-plugin": "x.x.x", + }, + "name": "webpack-project", + "scripts": { + "build": "webpack --mode=production --node-env=production", + "build:dev": "webpack --mode=development", + "build:prod": "webpack --mode=production --node-env=production", + "serve": "webpack serve", + "watch": "webpack --watch", + }, + "version": "1.0.0", +} +`; + +exports[`create-webpack-app cli should work with 'create' alias 1`] = ` { "description": "My webpack project", "devDependencies": { + "autoprefixer": "x.x.x", "html-loader": "x.x.x", "html-webpack-plugin": "x.x.x", + "postcss-loader": "x.x.x", "webpack": "x.x.x", "webpack-cli": "x.x.x", "webpack-dev-server": "x.x.x", "workbox-webpack-plugin": "x.x.x", }, - "name": "my-webpack-project", + "name": "webpack-project", "scripts": { "build": "webpack --mode=production --node-env=production", "build:dev": "webpack --mode=development", @@ -1000,18 +1103,20 @@ exports[`init command should work with 'c' alias 1`] = ` } `; -exports[`init command should work with 'create' alias 1`] = ` +exports[`create-webpack-app cli should work with 'n' alias 1`] = ` { "description": "My webpack project", "devDependencies": { + "autoprefixer": "x.x.x", "html-loader": "x.x.x", "html-webpack-plugin": "x.x.x", + "postcss-loader": "x.x.x", "webpack": "x.x.x", "webpack-cli": "x.x.x", "webpack-dev-server": "x.x.x", "workbox-webpack-plugin": "x.x.x", }, - "name": "my-webpack-project", + "name": "webpack-project", "scripts": { "build": "webpack --mode=production --node-env=production", "build:dev": "webpack --mode=development", @@ -1023,18 +1128,20 @@ exports[`init command should work with 'create' alias 1`] = ` } `; -exports[`init command should work with 'n' alias 1`] = ` +exports[`create-webpack-app cli should work with 'new' alias 1`] = ` { "description": "My webpack project", "devDependencies": { + "autoprefixer": "x.x.x", "html-loader": "x.x.x", "html-webpack-plugin": "x.x.x", + "postcss-loader": "x.x.x", "webpack": "x.x.x", "webpack-cli": "x.x.x", "webpack-dev-server": "x.x.x", "workbox-webpack-plugin": "x.x.x", }, - "name": "my-webpack-project", + "name": "webpack-project", "scripts": { "build": "webpack --mode=production --node-env=production", "build:dev": "webpack --mode=development", @@ -1046,18 +1153,38 @@ exports[`init command should work with 'n' alias 1`] = ` } `; -exports[`init command should work with 'new' alias 1`] = ` +exports[`create-webpack-app cli uses yarn as the package manager when opted 1`] = ` { "description": "My webpack project", "devDependencies": { + "webpack": "x.x.x", + "webpack-cli": "x.x.x", + }, + "name": "webpack-project", + "scripts": { + "build": "webpack --mode=production --node-env=production", + "build:dev": "webpack --mode=development", + "build:prod": "webpack --mode=production --node-env=production", + "watch": "webpack --watch", + }, + "version": "1.0.0", +} +`; + +exports[`should configure assets modules by default 1`] = ` +{ + "description": "My webpack project", + "devDependencies": { + "autoprefixer": "x.x.x", "html-loader": "x.x.x", "html-webpack-plugin": "x.x.x", + "postcss-loader": "x.x.x", "webpack": "x.x.x", "webpack-cli": "x.x.x", "webpack-dev-server": "x.x.x", "workbox-webpack-plugin": "x.x.x", }, - "name": "my-webpack-project", + "name": "webpack-project", "scripts": { "build": "webpack --mode=production --node-env=production", "build:dev": "webpack --mode=development", @@ -1069,18 +1196,93 @@ exports[`init command should work with 'new' alias 1`] = ` } `; -exports[`init command uses yarn as the package manager when opted 1`] = ` +exports[`should configure assets modules by default 2`] = ` +"// Generated using webpack-cli https://github.com/webpack/webpack-cli + +const path = require('path'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); +const WorkboxWebpackPlugin = require('workbox-webpack-plugin'); + +const isProduction = process.env.NODE_ENV === 'production'; + + +const stylesHandler = 'style-loader'; + + + +const config = { + entry: './src/index.js', + output: { + path: path.resolve(__dirname, 'dist'), + }, + devServer: { + open: true, + host: 'localhost', + }, + plugins: [ + new HtmlWebpackPlugin({ + template: 'index.html', + }), + + // Add your plugins here + // Learn more about plugins from https://webpack.js.org/configuration/plugins/ + ], + module: { + rules: [ + { + test: /\\.css$/i, + use: [stylesHandler, 'css-loader', 'postcss-loader'], + }, + { + test: /\\.(eot|svg|ttf|woff|woff2|png|jpg|gif)$/i, + type: 'asset', + }, + + { + test: /\\.html$/i, + use: ['html-loader'], + }, + + // Add your rules for custom modules here + // Learn more about loaders from https://webpack.js.org/loaders/ + ], + }, +}; + +module.exports = () => { + if (isProduction) { + config.mode = 'production'; + + + config.plugins.push(new WorkboxWebpackPlugin.GenerateSW()); + + } else { + config.mode = 'development'; + } + return config; +}; +" +`; + +exports[`should generate folders if non existing generation path is given 1`] = ` { "description": "My webpack project", "devDependencies": { + "autoprefixer": "x.x.x", + "html-loader": "x.x.x", + "html-webpack-plugin": "x.x.x", + "postcss-loader": "x.x.x", "webpack": "x.x.x", "webpack-cli": "x.x.x", + "webpack-dev-server": "x.x.x", + "workbox-webpack-plugin": "x.x.x", }, - "name": "my-webpack-project", + "name": "webpack-project", "scripts": { "build": "webpack --mode=production --node-env=production", "build:dev": "webpack --mode=development", "build:prod": "webpack --mode=production --node-env=production", + "serve": "webpack serve", "watch": "webpack --watch", }, "version": "1.0.0", diff --git a/test/create-webpack-app/create-webpack-app.test.js b/test/create-webpack-app/create-webpack-app.test.js index a77ec977a49..0671534e9be 100644 --- a/test/create-webpack-app/create-webpack-app.test.js +++ b/test/create-webpack-app/create-webpack-app.test.js @@ -2,28 +2,29 @@ const os = require("os"); const path = require("path"); const { mkdirSync, existsSync, readFileSync } = require("fs"); const { join, resolve } = require("path"); -const { - isWindows, - run, - runPromptWithAnswers, - uniqueDirectoryForTest, -} = require("../utils/test-utils"); +const { isWindows, run, runPromptWithAnswers, uniqueDirectoryForTest } = require("./test.utils"); +const { profile, profileEnd, Console } = require("console"); +// cspell: ignore gmrchk jest.setTimeout(480000); const ENTER = "\x0D"; const DOWN = "\x1B\x5B\x42"; +const defaultProjectName = "webpack-project"; + const defaultTemplateFiles = [ "package.json", "package-lock.json", "src", "src/index.js", "webpack.config.js", + "README.md", ]; // const reactTemplateFiles = [...defaultTemplateFiles, "index.html"]; +// helper function to resolve the path from the test directory to actual assets // Helper to read from package.json in a given path const readFromPkgJSON = (path) => { const pkgJSONPath = join(path, "package.json"); @@ -43,22 +44,27 @@ const readFromPkgJSON = (path) => { // Helper to read from webpack.config.js in a given path const readFromWebpackConfig = (path) => readFileSync(join(path, "webpack.config.js"), "utf8"); +afterEach(() => { + jest.useRealTimers(); + jest.clearAllTimers(); +}); -describe("init command", () => { +describe("create-webpack-app cli", () => { it("should generate default project when nothing is passed", async () => { const assetsPath = await uniqueDirectoryForTest(); - const { stdout, stderr } = await run(assetsPath, ["init", "--force"]); + const { stdout } = await run(assetsPath, ["init", "--force"]); expect(stdout).toContain("Project has been initialised with webpack!"); - expect(stderr).toContain("webpack.config.js"); + expect(stdout).toContain("webpack.config.js"); + const projectFolder = join(assetsPath, defaultProjectName); // Test files defaultTemplateFiles.forEach((file) => { - expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + expect(existsSync(projectFolder, file)).toBeTruthy(); }); // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + expect(readFromPkgJSON(projectFolder)).toMatchSnapshot(); }); it("should generate project when generationPath is supplied", async () => { @@ -66,86 +72,86 @@ describe("init command", () => { const { stdout, stderr } = await run(__dirname, ["init", assetsPath, "--force"]); expect(stdout).toContain("Project has been initialised with webpack!"); - expect(stderr).toContain("webpack.config.js"); - + // expect(stderr).toContain("webpack.config.js"); + const projectFolder = join(assetsPath, defaultProjectName); // Test files defaultTemplateFiles.forEach((file) => { - expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + expect(existsSync(resolve(projectFolder, file))).toBeTruthy(); }); // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + expect(readFromPkgJSON(projectFolder)).toMatchSnapshot(); }); - + // it("should generate folders if non existing generation path is given", async () => { const assetsPath = path.resolve(os.tmpdir(), Date.now().toString()); const { stdout, stderr } = await run(__dirname, ["init", assetsPath, "--force"]); expect(stdout).toContain("Project has been initialised with webpack!"); - expect(stderr).toContain(`create ${path.relative(__dirname, assetsPath)}`); - expect(stderr).toContain("webpack.config.js"); + + expect(stdout).toContain("webpack.config.js"); + const projectFolder = resolve(__dirname, assetsPath, defaultProjectName); // Test files defaultTemplateFiles.forEach((file) => { - expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + expect(existsSync(join(projectFolder, file))).toBeTruthy(); }); - // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + //Check if the generated package.json file content matches the snapshot + expect(readFromPkgJSON(projectFolder)).toMatchSnapshot(); }); - // + // // it("should configure assets modules by default", async () => { - const assetsPath = path.resolve(os.tmpdir(), Date.now().toString()); - const { stdout, stderr } = await run(__dirname, ["init", assetsPath, "--force"]); + const assetsPath = await uniqueDirectoryForTest(); + const { stdout, stderr } = await run(assetsPath, ["init", "--force"]); + const projectFolder = join(assetsPath, defaultProjectName); expect(stdout).toContain("Project has been initialised with webpack!"); - expect(stderr).toContain(`create ${path.relative(__dirname, assetsPath)}`); - expect(stderr).toContain("webpack.config.js"); - + expect(stdout).toContain("webpack.config.js"); // Test files defaultTemplateFiles.forEach((file) => { - expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + expect(existsSync(resolve(projectFolder, file))).toBeTruthy(); }); // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + expect(readFromPkgJSON(projectFolder)).toMatchSnapshot(); // Check if the generated webpack configuration matches the snapshot - expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); + expect(readFromWebpackConfig(projectFolder)).toMatchSnapshot(); }); - // - // it("should ask question when wrong template is supplied", async () => { - // const assetsPath = await uniqueDirectoryForTest(); - // const { stdout, stderr } = await runPromptWithAnswers( - // assetsPath, - // ["init", "--force", "--template=apple"], - // [`${ENTER}`], - // ); - // - // expect(stdout).toContain("Project has been initialised with webpack!"); - // expect(stderr).toContain("apple is not a valid template, please select one from below"); - // expect(stderr).toContain("webpack.config.js"); - // - // // Test files - // defaultTemplateFiles.forEach((file) => { - // expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); - // }); - // - // // Check if the generated package.json file content matches the snapshot - // expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); - // }); - // + // // + // // // it("should ask question when wrong template is supplied", async () => { + // // // const assetsPath = await uniqueDirectoryForTest(); + // // // const { stdout, stderr } = await runPromptWithAnswers( + // // // assetsPath, + // // // ["init", "--force", "--template=apple"], + // // // [`${ENTER}`], + // // // ); + // // // + // // // expect(stdout).toContain("Project has been initialised with webpack!"); + // // // expect(stderr).toContain("apple is not a valid template, please select one from below"); + // // // expect(stderr).toContain("webpack.config.js"); + // // // + // // // // Test files + // // // defaultTemplateFiles.forEach((file) => { + // // // expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + // // // }); + // // // + // // // // Check if the generated package.json file content matches the snapshot + // // // expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + // // // }); + // // // it("should generate typescript project correctly", async () => { const assetsPath = await uniqueDirectoryForTest(); const { stdout, stderr } = await runPromptWithAnswers( assetsPath, - ["init"], + ["init", ".", defaultProjectName], [`${DOWN}${DOWN}${ENTER}`, `n${ENTER}`, `n${ENTER}`, `n${ENTER}`, ENTER, ENTER], ); expect(stdout).toContain("Project has been initialised with webpack!"); - expect(stderr).toContain("webpack.config.js"); - expect(stderr).toContain("tsconfig.json"); + expect(stdout).toContain("webpack.config.js"); + expect(stdout).toContain("tsconfig.json"); // Test files const files = [ @@ -153,49 +159,51 @@ describe("init command", () => { "src/index.ts", "tsconfig.json", ]; + const projectFolder = join(assetsPath, defaultProjectName); files.forEach((file) => { - expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + expect(existsSync(resolve(projectFolder, file))).toBeTruthy(); }); // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + expect(readFromPkgJSON(projectFolder)).toMatchSnapshot(); // Check if the generated webpack configuration matches the snapshot - expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); + expect(readFromWebpackConfig(projectFolder)).toMatchSnapshot(); }); - // + // // it("should generate ES6 project correctly", async () => { const assetsPath = await uniqueDirectoryForTest(); const { stdout, stderr } = await runPromptWithAnswers( assetsPath, - ["init"], + ["init", ".", defaultProjectName], [`${DOWN}${ENTER}`, `n${ENTER}`, `n${ENTER}`, `n${ENTER}`, ENTER, ENTER], ); expect(stdout).toContain("Project has been initialised with webpack!"); - expect(stderr).toContain("webpack.config.js"); - expect(stderr).toContain(".babelrc"); + expect(stdout).toContain("webpack.config.js"); + expect(stdout).toContain("babel.config.json"); + const projectFolder = join(assetsPath, defaultProjectName); // Test files - const files = [...defaultTemplateFiles, ".babelrc"]; + const files = [...defaultTemplateFiles, "babel.config.json"]; files.forEach((file) => { - expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + expect(existsSync(resolve(projectFolder, file))).toBeTruthy(); }); // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + expect(readFromPkgJSON(projectFolder)).toMatchSnapshot(); // Check if the generated webpack configuration matches the snapshot - expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); + expect(readFromWebpackConfig(projectFolder)).toMatchSnapshot(); }); - + // it("should use sass in project when selected", async () => { const assetsPath = await uniqueDirectoryForTest(); const { stdout, stderr } = await runPromptWithAnswers( assetsPath, - ["init"], + ["init", ".", defaultProjectName], [ `${ENTER}`, `n${ENTER}`, @@ -210,25 +218,26 @@ describe("init command", () => { ); expect(stdout).toContain("Project has been initialised with webpack!"); - expect(stderr).toContain("webpack.config.js"); + expect(stdout).toContain("webpack.config.js"); + const projectFolder = join(assetsPath, defaultProjectName); // Test files defaultTemplateFiles.forEach((file) => { - expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + expect(existsSync(resolve(projectFolder, file))).toBeTruthy(); }); // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + expect(readFromPkgJSON(projectFolder)).toMatchSnapshot(); // Check if the generated webpack configuration matches the snapshot - expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); + expect(readFromWebpackConfig(projectFolder)).toMatchSnapshot(); }); - + // it("should use sass with postcss in project when selected", async () => { const assetsPath = await uniqueDirectoryForTest(); const { stdout, stderr } = await runPromptWithAnswers( assetsPath, - ["init"], + ["init", ".", defaultProjectName], [ `${ENTER}`, `n${ENTER}`, @@ -243,27 +252,28 @@ describe("init command", () => { ); expect(stdout).toContain("Project has been initialised with webpack!"); - expect(stderr).toContain("webpack.config.js"); + expect(stdout).toContain("webpack.config.js"); + const projectFolder = join(assetsPath, defaultProjectName); // Test files const files = [...defaultTemplateFiles, "postcss.config.js"]; files.forEach((file) => { - expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + expect(existsSync(resolve(projectFolder, file))).toBeTruthy(); }); // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + expect(readFromPkgJSON(projectFolder)).toMatchSnapshot(); // Check if the generated webpack configuration matches the snapshot - expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); + expect(readFromWebpackConfig(projectFolder)).toMatchSnapshot(); }); - + // it("should use mini-css-extract-plugin when selected", async () => { const assetsPath = await uniqueDirectoryForTest(); const { stdout, stderr } = await runPromptWithAnswers( assetsPath, - ["init"], + ["init", ".", defaultProjectName], [ `${ENTER}`, `n${ENTER}`, @@ -278,25 +288,26 @@ describe("init command", () => { ); expect(stdout).toContain("Project has been initialised with webpack!"); - expect(stderr).toContain("webpack.config.js"); + expect(stdout).toContain("webpack.config.js"); + const projectFolder = join(assetsPath, defaultProjectName); // Test files defaultTemplateFiles.forEach((file) => { - expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + expect(existsSync(resolve(projectFolder, file))).toBeTruthy(); }); // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + expect(readFromPkgJSON(projectFolder)).toMatchSnapshot(); // Check if the generated webpack configuration matches the snapshot - expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); + expect(readFromWebpackConfig(projectFolder)).toMatchSnapshot(); }); - + // it("should use sass and css with postcss in project when selected", async () => { const assetsPath = await uniqueDirectoryForTest(); const { stdout, stderr } = await runPromptWithAnswers( assetsPath, - ["init"], + ["init", ".", defaultProjectName], [ `${ENTER}`, `n${ENTER}`, @@ -311,27 +322,28 @@ describe("init command", () => { ); expect(stdout).toContain("Project has been initialised with webpack!"); - expect(stderr).toContain("webpack.config.js"); + expect(stdout).toContain("webpack.config.js"); + const projectFolder = join(assetsPath, defaultProjectName); // Test files const files = [...defaultTemplateFiles, "postcss.config.js"]; files.forEach((file) => { - expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + expect(existsSync(resolve(projectFolder, file))).toBeTruthy(); }); // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + expect(readFromPkgJSON(projectFolder)).toMatchSnapshot(); // Check if the generated webpack configuration matches the snapshot - expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); + expect(readFromWebpackConfig(projectFolder)).toMatchSnapshot(); }); - + // it("should use less in project when selected", async () => { const assetsPath = await uniqueDirectoryForTest(); const { stdout, stderr } = await runPromptWithAnswers( assetsPath, - ["init"], + ["init", ".", defaultProjectName], [ `${ENTER}`, `n${ENTER}`, @@ -346,25 +358,26 @@ describe("init command", () => { ); expect(stdout).toContain("Project has been initialised with webpack!"); - expect(stderr).toContain("webpack.config.js"); + expect(stdout).toContain("webpack.config.js"); + const projectFolder = join(assetsPath, defaultProjectName); // Test files defaultTemplateFiles.forEach((file) => { - expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + expect(existsSync(resolve(projectFolder, file))).toBeTruthy(); }); // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + expect(readFromPkgJSON(projectFolder)).toMatchSnapshot(); // Check if the generated webpack configuration matches the snapshot - expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); + expect(readFromWebpackConfig(projectFolder)).toMatchSnapshot(); }); - + // it("should use stylus in project when selected", async () => { const assetsPath = await uniqueDirectoryForTest(); const { stdout, stderr } = await runPromptWithAnswers( assetsPath, - ["init"], + ["init", ".", defaultProjectName], [ `${ENTER}`, `n${ENTER}`, @@ -379,49 +392,52 @@ describe("init command", () => { ); expect(stdout).toContain("Project has been initialised with webpack!"); - expect(stderr).toContain("webpack.config.js"); + expect(stdout).toContain("webpack.config.js"); + const projectFolder = join(assetsPath, defaultProjectName); // Test files defaultTemplateFiles.forEach((file) => { - expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + expect(existsSync(resolve(projectFolder, file))).toBeTruthy(); }); // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + expect(readFromPkgJSON(projectFolder)).toMatchSnapshot(); // Check if the generated webpack configuration matches the snapshot - expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); + expect(readFromWebpackConfig(projectFolder)).toMatchSnapshot(); }); - + // it("should configure WDS as opted", async () => { const assetsPath = await uniqueDirectoryForTest(); const { stdout, stderr } = await runPromptWithAnswers( assetsPath, - ["init"], + ["init", ".", defaultProjectName], [ENTER, ENTER, `n${ENTER}`, `n${ENTER}`, ENTER, ENTER], ); - expect(stdout).toContain("Do you want to use webpack-dev-server?"); + expect(stdout).toContain("Would you like to use Webpack Dev server?"); expect(stdout).toContain("Project has been initialised with webpack!"); - expect(stderr).toContain("webpack.config.js"); + expect(stdout).toContain("webpack.config.js"); + + const projectFolder = join(assetsPath, defaultProjectName); // Test files defaultTemplateFiles.forEach((file) => { - expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + expect(existsSync(resolve(projectFolder, file))).toBeTruthy(); }); // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + expect(readFromPkgJSON(projectFolder)).toMatchSnapshot(); // Check if the generated webpack configuration matches the snapshot - expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); + expect(readFromWebpackConfig(projectFolder)).toMatchSnapshot(); }); - + // it("should use postcss in project when selected", async () => { const assetsPath = await uniqueDirectoryForTest(); const { stdout, stderr } = await runPromptWithAnswers( assetsPath, - ["init"], + ["init", ".", defaultProjectName], [ `${ENTER}`, `n${ENTER}`, @@ -435,68 +451,71 @@ describe("init command", () => { ); expect(stdout).toContain("Project has been initialised with webpack!"); - expect(stderr).toContain("webpack.config.js"); + expect(stdout).toContain("webpack.config.js"); + const projectFolder = join(assetsPath, defaultProjectName); // Test files const files = [...defaultTemplateFiles, "postcss.config.js"]; files.forEach((file) => { - expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + expect(existsSync(resolve(projectFolder, file))).toBeTruthy(); }); // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + expect(readFromPkgJSON(projectFolder)).toMatchSnapshot(); // Check if the generated webpack configuration matches the snapshot - expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); + expect(readFromWebpackConfig(projectFolder)).toMatchSnapshot(); }); it("should configure html-webpack-plugin as opted", async () => { const assetsPath = await uniqueDirectoryForTest(); const { stdout, stderr } = await runPromptWithAnswers( assetsPath, - ["init"], + ["init", ".", defaultProjectName], [ENTER, `n${ENTER}`, ENTER, `n${ENTER}`, ENTER, ENTER], ); expect(stdout).toContain("Do you want to simplify the creation of HTML files for your bundle?"); expect(stdout).toContain("Project has been initialised with webpack!"); - expect(stderr).toContain("webpack.config.js"); + expect(stdout).toContain("webpack.config.js"); + const projectFolder = join(assetsPath, defaultProjectName); // Test files defaultTemplateFiles.forEach((file) => { - expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + expect(existsSync(resolve(projectFolder, file))).toBeTruthy(); }); // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + expect(readFromPkgJSON(projectFolder)).toMatchSnapshot(); // Check if the generated webpack configuration matches the snapshot - expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); + expect(readFromWebpackConfig(projectFolder)).toMatchSnapshot(); }); it("should configure workbox-webpack-plugin as opted", async () => { const assetsPath = await uniqueDirectoryForTest(); const { stdout, stderr } = await runPromptWithAnswers( assetsPath, - ["init"], + ["init", ".", defaultProjectName], [ENTER, `n${ENTER}`, ENTER, ENTER, ENTER, ENTER], ); expect(stdout).toContain("Do you want to add PWA support?"); expect(stdout).toContain("Project has been initialised with webpack!"); - expect(stderr).toContain("webpack.config.js"); + expect(stdout).toContain("webpack.config.js"); - // Test files + const projectFolder = join(assetsPath, defaultProjectName); + // Test file defaultTemplateFiles.forEach((file) => { - expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + expect(existsSync(resolve(projectFolder, file))).toBeTruthy(); }); // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + expect(readFromPkgJSON(projectFolder)).toMatchSnapshot(); // Check if the generated webpack configuration matches the snapshot - expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); + expect(readFromWebpackConfig(projectFolder)).toMatchSnapshot(); }); it("should throw if the current path is not writable", async () => { @@ -508,11 +527,11 @@ describe("init command", () => { const projectPath = join(assetsPath, "non-writable-path"); mkdirSync(projectPath, 0o500); - - const { exitCode, stderr } = await run(projectPath, ["init", "my-app"], { reject: false }); - + const { exitCode, stderr, stdout } = await run(projectPath, ["init", "my-app", "--force"], { + reject: false, + }); + expect(stderr).toContain("Failed to initialize the project with webpack!"); expect(exitCode).toBe(2); - expect(stderr).toContain("Failed to initialize the project."); }); it("should work with 'new' alias", async () => { @@ -520,15 +539,16 @@ describe("init command", () => { const { stdout, stderr } = await run(assetsPath, ["new", "--force"]); expect(stdout).toContain("Project has been initialised with webpack!"); - expect(stderr).toContain("webpack.config.js"); + expect(stdout).toContain("webpack.config.js"); + const projectFolder = join(assetsPath, defaultProjectName); // Test files defaultTemplateFiles.forEach((file) => { - expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + expect(existsSync(resolve(projectFolder, file))).toBeTruthy(); }); // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + expect(readFromPkgJSON(projectFolder)).toMatchSnapshot(); }); it("should work with 'create' alias", async () => { @@ -536,15 +556,16 @@ describe("init command", () => { const { stdout, stderr } = await run(assetsPath, ["create", "--force"]); expect(stdout).toContain("Project has been initialised with webpack!"); - expect(stderr).toContain("webpack.config.js"); + expect(stdout).toContain("webpack.config.js"); + const projectFolder = join(assetsPath, defaultProjectName); // Test files defaultTemplateFiles.forEach((file) => { - expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + expect(existsSync(resolve(projectFolder, file))).toBeTruthy(); }); // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + expect(readFromPkgJSON(projectFolder)).toMatchSnapshot(); }); it("should work with 'c' alias", async () => { @@ -552,15 +573,16 @@ describe("init command", () => { const { stdout, stderr } = await run(assetsPath, ["c", "--force"]); expect(stdout).toContain("Project has been initialised with webpack!"); - expect(stderr).toContain("webpack.config.js"); + expect(stdout).toContain("webpack.config.js"); + const projectFolder = join(assetsPath, defaultProjectName); // Test files defaultTemplateFiles.forEach((file) => { - expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + expect(existsSync(resolve(projectFolder, file))).toBeTruthy(); }); // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + expect(readFromPkgJSON(projectFolder)).toMatchSnapshot(); }); it("should work with 'n' alias", async () => { @@ -568,60 +590,62 @@ describe("init command", () => { const { stdout, stderr } = await run(assetsPath, ["n", "--force"]); expect(stdout).toContain("Project has been initialised with webpack!"); - expect(stderr).toContain("webpack.config.js"); + expect(stdout).toContain("webpack.config.js"); + const projectFolder = join(assetsPath, defaultProjectName); // Test files defaultTemplateFiles.forEach((file) => { - expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + expect(existsSync(resolve(projectFolder, file))).toBeTruthy(); }); // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + expect(readFromPkgJSON(projectFolder)).toMatchSnapshot(); }); - - // it("recognizes '-t' as an alias for '--template'", async () => { - // const assetsPath = await uniqueDirectoryForTest(); - // const { stdout, stderr } = await run(assetsPath, ["init", "-t", "default", "--force"]); - // - // expect(stdout).toContain("Project has been initialised with webpack!"); - // expect(stderr).toContain("webpack.config.js"); - // - // // Test files - // defaultTemplateFiles.forEach((file) => { - // expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); - // }); - // - // // Check if the generated package.json file content matches the snapshot - // expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); - // }); - // - // it("recognizes '-f' as an alias for '--force'", async () => { - // const assetsPath = await uniqueDirectoryForTest(); - // const { stdout, stderr } = await run(assetsPath, ["init", "-f"]); - // - // expect(stdout).toContain("Project has been initialised with webpack!"); - // expect(stderr).toContain("webpack.config.js"); - // - // // Test files - // defaultTemplateFiles.forEach((file) => { - // expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); - // }); - // - // // Check if the generated package.json file content matches the snapshot - // expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); - // }); - // + // // + // // // it("recognizes '-t' as an alias for '--template'", async () => { + // // // const assetsPath = await uniqueDirectoryForTest(); + // // // const { stdout, stderr } = await run(assetsPath, ["init", "-t", "default", "--force"]); + // // // + // // // expect(stdout).toContain("Project has been initialised with webpack!"); + // // // expect(stderr).toContain("webpack.config.js"); + // // // + // // // // Test files + // // // defaultTemplateFiles.forEach((file) => { + // // // expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + // // // }); + // // // + // // // // Check if the generated package.json file content matches the snapshot + // // // expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + // // // }); + // // // + // // // it("recognizes '-f' as an alias for '--force'", async () => { + // // // const assetsPath = await uniqueDirectoryForTest(); + // // // const { stdout, stderr } = await run(assetsPath, ["init", "-f"]); + // // // + // // // expect(stdout).toContain("Project has been initialised with webpack!"); + // // // expect(stderr).toContain("webpack.config.js"); + // // // + // // // // Test files + // // // defaultTemplateFiles.forEach((file) => { + // // // expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + // // // }); + // // // + // // // // Check if the generated package.json file content matches the snapshot + // // // expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + // // // }); + // // // it("uses yarn as the package manager when opted", async () => { const assetsPath = await uniqueDirectoryForTest(); const { stdout, stderr } = await runPromptWithAnswers( assetsPath, - ["init"], + ["init", ".", defaultProjectName], [ENTER, `n${ENTER}`, `n${ENTER}`, `n${ENTER}`, ENTER, `${DOWN}${ENTER}`], ); expect(stdout).toContain("Project has been initialised with webpack!"); - expect(stderr).toContain("webpack.config.js"); + expect(stdout).toContain("webpack.config.js"); + const projectFolder = join(assetsPath, defaultProjectName); // Test files const files = [ ...defaultTemplateFiles.filter((file) => file !== "package-lock.json"), @@ -629,52 +653,52 @@ describe("init command", () => { ]; files.forEach((file) => { - expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + expect(existsSync(resolve(projectFolder, file))).toBeTruthy(); }); // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + expect(readFromPkgJSON(projectFolder)).toMatchSnapshot(); }); - // it("should generate react template with prompt answers", async () => { - // const assetsPath = await uniqueDirectoryForTest(); - // const { stdout, stderr } = await runPromptWithAnswers( - // assetsPath, - // ["init"], - // [ENTER, `y${ENTER}`, `${DOWN}${ENTER}`, `y${ENTER}`, ENTER, ENTER], - // ); - // - // expect(stdout).toContain("Project has been initialised with webpack!"); - // expect(stderr).toContain("webpack.config.js"); - // - // // Test files - // reactTemplateFiles.forEach((file) => { - // expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); - // }); - // - // // Check if the generated package.json file content matches the snapshot - // expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); - // - // // Check if the generated webpack configuration matches the snapshot - // expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); - // }); - // - // it("should generate react template with --force", async () => { - // const assetsPath = await uniqueDirectoryForTest(); - // const { stdout, stderr } = await run(assetsPath, ["init", "--template=react", "--force"]); - // - // expect(stdout).toContain("Project has been initialised with webpack!"); - // expect(stderr).toContain("webpack.config.js"); - // - // // Test files - // reactTemplateFiles.forEach((file) => { - // expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); - // }); - // - // // Check if the generated package.json file content matches the snapshot - // expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); - // - // // Check if the generated webpack configuration matches the snapshot - // expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); - // }); + // // // it("should generate react template with prompt answers", async () => { + // // // const assetsPath = await uniqueDirectoryForTest(); + // // // const { stdout, stderr } = await runPromptWithAnswers( + // // // assetsPath, + // // // ["init"], + // // // [ENTER, `y${ENTER}`, `${DOWN}${ENTER}`, `y${ENTER}`, ENTER, ENTER], + // // // ); + // // // + // // // expect(stdout).toContain("Project has been initialised with webpack!"); + // // // expect(stderr).toContain("webpack.config.js"); + // // // + // // // // Test files + // // // reactTemplateFiles.forEach((file) => { + // // // expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + // // // }); + // // // + // // // // Check if the generated package.json file content matches the snapshot + // // // expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + // // // + // // // // Check if the generated webpack configuration matches the snapshot + // // // expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); + // // // }); + // // // + // // // it("should generate react template with --force", async () => { + // // // const assetsPath = await uniqueDirectoryForTest(); + // // // const { stdout, stderr } = await run(assetsPath, ["init", "--template=react", "--force"]); + // // // + // // // expect(stdout).toContain("Project has been initialised with webpack!"); + // // // expect(stderr).toContain("webpack.config.js"); + // // // + // // // // Test files + // // // reactTemplateFiles.forEach((file) => { + // // // expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + // // // }); + // // // + // // // // Check if the generated package.json file content matches the snapshot + // // // expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + // // // + // // // // Check if the generated webpack configuration matches the snapshot + // // // expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); + // // // }); }); From 0a42c646c76cb834dc39e04c83b1428af4b50d06 Mon Sep 17 00:00:00 2001 From: Uzair Date: Mon, 1 Jul 2024 15:14:12 +0530 Subject: [PATCH 49/81] fix: changed template package.json to improve compatibility with syntax earlier testing util spawn function was throwing error because of this. --- .../templates/init/default/package.json.tpl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/create-webpack-app/templates/init/default/package.json.tpl b/packages/create-webpack-app/templates/init/default/package.json.tpl index e6128170e88..0dc9da5d679 100644 --- a/packages/create-webpack-app/templates/init/default/package.json.tpl +++ b/packages/create-webpack-app/templates/init/default/package.json.tpl @@ -8,9 +8,9 @@ "build": "webpack --mode=production --node-env=production", "build:dev": "webpack --mode=development", "build:prod": "webpack --mode=production --node-env=production", - "watch": "webpack --watch", - <% if (devServer) { %> - "serve": "webpack serve" - <% } %> + <% if (devServer) { %> + "serve": "webpack serve", + <% } %> + "watch": "webpack --watch" } } From f0196bd33787abc02bfb9aae96188583dc27fe74 Mon Sep 17 00:00:00 2001 From: Uzair Date: Mon, 1 Jul 2024 15:14:37 +0530 Subject: [PATCH 50/81] chore: add peerDep webpack-cli and dep colorette --- packages/create-webpack-app/package.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/create-webpack-app/package.json b/packages/create-webpack-app/package.json index b46583b79d1..dc18cbd0921 100644 --- a/packages/create-webpack-app/package.json +++ b/packages/create-webpack-app/package.json @@ -39,7 +39,11 @@ "lib", "!**/*__tests__" ], + "peerDependencies": { + "webpack-cli": "^5.x.x" + }, "dependencies": { + "colorette": "^2.0.20", "commander": "^12.1.0", "cross-spawn": "^7.0.3", "ejs": "^3.1.10", From 8e9dfb625c01e84e4a2a44788f74ed5f57ae9ca0 Mon Sep 17 00:00:00 2001 From: Uzair Date: Fri, 5 Jul 2024 00:50:24 +0530 Subject: [PATCH 51/81] feat: remove projectName and use path only - remove projectName and the cumbersome projectPath/projectName resolution - adjust tests accordingly - change template/**/package.json.tpl accordingly - update readme accordingly --- packages/create-webpack-app/README.md | 6 - packages/create-webpack-app/src/index.ts | 6 +- packages/create-webpack-app/src/plopfile.ts | 23 +- .../templates/init/default/package.json.tpl | 2 +- .../create-webpack-app.test.js | 331 ++++++++---------- 5 files changed, 160 insertions(+), 208 deletions(-) diff --git a/packages/create-webpack-app/README.md b/packages/create-webpack-app/README.md index 27748b48d30..d2c3e7734bc 100644 --- a/packages/create-webpack-app/README.md +++ b/packages/create-webpack-app/README.md @@ -39,9 +39,3 @@ npx create-webpack-app -f, --force ```bash npx create-webpack-app [generation-path] ``` - -**To scaffold in a specified path with a custom project-name** - -```bash -npx create-webpack-app [generation-path] [project-name] -``` diff --git a/packages/create-webpack-app/src/index.ts b/packages/create-webpack-app/src/index.ts index b16686ba65b..6809abf57a7 100644 --- a/packages/create-webpack-app/src/index.ts +++ b/packages/create-webpack-app/src/index.ts @@ -14,7 +14,6 @@ const plop = await nodePlop(resolve(__dirname, "./plopfile.js")); const defaultValues = { init: { projectPath: process.cwd(), - projectName: "webpack-project", langType: "none", devServer: true, htmlWebpackPlugin: true, @@ -38,15 +37,13 @@ program .aliases(["i", "n", "c", "create", "new"]) .description("Initialize a new Webpack project") .argument("[projectPath]", "Path to create the project") - .argument("[projectName]", "Name of the project") .option("-f, --force", "Skip the prompt and use the default values", false) - .action(async function (projectPath, projectName, opts) { + .action(async function (projectPath, opts) { const { force } = opts; const initGenerator = plop.getGenerator("init"); const byPassValues: Array = []; if (projectPath) byPassValues.push(projectPath); - if (projectName) byPassValues.push(projectName); try { if (force) { logger.warn("Skipping the prompt and using the default values"); @@ -55,7 +52,6 @@ program await initGenerator.runActions( { ...defaultValues.init, - projectName: byPassValues[1] ? byPassValues[1] : defaultValues.init.projectName, projectPath: byPassValues[0] ? resolve(process.cwd(), byPassValues[0]) : defaultValues.init.projectPath, diff --git a/packages/create-webpack-app/src/plopfile.ts b/packages/create-webpack-app/src/plopfile.ts index 232115d4694..6dbee1542bc 100644 --- a/packages/create-webpack-app/src/plopfile.ts +++ b/packages/create-webpack-app/src/plopfile.ts @@ -66,18 +66,6 @@ export default function (plop: NodePlopAPI) { return resolve(process.cwd(), input); }, }, - { - type: "input", - name: "projectName", - message: "Enter your project name:", - default: "webpack-project", - validate(input) { - if (!input.trim()) { - return "Project name cannot be empty"; - } - return true; - }, - }, { type: "list", name: "langType", @@ -192,7 +180,6 @@ export default function (plop: NodePlopAPI) { answers.jsConfig = null; answers.jsConfig = answers.langType === "Typescript" ? "tsconfig.json" : "babel.config.json"; answers.cssConfig = answers.isPostCSS ? "postcss.config.js" : null; - // adding some dependencies based on the answers if (answers.devServer) { dependencies.push("webpack-dev-server"); @@ -210,7 +197,7 @@ export default function (plop: NodePlopAPI) { const actions: ActionType[] = [ { type: "addMany", - destination: "{{projectPath}}/{{dashCase projectName}}", + destination: "{{projectPath}}/", base: "../templates/init/default", templateFiles: "../templates/init/default/**/*", transform: (content: string, data: Answers) => { @@ -222,7 +209,7 @@ export default function (plop: NodePlopAPI) { }, { type: "add", - path: "{{projectPath}}/{{dashCase projectName}}/{{entryPoint}}", + path: "{{projectPath}}/{{entryPoint}}", force: true, transform: (data: Answers) => { if (data.langType === "Typescript") { @@ -239,7 +226,7 @@ export default function (plop: NodePlopAPI) { actions.push({ type: "add", templateFile: "../templates/init/customFiles/{{jsConfig}}", - path: "{{projectPath}}/{{dashCase projectName}}/{{jsConfig}}", + path: "{{projectPath}}/{{jsConfig}}", force: true, }); } @@ -247,13 +234,13 @@ export default function (plop: NodePlopAPI) { actions.push({ type: "add", templateFile: "../templates/init/customFiles/{{cssConfig}}", - path: "{{projectPath}}/{{dashCase projectName}}/{{cssConfig}}", + path: "{{projectPath}}/{{cssConfig}}", force: true, }); } actions.push({ type: "pkgInstall", - path: plop.renderString("{{projectPath}}/{{dashCase projectName}}", answers), + path: plop.renderString("{{projectPath}}/", answers), // Custom function don't automatically render hbs template as path hence manual rendering packages: dependencies, }); diff --git a/packages/create-webpack-app/templates/init/default/package.json.tpl b/packages/create-webpack-app/templates/init/default/package.json.tpl index 0dc9da5d679..6384b1f6293 100644 --- a/packages/create-webpack-app/templates/init/default/package.json.tpl +++ b/packages/create-webpack-app/templates/init/default/package.json.tpl @@ -3,7 +3,7 @@ "description": "My webpack project", - "name": "<%= projectName %>", + "name": "webpack-project", "scripts": { "build": "webpack --mode=production --node-env=production", "build:dev": "webpack --mode=development", diff --git a/test/create-webpack-app/create-webpack-app.test.js b/test/create-webpack-app/create-webpack-app.test.js index 0671534e9be..e5f12d1297b 100644 --- a/test/create-webpack-app/create-webpack-app.test.js +++ b/test/create-webpack-app/create-webpack-app.test.js @@ -5,14 +5,11 @@ const { join, resolve } = require("path"); const { isWindows, run, runPromptWithAnswers, uniqueDirectoryForTest } = require("./test.utils"); const { profile, profileEnd, Console } = require("console"); -// cspell: ignore gmrchk jest.setTimeout(480000); const ENTER = "\x0D"; const DOWN = "\x1B\x5B\x42"; -const defaultProjectName = "webpack-project"; - const defaultTemplateFiles = [ "package.json", "package-lock.json", @@ -57,14 +54,13 @@ describe("create-webpack-app cli", () => { expect(stdout).toContain("Project has been initialised with webpack!"); expect(stdout).toContain("webpack.config.js"); - const projectFolder = join(assetsPath, defaultProjectName); // Test files defaultTemplateFiles.forEach((file) => { - expect(existsSync(projectFolder, file)).toBeTruthy(); + expect(existsSync(assetsPath, file)).toBeTruthy(); }); // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(projectFolder)).toMatchSnapshot(); + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); }); it("should generate project when generationPath is supplied", async () => { @@ -73,14 +69,13 @@ describe("create-webpack-app cli", () => { expect(stdout).toContain("Project has been initialised with webpack!"); // expect(stderr).toContain("webpack.config.js"); - const projectFolder = join(assetsPath, defaultProjectName); // Test files defaultTemplateFiles.forEach((file) => { - expect(existsSync(resolve(projectFolder, file))).toBeTruthy(); + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); }); // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(projectFolder)).toMatchSnapshot(); + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); }); // it("should generate folders if non existing generation path is given", async () => { @@ -90,34 +85,32 @@ describe("create-webpack-app cli", () => { expect(stdout).toContain("Project has been initialised with webpack!"); expect(stdout).toContain("webpack.config.js"); - const projectFolder = resolve(__dirname, assetsPath, defaultProjectName); // Test files defaultTemplateFiles.forEach((file) => { - expect(existsSync(join(projectFolder, file))).toBeTruthy(); + expect(existsSync(join(assetsPath, file))).toBeTruthy(); }); //Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(projectFolder)).toMatchSnapshot(); + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); }); // // it("should configure assets modules by default", async () => { const assetsPath = await uniqueDirectoryForTest(); const { stdout, stderr } = await run(assetsPath, ["init", "--force"]); - const projectFolder = join(assetsPath, defaultProjectName); expect(stdout).toContain("Project has been initialised with webpack!"); expect(stdout).toContain("webpack.config.js"); // Test files defaultTemplateFiles.forEach((file) => { - expect(existsSync(resolve(projectFolder, file))).toBeTruthy(); + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); }); // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(projectFolder)).toMatchSnapshot(); + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); // Check if the generated webpack configuration matches the snapshot - expect(readFromWebpackConfig(projectFolder)).toMatchSnapshot(); + expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); }); // // // // // it("should ask question when wrong template is supplied", async () => { @@ -145,7 +138,7 @@ describe("create-webpack-app cli", () => { const assetsPath = await uniqueDirectoryForTest(); const { stdout, stderr } = await runPromptWithAnswers( assetsPath, - ["init", ".", defaultProjectName], + ["init", "."], [`${DOWN}${DOWN}${ENTER}`, `n${ENTER}`, `n${ENTER}`, `n${ENTER}`, ENTER, ENTER], ); @@ -159,24 +152,23 @@ describe("create-webpack-app cli", () => { "src/index.ts", "tsconfig.json", ]; - const projectFolder = join(assetsPath, defaultProjectName); files.forEach((file) => { - expect(existsSync(resolve(projectFolder, file))).toBeTruthy(); + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); }); // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(projectFolder)).toMatchSnapshot(); + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); // Check if the generated webpack configuration matches the snapshot - expect(readFromWebpackConfig(projectFolder)).toMatchSnapshot(); + expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); }); // // it("should generate ES6 project correctly", async () => { const assetsPath = await uniqueDirectoryForTest(); const { stdout, stderr } = await runPromptWithAnswers( assetsPath, - ["init", ".", defaultProjectName], + ["init", "."], [`${DOWN}${ENTER}`, `n${ENTER}`, `n${ENTER}`, `n${ENTER}`, ENTER, ENTER], ); @@ -184,26 +176,25 @@ describe("create-webpack-app cli", () => { expect(stdout).toContain("webpack.config.js"); expect(stdout).toContain("babel.config.json"); - const projectFolder = join(assetsPath, defaultProjectName); // Test files const files = [...defaultTemplateFiles, "babel.config.json"]; files.forEach((file) => { - expect(existsSync(resolve(projectFolder, file))).toBeTruthy(); + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); }); // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(projectFolder)).toMatchSnapshot(); + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); // Check if the generated webpack configuration matches the snapshot - expect(readFromWebpackConfig(projectFolder)).toMatchSnapshot(); + expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); }); // it("should use sass in project when selected", async () => { const assetsPath = await uniqueDirectoryForTest(); const { stdout, stderr } = await runPromptWithAnswers( assetsPath, - ["init", ".", defaultProjectName], + ["init", "."], [ `${ENTER}`, `n${ENTER}`, @@ -220,24 +211,23 @@ describe("create-webpack-app cli", () => { expect(stdout).toContain("Project has been initialised with webpack!"); expect(stdout).toContain("webpack.config.js"); - const projectFolder = join(assetsPath, defaultProjectName); // Test files defaultTemplateFiles.forEach((file) => { - expect(existsSync(resolve(projectFolder, file))).toBeTruthy(); + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); }); // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(projectFolder)).toMatchSnapshot(); + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); // Check if the generated webpack configuration matches the snapshot - expect(readFromWebpackConfig(projectFolder)).toMatchSnapshot(); + expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); }); // it("should use sass with postcss in project when selected", async () => { const assetsPath = await uniqueDirectoryForTest(); const { stdout, stderr } = await runPromptWithAnswers( assetsPath, - ["init", ".", defaultProjectName], + ["init", "."], [ `${ENTER}`, `n${ENTER}`, @@ -254,26 +244,25 @@ describe("create-webpack-app cli", () => { expect(stdout).toContain("Project has been initialised with webpack!"); expect(stdout).toContain("webpack.config.js"); - const projectFolder = join(assetsPath, defaultProjectName); // Test files const files = [...defaultTemplateFiles, "postcss.config.js"]; files.forEach((file) => { - expect(existsSync(resolve(projectFolder, file))).toBeTruthy(); + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); }); // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(projectFolder)).toMatchSnapshot(); + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); // Check if the generated webpack configuration matches the snapshot - expect(readFromWebpackConfig(projectFolder)).toMatchSnapshot(); + expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); }); // it("should use mini-css-extract-plugin when selected", async () => { const assetsPath = await uniqueDirectoryForTest(); const { stdout, stderr } = await runPromptWithAnswers( assetsPath, - ["init", ".", defaultProjectName], + ["init", "."], [ `${ENTER}`, `n${ENTER}`, @@ -290,24 +279,23 @@ describe("create-webpack-app cli", () => { expect(stdout).toContain("Project has been initialised with webpack!"); expect(stdout).toContain("webpack.config.js"); - const projectFolder = join(assetsPath, defaultProjectName); // Test files defaultTemplateFiles.forEach((file) => { - expect(existsSync(resolve(projectFolder, file))).toBeTruthy(); + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); }); // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(projectFolder)).toMatchSnapshot(); + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); // Check if the generated webpack configuration matches the snapshot - expect(readFromWebpackConfig(projectFolder)).toMatchSnapshot(); + expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); }); // it("should use sass and css with postcss in project when selected", async () => { const assetsPath = await uniqueDirectoryForTest(); const { stdout, stderr } = await runPromptWithAnswers( assetsPath, - ["init", ".", defaultProjectName], + ["init", "."], [ `${ENTER}`, `n${ENTER}`, @@ -324,26 +312,25 @@ describe("create-webpack-app cli", () => { expect(stdout).toContain("Project has been initialised with webpack!"); expect(stdout).toContain("webpack.config.js"); - const projectFolder = join(assetsPath, defaultProjectName); // Test files const files = [...defaultTemplateFiles, "postcss.config.js"]; files.forEach((file) => { - expect(existsSync(resolve(projectFolder, file))).toBeTruthy(); + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); }); // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(projectFolder)).toMatchSnapshot(); + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); // Check if the generated webpack configuration matches the snapshot - expect(readFromWebpackConfig(projectFolder)).toMatchSnapshot(); + expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); }); // it("should use less in project when selected", async () => { const assetsPath = await uniqueDirectoryForTest(); const { stdout, stderr } = await runPromptWithAnswers( assetsPath, - ["init", ".", defaultProjectName], + ["init", "."], [ `${ENTER}`, `n${ENTER}`, @@ -360,24 +347,23 @@ describe("create-webpack-app cli", () => { expect(stdout).toContain("Project has been initialised with webpack!"); expect(stdout).toContain("webpack.config.js"); - const projectFolder = join(assetsPath, defaultProjectName); // Test files defaultTemplateFiles.forEach((file) => { - expect(existsSync(resolve(projectFolder, file))).toBeTruthy(); + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); }); // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(projectFolder)).toMatchSnapshot(); + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); // Check if the generated webpack configuration matches the snapshot - expect(readFromWebpackConfig(projectFolder)).toMatchSnapshot(); + expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); }); - // + // // it("should use stylus in project when selected", async () => { const assetsPath = await uniqueDirectoryForTest(); const { stdout, stderr } = await runPromptWithAnswers( assetsPath, - ["init", ".", defaultProjectName], + ["init", "."], [ `${ENTER}`, `n${ENTER}`, @@ -394,24 +380,23 @@ describe("create-webpack-app cli", () => { expect(stdout).toContain("Project has been initialised with webpack!"); expect(stdout).toContain("webpack.config.js"); - const projectFolder = join(assetsPath, defaultProjectName); // Test files defaultTemplateFiles.forEach((file) => { - expect(existsSync(resolve(projectFolder, file))).toBeTruthy(); + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); }); // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(projectFolder)).toMatchSnapshot(); + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); // Check if the generated webpack configuration matches the snapshot - expect(readFromWebpackConfig(projectFolder)).toMatchSnapshot(); + expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); }); - // + // // it("should configure WDS as opted", async () => { const assetsPath = await uniqueDirectoryForTest(); const { stdout, stderr } = await runPromptWithAnswers( assetsPath, - ["init", ".", defaultProjectName], + ["init", "."], [ENTER, ENTER, `n${ENTER}`, `n${ENTER}`, ENTER, ENTER], ); @@ -419,25 +404,23 @@ describe("create-webpack-app cli", () => { expect(stdout).toContain("Project has been initialised with webpack!"); expect(stdout).toContain("webpack.config.js"); - const projectFolder = join(assetsPath, defaultProjectName); - // Test files defaultTemplateFiles.forEach((file) => { - expect(existsSync(resolve(projectFolder, file))).toBeTruthy(); + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); }); // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(projectFolder)).toMatchSnapshot(); + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); // Check if the generated webpack configuration matches the snapshot - expect(readFromWebpackConfig(projectFolder)).toMatchSnapshot(); + expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); }); - // + // // it("should use postcss in project when selected", async () => { const assetsPath = await uniqueDirectoryForTest(); const { stdout, stderr } = await runPromptWithAnswers( assetsPath, - ["init", ".", defaultProjectName], + ["init", "."], [ `${ENTER}`, `n${ENTER}`, @@ -453,26 +436,25 @@ describe("create-webpack-app cli", () => { expect(stdout).toContain("Project has been initialised with webpack!"); expect(stdout).toContain("webpack.config.js"); - const projectFolder = join(assetsPath, defaultProjectName); // Test files const files = [...defaultTemplateFiles, "postcss.config.js"]; files.forEach((file) => { - expect(existsSync(resolve(projectFolder, file))).toBeTruthy(); + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); }); // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(projectFolder)).toMatchSnapshot(); + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); // Check if the generated webpack configuration matches the snapshot - expect(readFromWebpackConfig(projectFolder)).toMatchSnapshot(); + expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); }); - + // it("should configure html-webpack-plugin as opted", async () => { const assetsPath = await uniqueDirectoryForTest(); const { stdout, stderr } = await runPromptWithAnswers( assetsPath, - ["init", ".", defaultProjectName], + ["init", "."], [ENTER, `n${ENTER}`, ENTER, `n${ENTER}`, ENTER, ENTER], ); @@ -480,24 +462,23 @@ describe("create-webpack-app cli", () => { expect(stdout).toContain("Project has been initialised with webpack!"); expect(stdout).toContain("webpack.config.js"); - const projectFolder = join(assetsPath, defaultProjectName); // Test files defaultTemplateFiles.forEach((file) => { - expect(existsSync(resolve(projectFolder, file))).toBeTruthy(); + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); }); // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(projectFolder)).toMatchSnapshot(); + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); // Check if the generated webpack configuration matches the snapshot - expect(readFromWebpackConfig(projectFolder)).toMatchSnapshot(); + expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); }); - + // it("should configure workbox-webpack-plugin as opted", async () => { const assetsPath = await uniqueDirectoryForTest(); const { stdout, stderr } = await runPromptWithAnswers( assetsPath, - ["init", ".", defaultProjectName], + ["init", "."], [ENTER, `n${ENTER}`, ENTER, ENTER, ENTER, ENTER], ); @@ -505,19 +486,18 @@ describe("create-webpack-app cli", () => { expect(stdout).toContain("Project has been initialised with webpack!"); expect(stdout).toContain("webpack.config.js"); - const projectFolder = join(assetsPath, defaultProjectName); // Test file defaultTemplateFiles.forEach((file) => { - expect(existsSync(resolve(projectFolder, file))).toBeTruthy(); + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); }); // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(projectFolder)).toMatchSnapshot(); + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); // Check if the generated webpack configuration matches the snapshot - expect(readFromWebpackConfig(projectFolder)).toMatchSnapshot(); + expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); }); - + // it("should throw if the current path is not writable", async () => { if (isWindows) { return; @@ -533,7 +513,7 @@ describe("create-webpack-app cli", () => { expect(stderr).toContain("Failed to initialize the project with webpack!"); expect(exitCode).toBe(2); }); - + // it("should work with 'new' alias", async () => { const assetsPath = await uniqueDirectoryForTest(); const { stdout, stderr } = await run(assetsPath, ["new", "--force"]); @@ -541,16 +521,15 @@ describe("create-webpack-app cli", () => { expect(stdout).toContain("Project has been initialised with webpack!"); expect(stdout).toContain("webpack.config.js"); - const projectFolder = join(assetsPath, defaultProjectName); // Test files defaultTemplateFiles.forEach((file) => { - expect(existsSync(resolve(projectFolder, file))).toBeTruthy(); + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); }); // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(projectFolder)).toMatchSnapshot(); + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); }); - + // it("should work with 'create' alias", async () => { const assetsPath = await uniqueDirectoryForTest(); const { stdout, stderr } = await run(assetsPath, ["create", "--force"]); @@ -558,16 +537,15 @@ describe("create-webpack-app cli", () => { expect(stdout).toContain("Project has been initialised with webpack!"); expect(stdout).toContain("webpack.config.js"); - const projectFolder = join(assetsPath, defaultProjectName); // Test files defaultTemplateFiles.forEach((file) => { - expect(existsSync(resolve(projectFolder, file))).toBeTruthy(); + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); }); // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(projectFolder)).toMatchSnapshot(); + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); }); - + // it("should work with 'c' alias", async () => { const assetsPath = await uniqueDirectoryForTest(); const { stdout, stderr } = await run(assetsPath, ["c", "--force"]); @@ -575,16 +553,15 @@ describe("create-webpack-app cli", () => { expect(stdout).toContain("Project has been initialised with webpack!"); expect(stdout).toContain("webpack.config.js"); - const projectFolder = join(assetsPath, defaultProjectName); // Test files defaultTemplateFiles.forEach((file) => { - expect(existsSync(resolve(projectFolder, file))).toBeTruthy(); + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); }); // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(projectFolder)).toMatchSnapshot(); + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); }); - + // it("should work with 'n' alias", async () => { const assetsPath = await uniqueDirectoryForTest(); const { stdout, stderr } = await run(assetsPath, ["n", "--force"]); @@ -592,60 +569,58 @@ describe("create-webpack-app cli", () => { expect(stdout).toContain("Project has been initialised with webpack!"); expect(stdout).toContain("webpack.config.js"); - const projectFolder = join(assetsPath, defaultProjectName); // Test files defaultTemplateFiles.forEach((file) => { - expect(existsSync(resolve(projectFolder, file))).toBeTruthy(); + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); }); // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(projectFolder)).toMatchSnapshot(); + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); }); - // // - // // // it("recognizes '-t' as an alias for '--template'", async () => { - // // // const assetsPath = await uniqueDirectoryForTest(); - // // // const { stdout, stderr } = await run(assetsPath, ["init", "-t", "default", "--force"]); - // // // - // // // expect(stdout).toContain("Project has been initialised with webpack!"); - // // // expect(stderr).toContain("webpack.config.js"); - // // // - // // // // Test files - // // // defaultTemplateFiles.forEach((file) => { - // // // expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); - // // // }); - // // // - // // // // Check if the generated package.json file content matches the snapshot - // // // expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); - // // // }); - // // // - // // // it("recognizes '-f' as an alias for '--force'", async () => { - // // // const assetsPath = await uniqueDirectoryForTest(); - // // // const { stdout, stderr } = await run(assetsPath, ["init", "-f"]); - // // // - // // // expect(stdout).toContain("Project has been initialised with webpack!"); - // // // expect(stderr).toContain("webpack.config.js"); - // // // - // // // // Test files - // // // defaultTemplateFiles.forEach((file) => { - // // // expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); - // // // }); - // // // - // // // // Check if the generated package.json file content matches the snapshot - // // // expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); - // // // }); - // // // + // // // + // // // // it("recognizes '-t' as an alias for '--template'", async () => { + // // // // const assetsPath = await uniqueDirectoryForTest(); + // // // // const { stdout, stderr } = await run(assetsPath, ["init", "-t", "default", "--force"]); + // // // // + // // // // expect(stdout).toContain("Project has been initialised with webpack!"); + // // // // expect(stderr).toContain("webpack.config.js"); + // // // // + // // // // // Test files + // // // // defaultTemplateFiles.forEach((file) => { + // // // // expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + // // // // }); + // // // // + // // // // // Check if the generated package.json file content matches the snapshot + // // // // expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + // // // // }); + // // // // + // // // // it("recognizes '-f' as an alias for '--force'", async () => { + // // // // const assetsPath = await uniqueDirectoryForTest(); + // // // // const { stdout, stderr } = await run(assetsPath, ["init", "-f"]); + // // // // + // // // // expect(stdout).toContain("Project has been initialised with webpack!"); + // // // // expect(stderr).toContain("webpack.config.js"); + // // // // + // // // // // Test files + // // // // defaultTemplateFiles.forEach((file) => { + // // // // expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + // // // // }); + // // // // + // // // // // Check if the generated package.json file content matches the snapshot + // // // // expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + // // // // }); + // // // // it("uses yarn as the package manager when opted", async () => { const assetsPath = await uniqueDirectoryForTest(); const { stdout, stderr } = await runPromptWithAnswers( assetsPath, - ["init", ".", defaultProjectName], + ["init", "."], [ENTER, `n${ENTER}`, `n${ENTER}`, `n${ENTER}`, ENTER, `${DOWN}${ENTER}`], ); expect(stdout).toContain("Project has been initialised with webpack!"); expect(stdout).toContain("webpack.config.js"); - const projectFolder = join(assetsPath, defaultProjectName); // Test files const files = [ ...defaultTemplateFiles.filter((file) => file !== "package-lock.json"), @@ -653,52 +628,52 @@ describe("create-webpack-app cli", () => { ]; files.forEach((file) => { - expect(existsSync(resolve(projectFolder, file))).toBeTruthy(); + expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); }); // Check if the generated package.json file content matches the snapshot - expect(readFromPkgJSON(projectFolder)).toMatchSnapshot(); + expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); }); - - // // // it("should generate react template with prompt answers", async () => { - // // // const assetsPath = await uniqueDirectoryForTest(); - // // // const { stdout, stderr } = await runPromptWithAnswers( - // // // assetsPath, - // // // ["init"], - // // // [ENTER, `y${ENTER}`, `${DOWN}${ENTER}`, `y${ENTER}`, ENTER, ENTER], - // // // ); - // // // - // // // expect(stdout).toContain("Project has been initialised with webpack!"); - // // // expect(stderr).toContain("webpack.config.js"); - // // // - // // // // Test files - // // // reactTemplateFiles.forEach((file) => { - // // // expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); - // // // }); - // // // - // // // // Check if the generated package.json file content matches the snapshot - // // // expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); - // // // - // // // // Check if the generated webpack configuration matches the snapshot - // // // expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); - // // // }); - // // // - // // // it("should generate react template with --force", async () => { - // // // const assetsPath = await uniqueDirectoryForTest(); - // // // const { stdout, stderr } = await run(assetsPath, ["init", "--template=react", "--force"]); - // // // - // // // expect(stdout).toContain("Project has been initialised with webpack!"); - // // // expect(stderr).toContain("webpack.config.js"); - // // // - // // // // Test files - // // // reactTemplateFiles.forEach((file) => { - // // // expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); - // // // }); - // // // - // // // // Check if the generated package.json file content matches the snapshot - // // // expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); - // // // - // // // // Check if the generated webpack configuration matches the snapshot - // // // expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); - // // // }); + // + // // // // it("should generate react template with prompt answers", async () => { + // // // // const assetsPath = await uniqueDirectoryForTest(); + // // // // const { stdout, stderr } = await runPromptWithAnswers( + // // // // assetsPath, + // // // // ["init"], + // // // // [ENTER, `y${ENTER}`, `${DOWN}${ENTER}`, `y${ENTER}`, ENTER, ENTER], + // // // // ); + // // // // + // // // // expect(stdout).toContain("Project has been initialised with webpack!"); + // // // // expect(stderr).toContain("webpack.config.js"); + // // // // + // // // // // Test files + // // // // reactTemplateFiles.forEach((file) => { + // // // // expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + // // // // }); + // // // // + // // // // // Check if the generated package.json file content matches the snapshot + // // // // expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + // // // // + // // // // // Check if the generated webpack configuration matches the snapshot + // // // // expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); + // // // // }); + // // // // + // // // // it("should generate react template with --force", async () => { + // // // // const assetsPath = await uniqueDirectoryForTest(); + // // // // const { stdout, stderr } = await run(assetsPath, ["init", "--template=react", "--force"]); + // // // // + // // // // expect(stdout).toContain("Project has been initialised with webpack!"); + // // // // expect(stderr).toContain("webpack.config.js"); + // // // // + // // // // // Test files + // // // // reactTemplateFiles.forEach((file) => { + // // // // expect(existsSync(resolve(assetsPath, file))).toBeTruthy(); + // // // // }); + // // // // + // // // // // Check if the generated package.json file content matches the snapshot + // // // // expect(readFromPkgJSON(assetsPath)).toMatchSnapshot(); + // // // // + // // // // // Check if the generated webpack configuration matches the snapshot + // // // // expect(readFromWebpackConfig(assetsPath)).toMatchSnapshot(); + // // // // }); }); From ff53355191232846b178f43ee716457d0652e8eb Mon Sep 17 00:00:00 2001 From: Uzair Date: Fri, 5 Jul 2024 00:51:09 +0530 Subject: [PATCH 52/81] chore: remove "uzair" as word from cspell.json --- .cspell.json | 1 - 1 file changed, 1 deletion(-) diff --git a/.cspell.json b/.cspell.json index 4afe60b55e2..156c2be7a33 100644 --- a/.cspell.json +++ b/.cspell.json @@ -92,7 +92,6 @@ "testname", "Tobio", "tsbuildinfo", - "uzair", "typeahead", "wagoid", "webassembly", From 22873e8ea2b6ebe3e8cb20d16aa0a8d341a8fbc4 Mon Sep 17 00:00:00 2001 From: Uzair Date: Sun, 7 Jul 2024 13:56:19 +0530 Subject: [PATCH 53/81] chore: remove test/create-webpack-app from .eslintignore --- .eslintignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.eslintignore b/.eslintignore index d2f46930b9d..91718ffa7a2 100644 --- a/.eslintignore +++ b/.eslintignore @@ -11,4 +11,3 @@ test/build/config/error-commonjs/syntax-error.js test/build/config/error-mjs/syntax-error.mjs test/build/config/error-array/webpack.config.js test/configtest/with-config-path/syntax-error.config.js -test/create-webpack-app/** From fdd61df30084c718ba210ce4fd19fd03db7c2371 Mon Sep 17 00:00:00 2001 From: Uzair Date: Sun, 7 Jul 2024 14:01:15 +0530 Subject: [PATCH 54/81] perf: change generator conditional flows and dependencies attached - change dependencies for each prompt according to init command reference - change default for --force flag to appropriate values in index.ts as well as plopfile.ts - change conditional file generation for javascript langType --- packages/create-webpack-app/src/index.ts | 6 +++--- packages/create-webpack-app/src/plopfile.ts | 16 ++++++++++------ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/packages/create-webpack-app/src/index.ts b/packages/create-webpack-app/src/index.ts index 6809abf57a7..f8bdf6f182d 100644 --- a/packages/create-webpack-app/src/index.ts +++ b/packages/create-webpack-app/src/index.ts @@ -18,9 +18,9 @@ const defaultValues = { devServer: true, htmlWebpackPlugin: true, workboxWebpackPlugin: true, - cssType: "CSS only", - isCSS: true, - isPostCSS: true, + cssType: "none", + isCSS: false, + isPostCSS: false, extractPlugin: "No", packageManager: "npm", }, diff --git a/packages/create-webpack-app/src/plopfile.ts b/packages/create-webpack-app/src/plopfile.ts index 6dbee1542bc..0560e509324 100644 --- a/packages/create-webpack-app/src/plopfile.ts +++ b/packages/create-webpack-app/src/plopfile.ts @@ -107,7 +107,7 @@ export default function (plop: NodePlopAPI) { name: "cssType", message: "Which of the following CSS solution do you want to use?", choices: ["none", "CSS only", "SASS", "LESS", "Stylus"], - default: "Css only", + default: "none", filter: (input, answers) => { if (input === "none") { answers.isCSS = false; @@ -120,7 +120,7 @@ export default function (plop: NodePlopAPI) { answers.isCSS = true; break; case "SASS": - dependencies.push("sass-loader", "node-sass"); + dependencies.push("sass-loader", "sass"); break; case "LESS": dependencies.push("less-loader", "less"); @@ -145,7 +145,7 @@ export default function (plop: NodePlopAPI) { type: "confirm", name: "isPostCSS", message: "Do you want to use PostCSS in your project?", - default: true, + default: (answers: Answers) => answers.cssType == "CSS only", }, { type: "list", @@ -177,8 +177,12 @@ export default function (plop: NodePlopAPI) { actions: function (answers: Answers) { // setting some default values based on the answers answers.entryPoint = answers.langType === "Typescript" ? "./src/index.ts" : "./src/index.js"; - answers.jsConfig = null; - answers.jsConfig = answers.langType === "Typescript" ? "tsconfig.json" : "babel.config.json"; + answers.jsConfig = + answers.langType === "Typescript" + ? "tsconfig.json" + : answers.langType === "ES6" + ? "babel.config.json" + : null; answers.cssConfig = answers.isPostCSS ? "postcss.config.js" : null; // adding some dependencies based on the answers if (answers.devServer) { @@ -191,7 +195,7 @@ export default function (plop: NodePlopAPI) { dependencies.push("workbox-webpack-plugin"); } if (answers.isPostCSS) { - dependencies.push("postcss-loader", "autoprefixer"); + dependencies.push("postcss-loader", "postcss", "autoprefixer"); } const actions: ActionType[] = [ From ea92e56708b98755fb12fd670f1f3eccb929bfd1 Mon Sep 17 00:00:00 2001 From: Uzair Date: Sun, 7 Jul 2024 14:04:00 +0530 Subject: [PATCH 55/81] refactor: remove redundant code from test.utils and extend from utils/test-utils.js - earlier this file was an exact copy with a line changed - now it imports all the utility function and only extends run**() functions with path value pointing to create-webpack-cli --- test/create-webpack-app/test.utils.js | 217 +------------------------- 1 file changed, 7 insertions(+), 210 deletions(-) diff --git a/test/create-webpack-app/test.utils.js b/test/create-webpack-app/test.utils.js index ab6960494eb..9eec8d5b2f3 100644 --- a/test/create-webpack-app/test.utils.js +++ b/test/create-webpack-app/test.utils.js @@ -1,41 +1,17 @@ /* eslint-disable node/no-unpublished-require */ - "use strict"; -const os = require("os"); -const stripAnsi = require("strip-ansi"); +const testUtilsPkg = require("../utils/test-utils.js"); +const { processKill } = testUtilsPkg; const path = require("path"); -const fs = require("fs"); const execa = require("execa"); -const internalIp = require("internal-ip"); -const { exec } = require("child_process"); const { node: execaNode } = execa; -const { Writable } = require("readable-stream"); +const stripAnsi = require("strip-ansi"); const concat = require("concat-stream"); -const { cli } = require("webpack"); +const { Writable } = require("readable-stream"); const CREATE_WEBPACK_PATH = path.resolve(__dirname, "../../packages/create-webpack-app/bin/cli.js"); const ENABLE_LOG_COMPILATION = process.env.ENABLE_PIPE || false; -const isWindows = process.platform === "win32"; - -const hyphenToUpperCase = (name) => { - if (!name) { - return name; - } - - return name.replace(/-([a-z])/g, function (g) { - return g[1].toUpperCase(); - }); -}; - -const processKill = (process) => { - if (isWindows) { - exec("taskkill /pid " + process.pid + " /T /F"); - } else { - process.kill(); - } -}; - /** * Webpack CLI test runner. * @@ -149,8 +125,7 @@ const runWatch = (cwd, args = [], options = {}) => { }); }); }; - -/** +/* * runPromptWithAnswers * @param {string} location location of current working directory * @param {string[]} args CLI args to pass in @@ -238,189 +213,11 @@ const runPromptWithAnswers = (location, args, answers) => { }); }; -const normalizeVersions = (output) => { - return output.replace( - /(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?/gi, - "x.x.x", - ); -}; - -const normalizeCwd = (output) => { - return output - .replace(/\\/g, "/") - .replace(new RegExp(process.cwd().replace(/\\/g, "/"), "g"), ""); -}; - -const normalizeError = (output) => { - return output - .replace(/SyntaxError: .+/, "SyntaxError: ") - .replace(/\s+at .+(}|\)|\d)/gs, "\n at stack"); -}; - -const normalizeStdout = (stdout) => { - if (typeof stdout !== "string") { - return stdout; - } - - if (stdout.length === 0) { - return stdout; - } - - let normalizedStdout = stripAnsi(stdout); - normalizedStdout = normalizeCwd(normalizedStdout); - normalizedStdout = normalizeVersions(normalizedStdout); - normalizedStdout = normalizeError(normalizedStdout); - - return normalizedStdout; -}; - -const normalizeStderr = (stderr) => { - if (typeof stderr !== "string") { - return stderr; - } - - if (stderr.length === 0) { - return stderr; - } - - let normalizedStderr = stripAnsi(stderr); - normalizedStderr = normalizeCwd(normalizedStderr); - - const networkIPv4 = internalIp.v4.sync(); - - if (networkIPv4) { - normalizedStderr = normalizedStderr.replace(new RegExp(networkIPv4, "g"), ""); - } - - const networkIPv6 = internalIp.v6.sync(); - - if (networkIPv6) { - normalizedStderr = normalizedStderr.replace(new RegExp(networkIPv6, "g"), ""); - } - - normalizedStderr = normalizedStderr.replace(/:[0-9]+\//g, ":/"); - - if (!/On Your Network \(IPv6\)/.test(stderr)) { - // Github Actions doesn't' support IPv6 on ubuntu in some cases - normalizedStderr = normalizedStderr.split("\n"); - - const ipv4MessageIndex = normalizedStderr.findIndex((item) => - /On Your Network \(IPv4\)/.test(item), - ); - - if (ipv4MessageIndex !== -1) { - normalizedStderr.splice( - ipv4MessageIndex + 1, - 0, - " [webpack-dev-server] On Your Network (IPv6): http://[]:/", - ); - } - - normalizedStderr = normalizedStderr.join("\n"); - } - - // TODO remove me after drop old Node.js versions and update deps - // Suppress warnings for Node.js version >= v22 - // [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead. - if (process.version.startsWith("v22")) { - normalizedStderr = normalizedStderr - .split("\n") - .filter((line) => { - return ( - !line.includes("DeprecationWarning: The `punycode` module is deprecated.") && - !line.includes("Use `node --trace-deprecation ...`") - ); - }) - .join("\n"); - } - - // the warning below is causing CI failure on some jobs - if (/Gracefully shutting down/.test(stderr)) { - normalizedStderr = normalizedStderr.replace( - "\n [webpack-dev-server] Gracefully shutting down. To force exit, press ^C again. Please wait...", - "", - ); - } - - normalizedStderr = normalizeVersions(normalizedStderr); - normalizedStderr = normalizeError(normalizedStderr); - - return normalizedStderr; -}; - -const getWebpackCliArguments = (startWith) => { - if (typeof startWith === "undefined") { - return cli.getArguments(); - } - - const result = {}; - - for (const [name, value] of Object.entries(cli.getArguments())) { - if (name.startsWith(startWith)) { - result[name] = value; - } - } - - return result; -}; - -const readFile = (path, options = {}) => - new Promise((resolve, reject) => { - fs.readFile(path, options, (err, stats) => { - if (err) { - reject(err); - } - resolve(stats); - }); - }); - -const readdir = (path) => - new Promise((resolve, reject) => { - fs.readdir(path, (err, stats) => { - if (err) { - reject(err); - } - resolve(stats); - }); - }); - -// cSpell:ignore Symbhas, ABCDEFGHNR, Vfgcti -const urlAlphabet = "ModuleSymbhasOwnPr-0123456789ABCDEFGHNRVfgctiUvz_KqYTJkLxpZXIjQW"; - -const uuid = (size = 21) => { - let id = ""; - let i = size; - - while (i--) { - // `| 0` is more compact and faster than `Math.floor()`. - id += urlAlphabet[(Math.random() * 64) | 0]; - } - - return id; -}; - -const uniqueDirectoryForTest = async () => { - const result = path.resolve(os.tmpdir(), uuid()); - - if (!fs.existsSync(result)) { - fs.mkdirSync(result); - } - - return result; -}; - module.exports = { + ...testUtilsPkg, + createProcess, run, runAndGetProcess, runWatch, runPromptWithAnswers, - isWindows, - normalizeStderr, - normalizeStdout, - uniqueDirectoryForTest, - readFile, - readdir, - hyphenToUpperCase, - processKill, - getWebpackCliArguments, }; From 3fb7b98b18db0f2d968c73aea234870594c5d1ce Mon Sep 17 00:00:00 2001 From: Uzair Date: Sun, 7 Jul 2024 14:05:09 +0530 Subject: [PATCH 56/81] test: update snapshots and test according to previous two commits - change snapshots according to new dependencies and defaults - change the import in create-webpack-app.test.js --- .../create-webpack-app.test.js.snap.webpack5 | 153 +----------------- .../create-webpack-app.test.js | 60 ++++--- 2 files changed, 34 insertions(+), 179 deletions(-) diff --git a/test/create-webpack-app/__snapshots__/create-webpack-app.test.js.snap.webpack5 b/test/create-webpack-app/__snapshots__/create-webpack-app.test.js.snap.webpack5 index e50fdd986cb..10a709d315b 100644 --- a/test/create-webpack-app/__snapshots__/create-webpack-app.test.js.snap.webpack5 +++ b/test/create-webpack-app/__snapshots__/create-webpack-app.test.js.snap.webpack5 @@ -72,10 +72,8 @@ exports[`create-webpack-app cli should configure assets modules by default 1`] = { "description": "My webpack project", "devDependencies": { - "autoprefixer": "x.x.x", "html-loader": "x.x.x", "html-webpack-plugin": "x.x.x", - "postcss-loader": "x.x.x", "webpack": "x.x.x", "webpack-cli": "x.x.x", "webpack-dev-server": "x.x.x", @@ -103,10 +101,6 @@ const WorkboxWebpackPlugin = require('workbox-webpack-plugin'); const isProduction = process.env.NODE_ENV === 'production'; -const stylesHandler = 'style-loader'; - - - const config = { entry: './src/index.js', output: { @@ -126,10 +120,6 @@ const config = { ], module: { rules: [ - { - test: /\\.css$/i, - use: [stylesHandler, 'css-loader', 'postcss-loader'], - }, { test: /\\.(eot|svg|ttf|woff|woff2|png|jpg|gif)$/i, type: 'asset', @@ -384,10 +374,8 @@ exports[`create-webpack-app cli should generate default project when nothing is { "description": "My webpack project", "devDependencies": { - "autoprefixer": "x.x.x", "html-loader": "x.x.x", "html-webpack-plugin": "x.x.x", - "postcss-loader": "x.x.x", "webpack": "x.x.x", "webpack-cli": "x.x.x", "webpack-dev-server": "x.x.x", @@ -409,10 +397,8 @@ exports[`create-webpack-app cli should generate folders if non existing generati { "description": "My webpack project", "devDependencies": { - "autoprefixer": "x.x.x", "html-loader": "x.x.x", "html-webpack-plugin": "x.x.x", - "postcss-loader": "x.x.x", "webpack": "x.x.x", "webpack-cli": "x.x.x", "webpack-dev-server": "x.x.x", @@ -434,10 +420,8 @@ exports[`create-webpack-app cli should generate project when generationPath is s { "description": "My webpack project", "devDependencies": { - "autoprefixer": "x.x.x", "html-loader": "x.x.x", "html-webpack-plugin": "x.x.x", - "postcss-loader": "x.x.x", "webpack": "x.x.x", "webpack-cli": "x.x.x", "webpack-dev-server": "x.x.x", @@ -606,7 +590,7 @@ exports[`create-webpack-app cli should use mini-css-extract-plugin when selected "description": "My webpack project", "devDependencies": { "css-loader": "x.x.x", - "node-sass": "x.x.x", + "sass": "x.x.x", "sass-loader": "x.x.x", "style-loader": "x.x.x", "webpack": "x.x.x", @@ -681,6 +665,7 @@ exports[`create-webpack-app cli should use postcss in project when selected 1`] "devDependencies": { "autoprefixer": "x.x.x", "css-loader": "x.x.x", + "postcss": "x.x.x", "postcss-loader": "x.x.x", "style-loader": "x.x.x", "webpack": "x.x.x", @@ -755,8 +740,9 @@ exports[`create-webpack-app cli should use sass and css with postcss in project "devDependencies": { "autoprefixer": "x.x.x", "css-loader": "x.x.x", - "node-sass": "x.x.x", + "postcss": "x.x.x", "postcss-loader": "x.x.x", + "sass": "x.x.x", "sass-loader": "x.x.x", "style-loader": "x.x.x", "webpack": "x.x.x", @@ -834,7 +820,7 @@ exports[`create-webpack-app cli should use sass in project when selected 1`] = ` "description": "My webpack project", "devDependencies": { "css-loader": "x.x.x", - "node-sass": "x.x.x", + "sass": "x.x.x", "sass-loader": "x.x.x", "style-loader": "x.x.x", "webpack": "x.x.x", @@ -909,8 +895,9 @@ exports[`create-webpack-app cli should use sass with postcss in project when sel "devDependencies": { "autoprefixer": "x.x.x", "css-loader": "x.x.x", - "node-sass": "x.x.x", + "postcss": "x.x.x", "postcss-loader": "x.x.x", + "sass": "x.x.x", "sass-loader": "x.x.x", "style-loader": "x.x.x", "webpack": "x.x.x", @@ -1057,10 +1044,8 @@ exports[`create-webpack-app cli should work with 'c' alias 1`] = ` { "description": "My webpack project", "devDependencies": { - "autoprefixer": "x.x.x", "html-loader": "x.x.x", "html-webpack-plugin": "x.x.x", - "postcss-loader": "x.x.x", "webpack": "x.x.x", "webpack-cli": "x.x.x", "webpack-dev-server": "x.x.x", @@ -1082,10 +1067,8 @@ exports[`create-webpack-app cli should work with 'create' alias 1`] = ` { "description": "My webpack project", "devDependencies": { - "autoprefixer": "x.x.x", "html-loader": "x.x.x", "html-webpack-plugin": "x.x.x", - "postcss-loader": "x.x.x", "webpack": "x.x.x", "webpack-cli": "x.x.x", "webpack-dev-server": "x.x.x", @@ -1107,10 +1090,8 @@ exports[`create-webpack-app cli should work with 'n' alias 1`] = ` { "description": "My webpack project", "devDependencies": { - "autoprefixer": "x.x.x", "html-loader": "x.x.x", "html-webpack-plugin": "x.x.x", - "postcss-loader": "x.x.x", "webpack": "x.x.x", "webpack-cli": "x.x.x", "webpack-dev-server": "x.x.x", @@ -1132,10 +1113,8 @@ exports[`create-webpack-app cli should work with 'new' alias 1`] = ` { "description": "My webpack project", "devDependencies": { - "autoprefixer": "x.x.x", "html-loader": "x.x.x", "html-webpack-plugin": "x.x.x", - "postcss-loader": "x.x.x", "webpack": "x.x.x", "webpack-cli": "x.x.x", "webpack-dev-server": "x.x.x", @@ -1170,121 +1149,3 @@ exports[`create-webpack-app cli uses yarn as the package manager when opted 1`] "version": "1.0.0", } `; - -exports[`should configure assets modules by default 1`] = ` -{ - "description": "My webpack project", - "devDependencies": { - "autoprefixer": "x.x.x", - "html-loader": "x.x.x", - "html-webpack-plugin": "x.x.x", - "postcss-loader": "x.x.x", - "webpack": "x.x.x", - "webpack-cli": "x.x.x", - "webpack-dev-server": "x.x.x", - "workbox-webpack-plugin": "x.x.x", - }, - "name": "webpack-project", - "scripts": { - "build": "webpack --mode=production --node-env=production", - "build:dev": "webpack --mode=development", - "build:prod": "webpack --mode=production --node-env=production", - "serve": "webpack serve", - "watch": "webpack --watch", - }, - "version": "1.0.0", -} -`; - -exports[`should configure assets modules by default 2`] = ` -"// Generated using webpack-cli https://github.com/webpack/webpack-cli - -const path = require('path'); -const HtmlWebpackPlugin = require('html-webpack-plugin'); -const WorkboxWebpackPlugin = require('workbox-webpack-plugin'); - -const isProduction = process.env.NODE_ENV === 'production'; - - -const stylesHandler = 'style-loader'; - - - -const config = { - entry: './src/index.js', - output: { - path: path.resolve(__dirname, 'dist'), - }, - devServer: { - open: true, - host: 'localhost', - }, - plugins: [ - new HtmlWebpackPlugin({ - template: 'index.html', - }), - - // Add your plugins here - // Learn more about plugins from https://webpack.js.org/configuration/plugins/ - ], - module: { - rules: [ - { - test: /\\.css$/i, - use: [stylesHandler, 'css-loader', 'postcss-loader'], - }, - { - test: /\\.(eot|svg|ttf|woff|woff2|png|jpg|gif)$/i, - type: 'asset', - }, - - { - test: /\\.html$/i, - use: ['html-loader'], - }, - - // Add your rules for custom modules here - // Learn more about loaders from https://webpack.js.org/loaders/ - ], - }, -}; - -module.exports = () => { - if (isProduction) { - config.mode = 'production'; - - - config.plugins.push(new WorkboxWebpackPlugin.GenerateSW()); - - } else { - config.mode = 'development'; - } - return config; -}; -" -`; - -exports[`should generate folders if non existing generation path is given 1`] = ` -{ - "description": "My webpack project", - "devDependencies": { - "autoprefixer": "x.x.x", - "html-loader": "x.x.x", - "html-webpack-plugin": "x.x.x", - "postcss-loader": "x.x.x", - "webpack": "x.x.x", - "webpack-cli": "x.x.x", - "webpack-dev-server": "x.x.x", - "workbox-webpack-plugin": "x.x.x", - }, - "name": "webpack-project", - "scripts": { - "build": "webpack --mode=production --node-env=production", - "build:dev": "webpack --mode=development", - "build:prod": "webpack --mode=production --node-env=production", - "serve": "webpack serve", - "watch": "webpack --watch", - }, - "version": "1.0.0", -} -`; diff --git a/test/create-webpack-app/create-webpack-app.test.js b/test/create-webpack-app/create-webpack-app.test.js index e5f12d1297b..114a7cf0d94 100644 --- a/test/create-webpack-app/create-webpack-app.test.js +++ b/test/create-webpack-app/create-webpack-app.test.js @@ -2,8 +2,7 @@ const os = require("os"); const path = require("path"); const { mkdirSync, existsSync, readFileSync } = require("fs"); const { join, resolve } = require("path"); -const { isWindows, run, runPromptWithAnswers, uniqueDirectoryForTest } = require("./test.utils"); -const { profile, profileEnd, Console } = require("console"); +const { isWindows, run, runPromptWithAnswers, uniqueDirectoryForTest } = require("./test.utils.js"); jest.setTimeout(480000); @@ -41,10 +40,6 @@ const readFromPkgJSON = (path) => { // Helper to read from webpack.config.js in a given path const readFromWebpackConfig = (path) => readFileSync(join(path, "webpack.config.js"), "utf8"); -afterEach(() => { - jest.useRealTimers(); - jest.clearAllTimers(); -}); describe("create-webpack-app cli", () => { it("should generate default project when nothing is passed", async () => { @@ -53,7 +48,6 @@ describe("create-webpack-app cli", () => { expect(stdout).toContain("Project has been initialised with webpack!"); expect(stdout).toContain("webpack.config.js"); - // Test files defaultTemplateFiles.forEach((file) => { expect(existsSync(assetsPath, file)).toBeTruthy(); @@ -65,7 +59,7 @@ describe("create-webpack-app cli", () => { it("should generate project when generationPath is supplied", async () => { const assetsPath = await uniqueDirectoryForTest(); - const { stdout, stderr } = await run(__dirname, ["init", assetsPath, "--force"]); + const { stdout } = await run(__dirname, ["init", assetsPath, "--force"]); expect(stdout).toContain("Project has been initialised with webpack!"); // expect(stderr).toContain("webpack.config.js"); @@ -80,7 +74,7 @@ describe("create-webpack-app cli", () => { // it("should generate folders if non existing generation path is given", async () => { const assetsPath = path.resolve(os.tmpdir(), Date.now().toString()); - const { stdout, stderr } = await run(__dirname, ["init", assetsPath, "--force"]); + const { stdout } = await run(__dirname, ["init", assetsPath, "--force"]); expect(stdout).toContain("Project has been initialised with webpack!"); @@ -97,7 +91,7 @@ describe("create-webpack-app cli", () => { // // it("should configure assets modules by default", async () => { const assetsPath = await uniqueDirectoryForTest(); - const { stdout, stderr } = await run(assetsPath, ["init", "--force"]); + const { stdout } = await run(assetsPath, ["init", "--force"]); expect(stdout).toContain("Project has been initialised with webpack!"); expect(stdout).toContain("webpack.config.js"); @@ -115,7 +109,7 @@ describe("create-webpack-app cli", () => { // // // // // it("should ask question when wrong template is supplied", async () => { // // // const assetsPath = await uniqueDirectoryForTest(); - // // // const { stdout, stderr } = await runPromptWithAnswers( + // // // const { stdout } = await runPromptWithAnswers( // // // assetsPath, // // // ["init", "--force", "--template=apple"], // // // [`${ENTER}`], @@ -136,7 +130,7 @@ describe("create-webpack-app cli", () => { // // // it("should generate typescript project correctly", async () => { const assetsPath = await uniqueDirectoryForTest(); - const { stdout, stderr } = await runPromptWithAnswers( + const { stdout } = await runPromptWithAnswers( assetsPath, ["init", "."], [`${DOWN}${DOWN}${ENTER}`, `n${ENTER}`, `n${ENTER}`, `n${ENTER}`, ENTER, ENTER], @@ -166,7 +160,7 @@ describe("create-webpack-app cli", () => { // // it("should generate ES6 project correctly", async () => { const assetsPath = await uniqueDirectoryForTest(); - const { stdout, stderr } = await runPromptWithAnswers( + const { stdout } = await runPromptWithAnswers( assetsPath, ["init", "."], [`${DOWN}${ENTER}`, `n${ENTER}`, `n${ENTER}`, `n${ENTER}`, ENTER, ENTER], @@ -192,7 +186,7 @@ describe("create-webpack-app cli", () => { // it("should use sass in project when selected", async () => { const assetsPath = await uniqueDirectoryForTest(); - const { stdout, stderr } = await runPromptWithAnswers( + const { stdout } = await runPromptWithAnswers( assetsPath, ["init", "."], [ @@ -225,7 +219,7 @@ describe("create-webpack-app cli", () => { // it("should use sass with postcss in project when selected", async () => { const assetsPath = await uniqueDirectoryForTest(); - const { stdout, stderr } = await runPromptWithAnswers( + const { stdout } = await runPromptWithAnswers( assetsPath, ["init", "."], [ @@ -260,7 +254,7 @@ describe("create-webpack-app cli", () => { // it("should use mini-css-extract-plugin when selected", async () => { const assetsPath = await uniqueDirectoryForTest(); - const { stdout, stderr } = await runPromptWithAnswers( + const { stdout } = await runPromptWithAnswers( assetsPath, ["init", "."], [ @@ -293,7 +287,7 @@ describe("create-webpack-app cli", () => { // it("should use sass and css with postcss in project when selected", async () => { const assetsPath = await uniqueDirectoryForTest(); - const { stdout, stderr } = await runPromptWithAnswers( + const { stdout } = await runPromptWithAnswers( assetsPath, ["init", "."], [ @@ -328,7 +322,7 @@ describe("create-webpack-app cli", () => { // it("should use less in project when selected", async () => { const assetsPath = await uniqueDirectoryForTest(); - const { stdout, stderr } = await runPromptWithAnswers( + const { stdout } = await runPromptWithAnswers( assetsPath, ["init", "."], [ @@ -361,7 +355,7 @@ describe("create-webpack-app cli", () => { // // it("should use stylus in project when selected", async () => { const assetsPath = await uniqueDirectoryForTest(); - const { stdout, stderr } = await runPromptWithAnswers( + const { stdout } = await runPromptWithAnswers( assetsPath, ["init", "."], [ @@ -394,7 +388,7 @@ describe("create-webpack-app cli", () => { // // it("should configure WDS as opted", async () => { const assetsPath = await uniqueDirectoryForTest(); - const { stdout, stderr } = await runPromptWithAnswers( + const { stdout } = await runPromptWithAnswers( assetsPath, ["init", "."], [ENTER, ENTER, `n${ENTER}`, `n${ENTER}`, ENTER, ENTER], @@ -418,7 +412,7 @@ describe("create-webpack-app cli", () => { // // it("should use postcss in project when selected", async () => { const assetsPath = await uniqueDirectoryForTest(); - const { stdout, stderr } = await runPromptWithAnswers( + const { stdout } = await runPromptWithAnswers( assetsPath, ["init", "."], [ @@ -452,7 +446,7 @@ describe("create-webpack-app cli", () => { // it("should configure html-webpack-plugin as opted", async () => { const assetsPath = await uniqueDirectoryForTest(); - const { stdout, stderr } = await runPromptWithAnswers( + const { stdout } = await runPromptWithAnswers( assetsPath, ["init", "."], [ENTER, `n${ENTER}`, ENTER, `n${ENTER}`, ENTER, ENTER], @@ -476,7 +470,7 @@ describe("create-webpack-app cli", () => { // it("should configure workbox-webpack-plugin as opted", async () => { const assetsPath = await uniqueDirectoryForTest(); - const { stdout, stderr } = await runPromptWithAnswers( + const { stdout } = await runPromptWithAnswers( assetsPath, ["init", "."], [ENTER, `n${ENTER}`, ENTER, ENTER, ENTER, ENTER], @@ -507,7 +501,7 @@ describe("create-webpack-app cli", () => { const projectPath = join(assetsPath, "non-writable-path"); mkdirSync(projectPath, 0o500); - const { exitCode, stderr, stdout } = await run(projectPath, ["init", "my-app", "--force"], { + const { exitCode, stderr } = await run(projectPath, ["init", "my-app", "--force"], { reject: false, }); expect(stderr).toContain("Failed to initialize the project with webpack!"); @@ -516,7 +510,7 @@ describe("create-webpack-app cli", () => { // it("should work with 'new' alias", async () => { const assetsPath = await uniqueDirectoryForTest(); - const { stdout, stderr } = await run(assetsPath, ["new", "--force"]); + const { stdout } = await run(assetsPath, ["new", "--force"]); expect(stdout).toContain("Project has been initialised with webpack!"); expect(stdout).toContain("webpack.config.js"); @@ -532,7 +526,7 @@ describe("create-webpack-app cli", () => { // it("should work with 'create' alias", async () => { const assetsPath = await uniqueDirectoryForTest(); - const { stdout, stderr } = await run(assetsPath, ["create", "--force"]); + const { stdout } = await run(assetsPath, ["create", "--force"]); expect(stdout).toContain("Project has been initialised with webpack!"); expect(stdout).toContain("webpack.config.js"); @@ -548,7 +542,7 @@ describe("create-webpack-app cli", () => { // it("should work with 'c' alias", async () => { const assetsPath = await uniqueDirectoryForTest(); - const { stdout, stderr } = await run(assetsPath, ["c", "--force"]); + const { stdout } = await run(assetsPath, ["c", "--force"]); expect(stdout).toContain("Project has been initialised with webpack!"); expect(stdout).toContain("webpack.config.js"); @@ -564,7 +558,7 @@ describe("create-webpack-app cli", () => { // it("should work with 'n' alias", async () => { const assetsPath = await uniqueDirectoryForTest(); - const { stdout, stderr } = await run(assetsPath, ["n", "--force"]); + const { stdout } = await run(assetsPath, ["n", "--force"]); expect(stdout).toContain("Project has been initialised with webpack!"); expect(stdout).toContain("webpack.config.js"); @@ -580,7 +574,7 @@ describe("create-webpack-app cli", () => { // // // // // // // it("recognizes '-t' as an alias for '--template'", async () => { // // // // const assetsPath = await uniqueDirectoryForTest(); - // // // // const { stdout, stderr } = await run(assetsPath, ["init", "-t", "default", "--force"]); + // // // // const { stdout } = await run(assetsPath, ["init", "-t", "default", "--force"]); // // // // // // // // expect(stdout).toContain("Project has been initialised with webpack!"); // // // // expect(stderr).toContain("webpack.config.js"); @@ -596,7 +590,7 @@ describe("create-webpack-app cli", () => { // // // // // // // // it("recognizes '-f' as an alias for '--force'", async () => { // // // // const assetsPath = await uniqueDirectoryForTest(); - // // // // const { stdout, stderr } = await run(assetsPath, ["init", "-f"]); + // // // // const { stdout } = await run(assetsPath, ["init", "-f"]); // // // // // // // // expect(stdout).toContain("Project has been initialised with webpack!"); // // // // expect(stderr).toContain("webpack.config.js"); @@ -612,7 +606,7 @@ describe("create-webpack-app cli", () => { // // // // it("uses yarn as the package manager when opted", async () => { const assetsPath = await uniqueDirectoryForTest(); - const { stdout, stderr } = await runPromptWithAnswers( + const { stdout } = await runPromptWithAnswers( assetsPath, ["init", "."], [ENTER, `n${ENTER}`, `n${ENTER}`, `n${ENTER}`, ENTER, `${DOWN}${ENTER}`], @@ -637,7 +631,7 @@ describe("create-webpack-app cli", () => { // // // // // it("should generate react template with prompt answers", async () => { // // // // const assetsPath = await uniqueDirectoryForTest(); - // // // // const { stdout, stderr } = await runPromptWithAnswers( + // // // // const { stdout } = await runPromptWithAnswers( // // // // assetsPath, // // // // ["init"], // // // // [ENTER, `y${ENTER}`, `${DOWN}${ENTER}`, `y${ENTER}`, ENTER, ENTER], @@ -660,7 +654,7 @@ describe("create-webpack-app cli", () => { // // // // // // // // it("should generate react template with --force", async () => { // // // // const assetsPath = await uniqueDirectoryForTest(); - // // // // const { stdout, stderr } = await run(assetsPath, ["init", "--template=react", "--force"]); + // // // // const { stdout } = await run(assetsPath, ["init", "--template=react", "--force"]); // // // // // // // // expect(stdout).toContain("Project has been initialised with webpack!"); // // // // expect(stderr).toContain("webpack.config.js"); From 2c5bfb3f123db7e883d5e5458ca07730ed572791 Mon Sep 17 00:00:00 2001 From: Uzair Khan Date: Sun, 25 Aug 2024 08:48:23 +0530 Subject: [PATCH 57/81] feat: loader and plugin commands, more templates (#4225) Co-authored-by: Anshuman Verma --- .cspell.json | 4 +- packages/create-webpack-app/README.md | 9 +- packages/create-webpack-app/package.json | 1 + .../src/generators/default.ts | 1 - .../src/generators/init/default.ts | 197 ++ .../src/generators/init/react.ts | 248 ++ .../src/generators/init/svelte.ts | 213 ++ .../src/generators/init/vue.ts | 248 ++ .../src/generators/loader/default.ts | 105 + .../src/generators/plugin/default.ts | 95 + packages/create-webpack-app/src/index.ts | 169 +- packages/create-webpack-app/src/plopfile.ts | 261 +- .../create-webpack-app/src/utils/logger.ts | 19 +- .../src/utils/pkgInstallAction.ts | 57 + .../init/default/{README.md => README.md.tpl} | 2 +- .../babel.config.json.tpl} | 0 .../postcss.config.js.tpl} | 0 .../templates/init/default/src/index.js.tpl | 1 + .../templates/init/default/src/index.ts.tpl | 1 + .../tsconfig.json.tpl} | 0 .../templates/init/react/README.md.tpl | 15 + .../templates/init/react/index.html.tpl | 2 +- .../templates/init/react/src/App.js.tpl | 13 - .../templates/init/react/src/App.jsx.tpl | 18 + .../templates/init/react/src/App.tsx.tpl | 23 +- .../react/src/components/HelloWorld.jsx.tpl | 17 + .../react/src/components/HelloWorld.tsx.tpl | 20 + .../init/react/src/components/Home.css.tpl | 28 + .../init/react/src/components/Home.jsx.tpl | 30 + .../init/react/src/components/Home.tsx.tpl | 30 + .../react/src/{index.js.tpl => index.jsx.tpl} | 0 .../templates/init/react/src/index.tsx.tpl | 4 +- .../init/react/src/router/index.jsx.tpl | 17 + .../init/react/src/router/index.tsx.tpl | 17 + .../init/react/src/styles/global.css.tpl | 11 +- .../init/react/src/styles/global.less.tpl | 10 +- .../init/react/src/styles/global.scss.tpl | 10 +- .../init/react/src/styles/global.styl.tpl | 10 +- .../templates/init/react/tsconfig.json.tpl | 20 +- .../init/react/webpack.config.js.tpl | 12 +- .../templates/init/svelte/README.md.tpl | 15 + .../templates/init/svelte/index.html.tpl | 12 + .../templates/init/svelte/package.json.tpl | 12 + .../templates/init/svelte/src/App.svelte.tpl | 49 + .../init/svelte/src/assets/webpack.png.tpl | Bin 0 -> 2337 bytes .../src/components/HelloWorld.svelte.tpl | 32 + .../templates/init/svelte/src/index.d.ts.tpl | 3 + .../templates/init/svelte/src/main.js.tpl | 22 + .../templates/init/svelte/src/main.ts.tpl | 22 + .../init/svelte/src/store/index.js.tpl | 41 + .../init/svelte/src/store/index.ts.tpl | 48 + .../init/svelte/src/styles/global.css.tpl | 27 + .../init/svelte/src/styles/global.less.tpl | 26 + .../init/svelte/src/styles/global.scss.tpl | 26 + .../init/svelte/src/styles/global.styl.tpl | 26 + .../templates/init/svelte/tsconfig.json.tpl | 21 + .../init/svelte/webpack.config.js.tpl | 119 + .../templates/init/vue/README.md.tpl | 15 + .../templates/init/vue/index.html.tpl | 12 + .../templates/init/vue/package.json.tpl | 12 + .../templates/init/vue/src/App.vue.tpl | 58 + .../init/vue/src/assets/webpack.png.tpl | Bin 0 -> 2337 bytes .../vue/src/components/HelloWorld.vue.tpl | 28 + .../templates/init/vue/src/main.js.tpl | 26 + .../templates/init/vue/src/main.ts.tpl | 26 + .../init/vue/src/router/index.js.tpl | 22 + .../init/vue/src/router/index.ts.tpl | 23 + .../templates/init/vue/src/store/index.js.tpl | 28 + .../templates/init/vue/src/store/index.ts.tpl | 36 + .../init/vue/src/styles/global.css.tpl | 27 + .../init/vue/src/styles/global.less.tpl | 26 + .../init/vue/src/styles/global.scss.tpl | 26 + .../init/vue/src/styles/global.styl.tpl | 26 + .../templates/init/vue/tsconfig.json.tpl | 35 + .../templates/init/vue/webpack.config.js.tpl | 115 + .../default/examples/simple/src/index.js.tpl | 11 + .../examples/simple/src/lazy-module.js.tpl | 1 + .../simple/src/static-esm-module.js.tpl | 1 + .../examples/simple/webpack.config.js.tpl | 27 + .../templates/loader/default/package.json.tpl | 5 + .../templates/loader/default/src/cjs.js.tpl | 1 + .../templates/loader/default/src/index.js.tpl | 25 + .../default/test/fixtures/simple-file.js.tpl | 3 + .../default/test/functional.test.js.tpl | 21 + .../loader/default/test/test-utils.js.tpl | 85 + .../loader/default/test/unit.test.js.tpl | 30 + .../default/examples/simple/src/index.js.tpl | 11 + .../examples/simple/src/lazy-module.js.tpl | 1 + .../simple/src/static-esm-module.js.tpl | 1 + .../examples/simple/webpack.config.js.tpl | 13 + .../templates/plugin/default/package.json.tpl | 5 + .../templates/plugin/default/src/cjs.js.tpl | 1 + .../templates/plugin/default/src/index.js.tpl | 16 + .../default/test/fixtures/simple-file.js.tpl | 3 + .../default/test/functional.test.js.tpl | 10 + .../plugin/default/test/test-utils.js.tpl | 85 + .../create-webpack-app.test.js.snap.webpack5 | 1151 --------- .../__snapshots__/init.test.js.snap.webpack5 | 2236 +++++++++++++++++ .../init.test.js} | 355 ++- .../loader/error-test/loader-error.test.js | 15 + .../loader/error-test/src/index.ts | 4 + .../loader/error-test/tsconfig.json | 11 + .../loader/error-test/webpack.config.js | 25 + test/create-webpack-app/loader/loader.test.js | 248 ++ .../warning-test/loader-warning.test.js | 13 + .../loader/warning-test/my-loader.js | 5 + .../loader/warning-test/src/main.js | 2 + .../loader/warning-test/webpack.config.js | 33 + test/create-webpack-app/plugin/plugin.test.js | 245 ++ test/create-webpack-app/test.utils.js | 373 +-- 110 files changed, 6294 insertions(+), 1758 deletions(-) delete mode 100644 packages/create-webpack-app/src/generators/default.ts create mode 100644 packages/create-webpack-app/src/generators/init/default.ts create mode 100644 packages/create-webpack-app/src/generators/init/react.ts create mode 100644 packages/create-webpack-app/src/generators/init/svelte.ts create mode 100644 packages/create-webpack-app/src/generators/init/vue.ts create mode 100644 packages/create-webpack-app/src/generators/loader/default.ts create mode 100644 packages/create-webpack-app/src/generators/plugin/default.ts create mode 100644 packages/create-webpack-app/src/utils/pkgInstallAction.ts rename packages/create-webpack-app/templates/init/default/{README.md => README.md.tpl} (62%) rename packages/create-webpack-app/templates/init/{customFiles/babel.config.json => default/babel.config.json.tpl} (100%) rename packages/create-webpack-app/templates/init/{customFiles/postcss.config.js => default/postcss.config.js.tpl} (100%) create mode 100644 packages/create-webpack-app/templates/init/default/src/index.js.tpl create mode 100644 packages/create-webpack-app/templates/init/default/src/index.ts.tpl rename packages/create-webpack-app/templates/init/{customFiles/tsconfig.json => default/tsconfig.json.tpl} (100%) create mode 100644 packages/create-webpack-app/templates/init/react/README.md.tpl delete mode 100644 packages/create-webpack-app/templates/init/react/src/App.js.tpl create mode 100644 packages/create-webpack-app/templates/init/react/src/App.jsx.tpl create mode 100644 packages/create-webpack-app/templates/init/react/src/components/HelloWorld.jsx.tpl create mode 100644 packages/create-webpack-app/templates/init/react/src/components/HelloWorld.tsx.tpl create mode 100644 packages/create-webpack-app/templates/init/react/src/components/Home.css.tpl create mode 100644 packages/create-webpack-app/templates/init/react/src/components/Home.jsx.tpl create mode 100644 packages/create-webpack-app/templates/init/react/src/components/Home.tsx.tpl rename packages/create-webpack-app/templates/init/react/src/{index.js.tpl => index.jsx.tpl} (100%) create mode 100644 packages/create-webpack-app/templates/init/react/src/router/index.jsx.tpl create mode 100644 packages/create-webpack-app/templates/init/react/src/router/index.tsx.tpl create mode 100644 packages/create-webpack-app/templates/init/svelte/README.md.tpl create mode 100644 packages/create-webpack-app/templates/init/svelte/index.html.tpl create mode 100644 packages/create-webpack-app/templates/init/svelte/package.json.tpl create mode 100644 packages/create-webpack-app/templates/init/svelte/src/App.svelte.tpl create mode 100644 packages/create-webpack-app/templates/init/svelte/src/assets/webpack.png.tpl create mode 100644 packages/create-webpack-app/templates/init/svelte/src/components/HelloWorld.svelte.tpl create mode 100644 packages/create-webpack-app/templates/init/svelte/src/index.d.ts.tpl create mode 100644 packages/create-webpack-app/templates/init/svelte/src/main.js.tpl create mode 100644 packages/create-webpack-app/templates/init/svelte/src/main.ts.tpl create mode 100644 packages/create-webpack-app/templates/init/svelte/src/store/index.js.tpl create mode 100644 packages/create-webpack-app/templates/init/svelte/src/store/index.ts.tpl create mode 100644 packages/create-webpack-app/templates/init/svelte/src/styles/global.css.tpl create mode 100644 packages/create-webpack-app/templates/init/svelte/src/styles/global.less.tpl create mode 100644 packages/create-webpack-app/templates/init/svelte/src/styles/global.scss.tpl create mode 100644 packages/create-webpack-app/templates/init/svelte/src/styles/global.styl.tpl create mode 100644 packages/create-webpack-app/templates/init/svelte/tsconfig.json.tpl create mode 100644 packages/create-webpack-app/templates/init/svelte/webpack.config.js.tpl create mode 100644 packages/create-webpack-app/templates/init/vue/README.md.tpl create mode 100644 packages/create-webpack-app/templates/init/vue/index.html.tpl create mode 100644 packages/create-webpack-app/templates/init/vue/package.json.tpl create mode 100644 packages/create-webpack-app/templates/init/vue/src/App.vue.tpl create mode 100644 packages/create-webpack-app/templates/init/vue/src/assets/webpack.png.tpl create mode 100644 packages/create-webpack-app/templates/init/vue/src/components/HelloWorld.vue.tpl create mode 100644 packages/create-webpack-app/templates/init/vue/src/main.js.tpl create mode 100644 packages/create-webpack-app/templates/init/vue/src/main.ts.tpl create mode 100644 packages/create-webpack-app/templates/init/vue/src/router/index.js.tpl create mode 100644 packages/create-webpack-app/templates/init/vue/src/router/index.ts.tpl create mode 100644 packages/create-webpack-app/templates/init/vue/src/store/index.js.tpl create mode 100644 packages/create-webpack-app/templates/init/vue/src/store/index.ts.tpl create mode 100644 packages/create-webpack-app/templates/init/vue/src/styles/global.css.tpl create mode 100644 packages/create-webpack-app/templates/init/vue/src/styles/global.less.tpl create mode 100644 packages/create-webpack-app/templates/init/vue/src/styles/global.scss.tpl create mode 100644 packages/create-webpack-app/templates/init/vue/src/styles/global.styl.tpl create mode 100644 packages/create-webpack-app/templates/init/vue/tsconfig.json.tpl create mode 100644 packages/create-webpack-app/templates/init/vue/webpack.config.js.tpl create mode 100644 packages/create-webpack-app/templates/loader/default/examples/simple/src/index.js.tpl create mode 100644 packages/create-webpack-app/templates/loader/default/examples/simple/src/lazy-module.js.tpl create mode 100644 packages/create-webpack-app/templates/loader/default/examples/simple/src/static-esm-module.js.tpl create mode 100644 packages/create-webpack-app/templates/loader/default/examples/simple/webpack.config.js.tpl create mode 100644 packages/create-webpack-app/templates/loader/default/package.json.tpl create mode 100644 packages/create-webpack-app/templates/loader/default/src/cjs.js.tpl create mode 100644 packages/create-webpack-app/templates/loader/default/src/index.js.tpl create mode 100644 packages/create-webpack-app/templates/loader/default/test/fixtures/simple-file.js.tpl create mode 100644 packages/create-webpack-app/templates/loader/default/test/functional.test.js.tpl create mode 100644 packages/create-webpack-app/templates/loader/default/test/test-utils.js.tpl create mode 100644 packages/create-webpack-app/templates/loader/default/test/unit.test.js.tpl create mode 100644 packages/create-webpack-app/templates/plugin/default/examples/simple/src/index.js.tpl create mode 100644 packages/create-webpack-app/templates/plugin/default/examples/simple/src/lazy-module.js.tpl create mode 100644 packages/create-webpack-app/templates/plugin/default/examples/simple/src/static-esm-module.js.tpl create mode 100644 packages/create-webpack-app/templates/plugin/default/examples/simple/webpack.config.js.tpl create mode 100644 packages/create-webpack-app/templates/plugin/default/package.json.tpl create mode 100644 packages/create-webpack-app/templates/plugin/default/src/cjs.js.tpl create mode 100644 packages/create-webpack-app/templates/plugin/default/src/index.js.tpl create mode 100644 packages/create-webpack-app/templates/plugin/default/test/fixtures/simple-file.js.tpl create mode 100644 packages/create-webpack-app/templates/plugin/default/test/functional.test.js.tpl create mode 100644 packages/create-webpack-app/templates/plugin/default/test/test-utils.js.tpl delete mode 100644 test/create-webpack-app/__snapshots__/create-webpack-app.test.js.snap.webpack5 create mode 100644 test/create-webpack-app/init/__snapshots__/init.test.js.snap.webpack5 rename test/create-webpack-app/{create-webpack-app.test.js => init/init.test.js} (65%) create mode 100644 test/create-webpack-app/loader/error-test/loader-error.test.js create mode 100644 test/create-webpack-app/loader/error-test/src/index.ts create mode 100644 test/create-webpack-app/loader/error-test/tsconfig.json create mode 100644 test/create-webpack-app/loader/error-test/webpack.config.js create mode 100644 test/create-webpack-app/loader/loader.test.js create mode 100644 test/create-webpack-app/loader/warning-test/loader-warning.test.js create mode 100644 test/create-webpack-app/loader/warning-test/my-loader.js create mode 100644 test/create-webpack-app/loader/warning-test/src/main.js create mode 100644 test/create-webpack-app/loader/warning-test/webpack.config.js create mode 100644 test/create-webpack-app/plugin/plugin.test.js diff --git a/.cspell.json b/.cspell.json index 156c2be7a33..02d12f393c6 100644 --- a/.cspell.json +++ b/.cspell.json @@ -64,6 +64,7 @@ "nwjs", "Oikawa", "pathinfo", + "plopfile", "pnpm", "postcss", "prebuild", @@ -101,7 +102,8 @@ "Yuuji", "Zangetsu", "Zenitsu", - "quickstart" + "quickstart", + "pinia" ], "dictionaries": ["npm", "software-terms"], "ignorePaths": [ diff --git a/packages/create-webpack-app/README.md b/packages/create-webpack-app/README.md index d2c3e7734bc..a23c5eee3f9 100644 --- a/packages/create-webpack-app/README.md +++ b/packages/create-webpack-app/README.md @@ -34,8 +34,15 @@ npx create-webpack-app npx create-webpack-app -f, --force ``` -**To scaffold in a specified path** +**To generate in a specified path** ```bash npx create-webpack-app [generation-path] ``` + +**To generate a project according to a template** + +```bash +npx create-webpack-app --template + +``` diff --git a/packages/create-webpack-app/package.json b/packages/create-webpack-app/package.json index dc18cbd0921..11c658c11b0 100644 --- a/packages/create-webpack-app/package.json +++ b/packages/create-webpack-app/package.json @@ -43,6 +43,7 @@ "webpack-cli": "^5.x.x" }, "dependencies": { + "@inquirer/prompts": "^5.1.2", "colorette": "^2.0.20", "commander": "^12.1.0", "cross-spawn": "^7.0.3", diff --git a/packages/create-webpack-app/src/generators/default.ts b/packages/create-webpack-app/src/generators/default.ts deleted file mode 100644 index a57ddced116..00000000000 --- a/packages/create-webpack-app/src/generators/default.ts +++ /dev/null @@ -1 +0,0 @@ -// refactored generator will be written here shortly diff --git a/packages/create-webpack-app/src/generators/init/default.ts b/packages/create-webpack-app/src/generators/init/default.ts new file mode 100644 index 00000000000..c7ef1d1edb7 --- /dev/null +++ b/packages/create-webpack-app/src/generators/init/default.ts @@ -0,0 +1,197 @@ +import { NodePlopAPI, Answers, ActionType } from "../../types"; +import { dirname, join, resolve } from "path"; +import ejs from "ejs"; +import { DynamicActionsFunction } from "node-plop"; +import { fileURLToPath } from "url"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +export default async function (plop: NodePlopAPI) { + // dependencies to be installed + const devDependencies: Array = ["webpack", "webpack-cli"]; + + await plop.load("../../utils/pkgInstallAction.js", {}, true); + + plop.setDefaultInclude({ generators: true, actionTypes: true }); + plop.setPlopfilePath(resolve(__dirname, "../../plopfile.js")); + // Define a custom action for installing packages + + // Define a base generator for the project structure + plop.setGenerator("init-default", { + description: "Create a basic webpack project", + prompts: [ + { + type: "input", + name: "projectPath", + message: "Enter the project destination:", + default: ".", + filter: (input) => { + return resolve(process.cwd(), input); + }, + }, + { + type: "list", + name: "langType", + message: "Which of the following JS solutions do you want to use?", + choices: ["none", "ES6", "Typescript"], + default: "none", + }, + { + type: "confirm", + name: "devServer", + message: "Would you like to use Webpack Dev server?", + default: true, + }, + { + type: "confirm", + name: "htmlWebpackPlugin", + message: "Do you want to simplify the creation of HTML files for your bundle?", + default: true, + }, + { + type: "confirm", + name: "workboxWebpackPlugin", + message: "Do you want to add PWA support?", + default: true, + }, + { + type: "list", + name: "cssType", + message: "Which of the following CSS solution do you want to use?", + choices: ["none", "CSS only", "SASS", "LESS", "Stylus"], + default: "none", + filter: (input, answers) => { + if (input === "none") { + answers.isCSS = false; + answers.isPostCSS = false; + answers.extractPlugin = "No"; + } else if (input === "CSS only") { + answers.isCSS = true; + } + return input; + }, + }, + { + type: "confirm", + name: "isCSS", + message: (answers) => + `Will you be using CSS styles along with ${answers.cssType} in your project?`, + when: (answers) => answers.cssType !== "CSS only", + default: true, + }, + { + type: "confirm", + name: "isPostCSS", + message: "Do you want to use PostCSS in your project?", + default: (answers: Answers) => answers.cssType == "CSS only", + }, + { + type: "list", + name: "extractPlugin", + message: "Do you want to extract CSS into separate files?", + choices: ["No", "Only for Production", "Yes"], + default: "No", + }, + { + type: "list", + name: "packageManager", + message: "Which package manager do you want to use?", + choices: ["npm", "yarn", "pnpm"], + default: "npm", + validate(input) { + if (!input.trim()) { + return "Package manager cannot be empty"; + } + return true; + }, + }, + ], + actions: function (answers: Answers) { + const actions: ActionType[] = []; + + switch (answers.langType) { + case "ES6": + devDependencies.push("babel-loader", "@babel/core", "@babel/preset-env"); + break; + case "Typescript": + devDependencies.push("typescript", "ts-loader"); + break; + } + + if (answers.devServer) { + devDependencies.push("webpack-dev-server"); + } + + if (answers.htmlWebpackPlugin) { + devDependencies.push("html-webpack-plugin", "html-loader"); + } + + if (answers.workboxWebpackPlugin) { + devDependencies.push("workbox-webpack-plugin"); + } + + if (answers.isPostCSS) { + devDependencies.push("postcss-loader", "postcss", "autoprefixer"); + } + + if (answers.extractPlugin !== "No") { + devDependencies.push("mini-css-extract-plugin"); + } + + if (answers.cssType !== "none") { + devDependencies.push("style-loader", "css-loader"); + switch (answers.cssType) { + case "SASS": + devDependencies.push("sass-loader", "sass"); + break; + case "LESS": + devDependencies.push("less-loader", "less"); + break; + case "Stylus": + devDependencies.push("stylus-loader", "stylus"); + break; + } + } + if (answers.extractPlugin !== "No") { + devDependencies.push("mini-css-extract-plugin"); + } + + const files = ["./index.html", "webpack.config.js", "package.json", "README.md"]; + + switch (answers.langType) { + case "Typescript": + answers.entryPoint = "./src/index.ts"; + files.push("tsconfig.json", answers.entryPoint as string); + break; + case "ES6": + answers.entryPoint = "./src/index.js"; + files.push("babel.config.json", answers.entryPoint as string); + break; + default: + answers.entryPoint = "./src/index.js"; + files.push(answers.entryPoint as string); + break; + } + if (answers.isPostCSS) { + files.push("postcss.config.js"); + } + + for (const file of files) { + actions.push({ + type: "add", + path: join(answers.projectPath, file), + templateFile: join(plop.getPlopfilePath(), "../templates/init/default", `${file}.tpl`), + transform: (content: string) => ejs.render(content, answers), + force: true, + }); + } + actions.push({ + type: "pkgInstall", + path: plop.renderString("{{projectPath}}/", answers), + // Custom function don't automatically render hbs template as path hence manual rendering + packages: devDependencies, + }); + return actions; + } as DynamicActionsFunction, + }); +} diff --git a/packages/create-webpack-app/src/generators/init/react.ts b/packages/create-webpack-app/src/generators/init/react.ts new file mode 100644 index 00000000000..45b34df2ae8 --- /dev/null +++ b/packages/create-webpack-app/src/generators/init/react.ts @@ -0,0 +1,248 @@ +import { NodePlopAPI, Answers, ActionType } from "../../types"; +import { dirname, resolve, join } from "path"; +import ejs from "ejs"; +import { DynamicActionsFunction } from "node-plop"; +import { fileURLToPath } from "url"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +export default async function (plop: NodePlopAPI) { + // dependencies to be installed + const devDependencies: Array = [ + "webpack", + "webpack-cli", + "react@18", + "react-dom@18", + "webpack-dev-server", + "html-webpack-plugin", + ]; + + await plop.load("../../utils/pkgInstallAction.js", {}, true); + + plop.setDefaultInclude({ generators: true, actionTypes: true }); + plop.setPlopfilePath(resolve(__dirname, "../../plopfile.js")); + // Define a custom action for installing packages + + // Define a base generator for the project structure + plop.setGenerator("init-react", { + description: "Create a basic React-webpack project", + prompts: [ + { + type: "input", + name: "projectPath", + message: "Enter the project destination:", + default: ".", + filter: (input) => { + return resolve(process.cwd(), input); + }, + }, + { + type: "list", + name: "langType", + message: "Which of the following JS solutions do you want to use?", + choices: ["ES6", "Typescript"], + default: "ES6", + }, + { + type: "confirm", + name: "useReactRouter", + message: "Do you want to use React Router in your project?", + default: true, + }, + { + type: "confirm", + name: "useReactState", + message: "Do you want to use React State in your project?", + default: true, + }, + { + type: "confirm", + name: "workboxWebpackPlugin", + message: "Do you want to add PWA support?", + default: true, + }, + { + type: "list", + name: "cssType", + message: "Which of the following CSS solution do you want to use?", + choices: ["none", "CSS only", "SASS", "LESS", "Stylus"], + default: "none", + filter: (input, answers) => { + if (input === "none") { + answers.isCSS = false; + answers.isPostCSS = false; + answers.extractPlugin = "No"; + } else if (input === "CSS only") { + answers.isCSS = true; + } + return input; + }, + }, + { + type: "confirm", + name: "isCSS", + message: (answers) => + `Will you be using CSS styles along with ${answers.cssType} in your project?`, + when: (answers) => answers.cssType !== "CSS only", + default: true, + }, + { + type: "confirm", + name: "isPostCSS", + message: "Do you want to use PostCSS in your project?", + default: (answers: Answers) => answers.cssType == "CSS only", + }, + { + type: "list", + name: "extractPlugin", + message: "Do you want to extract CSS into separate files?", + choices: ["No", "Only for Production", "Yes"], + default: "No", + }, + { + type: "list", + name: "packageManager", + message: "Which package manager do you want to use?", + choices: ["npm", "yarn", "pnpm"], + default: "npm", + validate(input) { + if (!input.trim()) { + return "Package manager cannot be empty"; + } + return true; + }, + }, + ], + actions: function (answers: Answers) { + // setting some default values based on the answers + const actions: ActionType[] = []; + answers.htmlWebpackPlugin = true; + answers.devServer = true; + switch (answers.langType) { + case "ES6": + devDependencies.push( + "babel-loader", + "@babel/core", + "@babel/preset-env", + "@babel/preset-react", + ); + break; + case "Typescript": + devDependencies.push("typescript", "ts-loader", "@types/react", "@types/react-dom"); + break; + } + if (answers.isPostCSS) { + devDependencies.push("postcss-loader", "postcss", "autoprefixer"); + } + if (answers.cssType === "none") { + answers.isCSS = false; + answers.isPostCSS = false; + answers.extractPlugin = "No"; + } else { + devDependencies.push("style-loader", "css-loader"); + switch (answers.cssType) { + case "CSS only": + answers.isCSS = true; + break; + case "SASS": + devDependencies.push("sass-loader", "sass"); + break; + case "LESS": + devDependencies.push("less-loader", "less"); + break; + case "Stylus": + devDependencies.push("stylus-loader", "stylus"); + break; + } + } + if (answers.extractPlugin !== "No") { + devDependencies.push("mini-css-extract-plugin"); + } + if (answers.workboxWebpackPlugin) { + devDependencies.push("workbox-webpack-plugin"); + } + + if (answers.useReactRouter) { + devDependencies.push("react-router-dom", "@types/react-router-dom"); + } + + const files = [ + "./index.html", + "./src/assets/webpack.png", + "webpack.config.js", + "package.json", + "README.md", + ]; + + switch (answers.langType) { + case "Typescript": + answers.entry = "./src/index.tsx"; + files.push( + "tsconfig.json", + "index.d.ts", + "./src/App.tsx", + "./src/components/HelloWorld.tsx", + "./src/components/Home.tsx", + answers.entry as string, + ); + break; + case "ES6": + answers.entry = "./src/index.jsx"; + files.push( + "./src/App.jsx", + "./src/components/HelloWorld.jsx", + "./src/components/Home.jsx", + answers.entry as string, + ); + break; + } + + if (answers.cssType !== "none" && answers.isCSS) { + files.push("./src/components/Home.css"); + } + + switch (answers.cssType) { + case "CSS only": + files.push("./src/styles/global.css"); + break; + case "SASS": + files.push("./src/styles/global.scss"); + break; + case "LESS": + files.push("./src/styles/global.less"); + break; + case "Stylus": + files.push("./src/styles/global.styl"); + break; + } + + if (answers.useReactRouter) { + switch (answers.langType) { + case "Typescript": + files.push("./src/router/index.tsx"); + break; + case "ES6": + files.push("./src/router/index.jsx"); + break; + } + } + + for (const file of files) { + actions.push({ + type: "add", + path: join(answers.projectPath, file), + templateFile: join(plop.getPlopfilePath(), "../templates/init/react", `${file}.tpl`), + transform: (content: string) => ejs.render(content, answers), + force: true, + }); + } + actions.push({ + type: "pkgInstall", + path: plop.renderString("{{projectPath}}/", answers), + // Custom function don't automatically render hbs template as path hence manual rendering + packages: devDependencies, + }); + return actions; + } as DynamicActionsFunction, + }); +} diff --git a/packages/create-webpack-app/src/generators/init/svelte.ts b/packages/create-webpack-app/src/generators/init/svelte.ts new file mode 100644 index 00000000000..1628c15c906 --- /dev/null +++ b/packages/create-webpack-app/src/generators/init/svelte.ts @@ -0,0 +1,213 @@ +import { NodePlopAPI, Answers, ActionType } from "../../types"; +import { dirname, resolve, join } from "path"; +import ejs from "ejs"; +import { DynamicActionsFunction } from "node-plop"; +import { fileURLToPath } from "url"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +export default async function (plop: NodePlopAPI) { + // dependencies to be installed + const devDependencies: Array = [ + "webpack", + "webpack-cli", + "svelte", + "svelte-loader", + "webpack-dev-server", + "html-webpack-plugin", + ]; + + await plop.load("../../utils/pkgInstallAction.js", {}, true); + + plop.setDefaultInclude({ generators: true, actionTypes: true }); + plop.setPlopfilePath(resolve(__dirname, "../../plopfile.js")); + + // Define a base generator for the Svelte project structure + plop.setGenerator("init-svelte", { + description: "Create a basic Svelte-webpack project", + prompts: [ + { + type: "input", + name: "projectPath", + message: "Enter the project destination:", + default: ".", + filter: (input) => { + return resolve(process.cwd(), input); + }, + }, + { + type: "list", + name: "langType", + message: "Which of the following JS solutions do you want to use?", + choices: ["ES6", "Typescript"], + default: "ES6", + }, + { + type: "confirm", + name: "workboxWebpackPlugin", + message: "Do you want to add PWA support?", + default: true, + }, + { + type: "list", + name: "cssType", + message: "Which of the following CSS solution do you want to use?", + choices: ["none", "CSS only", "SASS", "LESS", "Stylus"], + default: "none", + filter: (input, answers) => { + if (input === "none") { + answers.isCSS = false; + answers.isPostCSS = false; + answers.extractPlugin = "No"; + } else if (input === "CSS only") { + answers.isCSS = true; + } + return input; + }, + }, + { + type: "confirm", + name: "isCSS", + message: (answers) => + `Will you be using CSS styles along with ${answers.cssType} in your project?`, + when: (answers) => answers.cssType !== "CSS only", + default: true, + }, + { + type: "confirm", + name: "isPostCSS", + message: "Do you want to use PostCSS in your project?", + default: (answers: Answers) => answers.cssType == "CSS only", + }, + { + type: "list", + name: "extractPlugin", + message: "Do you want to extract CSS into separate files?", + choices: ["No", "Only for Production", "Yes"], + default: "No", + }, + { + type: "list", + name: "packageManager", + message: "Which package manager do you want to use?", + choices: ["npm", "yarn", "pnpm"], + default: "npm", + validate(input) { + if (!input.trim()) { + return "Package manager cannot be empty"; + } + return true; + }, + }, + ], + actions: function (answers: Answers) { + // setting some default values based on the answers + const actions: ActionType[] = []; + answers.htmlWebpackPlugin = true; + answers.devServer = true; + + switch (answers.langType) { + case "ES6": + devDependencies.push("babel-loader", "@babel/core", "@babel/preset-env"); + break; + case "Typescript": + devDependencies.push("typescript", "ts-loader", "@tsconfig/svelte"); + break; + } + + if (answers.isPostCSS) { + devDependencies.push("postcss-loader", "postcss", "autoprefixer"); + } + + if (answers.workboxWebpackPlugin) { + devDependencies.push("workbox-webpack-plugin"); + } + + if (answers.cssType === "none") { + answers.isCSS = false; + answers.isPostCSS = false; + answers.extractPlugin = "No"; + } else { + devDependencies.push("style-loader", "css-loader"); + switch (answers.cssType) { + case "CSS only": + answers.isCSS = true; + break; + case "SASS": + devDependencies.push("sass-loader", "sass"); + break; + case "LESS": + devDependencies.push("less-loader", "less"); + break; + case "Stylus": + devDependencies.push("stylus-loader", "stylus"); + break; + } + } + + if (answers.extractPlugin !== "No") { + devDependencies.push("mini-css-extract-plugin"); + } + + const files = [ + "./index.html", + "./src/assets/webpack.png", + "webpack.config.js", + "package.json", + "README.md", + "./src/components/HelloWorld.svelte", + "./src/App.svelte", + ]; + + switch (answers.langType) { + case "Typescript": + answers.entry = "./src/main.ts"; + files.push("tsconfig.json", "./src/index.d.ts", answers.entry as string); + break; + case "ES6": + answers.entry = "./src/main.js"; + files.push(answers.entry as string); + break; + } + + if (answers.langType === "Typescript") { + files.push("./src/store/index.ts"); + } else { + files.push("./src/store/index.js"); + } + + switch (answers.cssType) { + case "CSS only": + files.push("./src/styles/global.css"); + break; + case "SASS": + files.push("./src/styles/global.scss"); + break; + case "LESS": + files.push("./src/styles/global.less"); + break; + case "Stylus": + files.push("./src/styles/global.styl"); + break; + } + + for (const file of files) { + actions.push({ + type: "add", + path: join(answers.projectPath, file), + templateFile: join(plop.getPlopfilePath(), "../templates/init/svelte", `${file}.tpl`), + transform: (content: string) => ejs.render(content, answers), + force: true, + }); + } + + actions.push({ + type: "pkgInstall", + path: plop.renderString("{{projectPath}}/", answers), + packages: devDependencies, + }); + + return actions; + } as DynamicActionsFunction, + }); +} diff --git a/packages/create-webpack-app/src/generators/init/vue.ts b/packages/create-webpack-app/src/generators/init/vue.ts new file mode 100644 index 00000000000..395f7259b39 --- /dev/null +++ b/packages/create-webpack-app/src/generators/init/vue.ts @@ -0,0 +1,248 @@ +import { NodePlopAPI, Answers, ActionType } from "../../types"; +import { dirname, resolve, join } from "path"; +import ejs from "ejs"; +import { DynamicActionsFunction } from "node-plop"; +import { fileURLToPath } from "url"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +export default async function (plop: NodePlopAPI) { + // dependencies to be installed + const devDependencies: Array = [ + "webpack", + "webpack-cli", + "vue@3", + "webpack-dev-server", + "html-webpack-plugin", + "vue-loader@next", + "@vue/compiler-sfc", + ]; + + plop.setHelper("rawExpression", function (context: string): string { + return `{{${context}}}`; + }); + + await plop.load("../../utils/pkgInstallAction.js", {}, true); + + plop.setDefaultInclude({ generators: true, actionTypes: true }); + plop.setPlopfilePath(resolve(__dirname, "../../plopfile.js")); + + plop.setGenerator("init-vue", { + description: "Create a basic Vue-webpack project", + prompts: [ + { + type: "input", + name: "projectPath", + message: "Enter the project destination:", + default: ".", + filter: (input) => { + return resolve(process.cwd(), input); + }, + }, + { + type: "list", + name: "langType", + message: "Which of the following JS solutions do you want to use?", + choices: ["ES6", "Typescript"], + default: "ES6", + }, + { + type: "confirm", + name: "useVueRouter", + message: "Do you want to use Vue Router?", + default: false, + }, + { + type: "confirm", + name: "useVueStore", + message: "Do you want to use Pinia for store functionality?", + default: false, + }, + { + type: "confirm", + name: "workboxWebpackPlugin", + message: "Do you want to add PWA support?", + default: true, + }, + { + type: "list", + name: "cssType", + message: "Which of the following CSS solution do you want to use?", + choices: ["none", "CSS only", "SASS", "LESS", "Stylus"], + default: "CSS only", + filter: (input, answers) => { + if (input === "none") { + answers.isCSS = false; + answers.isPostCSS = false; + answers.extractPlugin = "No"; + } else if (input === "CSS only") { + answers.isCSS = true; + } + return input; + }, + }, + { + type: "confirm", + name: "isCSS", + message: (answers) => + `Will you be using CSS styles along with ${answers.cssType} in your project?`, + when: (answers) => answers.cssType !== "CSS only", + default: true, + }, + { + type: "confirm", + name: "isPostCSS", + message: "Do you want to use PostCSS in your project?", + default: (answers: Answers) => answers.cssType == "CSS only", + }, + { + type: "list", + name: "extractPlugin", + message: "Do you want to extract CSS into separate files?", + choices: ["No", "Only for Production", "Yes"], + default: "No", + }, + { + type: "list", + name: "packageManager", + message: "Which package manager do you want to use?", + choices: ["npm", "yarn", "pnpm"], + default: "npm", + validate(input) { + if (!input.trim()) { + return "Package manager cannot be empty"; + } + return true; + }, + }, + ], + actions: function (answers: Answers) { + // setting some default values based on the answers + const actions: ActionType[] = []; + answers.htmlWebpackPlugin = true; + answers.devServer = true; + + switch (answers.langType) { + case "ES6": + devDependencies.push("babel-loader", "@babel/core", "@babel/preset-env"); + break; + case "Typescript": + devDependencies.push("typescript", "ts-loader"); + break; + } + + if (answers.useVueRouter) { + devDependencies.push("vue-router@4"); + } + + if (answers.useVueStore) { + devDependencies.push("pinia"); + } + + if (answers.isPostCSS) { + devDependencies.push("postcss-loader", "postcss", "autoprefixer"); + } + + if (answers.workboxWebpackPlugin) { + devDependencies.push("workbox-webpack-plugin"); + } + + if (answers.cssType === "none") { + answers.isCSS = false; + answers.isPostCSS = false; + answers.extractPlugin = "No"; + } else { + devDependencies.push("vue-style-loader", "style-loader", "css-loader"); + switch (answers.cssType) { + case "CSS only": + answers.isCSS = true; + break; + case "SASS": + devDependencies.push("sass-loader", "sass"); + break; + case "LESS": + devDependencies.push("less-loader", "less"); + break; + case "Stylus": + devDependencies.push("stylus-loader", "stylus"); + break; + } + } + + if (answers.extractPlugin !== "No") { + devDependencies.push("mini-css-extract-plugin"); + } + + const files = [ + "./index.html", + "./src/assets/webpack.png", + "webpack.config.js", + "package.json", + "README.md", + ]; + + switch (answers.langType) { + case "Typescript": + answers.entry = "./src/main.ts"; + files.push( + "tsconfig.json", + "./src/App.vue", + "./src/components/HelloWorld.vue", + answers.entry as string, + ); + break; + case "ES6": + answers.entry = "./src/main.js"; + files.push("./src/App.vue", "./src/components/HelloWorld.vue", answers.entry as string); + break; + } + if (answers.useVueRouter) { + if (answers.langType === "Typescript") { + files.push("./src/router/index.ts"); + } else { + files.push("./src/router/index.js"); + } + } + + if (answers.useVueStore) { + if (answers.langType === "Typescript") { + files.push("./src/store/index.ts"); + } else { + files.push("./src/store/index.js"); + } + } + + switch (answers.cssType) { + case "CSS only": + files.push("./src/styles/global.css"); + break; + case "SASS": + files.push("./src/styles/global.scss"); + break; + case "LESS": + files.push("./src/styles/global.less"); + break; + case "Stylus": + files.push("./src/styles/global.styl"); + break; + } + + for (const file of files) { + actions.push({ + type: "add", + path: join(answers.projectPath, file), + templateFile: join(plop.getPlopfilePath(), "../templates/init/vue", `${file}.tpl`), + transform: (content: string) => ejs.render(content, answers), + force: true, + }); + } + + actions.push({ + type: "pkgInstall", + path: plop.renderString("{{projectPath}}/", answers), + packages: devDependencies, + }); + + return actions; + } as DynamicActionsFunction, + }); +} diff --git a/packages/create-webpack-app/src/generators/loader/default.ts b/packages/create-webpack-app/src/generators/loader/default.ts new file mode 100644 index 00000000000..b43415c342b --- /dev/null +++ b/packages/create-webpack-app/src/generators/loader/default.ts @@ -0,0 +1,105 @@ +import { NodePlopAPI, Answers, ActionType } from "../../types"; +import { dirname, join, resolve } from "path"; +import ejs from "ejs"; +import { DynamicActionsFunction } from "node-plop"; +import { fileURLToPath } from "url"; +// eslint-disable-next-line node/no-missing-import +import { logger } from "../../utils/logger.js"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +export default async function (plop: NodePlopAPI) { + // dependencies to be installed + const devDependencies: Array = ["webpack-defaults"]; + + await plop.load("../../utils/pkgInstallAction.js", {}, true); + + // custom helper function + plop.setHelper("makeLoaderName", (name: string) => { + name = plop.getHelper("kebabCase")(name); + + if (!/loader$/.test(name)) { + name += "-loader"; + } + return name; + }); + + plop.setDefaultInclude({ generators: true, actionTypes: true }); + plop.setPlopfilePath(resolve(__dirname, "../../plopfile.js")); + + // Define a base generator for the project structure + plop.setGenerator("loader-default", { + description: "Create a basic webpack loader.", + prompts: [ + { + type: "input", + name: "projectPath", + message: "Enter the project destination:", + default: ".", + filter: (input) => { + return resolve(process.cwd(), input); + }, + }, + { + type: "input", + name: "name", + message: "Loader name?", + default: "my-loader", + validate: (str: string): boolean => str.length > 0, + }, + { + type: "list", + name: "packageManager", + message: "Pick a package manager:", + choices: ["npm", "yarn", "pnpm"], + default: "npm", + validate(input) { + if (!input.trim()) { + return "Package manager cannot be empty"; + } + return true; + }, + }, + ], + actions: function (answers: Answers) { + const actions: ActionType[] = ["Starting actions..."]; + answers.projectPath = join(answers.projectPath, answers.name); + + logger.error(` + Your project must be inside a folder named ${answers.name} + I will create this folder for you. + `); + + const files: Array = [ + "./package.json", + "./examples/simple/src/index.js", + "./examples/simple/src/lazy-module.js", + "./examples/simple/src/static-esm-module.js", + "./examples/simple/webpack.config.js", + "./src/cjs.js", + "./test/fixtures/simple-file.js", + "./test/functional.test.js", + "./test/test-utils.js", + "./test/unit.test.js", + "./src/index.js", + ]; + + for (const file of files) { + actions.push({ + type: "add", + path: join(answers.projectPath, file), + templateFile: join(plop.getPlopfilePath(), "../templates/loader/default", `${file}.tpl`), + transform: (content: string) => ejs.render(content, answers), + verbose: true, + force: true, + }); + } + actions.push({ + type: "pkgInstall", + path: answers.projectPath, + packages: devDependencies, + }); + return actions; + } as DynamicActionsFunction, + }); +} diff --git a/packages/create-webpack-app/src/generators/plugin/default.ts b/packages/create-webpack-app/src/generators/plugin/default.ts new file mode 100644 index 00000000000..d08bdc976ce --- /dev/null +++ b/packages/create-webpack-app/src/generators/plugin/default.ts @@ -0,0 +1,95 @@ +import { NodePlopAPI, Answers, ActionType } from "../../types"; +import { dirname, join, resolve } from "path"; +import ejs from "ejs"; +import { DynamicActionsFunction } from "node-plop"; +import { fileURLToPath } from "url"; +// eslint-disable-next-line node/no-missing-import +import { logger } from "../../utils/logger.js"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +export default async function (plop: NodePlopAPI) { + // dependencies to be installed + const devDependencies: Array = ["webpack-defaults"]; + + await plop.load("../../utils/pkgInstallAction.js", {}, true); + + plop.setDefaultInclude({ generators: true, actionTypes: true }); + plop.setPlopfilePath(resolve(__dirname, "../../plopfile.js")); + + // Define a base generator for the project structure + plop.setGenerator("plugin-default", { + description: "Create a basic webpack plugin.", + prompts: [ + { + type: "input", + name: "projectPath", + message: "Enter the project destination:", + default: ".", + filter: (input) => { + return resolve(process.cwd(), input); + }, + }, + { + type: "input", + name: "name", + message: "Plugin name?", + default: "my-webpack-plugin", + filter: (input) => plop.getHelper("kebabCase")(input), + validate: (str: string): boolean => str.length > 0, + }, + { + type: "list", + name: "packageManager", + message: "Pick a package manager:", + choices: ["npm", "yarn", "pnpm"], + default: "npm", + validate(input) { + if (!input.trim()) { + return "Package manager cannot be empty"; + } + return true; + }, + }, + ], + actions: function (answers: Answers) { + const actions: ActionType[] = ["Starting actions..."]; + answers.projectPath = join(answers.projectPath, answers.name); + + logger.error(` + Your project must be inside a folder named ${answers.name} + I will create this folder for you. + `); + + const files: Array = [ + "./package.json", + "./examples/simple/src/index.js", + "./examples/simple/src/lazy-module.js", + "./examples/simple/src/static-esm-module.js", + "./examples/simple/webpack.config.js", + "./src/cjs.js", + "./test/fixtures/simple-file.js", + "./test/functional.test.js", + "./test/test-utils.js", + "./src/index.js", + ]; + + for (const file of files) { + actions.push({ + type: "add", + path: join(answers.projectPath, file), + templateFile: join(plop.getPlopfilePath(), "../templates/plugin/default", `${file}.tpl`), + transform: (content: string) => ejs.render(content, answers), + verbose: true, + force: true, + }); + } + actions.push({ + type: "pkgInstall", + path: answers.projectPath, + packages: devDependencies, + }); + return actions; + } as DynamicActionsFunction, + }); +} diff --git a/packages/create-webpack-app/src/index.ts b/packages/create-webpack-app/src/index.ts index f8bdf6f182d..0b3703e9bf0 100644 --- a/packages/create-webpack-app/src/index.ts +++ b/packages/create-webpack-app/src/index.ts @@ -1,31 +1,69 @@ -// Cspell:ignore plopfile, plopfile.js import { Command } from "commander"; import { resolve, dirname } from "path"; -import nodePlop from "node-plop"; +import { select } from "@inquirer/prompts"; +import nodePlop, { PlopGenerator } from "node-plop"; import { fileURLToPath } from "url"; // eslint-disable-next-line node/no-missing-import import { onSuccessHandler, onFailureHandler, logger } from "./utils/logger.js"; +import { Answers } from "./types"; const __dirname = dirname(fileURLToPath(import.meta.url)); const program = new Command(); + const plop = await nodePlop(resolve(__dirname, "./plopfile.js")); -const defaultValues = { - init: { - projectPath: process.cwd(), - langType: "none", - devServer: true, - htmlWebpackPlugin: true, - workboxWebpackPlugin: true, - cssType: "none", - isCSS: false, - isPostCSS: false, - extractPlugin: "No", - packageManager: "npm", + +const baseAnswers: Answers = { + projectPath: process.cwd(), + langType: "none", + devServer: true, + htmlWebpackPlugin: true, + workboxWebpackPlugin: true, + cssType: "none", + isCSS: false, + isPostCSS: false, + extractPlugin: "No", + packageManager: "npm", +}; +const initValues: Record = { + default: { + ...baseAnswers, + }, + react: { + ...baseAnswers, + langType: "ES6", + useReactRouter: false, + useReactState: true, + }, + vue: { + ...baseAnswers, + cssType: "CSS only", + langType: "ES6", + useVueRouter: false, + useVueStore: false, + }, + svelte: { + ...baseAnswers, + langType: "ES6", + useSvelteRouter: false, }, }; +const initGenerators: Record = { + default: plop.getGenerator("init-default"), + react: plop.getGenerator("init-react"), + vue: plop.getGenerator("init-vue"), + svelte: plop.getGenerator("init-svelte"), +}; +const loaderGenerators: Record = { + default: plop.getGenerator("loader-default"), +}; + +const pluginGenerators: Record = { + default: plop.getGenerator("plugin-default"), +}; + program .version("1.0.0", "-v, --version") .usage("[command] [options]") @@ -38,9 +76,24 @@ program .description("Initialize a new Webpack project") .argument("[projectPath]", "Path to create the project") .option("-f, --force", "Skip the prompt and use the default values", false) + .option("-t --template