dn-middleware-webpack5
Version:
--- group: middleware name: webpack5 title: Webpack5 ---
245 lines (222 loc) • 10.1 kB
text/typescript
import resolve from "resolve";
import * as path from "path";
import * as Dawn from "@dawnjs/types";
import { DefinePlugin, HotModuleReplacementPlugin } from "webpack";
import type { WebpackPluginInstance } from "webpack/types.d";
// import InterpolateHtmlPlugin from "react-dev-utils/InterpolateHtmlPlugin";
import ModuleNotFoundPlugin from "react-dev-utils/ModuleNotFoundPlugin";
// import WatchMissingNodeModulesPlugin from "react-dev-utils/WatchMissingNodeModulesPlugin";
import ForkTsCheckerWebpackPlugin from "react-dev-utils/ForkTsCheckerWebpackPlugin";
import typescriptFormatter from "react-dev-utils/typescriptFormatter";
import { ESBuildPlugin } from "esbuild-loader";
import MiniCssExtractPlugin from "mini-css-extract-plugin";
import { BundleAnalyzerPlugin } from "webpack-bundle-analyzer";
import ReactRefreshWebpackPlugin from "@pmmmwh/react-refresh-webpack-plugin";
import Webpackbar from "webpackbar";
const HtmlWebpackPlugin = require("html-webpack-plugin");
const CaseSensitivePathsPlugin = require("case-sensitive-paths-webpack-plugin");
const VModulePlugin = require("vmodule-webpack-plugin");
// import InlineChunkHtmlPlugin from "react-dev-utils/InlineChunkHtmlPlugin";
// import getCacheIdentifier from "react-dev-utils/getCacheIdentifier";
import { FileInfo, IGetWebpackConfigOpts } from "../types";
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
// Generate webpack plugins
const getPlugins = (options: IGetWebpackConfigOpts, ctx: Dawn.Context) => {
const plugins: WebpackPluginInstance[] = [];
// false or empty array
// if (options?.template?.length) {
// HTMLWebpackPlugin
// Generates an `index.html` file with the <script> injected.
(options.entry as FileInfo[]).forEach(({ name }) => {
const template = options.template?.find?.(temp => temp.name === name) ?? options.template?.[0];
if (!template) return;
const minifyOption = ctx.isEnvProduction // auto minify when production mode
? {
// https://github.com/DanielRuf/html-minifier-terser
// Strip HTML comments
removeComments: true,
// Collapse white space that contributes to text nodes in a document tree
collapseWhitespace: true,
// Remove attributes when value matches default.
removeRedundantAttributes: true,
// Replaces the doctype with the short (HTML5) doctype
useShortDoctype: true,
// Remove all attributes with whitespace-only values
removeEmptyAttributes: true,
// Remove type="text/css" from style and link tags. Other type attribute values are left intact
removeStyleLinkTypeAttributes: true,
// Keep the trailing slash on singleton elements
keepClosingSlash: true,
// Minify JavaScript in script elements and event attributes (uses Terser)
minifyJS: true,
// Minify CSS in style elements and style attributes (uses clean-css)
minifyCSS: true,
// Minify URLs in various attributes (uses relateurl)
minifyURLs: true,
}
: false;
plugins.push(
new HtmlWebpackPlugin({
inject: true,
filename: path.join(options?.folders?.html ?? "", `${name}.html`),
template: template.file,
chunks: [options?.common?.name, name].filter(Boolean),
minify: minifyOption,
}),
);
});
// }
// InlineChunkHtmlPlugin
// Inlines the webpack runtime script. This script is too small to warrant a network request.
// https://github.com/facebook/create-react-app/tree/master/packages/react-dev-utils#new-inlinechunkhtmlpluginhtmlwebpackplugin-htmlwebpackplugin-tests-regex
// ctx.isEnvProduction && plugins.push(new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime-.+[.]js/]) as any);
// InterpolateHtmlPlugin
// Makes some environment variables available in index.html.
// The public URL is available as %PUBLIC_URL% in index.html, e.g.:
// <link rel="icon" href="%PUBLIC_URL%/favicon.ico">
// It will be an empty string unless you specify "homepage" in `package.json`, in which case it will be the pathname of that URL.
// https://github.com/facebook/create-react-app/tree/master/packages/react-dev-utils#new-interpolatehtmlpluginhtmlwebpackplugin-htmlwebpackplugin-replacements-keystring-string
// plugins.push(
// new InterpolateHtmlPlugin(HtmlWebpackPlugin, {
// TODO: add some envs
// }) as any,
// );
// ModuleNotFoundPlugin
// This gives some necessary context to module not found errors, such as the requesting resource.
plugins.push(new ModuleNotFoundPlugin(options.cwd));
// DefinePlugin
// Makes some environment variables available to the JS code, for example:
// if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`.
// It is absolutely essential that NODE_ENV is set to production during a production build.
// Otherwise React will be compiled in the very slow development mode.
plugins.push(
new DefinePlugin({
"process.env": {
NODE_ENV: JSON.stringify(options.env), // production
},
}),
);
// CaseSensitivePathsPlugin
// Watcher doesn't work well if you mistype casing in a path so we use a plugin that prints an error when you attempt to do this.
// See https://github.com/facebook/create-react-app/issues/240
// https://github.com/Urthen/case-sensitive-paths-webpack-plugin
ctx.isEnvDevelopment && plugins.push(new CaseSensitivePathsPlugin());
// MiniCssExtractPlugin
// Options similar to the same options in webpackOptions.output both options are optional
!ctx.injectCSS &&
plugins.push(
new MiniCssExtractPlugin({
filename: path.join(options?.folders?.style ?? "", "[name].css"),
chunkFilename: path.join(options?.folders?.style ?? "", "[name].[contenthash:8].chunk.css"),
}) as any,
);
if (options.hot) {
// This is necessary to emit hot updates (CSS and Fast Refresh):
// https://github.com/pmmmwh/react-refresh-webpack-plugin/issues/232
plugins.push(
new ReactRefreshWebpackPlugin({
// include: /\.(js|jsx|ts|tsx|flow)$/i,
overlay: {
// entry: require.resolve("react-dev-utils/webpackHotDevClient"),
sockIntegration: "whm",
// entry: require.resolve('@pmmmwh/react-refresh-webpack-plugin/client/ErrorOverlayEntry'),
// module: require.resolve('@pmmmwh/react-refresh-webpack-plugin/overlay'),
// TODO: This is just a stub module. Clean this up if possible.
// module: require.resolve('./hotRefreshOverlayModuleStub'),
},
}),
);
plugins.push(new HotModuleReplacementPlugin());
}
if (options?.esbuild?.minify || options?.esbuild?.loader) {
plugins.push(new ESBuildPlugin());
}
const clearConsoleReporter: any = {
start() {
const allowScrollBack = true;
process.stdout.write(allowScrollBack ? "\x1B[H\x1B[2J" : "\x1B[2J\x1B[3J\x1B[H\x1Bc");
},
};
plugins.push(
new Webpackbar({
reporters: [
options.watch ? clearConsoleReporter : null,
"fancy",
options.analysis || ctx.isEnvProduction ? "stats" : null,
].filter(Boolean),
}),
);
// webpack.IgnorePlugin
// Moment.js is an extremely popular library that bundles large locale files
// by default due to how webpack interprets its code. This is a practical
// solution that requires the user to opt into importing specific locales.
// checkContext was removed from IgnorePlugin in webpack5.
// now IgnorePlugin must now be passed only one argument that can be an object, string or function.
// https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
// You can remove this if you don't use Moment.js:
// new IgnorePlugin({
// resourceRegExp: /^\.\/locale$/,
// contextRegExp: /moment$/
// });
// BundleAnalyzerPlugin
// https://github.com/webpack-contrib/webpack-bundle-analyzer
const analysisOptions = options.analysis === true ? {} : options.analysis;
options.analysis && plugins.push(new BundleAnalyzerPlugin(analysisOptions as BundleAnalyzerPlugin.Options));
// ForkTsCheckerWebpackPlugin
// TypeScript type checking
ctx.useTypeScript &&
plugins.push(
new ForkTsCheckerWebpackPlugin({
typescript: resolve.sync("typescript", {
basedir: path.join(options.cwd, "node_modules"),
}),
async: ctx.isEnvDevelopment,
checkSyntacticErrors: true,
tsconfig: path.join(options.cwd, "tsconfig.json"),
reportFiles: [
// This one is specifically to match during tests, as micromatch doesn't match
"../**/src/**/*.{ts,tsx}",
"**/src/**/*.{ts,tsx}",
"!**/src/**/__tests__/**",
"!**/src/**/?(*.)(spec|test).*",
"!**/src/setupProxy.*",
"!**/src/setupTests.*",
],
silent: true,
// The formatter is invoked directly in WebpackDevServerUtils during development
formatter: ctx.isEnvProduction ? typescriptFormatter : undefined,
}),
);
// https://webpack.docschina.org/concepts/module-federation/
options.moduleFederation &&
plugins.push(
new ModuleFederationPlugin({
// name: "app_one_remote",
// remotes: {
// app_two: "app_two_remote",
// app_three: "app_three_remote"
// },
// exposes: {
// AppContainer: "./src/App"
// },
shared: ["react", "react-dom"],
...options.moduleFederation,
}),
);
// $config Plugin
const configPath = path.resolve(options.cwd, options?.config?.path);
// @ts-ignore
ctx.utils.confman.env = options?.config?.env;
ctx.$config = ctx.utils.confman.load(configPath) || options?.config?.content;
ctx.$config &&
plugins.push(
new VModulePlugin.VModulePlugin({
env: options?.config?.env,
name: options?.config?.name,
content: ctx.$config,
watch: [configPath, `${configPath}.*`, `${configPath}/**/*.*`],
}),
);
return plugins;
};
export default getPlugins;