Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: create-webpack-app #3036

Draft
wants to merge 19 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -1,6 +1,6 @@
# webpack-cli init
# create-webpack-app

`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.
`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

Expand All @@ -27,39 +27,39 @@ npm install --save-dev webpack webpack-cli
### Running Locally

```bash
npx webpack-cli init
npx create-webpack-app
```

### Running Globally

```shell
webpack-cli init
create-webpack-app
```

### CLI options

**To generate default template**

```bash
webpack-cli init
create-webpack-app
```

**To generate with default answers**

```bash
webpack-cli init -f, --force
create-webpack-app -f, --force
```

**To scaffold in a specified path**

```bash
webpack-cli init [generation-path]
create-webpack-app [generation-path]
```

**To scaffold specified template**

```bash
webpack-cli init -t, --template <template-name>
create-webpack-app -t, --template <template-name>
```

## Description of questions asked by the generator
Expand Down
32 changes: 32 additions & 0 deletions packages/create-webpack-app/package.json
@@ -0,0 +1,32 @@
{
"name": "create-webpack-app",
"version": "1.0.0",
"description": "Create new project with webpack",
"main": "lib/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",
"template"
],
"dependencies": {
"yeoman-environment": "^2.10.3",
"yeoman-generator": "^4.12.0",
"commander": "^7.0.0"
},
"peerDependenciesMeta": {
"prettier": {
"optional": true
}
},
"devDependencies": {
"@types/yeoman-generator": "^4.11.3"
}
}
5 changes: 5 additions & 0 deletions packages/create-webpack-app/src/handlers.ts
@@ -0,0 +1,5 @@
import * as defaultHandler from "./handlers/default";

export default {
default: defaultHandler,
};
217 changes: 217 additions & 0 deletions 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, "../../init-template/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<string, any>,
): Promise<void> {
// 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 configuraion
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"));
}
}
63 changes: 63 additions & 0 deletions packages/create-webpack-app/src/helpers.ts
@@ -0,0 +1,63 @@
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).join("-").toLowerCase();
}

/**
* Convert str to UpperCamelCase
* @param str import string
* @returns {string} output string
*/
export function toUpperCamelCase(str: string): string {
return str
.match(regex)
.map((x) => x.slice(0, 1).toUpperCase() + x.slice(1).toLowerCase())
.join("");
}

export async function getInstaller(): Promise<string> {
const installers = this.cli.getAvailablePackageManagers();

if (installers.length === 1) {
return installers[0];
}

// Prompt for the package manager of choice
const defaultPackager = this.cli.getDefaultPackageManager();
const { packager } = await List(
this,
"packager",
"Pick a package manager:",
installers,
defaultPackager,
this.force,
);
return packager;
}

export async function getTemplate(): Promise<string> {
if (this.supportedTemplates.includes(this.template)) {
return this.template;
}

this.cli.logger.warn(`⚠ ${this.template} is not a valid template, please select one from below`);

const { selectedTemplate } = await List(
this,
"selectedTemplate",
"Select a valid template from below:",
this.supportedTemplates,
"default",
false,
);

return selectedTemplate;
}
1 change: 1 addition & 0 deletions packages/create-webpack-app/src/index.ts
@@ -0,0 +1 @@
import yeoman from "yeoman-environment";