diff --git a/.changeset/init-no-html-css-questions.md b/.changeset/init-no-html-css-questions.md
new file mode 100644
index 00000000000..589bd27f1ff
--- /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`, scaffold TypeScript on webpack's own support, and keep one question for the CSS tools webpack does not cover (PostCSS, Sass, Less, Stylus, and each preprocessor with PostCSS)
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..96ca1f8f710 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`.
+Picking Sass, Less, Stylus or PostCSS adds only that tool's loader on top.
+
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..36631184655 100644
--- a/packages/create-webpack-app/src/generators/init/default.ts
+++ b/packages/create-webpack-app/src/generators/init/default.ts
@@ -3,6 +3,12 @@ import { fileURLToPath } from "node:url";
import { type DynamicActionsFunction, type NodePlopAPI } from "node-plop";
import { type ActionType, type Answers, type FileRecord } from "../../types.js";
+const STYLE_EXTENSIONS: Record = {
+ SASS: "scss",
+ LESS: "less",
+ Stylus: "styl",
+};
+
export default async function defaultInitGenerator(plop: NodePlopAPI) {
const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -28,15 +34,28 @@ export default async function defaultInitGenerator(plop: NodePlopAPI) {
default: "none",
},
{
- type: "confirm",
- name: "devServer",
- message: "Would you like to use Webpack Dev server?",
- default: true,
+ type: "list",
+ name: "tsCompiler",
+ message: "How should TypeScript be compiled?",
+ // The labels spell out the trade-off; a label must not end with `)`,
+ // which is what the prompt tests watch for to send the next answer
+ choices: [
+ {
+ name: "webpack itself: strips types, no type checking, needs Node.js >= 22.6",
+ value: "built-in",
+ },
+ {
+ name: "ts-loader: type checks while it builds, and handles .tsx, enums and decorators",
+ value: "ts-loader",
+ },
+ ],
+ default: "built-in",
+ when: (answers: Answers) => answers.langType === "Typescript",
},
{
type: "confirm",
- name: "html",
- message: "Do you want to simplify the creation of HTML files for your bundle?",
+ name: "devServer",
+ message: "Would you like to use Webpack Dev server?",
default: true,
},
{
@@ -47,33 +66,19 @@ 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: "cssTool",
+ message: "Which CSS tool do you want to use?",
+ choices: [
+ "none",
+ "PostCSS",
+ "SASS",
+ "SASS with PostCSS",
+ "LESS",
+ "LESS with PostCSS",
+ "Stylus",
+ "Stylus with PostCSS",
+ ],
+ default: "none",
},
{
type: "list",
@@ -92,12 +97,25 @@ export default async function defaultInitGenerator(plop: NodePlopAPI) {
actions: function actions(answers: Answers) {
const actions: ActionType[] = [];
+ // the config template reads this, so it has to be set whatever the language is
+ answers.useTsLoader = false;
+
switch (answers.langType) {
case "ES6":
devDependencies.push("babel-loader", "@babel/core", "@babel/preset-env");
break;
case "Typescript":
- devDependencies.push("typescript", "ts-loader");
+ // Type stripping covers erasable syntax only, so ts-loader stays on
+ // offer; either way `typescript` backs the editor and `check:types`
+ answers.useTsLoader = answers.tsCompiler === "ts-loader";
+
+ if (answers.useTsLoader) {
+ // ts-loader 9 (its latest) throws on TypeScript 7, the native port,
+ // so pin the last JavaScript-line release it still works with
+ devDependencies.push("typescript@6", "ts-loader");
+ } else {
+ devDependencies.push("typescript");
+ }
break;
}
@@ -109,22 +127,31 @@ export default async function defaultInitGenerator(plop: NodePlopAPI) {
devDependencies.push("workbox-webpack-plugin");
}
- if (answers.isPostCSS) {
+ const cssTool = (answers.cssTool as string | undefined) ?? "none";
+ // PostCSS runs on its own or over a preprocessor, so both are read off the answer
+ const preprocessor = Object.keys(STYLE_EXTENSIONS).find((name) => cssTool.startsWith(name));
+
+ answers.usePostCSS = cssTool.includes("PostCSS");
+ answers.useSASS = preprocessor === "SASS";
+ answers.useLESS = preprocessor === "LESS";
+ answers.useStylus = preprocessor === "Stylus";
+ // The starter stylesheet is written in the language that was picked
+ answers.styleExtension = preprocessor ? STYLE_EXTENSIONS[preprocessor] : "css";
+
+ if (answers.usePostCSS) {
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;
- }
+ if (answers.useSASS) {
+ devDependencies.push("sass-loader", "sass");
+ }
+
+ if (answers.useLESS) {
+ devDependencies.push("less-loader", "less");
+ }
+
+ if (answers.useStylus) {
+ devDependencies.push("stylus-loader", "stylus");
}
const files: FileRecord[] = [
@@ -158,10 +185,15 @@ export default async function defaultInitGenerator(plop: NodePlopAPI) {
break;
}
- if (answers.isPostCSS) {
+ if (answers.usePostCSS) {
files.push({ filePath: "postcss.config.js", fileType: "text" });
}
+ files.push({
+ filePath: `./src/styles.${answers.styleExtension}`,
+ fileType: "text",
+ });
+
for (const file of files) {
actions.push({
type: "generate-files",
diff --git a/packages/create-webpack-app/src/generators/init/react.ts b/packages/create-webpack-app/src/generators/init/react.ts
index 69e93ae9532..2b342883cfb 100644
--- a/packages/create-webpack-app/src/generators/init/react.ts
+++ b/packages/create-webpack-app/src/generators/init/react.ts
@@ -3,6 +3,12 @@ import { fileURLToPath } from "node:url";
import { type DynamicActionsFunction, type NodePlopAPI } from "node-plop";
import { type ActionType, type Answers, type FileRecord } from "../../types.js";
+const STYLE_EXTENSIONS: Record = {
+ SASS: "scss",
+ LESS: "less",
+ Stylus: "styl",
+};
+
export default async function reactInitGenerator(plop: NodePlopAPI) {
const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -49,33 +55,19 @@ 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: "cssTool",
+ message: "Which CSS tool do you want to use?",
+ choices: [
+ "none",
+ "PostCSS",
+ "SASS",
+ "SASS with PostCSS",
+ "LESS",
+ "LESS with PostCSS",
+ "Stylus",
+ "Stylus with PostCSS",
+ ],
+ default: "none",
},
{
type: "list",
@@ -94,7 +86,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":
@@ -106,33 +97,40 @@ export default async function reactInitGenerator(plop: NodePlopAPI) {
);
break;
case "Typescript":
- devDependencies.push("typescript", "ts-loader", "@types/react", "@types/react-dom");
+ // ts-loader 9 (its latest) throws on TypeScript 7, the native port, so pin
+ // the last release of the JavaScript line it still works with
+ devDependencies.push("typescript@6", "ts-loader", "@types/react", "@types/react-dom");
break;
}
- if (answers.isPostCSS) {
+ if (answers.workboxWebpackPlugin) {
+ devDependencies.push("workbox-webpack-plugin");
+ }
+
+ const cssTool = (answers.cssTool as string | undefined) ?? "none";
+ // PostCSS runs on its own or over a preprocessor, so both are read off the answer
+ const preprocessor = Object.keys(STYLE_EXTENSIONS).find((name) => cssTool.startsWith(name));
+
+ answers.usePostCSS = cssTool.includes("PostCSS");
+ answers.useSASS = preprocessor === "SASS";
+ answers.useLESS = preprocessor === "LESS";
+ answers.useStylus = preprocessor === "Stylus";
+ // The starter stylesheet is written in the language that was picked
+ answers.styleExtension = preprocessor ? STYLE_EXTENSIONS[preprocessor] : "css";
+
+ if (answers.usePostCSS) {
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.useSASS) {
+ devDependencies.push("sass-loader", "sass");
}
- if (answers.workboxWebpackPlugin) {
- devDependencies.push("workbox-webpack-plugin");
+
+ if (answers.useLESS) {
+ devDependencies.push("less-loader", "less");
+ }
+
+ if (answers.useStylus) {
+ devDependencies.push("stylus-loader", "stylus");
}
const files: FileRecord[] = [
@@ -173,21 +171,15 @@ 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;
+ if (answers.usePostCSS) {
+ files.push({ filePath: "postcss.config.js", fileType: "text" });
}
+ files.push({
+ filePath: `./src/styles/global.${answers.styleExtension}`,
+ fileType: "text",
+ });
+
for (const file of files) {
actions.push({
type: "generate-files",
diff --git a/packages/create-webpack-app/src/generators/init/svelte.ts b/packages/create-webpack-app/src/generators/init/svelte.ts
index 947fa9de2a4..5c39c0a69a7 100644
--- a/packages/create-webpack-app/src/generators/init/svelte.ts
+++ b/packages/create-webpack-app/src/generators/init/svelte.ts
@@ -3,6 +3,12 @@ import { fileURLToPath } from "node:url";
import { type DynamicActionsFunction, type NodePlopAPI } from "node-plop";
import { type ActionType, type Answers, type FileRecord } from "../../types.js";
+const STYLE_EXTENSIONS: Record = {
+ SASS: "scss",
+ LESS: "less",
+ Stylus: "styl",
+};
+
export default async function svelteInitGenerator(plop: NodePlopAPI) {
const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -40,33 +46,19 @@ 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: "cssTool",
+ message: "Which CSS tool do you want to use?",
+ choices: [
+ "none",
+ "PostCSS",
+ "SASS",
+ "SASS with PostCSS",
+ "LESS",
+ "LESS with PostCSS",
+ "Stylus",
+ "Stylus with PostCSS",
+ ],
+ default: "none",
},
{
type: "list",
@@ -85,7 +77,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) {
@@ -93,36 +84,41 @@ export default async function svelteInitGenerator(plop: NodePlopAPI) {
devDependencies.push("babel-loader", "@babel/core", "@babel/preset-env");
break;
case "Typescript":
- devDependencies.push("typescript", "ts-loader", "@tsconfig/svelte");
+ // ts-loader 9 (its latest) throws on TypeScript 7, the native port, so pin
+ // the last release of the JavaScript line it still works with
+ devDependencies.push("typescript@6", "ts-loader", "@tsconfig/svelte");
break;
}
- if (answers.isPostCSS) {
+ if (answers.workboxWebpackPlugin) {
+ devDependencies.push("workbox-webpack-plugin");
+ }
+
+ const cssTool = (answers.cssTool as string | undefined) ?? "none";
+ // PostCSS runs on its own or over a preprocessor, so both are read off the answer
+ const preprocessor = Object.keys(STYLE_EXTENSIONS).find((name) => cssTool.startsWith(name));
+
+ answers.usePostCSS = cssTool.includes("PostCSS");
+ answers.useSASS = preprocessor === "SASS";
+ answers.useLESS = preprocessor === "LESS";
+ answers.useStylus = preprocessor === "Stylus";
+ // The starter stylesheet is written in the language that was picked
+ answers.styleExtension = preprocessor ? STYLE_EXTENSIONS[preprocessor] : "css";
+
+ if (answers.usePostCSS) {
devDependencies.push("postcss-loader", "postcss", "autoprefixer");
}
- if (answers.workboxWebpackPlugin) {
- devDependencies.push("workbox-webpack-plugin");
+ if (answers.useSASS) {
+ devDependencies.push("sass-loader", "sass");
}
- 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.useLESS) {
+ devDependencies.push("less-loader", "less");
+ }
+
+ if (answers.useStylus) {
+ devDependencies.push("stylus-loader", "stylus");
}
const files: FileRecord[] = [
@@ -159,21 +155,15 @@ 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;
+ if (answers.usePostCSS) {
+ files.push({ filePath: "postcss.config.js", fileType: "text" });
}
+ files.push({
+ filePath: `./src/styles/global.${answers.styleExtension}`,
+ fileType: "text",
+ });
+
for (const file of files) {
actions.push({
type: "generate-files",
diff --git a/packages/create-webpack-app/src/generators/init/vue.ts b/packages/create-webpack-app/src/generators/init/vue.ts
index edc3b43a648..035b508689c 100644
--- a/packages/create-webpack-app/src/generators/init/vue.ts
+++ b/packages/create-webpack-app/src/generators/init/vue.ts
@@ -3,6 +3,12 @@ import { fileURLToPath } from "node:url";
import { type DynamicActionsFunction, type NodePlopAPI } from "node-plop";
import { type ActionType, type Answers, type FileRecord } from "../../types.js";
+const STYLE_EXTENSIONS: Record = {
+ SASS: "scss",
+ LESS: "less",
+ Stylus: "styl",
+};
+
export default async function vueInitGenerator(plop: NodePlopAPI) {
const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -48,33 +54,19 @@ 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: "cssTool",
+ message: "Which CSS tool do you want to use?",
+ choices: [
+ "none",
+ "PostCSS",
+ "SASS",
+ "SASS with PostCSS",
+ "LESS",
+ "LESS with PostCSS",
+ "Stylus",
+ "Stylus with PostCSS",
+ ],
+ default: "none",
},
{
type: "list",
@@ -93,7 +85,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) {
@@ -101,7 +92,9 @@ export default async function vueInitGenerator(plop: NodePlopAPI) {
devDependencies.push("babel-loader", "@babel/core", "@babel/preset-env");
break;
case "Typescript":
- devDependencies.push("typescript", "ts-loader");
+ // ts-loader 9 (its latest) throws on TypeScript 7, the native port, so pin
+ // the last release of the JavaScript line it still works with
+ devDependencies.push("typescript@6", "ts-loader");
break;
}
@@ -109,32 +102,35 @@ export default async function vueInitGenerator(plop: NodePlopAPI) {
devDependencies.push("pinia");
}
- if (answers.isPostCSS) {
+ if (answers.workboxWebpackPlugin) {
+ devDependencies.push("workbox-webpack-plugin");
+ }
+
+ const cssTool = (answers.cssTool as string | undefined) ?? "none";
+ // PostCSS runs on its own or over a preprocessor, so both are read off the answer
+ const preprocessor = Object.keys(STYLE_EXTENSIONS).find((name) => cssTool.startsWith(name));
+
+ answers.usePostCSS = cssTool.includes("PostCSS");
+ answers.useSASS = preprocessor === "SASS";
+ answers.useLESS = preprocessor === "LESS";
+ answers.useStylus = preprocessor === "Stylus";
+ // The starter stylesheet is written in the language that was picked
+ answers.styleExtension = preprocessor ? STYLE_EXTENSIONS[preprocessor] : "css";
+
+ if (answers.usePostCSS) {
devDependencies.push("postcss-loader", "postcss", "autoprefixer");
}
- if (answers.workboxWebpackPlugin) {
- devDependencies.push("workbox-webpack-plugin");
+ if (answers.useSASS) {
+ devDependencies.push("sass-loader", "sass");
}
- 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.useLESS) {
+ devDependencies.push("less-loader", "less");
+ }
+
+ if (answers.useStylus) {
+ devDependencies.push("stylus-loader", "stylus");
}
const files: FileRecord[] = [
@@ -181,21 +177,15 @@ 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;
+ if (answers.usePostCSS) {
+ files.push({ filePath: "postcss.config.js", fileType: "text" });
}
+ files.push({
+ filePath: `./src/styles/global.${answers.styleExtension}`,
+ fileType: "text",
+ });
+
for (const file of files) {
actions.push({
type: "generate-files",
diff --git a/packages/create-webpack-app/src/index.ts b/packages/create-webpack-app/src/index.ts
index 795cb913f13..8749367f6d7 100644
--- a/packages/create-webpack-app/src/index.ts
+++ b/packages/create-webpack-app/src/index.ts
@@ -17,11 +17,9 @@ const baseAnswers: Answers = {
projectPath: process.cwd(),
langType: "none",
devServer: true,
- html: true,
workboxWebpackPlugin: true,
- cssType: "none",
- isCSS: false,
- isPostCSS: false,
+ cssTool: "none",
+ tsCompiler: "built-in",
packageManager: "npm",
};
const initValues: Record = {
@@ -32,18 +30,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..c706ab547a7 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,47 @@ 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.
+
+Sass, Less, Stylus and PostCSS keep that support and only add their own loader on
+top of it, as a `css/auto` rule:
+
+```js
+{
+ test: /\.s[ac]ss$/i,
+ type: "css/auto",
+ use: ["sass-loader"],
+}
+```
+
+They combine, too: webpack applies `use` right to left, so `["postcss-loader",
+"sass-loader"]` compiles the Sass first and runs PostCSS over the result.
+<% if (langType === "Typescript") { %>
+## TypeScript
+
+<% if (useTsLoader) { %>This project compiles TypeScript with `ts-loader`, which type
+checks as it builds and handles every TypeScript feature, `.tsx` included.
+
+webpack can also strip the types itself, with no loader at all — it is faster, but it
+covers erasable syntax only (types, generics, `import type`, casts) and checks nothing.
+Re-run `create-webpack-app` and answer `built-in` to that question to try it.<% } else { %>webpack strips the types itself (it needs Node.js >= 22.6), so this project has no
+TypeScript loader. Stripping is erasable syntax only — types, generics, `import type`
+and casts — so `enum`, `namespace`, parameter properties, decorator metadata and `.tsx`
+need a loader instead: re-run `create-webpack-app` and answer `ts-loader` to that
+question, which also type checks as it builds.<% } %>
+
+<% if (useTsLoader) { %>Either way, `tsconfig.json` is what your editor reads, and:<% } else { %>Nothing is type checked during the build. `tsconfig.json` is what your editor reads,
+and:<% } %>
+
+```bash
+npm run check:types
+```
+
+runs `tsc --noEmit` over the project.
+<% } %>
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) { %>