diff --git a/packages/create-webpack-app/README.md b/packages/create-webpack-app/README.md new file mode 100644 index 00000000000..dd8bd651ebe --- /dev/null +++ b/packages/create-webpack-app/README.md @@ -0,0 +1,63 @@ +# create-webpack-app + +`create-webpack-app` is used to initialize `webpack` projects quickly by scaffolding configuration and creating a runnable project with all the dependencies based on the user preferences. + +## Table of Contents + +- [Initial Setup](#initial-setup) + - [Local Setup](#local-setup) + - [Global Setup](#global-setup) +- [Usage](#usage) + - [Running Locally](#running-locally) + - [Running Globally](#running-globally) + - [CLI options](#cli-options) +- [Description of questions asked by the generator](#description-of-questions-asked-by-the-generator) + - [Default Template](#default-template) + +## Setup + +Install `webpack` and `webpack-cli` as devDependencies + +```shell +npm install --save-dev webpack webpack-cli +``` + +## Usage + +### Running Locally + +```bash +npx create-webpack-app +``` + +### Running Globally + +```shell +create-webpack-app +``` + +### CLI options + +**To generate default template** + +```bash +create-webpack-app +``` + +**To generate with default answers** + +```bash +create-webpack-app -f, --force +``` + +**To scaffold in a specified path** + +```bash +create-webpack-app [generation-path] +``` + +**To scaffold specified template** + +```bash +create-webpack-app -t, --template +``` diff --git a/packages/create-webpack-app/bin/index.js b/packages/create-webpack-app/bin/index.js new file mode 100755 index 00000000000..66ad0b226cf --- /dev/null +++ b/packages/create-webpack-app/bin/index.js @@ -0,0 +1,5 @@ +#!/usr/bin/env node + +const run = require("../lib/index"); + +run(); diff --git a/packages/create-webpack-app/package.json b/packages/create-webpack-app/package.json new file mode 100644 index 00000000000..37f838a3f24 --- /dev/null +++ b/packages/create-webpack-app/package.json @@ -0,0 +1,35 @@ +{ + "name": "create-webpack-app", + "version": "1.0.0", + "description": "Create new project with webpack", + "main": "lib/index.js", + "bin": "bin/index.js", + "types": "lib/index.d.ts", + "license": "MIT", + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "https://github.com/webpack/webpack-cli.git" + }, + "files": [ + "lib", + "templates", + "bin" + ], + "dependencies": { + "yeoman-environment": "^2.10.3", + "yeoman-generator": "^4.12.0", + "colorette": "^2.0.14", + "commander": "^7.0.0" + }, + "peerDependenciesMeta": { + "prettier": { + "optional": true + } + }, + "devDependencies": { + "@types/yeoman-generator": "^4.11.3" + } +} diff --git a/packages/create-webpack-app/src/generator.ts b/packages/create-webpack-app/src/generator.ts new file mode 100644 index 00000000000..b9908569911 --- /dev/null +++ b/packages/create-webpack-app/src/generator.ts @@ -0,0 +1,123 @@ +import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs"; +import path from "path"; + +import { CustomGenerator } from "./types"; +import { getInstaller, getTemplate } from "./helpers"; +import * as Question from "./scaffold-utils"; +import handlers from "./handlers"; + +/** + * + * Generator for initializing a webpack config + * + * @class Generator + * @extends CustomGenerator + * @returns {Void} After execution, transforms are triggered + * + */ +export default class Generator extends CustomGenerator { + public answers: Record; + public configurationPath!: string; + public force: boolean; + public generationPath: string; + public packageManager!: string; + public resolvedGenerationPath: string; + public supportedTemplates: string[]; + public template: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + public cli: any; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + public constructor(args: any, opts: any) { + super(args, opts); + + const { options } = opts; + + this.template = options.template; + this.generationPath = options.generationPath; + this.resolvedGenerationPath = path.resolve(process.cwd(), this.generationPath); + this.force = options.force; + this.dependencies = ["webpack", "webpack-cli"]; + this.supportedTemplates = Object.keys(handlers); + this.answers = {}; + this.cli = opts.cli; + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + public async prompting(): Promise { + if (!existsSync(this.resolvedGenerationPath)) { + this.cli.logger.log( + `${this.cli.colors.blue( + "ℹ INFO ", + )} supplied generation path doesn't exist, required folders will be created.`, + ); + try { + mkdirSync(this.resolvedGenerationPath, { recursive: true }); + } catch (error) { + this.cli.logger.error(`Failed to create directory.\n ${error}`); + process.exit(2); + } + } + + this.template = await getTemplate(this); + + await handlers[this.template].questions(this, Question); + + // Handle installation of prettier + try { + // eslint-disable-next-line node/no-extraneous-require + require.resolve("prettier"); + } catch (err) { + const { installPrettier } = await Question.Confirm( + this, + "installPrettier", + "Do you like to install prettier to format generated configuration?", + true, + false, + ); + + if (installPrettier) { + this.dependencies.push("prettier"); + } + } + } + + public async installPlugins(): Promise { + this.packageManager = await getInstaller(this); + + const opts: { + dev?: boolean; + "save-dev"?: boolean; + } = this.packageManager === "yarn" ? { dev: true } : { "save-dev": true }; + + this.scheduleInstallTask(this.packageManager, this.dependencies, opts, { + cwd: this.generationPath, + }); + } + + public writing(): void { + this.cli.logger.log(`${this.cli.colors.blue("ℹ INFO ")} Initialising project...`); + + handlers[this.template].generate(this); + } + + public end(): void { + // Prettify configuration file if possible + try { + // eslint-disable-next-line node/no-extraneous-require, @typescript-eslint/no-var-requires + const prettier = require("prettier"); + const source = readFileSync(this.configurationPath, { encoding: "utf8" }); + const formattedSource = prettier.format(source, { parser: "babel" }); + + writeFileSync(this.configurationPath, formattedSource); + } catch (err) { + this.cli.logger.log( + `${this.cli.colors.yellow( + `⚠ Generated configuration may not be properly formatted as prettier is not installed.`, + )}`, + ); + + return; + } + } +} diff --git a/packages/create-webpack-app/src/handlers.ts b/packages/create-webpack-app/src/handlers.ts new file mode 100644 index 00000000000..bbec8ff339a --- /dev/null +++ b/packages/create-webpack-app/src/handlers.ts @@ -0,0 +1,5 @@ +import * as defaultHandler from "./handlers/default"; + +export default { + default: defaultHandler, +} as Record; diff --git a/packages/create-webpack-app/src/handlers/default.ts b/packages/create-webpack-app/src/handlers/default.ts new file mode 100644 index 00000000000..e4251ceaa46 --- /dev/null +++ b/packages/create-webpack-app/src/handlers/default.ts @@ -0,0 +1,217 @@ +import path from "path"; +import { CustomGenerator } from "../types"; + +const templatePath = path.resolve(__dirname, "../../templates/default"); +const resolveFile = (file: string): string => { + return path.resolve(templatePath, file); +}; + +/** + * Asks questions to the user used to modify generation + * @param self Generator values + * @param Question Contains questions + */ + +export async function questions( + self: CustomGenerator, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + Question: Record, +): Promise { + // Handle JS language solutions + const { langType } = await Question.List( + self, + "langType", + "Which of the following JS solutions do you want to use?", + ["none", "ES6", "Typescript"], + "none", + self.force, + ); + + switch (langType) { + case "ES6": + self.dependencies.push("babel-loader", "@babel/core", "@babel/preset-env"); + break; + case "Typescript": + self.dependencies.push("typescript", "ts-loader"); + break; + } + + // Configure devServer configuration + const { devServer } = await Question.Confirm( + self, + "devServer", + "Do you want to use webpack-dev-server?", + true, + self.force, + ); + if (devServer) { + self.dependencies.push("webpack-dev-server"); + } + + // Handle addition of html-webpack-plugin + const { htmlWebpackPlugin } = await Question.Confirm( + self, + "htmlWebpackPlugin", + "Do you want to simplify the creation of HTML files for your bundle?", + true, + self.force, + ); + if (htmlWebpackPlugin) { + self.dependencies.push("html-webpack-plugin"); + } + + // Handle addition of workbox-webpack-plugin + const { workboxWebpackPlugin } = await Question.Confirm( + self, + "workboxWebpackPlugin", + "Do you want to add PWA support?", + true, + self.force, + ); + if (workboxWebpackPlugin) { + self.dependencies.push("workbox-webpack-plugin"); + } + + // Store all answers for generation + self.answers = { + ...self.answers, + langType, + devServer, + htmlWebpackPlugin, + workboxWebpackPlugin, + }; + + // Handle CSS solutions + const { cssType } = await Question.List( + self, + "cssType", + "Which of the following CSS solutions do you want to use?", + ["none", "CSS only", "SASS", "LESS", "Stylus"], + "none", + self.force, + ); + + if (cssType == "none") { + self.answers = { + ...self.answers, + cssType, + isCSS: false, + isPostCSS: false, + extractPlugin: "No", + }; + return; + } + + const { isCSS } = + cssType != "CSS only" + ? await Question.Confirm( + self, + "isCSS", + `Will you be using CSS styles along with ${cssType} in your project?`, + true, + self.force, + ) + : { isCSS: true }; + + const { isPostCSS } = await Question.Confirm( + self, + "isPostCSS", + "Will you be using PostCSS in your project?", + cssType == "CSS only", + self.force, + ); + + const { extractPlugin } = await Question.List( + self, + "extractPlugin", + "Do you want to extract CSS for every file?", + ["No", "Only for Production", "Yes"], + "No", + self.force, + ); + + switch (cssType) { + case "SASS": + self.dependencies.push("sass-loader", "sass"); + break; + case "LESS": + self.dependencies.push("less-loader", "less"); + break; + case "Stylus": + self.dependencies.push("stylus-loader", "stylus"); + break; + } + + if (isCSS) { + self.dependencies.push("style-loader", "css-loader"); + } + + if (isPostCSS) { + self.dependencies.push("postcss-loader", "postcss", "autoprefixer"); + } + + if (extractPlugin !== "No") { + self.dependencies.push("mini-css-extract-plugin"); + } + + self.answers = { + ...self.answers, + cssType, + isCSS, + isPostCSS, + extractPlugin, + }; +} + +/** + * Handles generation of project files + * @param self Generator values + */ +export function generate(self: CustomGenerator): void { + self.fs.extendJSON( + self.destinationPath("package.json"), + // eslint-disable-next-line @typescript-eslint/no-var-requires + require(resolveFile("package.json.js"))(self.answers.devServer), + ); + + // Generate entry file + let entry = "./src/index."; + if (self.answers.langType == "Typescript") { + entry += "ts"; + } else { + entry += "js"; + } + self.fs.copyTpl(resolveFile("index.js"), self.destinationPath(entry)); + + // Generate README + self.fs.copyTpl(resolveFile("README.md"), self.destinationPath("README.md"), {}); + + // Generate HTML file + self.fs.copyTpl( + resolveFile("template.html.tpl"), + self.destinationPath("index.html"), + self.answers, + ); + + // Generate webpack configuration + self.fs.copyTpl(resolveFile("webpack.configjs.tpl"), self.destinationPath("webpack.config.js"), { + ...self.answers, + entry, + }); + self.configurationPath = self.destinationPath("webpack.config.js"); + + // Generate JS language essentials + switch (self.answers.langType) { + case "ES6": + self.fs.copyTpl(resolveFile(".babelrc"), self.destinationPath(".babelrc")); + break; + case "Typescript": + self.fs.copyTpl(resolveFile("tsconfig.json"), self.destinationPath("tsconfig.json")); + break; + } + + // Generate postcss configuration + if (self.answers.isPostCSS) { + self.fs.copyTpl(resolveFile("postcss.config.js"), self.destinationPath("postcss.config.js")); + } +} diff --git a/packages/create-webpack-app/src/helpers.ts b/packages/create-webpack-app/src/helpers.ts new file mode 100644 index 00000000000..596bac8af37 --- /dev/null +++ b/packages/create-webpack-app/src/helpers.ts @@ -0,0 +1,63 @@ +import Generator from "./generator"; +import { List } from "./scaffold-utils"; + +const regex = /[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g; + +/** + * Convert str to kebab-case + * @param str input string + * @returns output string + */ +export function toKebabCase(str: string): string { + return (str.match(regex) as string[]).join("-").toLowerCase(); +} + +/** + * Convert str to UpperCamelCase + * @param str import string + * @returns {string} output string + */ +export function toUpperCamelCase(str: string): string { + return (str.match(regex) as string[]) + .map((x) => x.slice(0, 1).toUpperCase() + x.slice(1).toLowerCase()) + .join(""); +} + +export async function getInstaller(self: Generator): Promise { + const installers = self.cli.getAvailablePackageManagers(); + + if (installers.length === 1) { + return installers[0]; + } + + // Prompt for the package manager of choice + const defaultPackager = self.cli.getDefaultPackageManager(); + const { packager } = await List( + self, + "packager", + "Pick a package manager:", + installers, + defaultPackager, + self.force, + ); + return packager; +} + +export async function getTemplate(self: Generator): Promise { + if (self.supportedTemplates.includes(self.template)) { + return self.template; + } + + self.cli.logger.warn(`⚠ ${self.template} is not a valid template, please select one from below`); + + const { selectedTemplate } = await List( + self, + "selectedTemplate", + "Select a valid template from below:", + self.supportedTemplates, + "default", + false, + ); + + return selectedTemplate; +} diff --git a/packages/create-webpack-app/src/index.ts b/packages/create-webpack-app/src/index.ts new file mode 100644 index 00000000000..848763a45de --- /dev/null +++ b/packages/create-webpack-app/src/index.ts @@ -0,0 +1,65 @@ +import yeoman from "yeoman-environment"; +import { Command } from "commander"; +import Generator from "./generator"; +import util from "util"; +import { createColors, isColorSupported } from "colorette"; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function getLogger(colors: any) { + return { + error: (val: string) => console.error(`[create-webpack-app] ${colors.red(util.format(val))}`), + warn: (val: string) => console.warn(`[create-webpack-app] ${colors.yellow(val)}`), + info: (val: string) => console.info(`[create-webpack-app] ${colors.cyan(val)}`), + success: (val: string) => console.log(`[create-webpack-app] ${colors.green(val)}`), + log: (val: string) => console.log(`[create-webpack-app] ${val}`), + raw: (val: string) => console.log(val), + }; +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function getColors(useColor?: any) { + let shouldUseColor: boolean; + + if (useColor) { + shouldUseColor = useColor; + } else { + shouldUseColor = isColorSupported; + } + + return { ...createColors({ useColor: shouldUseColor }), isColorSupported: shouldUseColor }; +} + +function getOptions() { + const program = new Command(); + + program.name("create-webpack-app"); + program.description("create new webpack project", { + "generation-path": "Path to the installation directory, e.g. ./projectName", + }); + program.usage("[generation-path] [options]"); + program.option("-t, --template [type]", "Type of template", "default"); + program.option("-f, --force", "Generate without questions (ideally) using default answers"); + + program.parse(); + + return program.opts(); +} + +export default function (): void { + const colors = getColors(); + const logger = getLogger(colors); + const { generationPath, options } = getOptions(); + + options.generationPath = generationPath || "."; + + const env = yeoman.createEnv([], { + cwd: options.generationPath, + }); + const generatorName = "create-webpack-app-generator"; + + env.registerStub(Generator as never, generatorName); + + env.run(generatorName, { cli: { colors, logger }, options }, () => { + logger.success("Project has been initialised with webpack!"); + }); +} diff --git a/packages/create-webpack-app/src/scaffold-utils.ts b/packages/create-webpack-app/src/scaffold-utils.ts new file mode 100644 index 00000000000..ec9749e0068 --- /dev/null +++ b/packages/create-webpack-app/src/scaffold-utils.ts @@ -0,0 +1,75 @@ +import Generator from "yeoman-generator"; + +type CustomGeneratorStringPrompt = { [x: string]: string } | Promise<{ [x: string]: string }>; +type CustomGeneratorBoolPrompt = { [x: string]: boolean } | Promise<{ [x: string]: boolean }>; + +/* eslint-disable @typescript-eslint/no-explicit-any */ + +export function List( + self: Generator, + name: string, + message: string, + choices: string[], + defaultChoice: string, + skip = false, +): CustomGeneratorStringPrompt { + if (skip) { + return { [name]: defaultChoice }; + } + + return self.prompt([{ choices, message, name, type: "list", default: defaultChoice }]); +} + +export function Input( + self: Generator, + name: string, + message: string, + defaultChoice: string, + skip = false, +): CustomGeneratorStringPrompt { + if (skip) { + return { [name]: defaultChoice }; + } + + return self.prompt([{ default: defaultChoice, message, name, type: "input" }]); +} + +export function InputValidate( + self: Generator, + name: string, + message: string, + cb?: (input: string) => string | boolean, + defaultChoice?: string, + skip = false, +): Record | any { + if (skip) { + return { [name]: defaultChoice }; + } + + const input: Generator.Question = { + message, + name, + type: "input", + validate: cb, + }; + + if (defaultChoice) { + input.default = defaultChoice; + } + + return self.prompt([input]); +} + +export function Confirm( + self: Generator, + name: string, + message: string, + defaultChoice = true, + skip = false, +): CustomGeneratorBoolPrompt { + if (skip) { + return { [name]: defaultChoice }; + } + + return self.prompt([{ default: defaultChoice, message, name, type: "confirm" }]); +} diff --git a/packages/create-webpack-app/src/types/index.ts b/packages/create-webpack-app/src/types/index.ts new file mode 100644 index 00000000000..de1cfb94aa6 --- /dev/null +++ b/packages/create-webpack-app/src/types/index.ts @@ -0,0 +1,8 @@ +import Generator from "yeoman-generator"; + +export class CustomGenerator extends Generator { + public force!: boolean; + public dependencies!: string[]; + public answers!: Record; + public configurationPath!: string; +} diff --git a/packages/create-webpack-app/templates/default/.babelrc b/packages/create-webpack-app/templates/default/.babelrc new file mode 100644 index 00000000000..f2c9da3ae6e --- /dev/null +++ b/packages/create-webpack-app/templates/default/.babelrc @@ -0,0 +1,11 @@ +{ + "plugins": ["@babel/syntax-dynamic-import"], + "presets": [ + [ + "@babel/preset-env", + { + "modules": false + } + ] + ] +} diff --git a/packages/create-webpack-app/templates/default/README.md b/packages/create-webpack-app/templates/default/README.md new file mode 100644 index 00000000000..fa32788f72a --- /dev/null +++ b/packages/create-webpack-app/templates/default/README.md @@ -0,0 +1,15 @@ +# 🚀 Welcome to your new awesome project! + +This project has been created using **webpack-cli**, you can now run + +``` +npm run build +``` + +or + +``` +yarn build +``` + +to bundle your application diff --git a/packages/create-webpack-app/templates/default/index.js b/packages/create-webpack-app/templates/default/index.js new file mode 100644 index 00000000000..019c0f4bc8e --- /dev/null +++ b/packages/create-webpack-app/templates/default/index.js @@ -0,0 +1 @@ +console.log("Hello World!"); diff --git a/packages/create-webpack-app/templates/default/package.json.js b/packages/create-webpack-app/templates/default/package.json.js new file mode 100644 index 00000000000..79d967ebe16 --- /dev/null +++ b/packages/create-webpack-app/templates/default/package.json.js @@ -0,0 +1,18 @@ +module.exports = (isUsingDevServer) => { + const scripts = { + build: "webpack --mode=production --node-env=production", + "build:dev": "webpack --mode=development", + "build:prod": "webpack --mode=production --node-env=production", + watch: "webpack --watch", + }; + if (isUsingDevServer) { + scripts.serve = "webpack serve"; + } + + return { + version: "1.0.0", + description: "My webpack project", + name: "my-webpack-project", + scripts, + }; +}; diff --git a/packages/create-webpack-app/templates/default/postcss.config.js b/packages/create-webpack-app/templates/default/postcss.config.js new file mode 100644 index 00000000000..3fa4289052e --- /dev/null +++ b/packages/create-webpack-app/templates/default/postcss.config.js @@ -0,0 +1,5 @@ +module.exports = { + // Add you postcss configuration here + // Learn more about it at https://github.com/webpack-contrib/postcss-loader#config-files + plugins: [["autoprefixer"]], +}; diff --git a/packages/create-webpack-app/templates/default/template.html.tpl b/packages/create-webpack-app/templates/default/template.html.tpl new file mode 100644 index 00000000000..d61cf83b04b --- /dev/null +++ b/packages/create-webpack-app/templates/default/template.html.tpl @@ -0,0 +1,26 @@ + + + + + Webpack App + + +

