From 3e984e033383660b0c666eb17ed3166ab1949ea7 Mon Sep 17 00:00:00 2001 From: Alexander Akait Date: Thu, 27 Aug 2026 12:38:34 +0000 Subject: [PATCH 1/2] feat(create-webpack-app): stop asking about HTML and CSS webpack handles both out of the box, so the templates no longer ask whether to simplify HTML creation, which CSS solution to use, whether PostCSS is wanted or whether CSS should be extracted. `index.html` is always the entry point, a stylesheet is always scaffolded, and one question remains for the preprocessors webpack does not cover, wired through a `type: "css/auto"` rule that adds only sass-loader/less-loader/stylus-loader. Also fixes what the generated projects needed to build and run: `baseUrl` for the Vue TypeScript `paths`, a fully specified `./router/index.js` import under `"type": "module"`, Svelte 5's `mount()` instead of `new App()`, and style-module declarations for the TypeScript templates. --- .changeset/init-no-html-css-questions.md | 5 + .cspell.json | 1 + packages/create-webpack-app/README.md | 5 + .../src/generators/init/default.ts | 76 ++--- .../src/generators/init/react.ts | 90 ++---- .../src/generators/init/svelte.ts | 88 ++---- .../src/generators/init/vue.ts | 88 ++---- packages/create-webpack-app/src/index.ts | 8 +- .../templates/init/default/README.md.tpl | 17 ++ .../templates/init/default/index.html.tpl | 5 +- .../init/default/postcss.config.js.tpl | 7 - .../templates/init/default/src/styles.css.tpl | 7 + .../init/default/src/styles.less.tpl | 10 + .../init/default/src/styles.scss.tpl | 10 + .../init/default/src/styles.styl.tpl | 9 + .../templates/init/default/webpack.config.tpl | 24 +- .../templates/init/react/README.md.tpl | 7 + .../templates/init/react/index.d.ts.tpl | 4 + .../templates/init/react/src/index.jsx.tpl | 6 +- .../templates/init/react/src/index.tsx.tpl | 6 +- .../templates/init/react/webpack.config.tpl | 24 +- .../templates/init/svelte/README.md.tpl | 7 + .../templates/init/svelte/src/App.svelte.tpl | 2 - .../templates/init/svelte/src/index.d.ts.tpl | 5 + .../templates/init/svelte/src/main.js.tpl | 9 +- .../templates/init/svelte/src/main.ts.tpl | 11 +- .../templates/init/svelte/webpack.config.tpl | 24 +- .../templates/init/vue/README.md.tpl | 7 + .../templates/init/vue/src/main.js.tpl | 8 +- .../templates/init/vue/src/main.ts.tpl | 6 +- .../templates/init/vue/tsconfig.json.tpl | 1 + .../templates/init/vue/webpack.config.tpl | 27 +- .../__snapshots__/init.test.js.snap.webpack5 | 279 ++++-------------- test/create-webpack-app/init/init.test.js | 119 +++----- 34 files changed, 346 insertions(+), 656 deletions(-) create mode 100644 .changeset/init-no-html-css-questions.md delete mode 100644 packages/create-webpack-app/templates/init/default/postcss.config.js.tpl create mode 100644 packages/create-webpack-app/templates/init/default/src/styles.css.tpl create mode 100644 packages/create-webpack-app/templates/init/default/src/styles.less.tpl create mode 100644 packages/create-webpack-app/templates/init/default/src/styles.scss.tpl create mode 100644 packages/create-webpack-app/templates/init/default/src/styles.styl.tpl diff --git a/.changeset/init-no-html-css-questions.md b/.changeset/init-no-html-css-questions.md new file mode 100644 index 00000000000..59e8757d8e7 --- /dev/null +++ b/.changeset/init-no-html-css-questions.md @@ -0,0 +1,5 @@ +--- +"create-webpack-app": minor +--- + +feat: stop asking about HTML and CSS in `init`, keeping one question for the CSS preprocessors webpack does not cover diff --git a/.cspell.json b/.cspell.json index dd262fb97b4..c79593fbea6 100644 --- a/.cspell.json +++ b/.cspell.json @@ -87,6 +87,7 @@ "splitted", "Statsv", "styl", + "stylesheet", "Tanjiro", "tapable", "taskkill", diff --git a/packages/create-webpack-app/README.md b/packages/create-webpack-app/README.md index 6654d257426..bf1b39d065d 100644 --- a/packages/create-webpack-app/README.md +++ b/packages/create-webpack-app/README.md @@ -65,6 +65,11 @@ Available templates: - [`vue`](https://vuejs.org/) - [`svelte`](https://svelte.dev/) +Every template scaffolds `index.html` as the entry point of the project and relies on +webpack's built-in HTML and CSS support (webpack >= 5.109), so the generated projects +carry no `html-webpack-plugin`, `css-loader`, `style-loader` or `mini-css-extract-plugin`. +A Sass/Less/Stylus project adds only its preprocessor loader. + Available templates for `loader` and `plugin` generators: - `default` (used by default when nothing specified) - generate bootstrap code diff --git a/packages/create-webpack-app/src/generators/init/default.ts b/packages/create-webpack-app/src/generators/init/default.ts index 6175ba86848..fe8ed314b85 100644 --- a/packages/create-webpack-app/src/generators/init/default.ts +++ b/packages/create-webpack-app/src/generators/init/default.ts @@ -33,12 +33,6 @@ export default async function defaultInitGenerator(plop: NodePlopAPI) { message: "Would you like to use Webpack Dev server?", default: true, }, - { - type: "confirm", - name: "html", - message: "Do you want to simplify the creation of HTML files for your bundle?", - default: true, - }, { type: "confirm", name: "workboxWebpackPlugin", @@ -47,33 +41,10 @@ export default async function defaultInitGenerator(plop: NodePlopAPI) { }, { 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: string, answers: Answers) => { - if (input === "none") { - answers.isCSS = false; - answers.isPostCSS = false; - } else if (input === "CSS only") { - answers.isCSS = true; - } - return input; - }, - }, - { - type: "confirm", - name: "isCSS", - message: (answers: Answers) => - `Will you be using CSS styles along with ${answers.cssType} in your project?`, - when: (answers: 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", + name: "cssPreprocessor", + message: "Which CSS preprocessor do you want to use?", + choices: ["none", "SASS", "LESS", "Stylus"], + default: "none", }, { type: "list", @@ -109,22 +80,22 @@ export default async function defaultInitGenerator(plop: NodePlopAPI) { devDependencies.push("workbox-webpack-plugin"); } - if (answers.isPostCSS) { - devDependencies.push("postcss-loader", "postcss", "autoprefixer"); - } - - if (answers.cssType !== "none") { - 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; - } + switch (answers.cssPreprocessor) { + case "SASS": + answers.styleExtension = "scss"; + devDependencies.push("sass-loader", "sass"); + break; + case "LESS": + answers.styleExtension = "less"; + devDependencies.push("less-loader", "less"); + break; + case "Stylus": + answers.styleExtension = "styl"; + devDependencies.push("stylus-loader", "stylus"); + break; + default: + answers.styleExtension = "css"; + break; } const files: FileRecord[] = [ @@ -158,9 +129,10 @@ export default async function defaultInitGenerator(plop: NodePlopAPI) { break; } - if (answers.isPostCSS) { - files.push({ filePath: "postcss.config.js", fileType: "text" }); - } + files.push({ + filePath: `./src/styles.${answers.styleExtension}`, + fileType: "text", + }); for (const file of files) { actions.push({ diff --git a/packages/create-webpack-app/src/generators/init/react.ts b/packages/create-webpack-app/src/generators/init/react.ts index 69e93ae9532..5b67225fc43 100644 --- a/packages/create-webpack-app/src/generators/init/react.ts +++ b/packages/create-webpack-app/src/generators/init/react.ts @@ -49,33 +49,10 @@ export default async function reactInitGenerator(plop: NodePlopAPI) { }, { 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: string, answers: Answers) => { - if (input === "none") { - answers.isCSS = false; - answers.isPostCSS = false; - } else if (input === "CSS only") { - answers.isCSS = true; - } - return input; - }, - }, - { - type: "confirm", - name: "isCSS", - message: (answers: Answers) => - `Will you be using CSS styles along with ${answers.cssType} in your project?`, - when: (answers: 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", + name: "cssPreprocessor", + message: "Which CSS preprocessor do you want to use?", + choices: ["none", "SASS", "LESS", "Stylus"], + default: "none", }, { type: "list", @@ -94,7 +71,6 @@ export default async function reactInitGenerator(plop: NodePlopAPI) { actions: function actions(answers: Answers) { // setting some default values based on the answers const actions: ActionType[] = []; - answers.html = true; answers.devServer = true; switch (answers.langType) { case "ES6": @@ -109,32 +85,28 @@ export default async function reactInitGenerator(plop: NodePlopAPI) { 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; - } else { - 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.workboxWebpackPlugin) { devDependencies.push("workbox-webpack-plugin"); } + switch (answers.cssPreprocessor) { + case "SASS": + answers.styleExtension = "scss"; + devDependencies.push("sass-loader", "sass"); + break; + case "LESS": + answers.styleExtension = "less"; + devDependencies.push("less-loader", "less"); + break; + case "Stylus": + answers.styleExtension = "styl"; + devDependencies.push("stylus-loader", "stylus"); + break; + default: + answers.styleExtension = "css"; + break; + } + const files: FileRecord[] = [ { filePath: "./index.html", fileType: "text" }, { @@ -173,20 +145,10 @@ export default async function reactInitGenerator(plop: NodePlopAPI) { break; } - switch (answers.cssType) { - case "CSS only": - files.push({ filePath: "./src/styles/global.css", fileType: "text" }); - break; - case "SASS": - files.push({ filePath: "./src/styles/global.scss", fileType: "text" }); - break; - case "LESS": - files.push({ filePath: "./src/styles/global.less", fileType: "text" }); - break; - case "Stylus": - files.push({ filePath: "./src/styles/global.styl", fileType: "text" }); - break; - } + files.push({ + filePath: `./src/styles/global.${answers.styleExtension}`, + fileType: "text", + }); for (const file of files) { actions.push({ diff --git a/packages/create-webpack-app/src/generators/init/svelte.ts b/packages/create-webpack-app/src/generators/init/svelte.ts index 947fa9de2a4..f195df127d0 100644 --- a/packages/create-webpack-app/src/generators/init/svelte.ts +++ b/packages/create-webpack-app/src/generators/init/svelte.ts @@ -40,33 +40,10 @@ export default async function svelteInitGenerator(plop: NodePlopAPI) { }, { 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: string, answers: Answers) => { - if (input === "none") { - answers.isCSS = false; - answers.isPostCSS = false; - } else if (input === "CSS only") { - answers.isCSS = true; - } - return input; - }, - }, - { - type: "confirm", - name: "isCSS", - message: (answers: Answers) => - `Will you be using CSS styles along with ${answers.cssType} in your project?`, - when: (answers: 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", + name: "cssPreprocessor", + message: "Which CSS preprocessor do you want to use?", + choices: ["none", "SASS", "LESS", "Stylus"], + default: "none", }, { type: "list", @@ -85,7 +62,6 @@ export default async function svelteInitGenerator(plop: NodePlopAPI) { actions: function actions(answers: Answers) { // setting some default values based on the answers const actions: ActionType[] = []; - answers.html = true; answers.devServer = true; switch (answers.langType) { @@ -97,32 +73,26 @@ export default async function svelteInitGenerator(plop: NodePlopAPI) { 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; - } else { - 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; - } + switch (answers.cssPreprocessor) { + case "SASS": + answers.styleExtension = "scss"; + devDependencies.push("sass-loader", "sass"); + break; + case "LESS": + answers.styleExtension = "less"; + devDependencies.push("less-loader", "less"); + break; + case "Stylus": + answers.styleExtension = "styl"; + devDependencies.push("stylus-loader", "stylus"); + break; + default: + answers.styleExtension = "css"; + break; } const files: FileRecord[] = [ @@ -159,20 +129,10 @@ export default async function svelteInitGenerator(plop: NodePlopAPI) { files.push({ filePath: "./src/store/index.js", fileType: "text" }); } - switch (answers.cssType) { - case "CSS only": - files.push({ filePath: "./src/styles/global.css", fileType: "text" }); - break; - case "SASS": - files.push({ filePath: "./src/styles/global.scss", fileType: "text" }); - break; - case "LESS": - files.push({ filePath: "./src/styles/global.less", fileType: "text" }); - break; - case "Stylus": - files.push({ filePath: "./src/styles/global.styl", fileType: "text" }); - break; - } + files.push({ + filePath: `./src/styles/global.${answers.styleExtension}`, + fileType: "text", + }); for (const file of files) { actions.push({ diff --git a/packages/create-webpack-app/src/generators/init/vue.ts b/packages/create-webpack-app/src/generators/init/vue.ts index edc3b43a648..889749826db 100644 --- a/packages/create-webpack-app/src/generators/init/vue.ts +++ b/packages/create-webpack-app/src/generators/init/vue.ts @@ -48,33 +48,10 @@ export default async function vueInitGenerator(plop: NodePlopAPI) { }, { 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: string, answers: Answers) => { - if (input === "none") { - answers.isCSS = false; - answers.isPostCSS = false; - } else if (input === "CSS only") { - answers.isCSS = true; - } - return input; - }, - }, - { - type: "confirm", - name: "isCSS", - message: (answers: Answers) => - `Will you be using CSS styles along with ${answers.cssType} in your project?`, - when: (answers: 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", + name: "cssPreprocessor", + message: "Which CSS preprocessor do you want to use?", + choices: ["none", "SASS", "LESS", "Stylus"], + default: "none", }, { type: "list", @@ -93,7 +70,6 @@ export default async function vueInitGenerator(plop: NodePlopAPI) { actions: function actions(answers: Answers) { // setting some default values based on the answers const actions: ActionType[] = []; - answers.html = true; answers.devServer = true; switch (answers.langType) { @@ -109,32 +85,26 @@ export default async function vueInitGenerator(plop: NodePlopAPI) { 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; - } else { - 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; - } + switch (answers.cssPreprocessor) { + case "SASS": + answers.styleExtension = "scss"; + devDependencies.push("sass-loader", "sass"); + break; + case "LESS": + answers.styleExtension = "less"; + devDependencies.push("less-loader", "less"); + break; + case "Stylus": + answers.styleExtension = "styl"; + devDependencies.push("stylus-loader", "stylus"); + break; + default: + answers.styleExtension = "css"; + break; } const files: FileRecord[] = [ @@ -181,20 +151,10 @@ export default async function vueInitGenerator(plop: NodePlopAPI) { } } - switch (answers.cssType) { - case "CSS only": - files.push({ filePath: "./src/styles/global.css", fileType: "text" }); - break; - case "SASS": - files.push({ filePath: "./src/styles/global.scss", fileType: "text" }); - break; - case "LESS": - files.push({ filePath: "./src/styles/global.less", fileType: "text" }); - break; - case "Stylus": - files.push({ filePath: "./src/styles/global.styl", fileType: "text" }); - break; - } + files.push({ + filePath: `./src/styles/global.${answers.styleExtension}`, + fileType: "text", + }); for (const file of files) { actions.push({ diff --git a/packages/create-webpack-app/src/index.ts b/packages/create-webpack-app/src/index.ts index 795cb913f13..90ddfd246e0 100644 --- a/packages/create-webpack-app/src/index.ts +++ b/packages/create-webpack-app/src/index.ts @@ -17,11 +17,8 @@ const baseAnswers: Answers = { projectPath: process.cwd(), langType: "none", devServer: true, - html: true, workboxWebpackPlugin: true, - cssType: "none", - isCSS: false, - isPostCSS: false, + cssPreprocessor: "none", packageManager: "npm", }; const initValues: Record = { @@ -32,18 +29,15 @@ const initValues: Record = { ...baseAnswers, langType: "ES6", useReactState: true, - cssType: "CSS only", }, vue: { ...baseAnswers, langType: "ES6", useVueStore: true, - cssType: "CSS only", }, svelte: { ...baseAnswers, langType: "ES6", - cssType: "CSS only", }, }; diff --git a/packages/create-webpack-app/templates/init/default/README.md.tpl b/packages/create-webpack-app/templates/init/default/README.md.tpl index 77c91681f17..56d88cd4933 100644 --- a/packages/create-webpack-app/templates/init/default/README.md.tpl +++ b/packages/create-webpack-app/templates/init/default/README.md.tpl @@ -13,3 +13,20 @@ yarn build ``` to bundle your application + +## HTML and CSS + +`index.html` is the entry point of this project — webpack bundles every script and +stylesheet the page references and emits the page as `dist/index.html`, with the +references rewritten to the generated assets. Both are supported out of the box, so +no loader or plugin is needed for `.html` and `.css` files. + +A preprocessor keeps that support and only adds its own loader, e.g. for Sass: + +```js +{ + test: /\.s[ac]ss$/i, + type: "css/auto", + use: ["sass-loader"], +} +``` diff --git a/packages/create-webpack-app/templates/init/default/index.html.tpl b/packages/create-webpack-app/templates/init/default/index.html.tpl index 4cf8cdb1986..099b4addcd9 100644 --- a/packages/create-webpack-app/templates/init/default/index.html.tpl +++ b/packages/create-webpack-app/templates/init/default/index.html.tpl @@ -3,11 +3,12 @@ Webpack App + +

Hello world!

-

Tip: Check your console

<% if (html) { %> - <% } %> +

Tip: Check your console

<% if (workboxWebpackPlugin) { %>