UNPKG

nobuilder-json

Version:

nobuilder

332 lines (313 loc) 10.5 kB
"use strict"; const path = require("path"); const webpack = require("webpack"); const chalk = require("chalk"); const autoprefixer = require("autoprefixer"); const ModuleScopePlugin = require("react-dev-utils/ModuleScopePlugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const ProgressBarPlugin = require("progress-bar-webpack-plugin"); const CaseSensitivePathsPlugin = require("case-sensitive-paths-webpack-plugin"); const getClientEnvironment = require("./env"); const paths = require("./paths"); const babelConfig = require("./libBabelConfig"); const { getDocsEntry } = require("./helper"); const libBase = require("../libBase"); // TODO: 目前的实现和https://github.com/facebook/create-react-app/pull/4077一致 // 等合并之后,这里就使用react-dev-utils/WatchMissingNodeModulesPlugin的版本 // const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin') const WatchMissingNodeModulesPlugin = require("./MissingModulePlugin"); const babelLoaderOptions = Object.assign( { cacheDirectory: true }, babelConfig ); // 公开路径 (使用相对路径) const publicPath = "/"; // 公开路径 (去除头部 '/') const publicUrl = ""; // 当前应用的环境变量 const env = getClientEnvironment(publicUrl); module.exports = options => { // -------------------------------------------------------------------- // // 基础配置 // // -------------------------------------------------------------------- // const isProductMode = !!options.build || !!options.profile; const isDevMode = options.dev && process.env.NODE_ENV !== "production"; const productEntry = options.entry; const devOutput = { // 添加在 require() 处添加 /* 文件名 */ 注释 pathinfo: true, // 构建目录 filename: "static/js/[name].js", // chunk 名 chunkFilename: "static/js/[name].chunk.js", publicPath, // 处理 sourceMap 源路径 devtoolModuleFilenameTemplate: info => path.relative(paths.appSrc, info.absoluteResourcePath).replace(/\\/g, "/") }; const prodOutput = { // 添加在 require() 处添加 /* 文件名 */ 注释 pathinfo: true, // 构建目录 path: paths.appBuild, // 文件名 filename: "[name].js", // chunk 文件名 (添加 chunk 后缀) chunkFilename: "[name].js", publicPath, libraryTarget: "umd" // 处理 sourceMap 源路径 // devtoolModuleFilenameTemplate: info => // path.relative(paths.appSrc, info.absoluteResourcePath).replace(/\\/g, '/') }; // 标准配置 const config = { mode: process.env.NODE_ENV, // dev 模式下, 以组件的 docs/*.md 文件作为入口 entry: isDevMode ? getDocsEntry(paths.appDocs) : productEntry, output: isDevMode ? devOutput : prodOutput, resolve: { // 模块查找顺序: // - node_modules // - {app}/node_modules // - global path modules: ["node_modules", paths.appNodeModules].concat( process.env.NODE_PATH.split(path.delimiter).filter(Boolean) ), // 支持的扩展名 extensions: [".mjs", ".js", ".json", ".jsx", ".tsx", ".ts"], plugins: [ // 限制 src 目录以外的文件引用 (允许引用 package.json) new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]) ] }, module: { strictExportPresence: true, rules: [ { // 使用 oneOf, 会依次配置 loader, 如果未匹配到的会默认使用最后的 file-loader oneOf: [ // 加载图片 { test: /\.(bmp|gif|png|jpe?g)$/, loader: require.resolve("url-loader"), options: { limit: 10000, name: "static/media/[name].[hash:8].[ext]" } }, // 加载 js { test: /\.(js|jsx|mjs)$/, loader: require.resolve("babel-loader"), include: paths.appSrc, options: babelLoaderOptions }, { test: /\.(tsx|ts)$/, loader: require.resolve("ts-loader") }, // 支持引用 html 模板 { test: /\.(html|htm|tpl)$/, loader: require.resolve("html-loader") }, // 加载 css { id: "css", test: /\.css$/, use: [ // MiniCssExtractPlugin.loader, { loader: require.resolve("css-loader"), options: { importLoaders: 1 } }, { loader: require.resolve("postcss-loader"), options: { ident: "postcss", plugins: () => [ require("postcss-flexbugs-fixes"), autoprefixer({ browsers: [ ">1%", "last 4 versions", "Firefox ESR", "not ie < 9" // React doesn't support IE8 anyway ], flexbox: "no-2009" }) ] } } ] }, // 加载 sass { id: "sass", test: /\.(sass|scss)$/, use: [ // MiniCssExtractPlugin.loader, require.resolve("style-loader"), { loader: require.resolve("css-loader"), options: { importLoaders: 1 } }, { loader: require.resolve("postcss-loader"), options: { ident: "postcss", plugins: () => [ require("postcss-flexbugs-fixes"), autoprefixer({ browsers: [ ">1%", "last 4 versions", "Firefox ESR", "not ie < 9" // React doesn't support IE8 anyway ], flexbox: "no-2009" }) ] } }, require.resolve("fast-sass-loader") ] }, // 加载 docs/*.md { test: /docs[/\\].+\.md$/, use: [ { loader: require.resolve("babel-loader"), options: babelLoaderOptions }, { loader: require.resolve("./docLoader.js") } ] }, // 默认的 loader { exclude: [/\.(js|jsx|mjs|tsx)$/, /\.html$/, /\.json$/], loader: require.resolve("file-loader"), options: { name: "static/media/[name].[hash:8].[ext]" } } ] } ] }, plugins: [ // 让模块名称可见 new webpack.NamedModulesPlugin(), // 注入环境变量 new webpack.DefinePlugin(env.stringified), // HRM 插件 new webpack.HotModuleReplacementPlugin(), // 处理路径大小写问题 new CaseSensitivePathsPlugin(), // 监视缺失的模块 new WatchMissingNodeModulesPlugin(paths.appNodeModules), // 处理 .locale 文件 new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/) ], // 注入空 node 模块 node: { dgram: "empty", fs: "empty", net: "empty", tls: "empty", child_process: "empty" }, // 关闭性能提示 performance: { hints: false } }; // optimization非空设定,优化配置写入 if (!config.optimization) config.optimization = {}; if (isDevMode) { config.plugins.push(function() { this.plugin("watch-run", (compiler, callback) => { const ES_PATTERN = /\.(js|jsx|mjs|tsx)$/; const localPath = process.cwd(); const changedTimes = compiler.watchFileSystem.watcher.mtimes; const changedFiles = Object.keys(changedTimes).filter(changeItem => { return ( changeItem.startsWith(localPath) && changeItem.match(ES_PATTERN) ); }); if (changedFiles.length) { libBase(localPath, changedFiles).catch(err => { console.error(chalk.red(err.stack || err)); // process.exit(1); }); } callback(); }); }); config.devtool = "cheap-module-source-map"; // 定义 sourcemap 配置 } if (isProductMode) { console.log( chalk.cyan("INFO:"), `当前构建模式为 ${process.env.NODE_ENV} 模式` ); const sourceMap = false; // 修正 sourceMap config.devtool = sourceMap; // 设置 css / scss loader config.module.rules[0].oneOf.forEach((configItem, configIndex) => { const { id } = configItem; if ("css" === id) { config.module.rules[0].oneOf[configIndex].use.unshift( MiniCssExtractPlugin.loader ); } else if ("sass" === id) { config.module.rules[0].oneOf[configIndex].use.splice( 0, 1, MiniCssExtractPlugin.loader ); } }); config.plugins = [ new webpack.DefinePlugin(env.stringified), // webpack4抽取css文件 new MiniCssExtractPlugin({ filename: "[name].css" }), // 显示构建进度 new ProgressBarPlugin(), // 处理路径大小写问题 new CaseSensitivePathsPlugin(), // 处理 .locale 文件 new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/) ]; // 如果开启 profile if (options.profile) { console.log( chalk.cyan("INFO:"), "已开启 profile (webpack bundle analyze)" ); const BundleAnalyzerPlugin = require("webpack-bundle-analyzer") .BundleAnalyzerPlugin; config.plugins.push(new BundleAnalyzerPlugin()); } // 设置性能提示 config.performance = {}; } // 删除掉所有 loader 上的 id for (const item of config.module.rules[0].oneOf) { delete item.id; } return config; };