Hello world!

+

Tip: Check your console

+ + <% if (workboxWebpackPlugin) { %> + <% } %> + diff --git a/packages/create-webpack-app/templates/default/tsconfig.json b/packages/create-webpack-app/templates/default/tsconfig.json new file mode 100644 index 00000000000..31176e658c2 --- /dev/null +++ b/packages/create-webpack-app/templates/default/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "allowSyntheticDefaultImports": true, + "noImplicitAny": true, + "module": "es6", + "target": "es5", + "allowJs": true + }, + "files": ["src/index.ts"] +} diff --git a/packages/create-webpack-app/templates/default/webpack.configjs.tpl b/packages/create-webpack-app/templates/default/webpack.configjs.tpl new file mode 100644 index 00000000000..68d9a7ddc59 --- /dev/null +++ b/packages/create-webpack-app/templates/default/webpack.configjs.tpl @@ -0,0 +1,96 @@ +// Generated using webpack-cli https://github.com/webpack/webpack-cli + +const path = require('path');<% if (htmlWebpackPlugin) { %> +const HtmlWebpackPlugin = require('html-webpack-plugin');<% } %><% if (extractPlugin !== 'No') { %> +const MiniCssExtractPlugin = require('mini-css-extract-plugin');<% } %><% if (workboxWebpackPlugin) { %> +const WorkboxWebpackPlugin = require('workbox-webpack-plugin');<% } %> + +const isProduction = process.env.NODE_ENV == 'production'; +<% if (isCSS) { %> +<% if (extractPlugin === "Yes") { %> +const stylesHandler = MiniCssExtractPlugin.loader; +<% } else if (extractPlugin === "Only for Production") { %> +const stylesHandler = isProduction ? MiniCssExtractPlugin.loader : 'style-loader'; +<% } else { %> +const stylesHandler = 'style-loader'; +<% } %> +<% } %> + +const config = { + entry: '<%= entry %>', + output: { + path: path.resolve(__dirname, 'dist'), + },<% if (devServer) { %> + devServer: { + open: true, + host: 'localhost', + },<% } %> + plugins: [<% if (htmlWebpackPlugin) { %> + new HtmlWebpackPlugin({ + template: 'index.html', + }), +<% } %><% if (extractPlugin === "Yes") { %> + new MiniCssExtractPlugin(), +<% } %> + // Add your plugins here + // Learn more about plugins from https://webpack.js.org/configuration/plugins/ + ], + module: { + rules: [<% if (langType == "ES6") { %> + { + test: /\.(js|jsx)$/i, + loader: 'babel-loader', + },<% } %><% if (langType == "Typescript") { %> + { + test: /\.(ts|tsx)$/i, + loader: 'ts-loader', + exclude: ['/node_modules/'], + },<% } %><% if (isCSS && !isPostCSS) { %> + { + test: /\.css$/i, + use: [stylesHandler,'css-loader'], + },<% } %><% if (cssType == 'SASS') { %> + { + test: /\.s[ac]ss$/i, + use: [stylesHandler, 'css-loader', <% if (isPostCSS) { %>'postcss-loader', <% } %>'sass-loader'], + },<% } %><% if (cssType == 'LESS') { %> + { + test: /\.less$/i, + use: [<% if (isPostCSS) { %>stylesHandler, 'css-loader', 'postcss-loader', <% } %>'less-loader'], + },<% } %><% if (cssType == 'Stylus') { %> + { + test: /\.styl$/i, + use: [<% if (isPostCSS) { %>stylesHandler, 'css-loader', 'postcss-loader', <% } %>'stylus-loader'], + },<% } %><% if (isPostCSS && isCSS) { %> + { + test: /\.css$/i, + use: [stylesHandler, 'css-loader', 'postcss-loader'], + },<% } %> + { + test: /\.(eot|svg|ttf|woff|woff2|png|jpg|gif)$/i, + type: 'asset', + }, + + // Add your rules for custom modules here + // Learn more about loaders from https://webpack.js.org/loaders/ + ], + },<% if (langType == "Typescript") {%> + resolve: { + extensions: ['.tsx', '.ts', '.js'], + },<% } %> +}; + +module.exports = () => { + if (isProduction) { + config.mode = 'production'; + <% if (extractPlugin === "Only for Production") { %> + config.plugins.push(new MiniCssExtractPlugin()); + <% } %> + <% if (workboxWebpackPlugin) { %> + config.plugins.push(new WorkboxWebpackPlugin.GenerateSW()); + <% } %> + } else { + config.mode = 'development'; + } + return config; +}; diff --git a/packages/create-webpack-app/tsconfig.json b/packages/create-webpack-app/tsconfig.json new file mode 100644 index 00000000000..173f75d3f62 --- /dev/null +++ b/packages/create-webpack-app/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "exclude": ["src/utils/__tests__"], + "compilerOptions": { + "outDir": "lib", + "rootDir": "src" + }, + "include": ["src"] +} diff --git a/packages/generators/INIT.md b/packages/generators/INIT.md deleted file mode 100644 index 6961aba88d1..00000000000 --- a/packages/generators/INIT.md +++ /dev/null @@ -1,93 +0,0 @@ -# webpack-cli init - -`webpack-cli init` is used to initialize `webpack` projects quickly by scaffolding configuration and creating a runnable project with all the dependencies based on the user preferences. - -## Table of Contents - -- [Initial Setup](#initial-setup) - - [Local Setup](#local-setup) - - [Global Setup](#global-setup) -- [Usage](#usage) - - [Running Locally](#running-locally) - - [Running Globally](#running-globally) - - [CLI options](#cli-options) -- [Description of questions asked by the generator](#description-of-questions-asked-by-the-generator) - - [Default Template](#default-template) - -## Setup - -Install `webpack` and `webpack-cli` as devDependencies - -```shell -npm install --save-dev webpack webpack-cli -``` - -## Usage - -### Running Locally - -```bash -npx webpack-cli init -``` - -### Running Globally - -```shell -webpack-cli init -``` - -### CLI options - -**To generate default template** - -```bash -webpack-cli init -``` - -**To generate with default answers** - -```bash -webpack-cli init -f, --force -``` - -**To scaffold in a specified path** - -```bash -webpack-cli init [generation-path] -``` - -**To scaffold specified template** - -```bash -webpack-cli init -t, --template -``` - -## Description of questions asked by the generator - -### Default template - -1. `Which of the following JS solutions do you want to use?` - -> _Property/key resolved: [module.rules](https://webpack.js.org/configuration/module/#module-rules) (for .js, .ts and other related files)_ - -This enables webpack to parse [`ES2015`](https://babeljs.io/learn-es2015/) code or Typescript code as per choice. - -2. `Do you want to use webpack-dev-server?` - -> _Property/key resolved: [module.rules](https://webpack.js.org/configuration/dev-server/)_ - -Adds a development server to serve webpack bundles and hence make development faster. - -3. `Do you want to simplify the creation of HTML files for your bundle?` - -Adds `html-webpack-plugin` that simplifies creation of HTML files to serve your bundles. - -4. `Do you want to add PWA support?` - -Adds [`workbox-webpack-plugin`](https://developers.google.com/web/tools/workbox/modules/workbox-webpack-plugin) which generates a complete service worker for you. - -5. `Which of the following CSS solutions do you want to use?` - -> _Property/key resolved: [module.rules](https://webpack.js.org/configuration/module/#module-rules) (for .css files)_ - -If you use any sort of style in your project, such as [`.css`](https://developer.mozilla.org/en-US/docs/Web/CSS) you will need to select this here. If you don't use CSS, answer `none`. diff --git a/tsconfig.json b/tsconfig.json index 796ecc1c96e..8ba5108e005 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -33,6 +33,9 @@ { "path": "packages/serve" }, + { + "path": "packages/create-webpack-app" + }, { "path": "packages/webpack-cli" } diff --git a/yarn.lock b/yarn.lock index b92380837e1..3d83b721af6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4550,11 +4550,6 @@ detect-indent@^6.0.0: resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== -detect-newline@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" - integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== - detect-node@^2.0.4: version "2.1.0" resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1"