在试图建设该项目时,产生了两个超文本文档,即(dist/test.html和dist/src/index.html)。
dist/test.html>
/dist/src/index.html?88827167<script type="text/javascript"
src="/assets/vendors.chunk.js"></script>
<script type="text/javascript" src="/assets/client.js"></script>
dist/src/index.html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta http-equiv="cache-control" content="max-age=0" />
<meta http-equiv="cache-control" content="no-cache" />
<meta http-equiv="expires" content="0" />
<meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT" />
<meta http-equiv="pragma" content="no-cache" />
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<div id="app"></div>
</body>
</html>
web Pack.config.js
import fs from fs ;
import path from path ;
import webpack from webpack ;
import WebpackAssetsManifest from webpack-assets-manifest ;
import nodeExternals from webpack-node-externals ;
import { BundleAnalyzerPlugin } from webpack-bundle-analyzer ;
import WriteFilePlugin from write-file-webpack-plugin ;
import HtmlWebpackPlugin from html-webpack-plugin ;
import overrideRules from ./lib/overrideRules ;
import pkg from ../package.json ;
const ROOT_DIR = path.resolve(__dirname, .. );
const resolvePath = (...args) => path.resolve(ROOT_DIR, ...args);
const SRC_DIR = resolvePath( src );
const BUILD_DIR = resolvePath( build );
const isDebug = !process.argv.includes( --release );
const isVerbose = process.argv.includes( --verbose );
const isAnalyze =
process.argv.includes( --analyze ) || process.argv.includes( --analyse );
const reScript = /.(js|jsx|mjs)$/;
const reStyle = /.(css|less|styl|scss|sass|sss)$/;
const reImage = /.(bmp|gif|jpg|jpeg|png|svg)$/;
const staticAssetName = isDebug
? [path][name].[ext]?[hash:8]
: [hash:8].[ext] ;
// CSS Nano options http://cssnano.co/
const minimizeCssOptions = {
discardComments: { removeAll: true },
};
//
// Common configuration chunk to be used for both
// client-side (client.js) and server-side (server.js) bundles
// -----------------------------------------------------------------------------
const config = {
context: ROOT_DIR,
mode: isDebug ? development : production ,
output: {
path: resolvePath(BUILD_DIR, public/assets ),
publicPath: /assets/ ,
pathinfo: isVerbose,
filename: isDebug ? [name].js : [name].[chunkhash:8].js ,
chunkFilename: isDebug
? [name].chunk.js
: [name].[chunkhash:8].chunk.js ,
// Point sourcemap entries to original disk location (format as URL on Windows)
devtoolModuleFilenameTemplate: info =>
path.resolve(info.absoluteResourcePath).replace(/\/g, / ),
},
resolve: {
// Allow absolute paths in imports, e.g. import Button from components/Button
// Keep in sync with .flowconfig and .eslintrc
modules: [ node_modules , src ],
},
module: {
// Make missing exports an error instead of warning
strictExportPresence: true,
rules: [
// Rules for JS / JSX
{
test: reScript,
include: [SRC_DIR, resolvePath( tools )],
loader: babel-loader ,
options: {
// https://github.com/babel/babel-loader#options
cacheDirectory: isDebug,
// https://babeljs.io/docs/usage/options/
babelrc: false,
configFile: false,
presets: [
// A Babel preset that can automatically determine the Babel plugins and polyfills
// https://github.com/babel/babel-preset-env
[
@babel/preset-env ,
{
targets: {
browsers: pkg.browserslist,
},
forceAllTransforms: !isDebug, // for UglifyJS
modules: false,
useBuiltIns: false,
debug: false,
},
],
// Flow
// https://github.com/babel/babel/tree/master/packages/babel-preset-flow
@babel/preset-flow ,
// JSX
// https://github.com/babel/babel/tree/master/packages/babel-preset-react
[ @babel/preset-react , { development: isDebug }],
],
plugins: [
// Experimental ECMAScript proposals
@babel/plugin-proposal-class-properties ,
@babel/plugin-syntax-dynamic-import ,
// Treat React JSX elements as value types and hoist them to the highest scope
// https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-react-constant-elements
...(isDebug ? [] : [ @babel/transform-react-constant-elements ]),
// Replaces the React.createElement function with one that is more optimized for production
// https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-react-inline-elements
...(isDebug ? [] : [ @babel/transform-react-inline-elements ]),
// Remove unnecessary React propTypes from the production build
// https://github.com/oliviertassinari/babel-plugin-transform-react-remove-prop-types
...(isDebug ? [] : [ transform-react-remove-prop-types ]),
],
},
},
// Rules for Style Sheets
{
test: reStyle,
rules: [
// Convert CSS into JS module
{
issuer: { not: [reStyle] },
use: isomorphic-style-loader ,
},
// Process external/third-party styles
{
exclude: SRC_DIR,
loader: css-loader ,
options: {
sourceMap: isDebug,
minimize: isDebug ? false : minimizeCssOptions,
},
},
// Process internal/project styles (from src folder)
{
include: SRC_DIR,
loader: css-loader ,
options: {
// CSS Loader https://github.com/webpack/css-loader
importLoaders: 1,
sourceMap: isDebug,
// CSS Modules https://github.com/css-modules/css-modules
modules: true,
localIdentName: isDebug
? [name]-[local]-[hash:base64:5]
: [hash:base64:5] ,
// CSS Nano http://cssnano.co/
minimize: isDebug ? false : minimizeCssOptions,
},
},
// Apply PostCSS plugins including autoprefixer
{
loader: postcss-loader ,
options: {
config: {
path: ./tools/postcss.config.js ,
},
},
},
// Compile Less to CSS
// https://github.com/webpack-contrib/less-loader
// Install dependencies before uncommenting: yarn add --dev less-loader less
// {
// test: /.less$/,
// loader: less-loader ,
// },
// Compile Sass to CSS
// https://github.com/webpack-contrib/sass-loader
// Install dependencies before uncommenting: yarn add --dev sass-loader node-sass
// {
// test: /.(scss|sass)$/,
// loader: sass-loader ,
// },
],
},
// Rules for images
{
test: reImage,
oneOf: [
// Inline lightweight images into CSS
{
issuer: reStyle,
oneOf: [
// Inline lightweight SVGs as UTF-8 encoded DataUrl string
{
test: /.svg$/,
loader: svg-url-loader ,
options: {
name: staticAssetName,
limit: 4096, // 4kb
},
},
// Inline lightweight images as Base64 encoded DataUrl string
{
loader: url-loader ,
options: {
name: staticAssetName,
limit: 4096, // 4kb
},
},
],
},
// Or return public URL to image resource
{
loader: file-loader ,
options: {
name: staticAssetName,
},
},
],
},
// Convert plain text into JS module
{
test: /.txt$/,
loader: raw-loader ,
},
// Convert Markdown into HTML
{
test: /.md$/,
loader: path.resolve(__dirname, ./lib/markdown-loader.js ),
},
// Return public URL for all assets unless explicitly excluded
// DO NOT FORGET to update `exclude` list when you adding a new loader
{
exclude: [reScript, reStyle, reImage, /.json$/, /.txt$/, /.md$/],
loader: file-loader ,
options: {
name: staticAssetName,
},
},
// Exclude dev modules from production build
...(isDebug
? []
: [
{
test: resolvePath(
node_modules/react-deep-force-update/lib/index.js ,
),
loader: null-loader ,
},
]),
],
},
// Don t attempt to continue if there are any errors.
bail: !isDebug,
cache: isDebug,
// Specify what bundle information gets displayed
// https://webpack.js.org/configuration/stats/
stats: {
cached: isVerbose,
cachedAssets: isVerbose,
chunks: isVerbose,
chunkModules: isVerbose,
colors: true,
hash: isVerbose,
modules: isVerbose,
reasons: isDebug,
timings: true,
version: isVerbose,
},
// Choose a developer tool to enhance debugging
// https://webpack.js.org/configuration/devtool/#devtool
devtool: isDebug ? cheap-module-inline-source-map : source-map ,
};
//
// Configuration for the client-side bundle (client.js)
// -----------------------------------------------------------------------------
const clientConfig = {
...config,
name: client ,
target: web ,
entry: {
client: [ @babel/polyfill , ./src/client.js ],
},
plugins: [
// Forces webpack-dev-server program to write bundle files to the file system.
new WriteFilePlugin(),
new HtmlWebpackPlugin({
inject: body ,
template: ./src/index.html ,
filename: test.html ,
minify: false,
title: React Kit ,
}),
// Define free variables
// https://webpack.js.org/plugins/define-plugin/
new webpack.DefinePlugin({
process.env.BROWSER : true,
__DEV__: isDebug,
}),
// Emit a file with assets paths
// https://github.com/webdeveric/webpack-assets-manifest#options
new WebpackAssetsManifest({
output: `${BUILD_DIR}/asset-manifest.json`,
publicPath: true,
writeToDisk: true,
customize: ({ key, value }) => {
// You can prevent adding items to the manifest by returning false.
if (key.toLowerCase().endsWith( .map )) return false;
return { key, value };
},
done: (manifest, stats) => {
// Write chunk-manifest.json.json
const chunkFileName = `${BUILD_DIR}/chunk-manifest.json`;
try {
const fileFilter = file => !file.endsWith( .map );
const addPath = file => manifest.getPublicPath(file);
const chunkFiles = stats.compilation.chunkGroups.reduce((acc, c) => {
acc[c.name] = [
...(acc[c.name] || []),
...c.chunks.reduce(
(files, cc) => [
...files,
...cc.files.filter(fileFilter).map(addPath),
],
[],
),
];
return acc;
}, Object.create(null));
fs.writeFileSync(chunkFileName, JSON.stringify(chunkFiles, null, 2));
} catch (err) {
console.error(`ERROR: Cannot write ${chunkFileName}: `, err);
if (!isDebug) process.exit(1);
}
},
}),
...(isDebug
? []
: [
// Webpack Bundle Analyzer
// https://github.com/th0r/webpack-bundle-analyzer
...(isAnalyze ? [new BundleAnalyzerPlugin()] : []),
]),
],
// Move modules that occur in multiple entry chunks to a new entry chunk (the commons chunk).
optimization: {
splitChunks: {
cacheGroups: {
commons: {
chunks: initial ,
test: /[\/]node_modules[\/]/,
name: vendors ,
},
},
},
},
// Some libraries import Node modules but don t use them in the browser.
// Tell Webpack to provide empty mocks for them so importing them works.
// https://webpack.js.org/configuration/node/
// https://github.com/webpack/node-libs-browser/tree/master/mock
node: {
fs: empty ,
net: empty ,
tls: empty ,
},
};
//
// Configuration for the server-side bundle (server.js)
// -----------------------------------------------------------------------------
const serverConfig = {
...config,
name: server ,
target: node ,
entry: {
server: [ @babel/polyfill , ./src/server.js ],
},
output: {
...config.output,
path: BUILD_DIR,
filename: [name].js ,
chunkFilename: chunks/[name].js ,
libraryTarget: commonjs2 ,
},
// Webpack mutates resolve object, so clone it to avoid issues
// https://github.com/webpack/webpack/issues/4817
resolve: {
...config.resolve,
},
module: {
...config.module,
rules: overrideRules(config.module.rules, rule => {
// Override babel-preset-env configuration for Node.js
if (rule.loader === babel-loader ) {
return {
...rule,
options: {
...rule.options,
presets: rule.options.presets.map(preset =>
preset[0] !== @babel/preset-env
? preset
: [
@babel/preset-env ,
{
targets: {
node: pkg.engines.node.match(/(d+.?)+/)[0],
},
modules: false,
useBuiltIns: false,
debug: false,
},
],
),
},
};
}
// Override paths to static assets
if (
rule.loader === file-loader ||
rule.loader === url-loader ||
rule.loader === svg-url-loader
) {
return {
...rule,
options: {
...rule.options,
emitFile: false,
},
};
}
return rule;
}),
},
externals: [
./chunk-manifest.json ,
./asset-manifest.json ,
nodeExternals({
whitelist: [reStyle, reImage],
}),
],
plugins: [
// Define free variables
// https://webpack.js.org/plugins/define-plugin/
new webpack.DefinePlugin({
process.env.BROWSER : false,
__DEV__: isDebug,
}),
// Adds a banner to the top of each generated chunk
// https://webpack.js.org/plugins/banner-plugin/
new webpack.BannerPlugin({
banner: require("source-map-support").install(); ,
raw: true,
entryOnly: false,
}),
],
// Do not replace node globals with polyfills
// https://webpack.js.org/configuration/node/
node: {
console: false,
global: false,
process: false,
Buffer: false,
__filename: false,
__dirname: false,
},
};
export default [clientConfig, serverConfig];
<> 问答> 为什么有两种超文本档案,其中一种是提及另一个文件?
html-web Pack-plugin: ^3.2.0