UNPKG

nobuilder

Version:

nobuilder

312 lines (293 loc) 10.3 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'], 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 }, // 支持引用 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)$/, /\.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)$/; 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 }