123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421 |
- "use strict";
- const Generator = require("yeoman-generator");
- const chalk = require("chalk");
- const logSymbols = require("log-symbols");
- const Input = require("webpack-addons").Input;
- const Confirm = require("webpack-addons").Confirm;
- const List = require("webpack-addons").List;
- const getPackageManager = require("../utils/package-manager").getPackageManager;
- const entryQuestions = require("./utils/entry");
- const getBabelPlugin = require("./utils/module");
- const getDefaultPlugins = require("./utils/plugins");
- const tooltip = require("./utils/tooltip");
- /**
- *
- * Generator for initializing a webpack config
- *
- * @class InitGenerator
- * @extends Generator
- * @returns {Void} After execution, transforms are triggered
- *
- */
- module.exports = class InitGenerator extends Generator {
- constructor(args, opts) {
- super(args, opts);
- this.isProd = false;
- this.dependencies = ["webpack", "webpack-cli", "uglifyjs-webpack-plugin"];
- this.configuration = {
- config: {
- webpackOptions: {},
- topScope: []
- }
- };
- }
- prompting() {
- const done = this.async();
- const self = this;
- let regExpForStyles;
- let ExtractUseProps;
- let outputPath = "dist";
- process.stdout.write(
- "\n" +
- logSymbols.info +
- chalk.blue(" INFO ") +
- "For more information and a detailed description of each question, have a look at " +
- chalk.bold.green(
- "https://github.com/webpack/webpack-cli/blob/master/INIT.md"
- ) +
- "\n"
- );
- process.stdout.write(
- logSymbols.info +
- chalk.blue(" INFO ") +
- "Alternatively, run `webpack(-cli) --help` for usage info." +
- "\n\n"
- );
- this.configuration.config.webpackOptions.module = {
- rules: []
- };
- this.configuration.config.webpackOptions.plugins = getDefaultPlugins();
- this.configuration.config.topScope.push(
- "const webpack = require('webpack')",
- "const path = require('path')",
- tooltip.uglify(),
- "const UglifyJSPlugin = require('uglifyjs-webpack-plugin');",
- "\n"
- );
- this.prompt([
- Confirm("entryType", "Will your application have multiple bundles?")
- ])
- .then(entryTypeAnswer => {
- // Ask different questions for entry points
- return entryQuestions(self, entryTypeAnswer);
- })
- .then(entryOptions => {
- if (entryOptions !== "\"\"") {
- this.configuration.config.webpackOptions.entry = entryOptions;
- }
- return this.prompt([
- Input(
- "outputType",
- "Which folder will your generated bundles be in? [default: dist]:"
- )
- ]);
- })
- .then(outputTypeAnswer => {
- // As entry is not required anymore and we dont set it to be an empty string or """""
- // it can be undefined so falsy check is enough (vs entry.length);
- if (!this.configuration.config.webpackOptions.entry) {
- this.configuration.config.webpackOptions.output = {
- filename: "'[name].[chunkhash].js'",
- chunkFilename: "'[name].[chunkhash].js'"
- };
- } else {
- this.configuration.config.webpackOptions.output = {
- filename: "'[name].[chunkhash].js'"
- };
- }
- if (outputTypeAnswer["outputType"].length) {
- outputPath = outputTypeAnswer["outputType"];
- }
- this.configuration.config.webpackOptions.output.path = `path.resolve(__dirname, '${outputPath}')`;
- })
- .then(() => {
- return this.prompt([
- Confirm("prodConfirm", "Are you going to use this in production?")
- ]);
- })
- .then(prodConfirmAnswer => {
- this.isProd = prodConfirmAnswer["prodConfirm"];
- this.configuration.config.webpackOptions.mode = this.isProd
- ? "'production'"
- : "'development'";
- })
- .then(() => {
- return this.prompt([
- Confirm("babelConfirm", "Will you be using ES2015?")
- ]);
- })
- .then(babelConfirmAnswer => {
- if (babelConfirmAnswer["babelConfirm"] === true) {
- this.configuration.config.webpackOptions.module.rules.push(
- getBabelPlugin()
- );
- this.dependencies.push(
- "babel-core",
- "babel-loader",
- "babel-preset-env"
- );
- }
- })
- .then(() => {
- return this.prompt([
- List("stylingType", "Will you use one of the below CSS solutions?", [
- "SASS",
- "LESS",
- "CSS",
- "PostCSS",
- "No"
- ])
- ]);
- })
- .then(stylingTypeAnswer => {
- ExtractUseProps = [];
- switch (stylingTypeAnswer["stylingType"]) {
- case "SASS":
- this.dependencies.push(
- "sass-loader",
- "node-sass",
- "style-loader",
- "css-loader"
- );
- regExpForStyles = `${new RegExp(/\.(scss|css)$/)}`;
- if (this.isProd) {
- ExtractUseProps.push(
- {
- loader: "'css-loader'",
- options: {
- sourceMap: true
- }
- },
- {
- loader: "'sass-loader'",
- options: {
- sourceMap: true
- }
- }
- );
- } else {
- ExtractUseProps.push(
- {
- loader: "'style-loader'"
- },
- {
- loader: "'css-loader'"
- },
- {
- loader: "'sass-loader'"
- }
- );
- }
- break;
- case "LESS":
- regExpForStyles = `${new RegExp(/\.(less|css)$/)}`;
- this.dependencies.push(
- "less-loader",
- "less",
- "style-loader",
- "css-loader"
- );
- if (this.isProd) {
- ExtractUseProps.push(
- {
- loader: "'css-loader'",
- options: {
- sourceMap: true
- }
- },
- {
- loader: "'less-loader'",
- options: {
- sourceMap: true
- }
- }
- );
- } else {
- ExtractUseProps.push(
- {
- loader: "'css-loader'",
- options: {
- sourceMap: true
- }
- },
- {
- loader: "'less-loader'",
- options: {
- sourceMap: true
- }
- }
- );
- }
- break;
- case "PostCSS":
- this.configuration.config.topScope.push(
- tooltip.postcss(),
- "const autoprefixer = require('autoprefixer');",
- "const precss = require('precss');",
- "\n"
- );
- this.dependencies.push(
- "style-loader",
- "css-loader",
- "postcss-loader",
- "precss",
- "autoprefixer"
- );
- regExpForStyles = `${new RegExp(/\.css$/)}`;
- if (this.isProd) {
- ExtractUseProps.push(
- {
- loader: "'css-loader'",
- options: {
- sourceMap: true,
- importLoaders: 1
- }
- },
- {
- loader: "'postcss-loader'",
- options: {
- plugins: `function () {
- return [
- precss,
- autoprefixer
- ];
- }`
- }
- }
- );
- } else {
- ExtractUseProps.push(
- {
- loader: "'style-loader'"
- },
- {
- loader: "'css-loader'",
- options: {
- sourceMap: true,
- importLoaders: 1
- }
- },
- {
- loader: "'postcss-loader'",
- options: {
- plugins: `function () {
- return [
- precss,
- autoprefixer
- ];
- }`
- }
- }
- );
- }
- break;
- case "CSS":
- this.dependencies.push("style-loader", "css-loader");
- regExpForStyles = `${new RegExp(/\.css$/)}`;
- if (this.isProd) {
- ExtractUseProps.push({
- loader: "'css-loader'",
- options: {
- sourceMap: true
- }
- });
- } else {
- ExtractUseProps.push(
- {
- loader: "'style-loader'",
- options: {
- sourceMap: true
- }
- },
- {
- loader: "'css-loader'"
- }
- );
- }
- break;
- default:
- regExpForStyles = null;
- }
- })
- .then(() => {
- if (this.isProd) {
- // Ask if the user wants to use extractPlugin
- return this.prompt([
- Input(
- "extractPlugin",
- "If you want to bundle your CSS files, what will you name the bundle? (press enter to skip)"
- )
- ]);
- }
- })
- .then(extractPluginAnswer => {
- if (regExpForStyles) {
- if (this.isProd) {
- const cssBundleName = extractPluginAnswer["extractPlugin"];
- this.configuration.config.topScope.push(tooltip.cssPlugin());
- this.dependencies.push("mini-css-extract-plugin");
- if (cssBundleName.length !== 0) {
- this.configuration.config.webpackOptions.plugins.push(
- // TODO: use [contenthash] after it is supported
- `new MiniCssExtractPlugin({ filename:'${cssBundleName}.[chunkhash].css' })`
- );
- } else {
- this.configuration.config.webpackOptions.plugins.push(
- "new MiniCssExtractPlugin({ filename:'style.css' })"
- );
- }
- ExtractUseProps.unshift({
- loader: "MiniCssExtractPlugin.loader"
- });
- const moduleRulesObj = {
- test: regExpForStyles,
- use: ExtractUseProps
- };
- this.configuration.config.webpackOptions.module.rules.push(
- moduleRulesObj
- );
- this.configuration.config.topScope.push(
- "const MiniCssExtractPlugin = require('mini-css-extract-plugin');",
- "\n"
- );
- } else {
- const moduleRulesObj = {
- test: regExpForStyles,
- use: ExtractUseProps
- };
- this.configuration.config.webpackOptions.module.rules.push(
- moduleRulesObj
- );
- }
- }
- // add splitChunks options for transparency
- // defaults coming from: https://webpack.js.org/plugins/split-chunks-plugin/#optimization-splitchunks
- this.configuration.config.topScope.push(tooltip.splitChunks());
- this.configuration.config.webpackOptions.optimization = {
- splitChunks: {
- chunks: "'async'",
- minSize: 30000,
- minChunks: 1,
- // for production name is recommended to be off
- name: !this.isProd,
- cacheGroups: {
- vendors: {
- test: "/[\\\\/]node_modules[\\\\/]/",
- priority: -10
- }
- }
- }
- };
- done();
- });
- }
- installPlugins() {
- const asyncNamePrompt = this.async();
- const defaultName = this.isProd ? "prod" : "config";
- this.prompt([
- Input(
- "nameType",
- `Name your 'webpack.[name].js?' [default: '${defaultName}']:`
- )
- ])
- .then(nameTypeAnswer => {
- this.configuration.config.configName = nameTypeAnswer["nameType"].length
- ? nameTypeAnswer["nameType"]
- : defaultName;
- })
- .then(() => {
- asyncNamePrompt();
- const packager = getPackageManager();
- const opts = packager === "yarn" ? { dev: true } : { "save-dev": true };
- this.runInstall(packager, this.dependencies, opts);
- });
- }
- writing() {
- this.config.set("configuration", this.configuration);
- }
- };
|