oak-cli/config/mp/webpack.config.js

439 lines
16 KiB
JavaScript

const webpack = require('webpack');
const chalk = require('chalk');
const path = require('path');
const fs = require('fs');
const resolve = require('resolve');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const StylelintPlugin = require('stylelint-webpack-plugin');
const ProgressBarPlugin = require('progress-bar-webpack-plugin');
const UiExtractPlugin = require('ui-extract-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const ForkTsCheckerWebpackPlugin =
process.env.TSC_COMPILE_ON_ERROR === 'true'
? require('./../../plugins/ForkTsCheckerWarningWebpackPlugin')
: require('react-dev-utils/ForkTsCheckerWebpackPlugin');
const OakWeChatMpPlugin = require('../../plugins/WechatMpPlugin');
const getClientEnvironment = require('./env');
const paths = require('./paths');
const env = getClientEnvironment();
const pkg = require(paths.appPackageJson);
// Check if TypeScript is setup
const useTypeScript = fs.existsSync(paths.appTsConfig);
const createEnvironmentHash = require('./webpack/persistentCache/createEnvironmentHash');
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false';
const copyPatterns = [].concat(pkg.copyWebpack || []).map((pattern) =>
typeof pattern === 'string'
? {
from: path.resolve(paths.appSrc, pattern),
to: path.resolve(paths.appBuild, pattern),
}
: pattern
);
const oakRegex = /(\/*[a-zA-Z0-9_-])*\/app\/|(\\*[a-zA-Z0-9_-])*\\app\\/;
const localRegex = /(\/*[a-zA-Z0-9_-])*\/src+\/|(\\*[a-zA-Z0-9_-])*\\src+\\/;
module.exports = function (webpackEnv) {
const isEnvDevelopment = webpackEnv === 'development';
const isEnvProduction = webpackEnv === 'production';
const relativeFileLoader = (ext = '[ext]') => {
return {
loader: 'file-loader',
options: {
useRelativePath: true,
name: `[path][name].${ext}`,
context: paths.appSrc,
},
};
};
const oakFileLoader = (ext = '[ext]') => {
return {
loader: 'file-loader',
options: {
useRelativePath: true,
name: `[path][name].${ext}`,
outputPath: (url, resourcePath, context) => {
const outputPath = url.replace(oakRegex, '');
return outputPath;
},
context: paths.appSrc,
},
};
};
const localFileLoader = (ext = '[ext]') => {
return {
loader: 'file-loader',
options: {
useRelativePath: true,
name: `[path][name].${ext}`,
outputPath: (url, resourcePath, context) => {
const outputPath = url.replace(localRegex, '');
return outputPath;
},
context: paths.appSrc,
},
};
};
const getOakInclude = () => {
return isEnvProduction
? [/oak-general-business/]
: [
/oak-domain/,
/oak-external-sdk/,
/oak-frontend-base/,
/oak-general-business/,
/oak-memory-tree-store/,
/oak-common-aspect/,
];
};
return {
target: ['web'],
// Webpack noise constrained to errors and warnings
stats: 'errors-warnings',
mode: isEnvProduction
? 'production'
: isEnvDevelopment && 'development',
// Stop compilation early in production
bail: isEnvProduction,
devtool: isEnvProduction
? shouldUseSourceMap
? 'source-map'
: false
: isEnvDevelopment && 'cheap-module-source-map',
entry: {
app: paths.appIndexJs,
},
output: {
path: paths.appBuild,
filename: '[name].js',
publicPath: paths.publicUrlOrPath,
globalObject: 'global',
},
resolve: {
alias: {
assert: require.resolve('assert'),
'@': paths.appSrc,
'@project': paths.appRootSrc,
'@oak-general-business': paths.oakGeneralBusinessAppPath,
},
extensions: paths.moduleFileExtensions.map((ext) => `.${ext}`),
symlinks: true,
fallback: {
crypto: require.resolve('crypto-browserify'),
buffer: require.resolve('safe-buffer'),
stream: require.resolve('stream-browserify'),
},
},
resolveLoader: {
// 第一种使用别名的方式引入自定义的loader
alias: {
'wxml-loader': path.resolve(
__dirname,
'../loaders/wxml-loader.js'
),
},
// 第二种方式选查找自己的loaders文件中有没有这个loader再查找node_modules文件
// modules: [path.resolve(__dirname, 'loaders'), 'node_modules'],
},
cache: {
type: 'filesystem',
version: createEnvironmentHash(env.raw),
cacheDirectory: paths.appWebpackCache,
store: 'pack',
buildDependencies: {
defaultWebpack: ['webpack/lib/'],
config: [__filename],
tsconfig: [paths.appTsConfig, paths.appJsConfig].filter((f) =>
fs.existsSync(f)
),
},
},
infrastructureLogging: {
level: 'none',
},
optimization: {
// 标记未被使用的代码
usedExports: true,
// 删除 usedExports 标记的未使用的代码
minimize: isEnvProduction,
minimizer: [
new TerserPlugin({
extractComments: false,
}),
],
},
module: {
rules: [
{
test: /\.wxs$/,
include: paths.appSrc,
type: 'javascript/auto',
use: [relativeFileLoader()],
},
{
test: /\.wxs$/,
include: oakRegex,
exclude: paths.appSrc,
type: 'javascript/auto',
use: [oakFileLoader('wxs')],
},
{
test: /\.wxs$/,
include: paths.appRootSrc,
type: 'javascript/auto',
use: [oakFileLoader('wxs')],
},
{
test: /\.(png|jpg|gif|svg)$/,
include: paths.appSrc,
type: 'javascript/auto',
use: relativeFileLoader(),
},
{
test: /\.(png|jpg|gif|svg)$/,
include: oakRegex,
exclude: paths.appSrc,
type: 'javascript/auto',
use: oakFileLoader(),
},
{
test: /\.(png|jpg|gif|svg)$/,
include: paths.appRootSrc,
type: 'javascript/auto',
use: localFileLoader(),
},
{
test: /\.less$/,
include: paths.appSrc,
exclude: /node_modules/,
use: [
relativeFileLoader('wxss'),
{
loader: 'less-loader',
},
],
},
{
test: /\.less$/,
include: oakRegex,
exclude: paths.appSrc,
type: 'javascript/auto',
use: [
oakFileLoader('wxss'),
{
loader: 'less-loader',
options: {
lessOptions: () => {
const oakConfigJson = require(paths.oakConfigJson);
return {
javascriptEnabled: true,
modifyVars: oakConfigJson.theme,
};
},
},
},
],
},
{
test: /\.less$/,
include: paths.appRootSrc,
type: 'javascript/auto',
use: [
localFileLoader('wxss'),
{
loader: 'less-loader',
options: {
lessOptions: () => {
const oakConfigJson = require(paths.oakConfigJson);
return {
javascriptEnabled: true,
modifyVars: oakConfigJson.theme,
};
},
},
},
],
},
{
test: /\.js$/,
include: [paths.appSrc, paths.appRootSrc].concat(
getOakInclude()
),
exclude: /node_modules/,
loader: 'babel-loader',
},
{
test: /\.((?!tsx)ts)$/,
include: [paths.appSrc, paths.appRootSrc].concat(
getOakInclude()
),
exclude: /node_modules/,
loader: 'ts-loader',
options: {
configFile: paths.appTsConfig,
context: paths.appRootPath,
transpileOnly: true,
},
},
// {
// test: /\.json$/,
// include: paths.appSrc,
// exclude: /node_modules/,
// type: 'asset/resource',
// generator: {
// filename: `[path][name].[ext]`,
// },
// // type: 'javascript/auto',
// // use: [relativeFileLoader('json')],
// },
{
test: /\.(xml|wxml)$/,
include: paths.appSrc,
exclude: /node_modules/,
type: 'javascript/auto',
use: [
relativeFileLoader('wxml'),
{
loader: 'wxml-loader',
options: {
context: paths.appSrc,
},
},
],
},
{
test: /\.(xml|wxml)$/,
include: oakRegex,
exclude: paths.appSrc,
type: 'javascript/auto',
use: [
oakFileLoader('wxml'),
{
loader: 'wxml-loader',
options: {
context: paths.appSrc,
},
},
],
},
{
test: /\.(xml|wxml)$/,
include: paths.appRootSrc,
type: 'javascript/auto',
use: [
localFileLoader('wxml'),
{
loader: 'wxml-loader',
options: {
context: paths.appSrc,
},
},
],
},
],
},
plugins: [
new UiExtractPlugin({ context: paths.appSrc }),
new OakWeChatMpPlugin({
context: paths.appSrc,
extensions: paths.moduleFileExtensions.map((ext) => `.${ext}`),
exclude: ['*/weui-miniprogram/*'],
include: ['project.config.json', 'sitemap.json'],
split: !isEnvDevelopment,
}),
new webpack.DefinePlugin(env.stringified),
new StylelintPlugin({
fix: true,
files: '**/*.(sa|sc|le|wx|c)ss',
context: paths.appSrc,
}),
new ProgressBarPlugin({
summary: false,
format: ':msg :percent (:elapsed seconds)',
customSummary: (buildTime) =>
console.log(
chalk.gray(`\n[${new Date().toLocaleDateString()}`),
chalk.green(`Compiled successfully!(${buildTime})\n`)
),
}),
// TypeScript type checking
useTypeScript &&
new ForkTsCheckerWebpackPlugin({
async: isEnvDevelopment,
typescript: {
typescriptPath: resolve.sync('typescript', {
basedir: paths.appNodeModules,
}),
// configOverwrite: {
// compilerOptions: {
// sourceMap: isEnvProduction
// ? shouldUseSourceMap
// : isEnvDevelopment,
// skipLibCheck: true,
// inlineSourceMap: false,
// declarationMap: false,
// noEmit: true,
// incremental: true,
// tsBuildInfoFile: paths.appTsBuildInfoFile,
// },
// },
configFile: paths.appTsConfig,
context: paths.appRootPath,
diagnosticOptions: {
// semantic: true,
syntactic: true,
},
mode: 'write-references',
// profile: true,
},
issue: {
// This one is specifically to match during CI tests,
// as micromatch doesn't match
// otherwise.
include: [
{ file: '../**/app/**/*.ts' },
{ file: '**/app/**/*.ts' },
{ file: '../**/src/**/*.ts' },
{ file: '**/src/**/*.ts' },
],
exclude: [
{ file: '**/src/**/__tests__/**' },
{ file: '**/src/**/?(*.){spec|test}.*' },
{ file: '**/src/setupProxy.*' },
{ file: '**/src/setupTests.*' },
],
},
logger: {
infrastructure: 'silent',
},
}),
new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer'],
}),
].concat(
copyPatterns.length > 0
? [
new CopyWebpackPlugin({
patterns: copyPatterns,
}),
]
: []
),
watch: true,
watchOptions: {
aggregateTimeout: 600,
ignored: '**/node_modules',
followSymlinks: true,
},
};
};