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..75ee6d20e2d 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,17 @@ 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?", + choices: ["built-in", "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 +55,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 +86,24 @@ 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, so pin what it supports + devDependencies.push("typescript@5", "ts-loader"); + } else { + devDependencies.push("typescript"); + } break; } @@ -109,22 +115,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 +173,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..23f70b2225e 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,39 @@ 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, so pin what it supports + devDependencies.push("typescript@5", "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 +170,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..0d8fedfdc1e 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,40 @@ 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, so pin what it supports + devDependencies.push("typescript@5", "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 +154,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..144d1a0ae0e 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,8 @@ 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, so pin what it supports + devDependencies.push("typescript@5", "ts-loader"); break; } @@ -109,32 +101,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 +176,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) { %>