UNPKG

@rockpack/compiler

Version:

The easiest webpack config generator.

340 lines (281 loc) 10.6 kB
const LoadablePlugin = require('@loadable/webpack-plugin'); const FriendlyErrorsWebpackPlugin = require('@nuxt/friendly-errors-webpack-plugin'); const { existsSync } = require('node:fs'); const path = require('node:path'); const StatoscopeWebpackPlugin = require('@statoscope/webpack-plugin').default; const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin'); const Dotenv = require('dotenv-webpack'); const EslintWebpackPlugin = require('eslint-webpack-plugin'); const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); const HtmlInlineCssWebpackPlugin = require('html-inline-css-webpack-plugin').default; const HtmlInlineScriptWebpackPlugin = require('html-inline-script-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const NodemonPlugin = require('nodemon-webpack-plugin'); const StylelintWebpackPlugin = require('stylelint-webpack-plugin'); const { isArray, isBoolean, isObject, isString } = require('valid-types'); const VConsoleWebpackPlugin = require('vconsole-webpack-plugin'); const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); const FlagDependencyUsagePlugin = require('webpack/lib/FlagDependencyUsagePlugin'); const FlagIncludedChunksPlugin = require('webpack/lib/optimize/FlagIncludedChunksPlugin'); const WriteFilePlugin = require('write-file-webpack-plugin'); const BreakingChangesWebpack4 = require('../plugins/BreakingChangesWebpack4'); const SSRDevelopment = require('../plugins/SSRDevelopment'); const WebViewHTMLWrapper = require('../plugins/WebViewHTMLWrapper'); const Collection = require('../utils/collection'); const fpPromise = require('../utils/find-free-port'); const { getRandomInt, getTitle } = require('../utils/other'); const pathToEslintrc = require('../utils/path-to-eslintrc'); const pathToStylelint = require('../utils/path-to-stylelint'); const pathToTSConf = require('../utils/path-to-ts-conf'); const makeBanner = require('./make-banner'); const makeResolve = require('./make-resolve'); const getNodemonOptions = async (distFolder, distPath, conf) => { const defaultInspectPort = global.ISOMORPHIC ? getRandomInt(9000, 9999) : 9224; const freeInspectPort = await fpPromise(defaultInspectPort); const script = path.join(distFolder, path.basename(distPath)); const opts = { ext: 'js', ignore: ['*.map', '*.hot-update.json', '*.hot-update.js', 'stats.json'], nodeArgs: [`--inspect=${freeInspectPort}`, '--require="source-map-support/register"'], quiet: true, script, verbose: false, watch: distFolder, }; conf.messages.push('nodemon is running'); if (!conf.__isIsomorphicBackend) { conf.messages.push(`node-inspect is available on ${freeInspectPort} port`); } return opts; }; const getPlugins = async (conf, mode, root, packageJson, webpack, context) => { const tsConfig = pathToTSConf(root, mode, false); const { extensions } = makeResolve(root); const distPath = path.isAbsolute(conf.dist) ? conf.dist : path.resolve(root, conf.dist); const distFolder = path.dirname(distPath); const isTypeScript = isString(tsConfig); const plugins = {}; plugins.BreakingChangesWebpack4 = new BreakingChangesWebpack4(); plugins.FriendlyErrorsPlugin = new FriendlyErrorsWebpackPlugin({ clearConsole: false, compilationSuccessInfo: { messages: conf.messages, }, }); if (isTypeScript) { plugins.ForkTsCheckerPlugin = new ForkTsCheckerWebpackPlugin(); } if (existsSync(path.resolve(root, '.env'))) { const isExample = existsSync(path.resolve(root, '.env.example')); const isDefaults = existsSync(path.resolve(root, '.env.defaults')); /** * dotenv-webpack 6.0 fix * {}.DEBUG = namespaces; * Error * */ const dotenv = new Dotenv({ allowEmptyValues: true, defaults: isDefaults, path: path.resolve(root, '.env'), safe: isExample, }); if (dotenv.definitions && dotenv.definitions['process.env']) { delete dotenv.definitions['process.env']; } plugins.Dotenv = dotenv; } if (mode !== 'production' || conf.debug) { plugins.WriteFilePlugin = new WriteFilePlugin(); } let banner = makeBanner(packageJson, root); if (conf.banner) { if (isString(conf.banner)) { banner = conf.banner; } } else if (isBoolean(conf.banner) && conf.banner === false) { banner = false; } conf.banner = banner; if (conf.banner) { plugins.BannerPlugin = new webpack.BannerPlugin({ banner: !banner ? '' : banner, entryOnly: true, }); } let pages = []; let HTMLProcessing = true; if (typeof conf.html !== 'undefined' && isBoolean(conf.html) && conf.html === false) { HTMLProcessing = false; } if (global.ISOMORPHIC) { HTMLProcessing = false; } if (HTMLProcessing) { if (conf.html && isArray(conf.html)) { pages = conf.html; } else { pages = [ { code: conf.html && conf.html.code ? conf.html.code : null, favicon: conf.html && conf.html.favicon ? conf.html.favicon : null, filename: conf.html && conf.html.filename ? conf.html.filename : conf.webview ? 'index.html' : false, template: (conf.html && conf.html.template) || (conf.webview ? path.resolve(__dirname, '..', './index.webview.ejs') : path.resolve(__dirname, '..', './index.ejs')), title: (conf.html && conf.html.title) || (getTitle(packageJson) ? getTitle(packageJson) : ''), }, ]; } pages = pages.map((page) => { if (!page.template) { page.template = conf.webview ? path.resolve(__dirname, '..', './index.webview.ejs') : path.resolve(__dirname, '..', './index.ejs'); } if (!page.filename) { page.filename = page.template.slice(page.template.lastIndexOf(path.sep) + 1, page.template.lastIndexOf('.')); page.filename += '.html'; } if (conf.webview) { page.cache = false; page.inject = 'body'; } else { page.inject = false; } page.minify = { collapseWhitespace: mode === 'production', }; return page; }); pages.forEach((page, index) => { const q = `HtmlWebpackPlugin${index}`; plugins[q] = new HtmlWebpackPlugin(page); }); } const eslintRc = pathToEslintrc(root, mode); const stylelint = pathToStylelint(root, mode); if (stylelint) { plugins.StylelintWebpackPlugin = new StylelintWebpackPlugin({ configFile: stylelint, }); } if (isString(eslintRc)) { plugins.EslintWebpackPlugin = new EslintWebpackPlugin({ context, eslintPath: require.resolve('eslint'), extensions, failOnError: mode === 'production' && !conf.debug, }); } const env = conf.global || {}; if (conf.__isBackend) { env.ROOT_DIRNAME = root; } if (typeof global.LIVE_RELOAD_PORT === 'number') { env.LIVE_RELOAD_PORT = global.LIVE_RELOAD_PORT; } const definePluginOpts = { 'process.env.NODE_ENV': JSON.stringify(mode), ...Object.keys(env).reduce((prev, curr) => { prev[`process.env.${curr}`] = JSON.stringify(env[curr]); return prev; }, {}), }; plugins.DefinePlugin = new webpack.DefinePlugin(definePluginOpts); if (conf.copy) { let _prop = null; let _opts = {}; if (isObject(conf.copy)) { if (conf.copy.from && conf.copy.to) { _prop = [conf.copy]; } else if (conf.copy.files) { _prop = conf.copy.files; _opts = conf.copy.opts || {}; } } else if (isArray(conf.copy)) { _prop = conf.copy; } if (_prop) { plugins.CopyWebpackPlugin = new CopyWebpackPlugin({ options: _opts, patterns: _prop }); } } /** * DEVELOPMENT * */ if (mode === 'development' && !conf.webview) { if (!conf.__library && conf.nodejs && !global.ISOMORPHIC) { const opts = await getNodemonOptions(distFolder, distPath, conf); plugins.NodemonPlugin = new NodemonPlugin(opts); } else if (global.ISOMORPHIC && conf.__isIsomorphicBackend) { const opts = await getNodemonOptions(distFolder, distPath, conf); plugins.SSRDevelopment = new SSRDevelopment(opts); } plugins.WatchIgnorePlugin = new webpack.WatchIgnorePlugin({ paths: [/css\.d\.ts$/], }); if (conf.__isIsomorphicStyles) { plugins.MiniCssExtractPlugin = new MiniCssExtractPlugin({ filename: 'css/styles.css', }); } } /** * PRODUCTION * */ if (mode === 'production') { plugins.CaseSensitivePathsPlugin = new CaseSensitivePathsPlugin(); const styleName = conf.styles && conf.styles.indexOf('.css') >= 0 ? conf.styles : 'css/styles.css'; plugins.MiniCssExtractPlugin = new MiniCssExtractPlugin({ filename: styleName, }); plugins.FlagDependencyUsagePlugin = new FlagDependencyUsagePlugin(); plugins.FlagIncludedChunksPlugin = new FlagIncludedChunksPlugin(); plugins.NoEmitOnErrorsPlugin = new webpack.NoEmitOnErrorsPlugin(); plugins.SideEffectsFlagPlugin = new webpack.optimize.SideEffectsFlagPlugin(); } if (conf.analyzer) { const analyzerPort = await fpPromise(8888); plugins.BundleAnalyzerPlugin = new BundleAnalyzerPlugin( mode === 'development' ? { analyzerPort, } : { analyzerMode: 'static', openAnalyzer: false, reportFilename: 'webpack-report.html', }, ); plugins.StatoscopeWebpackPlugin = new StatoscopeWebpackPlugin({ watchMode: mode === 'development', }); } if (conf.__isIsomorphic) { plugins.LoadablePlugin = new LoadablePlugin({ filename: 'stats.json', writeToDisk: true }); } if (conf.webview) { plugins.HtmlInlineScriptWebpackPlugin = new HtmlInlineScriptWebpackPlugin(); plugins.HtmlInlineCssWebpackPlugin = new HtmlInlineCssWebpackPlugin(); plugins.WebViewHTMLWrapper = new WebViewHTMLWrapper({ dist: distPath, }); if (mode === 'development' && conf.debug) { plugins.VConsoleWebpackPlugin = new VConsoleWebpackPlugin({ enable: true, }); } } return plugins; }; const makePlugins = async (conf, root, packageJson, mode, webpack, context) => { const plugins = await getPlugins(conf, mode, root, packageJson, webpack, context); return new Collection({ data: plugins, props: {}, }); }; module.exports = makePlugins;