ae-biu
Version:
Born For AE, Born To Do
275 lines (261 loc) • 7.36 kB
JavaScript
// @flow
import path from 'path'
import webpack from 'webpack'
import HtmlWebpackPlugin from 'html-webpack-plugin'
import CopyWebpackPlugin from 'copy-webpack-plugin'
import ExtractTextPlugin from 'extract-text-webpack-plugin'
import OptimizeCSSPlugin from 'optimize-css-assets-webpack-plugin'
import FaviconsWebpackPlugin from 'favicons-webpack-plugin'
import flat from 'flat'
import crypto from 'crypto'
import fs from 'fs'
import logger from '../utils/logger'
import getPostcssConfig from './postcss.config'
import lessOptions from './less.config'
import { workDir, srcDir, tplPath } from '../utils/paths'
import pkg from '../utils/package'
import getPolyfills from '../utils/get-polyfills'
import { readDirRecursiveSync } from '../utils/read-dir-recursive'
import webpackCommonConfig from './webpack.common'
import { DEFAULT_LANG_MAP } from '../utils/constants'
import useCDN from '../utils/use-cdn'
const { ae = {} } = pkg
const { languages: LANGS = DEFAULT_LANG_MAP, output: dist = 'dist', org: ORG_NAME, addHashForI18N = false } = ae
const distDir = path.resolve(workDir, dist)
const staticDir = path.resolve(srcDir, 'static')
const modulesDir = path.resolve(srcDir, 'modules')
const modulesI18NTransformer = content => JSON.stringify(flat(JSON.parse(content)))
const copyPluginPatterns = [
{
from: staticDir,
ignore: 'locale/**/*.json' // ignore locale files, these files will be handled by below pattern
},
{
context: staticDir,
from: 'locale/**/*.json',
to: addHashForI18N ? '[path][name].[hash].[ext]' : ''
},
{
context: modulesDir,
from: '**/*.json',
to: addHashForI18N ? 'locale/[path][name].[hash].[ext]' : 'locale',
transform: modulesI18NTransformer
}
]
console.log()
const i18nHashMap = {}
if (addHashForI18N) {
logger.info('Create hash for i18n files\n')
const staticI18nFiles = readDirRecursiveSync(staticDir, '*.json')
const moduleI18nFiles = readDirRecursiveSync(modulesDir, '*.json')
const files = [...staticI18nFiles, ...moduleI18nFiles]
files.map(file => {
let content = fs.readFileSync(file, { encoding: 'utf8' })
// modules 下面的 i18n 文件需要扁平化
if (~moduleI18nFiles.indexOf(file)) {
content = modulesI18NTransformer(content)
}
const hash = crypto.createHash('md5')
const hashSum = hash.update(content).digest('hex')
const lang = path.basename(file, '.json')
const scope = path.dirname(file).replace(`${modulesDir}`, '').replace(`${staticDir}/locale`, '').replace('/', '')
const key = `${scope}/${lang}`
i18nHashMap[key] = `${lang}.${hashSum}`
})
}
logger.info('Create configuration for NODE_ENV: production\n')
const SDP_ENV = (process.env.SDP_ENV || 'test').trim()
logger.info(`Project build for SDP_ENV: ${SDP_ENV}\n`)
const polyfills = getPolyfills()
// generate css loader for specific lang
function getCSSLoader (lang, i18nLangs) {
const loaders = [
{
loader: 'style-loader'
},
{
loader: 'css-loader',
options: {
importLoaders: 1
}
},
{
loader: 'postcss-loader',
options: getPostcssConfig(i18nLangs)
}
]
if (lang === 'less') {
loaders.push({
loader: 'less-loader',
options: lessOptions
})
}
return ExtractTextPlugin.extract({
fallback: loaders.shift().loader,
use: loaders
})
}
const webpackConfig = {
// current only have web
target: 'web',
// entry for AE
entry: {
app: [
...polyfills,
path.resolve(srcDir, 'index.js')
]
},
output: {
path: distDir,
publicPath: '',
filename: '[name].[chunkhash].js',
chunkFilename: '[id].[chunkhash].js'
},
resolve: {
// treat source directory as root
modules: [srcDir, 'node_modules'],
extensions: ['.js', '.jsx', '.ts', '.tsx', '.css', 'less'],
alias: {
'~': srcDir,
...webpackCommonConfig.alias
}
},
node: {
fs: 'empty',
net: 'empty'
},
devtool: 'source-map',
performance: {
hints: 'warning'
},
module: {
rules: [
{
test: /\.jsx?$/,
include: srcDir,
use: [{
loader: 'eslint-loader',
options: {
emitWarning: false, // no warning in production
formatter: require('eslint-friendly-formatter')
}
}],
enforce: 'pre'
}, {
test: /\.jsx?$/,
include: [
srcDir,
// AE and ES modules need transform
/@ae\.sdp\.nd[/\\](?!node_modules)/,
/[/\\]node_modules.*[/\\]((lodash-)?es)[/\\](?!node_modules)/
],
use: 'babel-loader'
},
{
test: /\.tsx?$/,
include: [
srcDir,
/@ae\.sdp\.nd[/\\](?!node_modules)/
],
use: [{
loader: 'babel-loader'
}, {
loader: 'ts-loader',
options: {
transpileOnly: true
}
}]
},
{
test: /\.css$/,
use: getCSSLoader('css', LANGS)
},
{
test: /\.less$/,
use: getCSSLoader('less', LANGS)
},
{
test: /\.(png|jpg|gif|svg|woff2?|eot|ttf)(\?.*)?$/,
use: [{
loader: 'url-loader',
options: {
limit: 8192,
name: '[name].[ext]?[hash:7]'
}
}]
}
]
},
plugins: [
...webpackCommonConfig.plugins,
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production'),
'process.env.SDP_ENV': JSON.stringify(SDP_ENV),
'process.env.LANGS': JSON.stringify(LANGS),
'process.env.I18N': JSON.stringify(i18nHashMap)
}),
new HtmlWebpackPlugin({
SDP_ENV: process.env.SDP_ENV,
ORG_NAME,
CDN: ae.cdn || {},
filename: 'index.html',
template: tplPath,
title: `${pkg.name} - ${pkg.description}`,
hash: false,
inject: !useCDN(pkg.cdn, process.env.SDP_ENV),
minify: {
collapseWhitespace: false,
minifyJS: false
}
}),
new CopyWebpackPlugin(copyPluginPatterns, {
ignore: ['README.md']
}),
new FaviconsWebpackPlugin({
logo: path.resolve(srcDir, 'assets/favicon.svg'),
prefix: 'icons-[hash:7]/',
icons: {
android: false,
appleIcon: false,
appleStartup: false,
coast: false,
favicons: true,
firefox: false,
opengraph: false,
twitter: false,
yandex: false,
windows: true
}
}),
// 按需优化 moment
// TODO: 根据配置的语言,而非默认中英文
new webpack.ContextReplacementPlugin(/moment[\\/]locale$/, /^\.\/(zh-cn|en-us)$/),
new webpack.LoaderOptionsPlugin({
minimize: true,
options: {
context: workDir
}
}),
new webpack.optimize.UglifyJsPlugin({
compress: {
unused: true,
dead_code: true,
warnings: false
},
sourceMap: true
}),
// Compress extracted CSS. We are using this plugin so that possible
// duplicated CSS from different components can be deduped.
new OptimizeCSSPlugin({
cssProcessorOptions: {
safe: true
}
}),
// extract css into its own file
new ExtractTextPlugin({
filename: '[name].[contenthash].css',
allChunks: true
})
]
}
export default webpackConfig