chameleon-tool
Version:
chameleon 脚手架工具
306 lines (282 loc) • 9.78 kB
JavaScript
const path = require('path');
const webpack = require('webpack');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const ProgressBarPlugin = require('progress-bar-webpack-plugin');
const {getBabelPath, getExcludeBabelPath, getGlobalCheckWhiteList, getFreePort, addCahceLoader} = require('./utils');
var UglifyJsPlugin = require('uglifyjs-webpack-plugin')
var OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
const ChameleonWebpackPlugin = require('chameleon-webpack-plugin')
const WebpackCheckPlugin = require('webpack-check-plugin')
const config = require('./config.js');
const ChameleonErrorsWebpackPlugin = require('chameleon-errors-webpack-plugin');
const fs = require('fs');
const cmlUtils = require('chameleon-tool-utils');
const ExtraWatchWebpackPlugin = require('extra-watch-webpack-plugin');
const DuplicatePackageCheckerPlugin = require('duplicate-package-checker-webpack-plugin');
const CircularDependencyPlugin = require('circular-dependency-plugin');
module.exports = function (options) {
let {
type,
media,
root
} = options;
function getstaticPath(filetype) {
let staticPath = `static/${filetype}/[name]_[hash:7].[ext]`;
if (options.staticPath) {
staticPath = options.staticPath + staticPath;
}
return staticPath;
}
let webServerPort = getFreePort().webServerPort;
let publicPath;
let defaultPublichPathMap = {
'wx': `http://${config.ip}:${webServerPort}/wx/`,
'qq': `http://${config.ip}:${webServerPort}/qq/`,
'tt': `http://${config.ip}:${webServerPort}/tt/`,
'alipay': `http://${config.ip}:${webServerPort}/alipay/`,
'baidu': `http://${config.ip}:${webServerPort}/baidu/`, // baidu小程序的publicPath不能设置能/ 所以在启动dev服务的时候 也将dist作为静态资源
'web': `http://${config.ip}:${webServerPort}/`,
'weex': `http://${config.ip}:${webServerPort}/weex/`
}
publicPath = options.publicPath || defaultPublichPathMap[type];
if (!publicPath) {
publicPath = `http://${config.ip}:${webServerPort}/${type}/`
}
let chameleonConfig = cml.config.get();
let commonConfig = {
stats: cml.logLevel === 'debug' ? 'verbose' : 'none',
output: {
publicPath: publicPath
},
resolve: {
symlinks: false,
extensions: ['.cml', '.interface', '.vue', '.js'],
alias: {
'$CMLPROJECT': path.resolve(cml.root),
'/components': path.resolve(cml.projectRoot, 'src/components'),
'$PROJECT': path.resolve(root),
'$ROUTER_CONFIG': path.resolve(root, './src/router.config.json')
},
modules: [
'node_modules',
path.join(cml.root, '/node_modules')
]
},
resolveLoader: {
modules: [
path.join(cml.root, '/node_modules'),
'node_modules'
]
},
module: {
rules: [{
test: path.resolve(root, 'node_modules/chameleon-runtime/.temp/router.js'),
loader: path.join(__dirname, 'routerLoader.js')
},
{
test: /\.js$/,
exclude: getExcludeBabelPath(),
// 不能babel babel-runtime
include: getBabelPath(),
use: [{
loader: 'babel-loader',
options: {
'filename': path.join(cml.root, 'chameleon.js')
}
}]
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'chameleon-url-loader',
options: {
limit: false, // 不做limit的base64转换,需要添加?inline参数
name: getstaticPath('img'),
outputPath: function(output) {
// 处理图片中的@符号 改成_ 解决在支付宝小程序中上传失败的问题
output = cml.utils.handleSpecialChar(output)
return output;
}
}
}, {
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
loader: 'file-loader',
options: {
name: getstaticPath('media'),
outputPath: function(output) {
// 处理图片中的@符号 改成_ 解决在支付宝小程序中上传失败的问题
output = cml.utils.handleSpecialChar(output)
return output;
}
}
},
{
test: /\.(woff|woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'file-loader',
options: {
name: getstaticPath('fonts'),
outputPath: function(output) {
// 处理图片中的@符号 改成_ 解决在支付宝小程序中上传失败的问题
output = cml.utils.handleSpecialChar(output)
return output;
}
}
},
{
test: /\.interface$/,
// exclude: /(node_modules|bower_components)/,
// 不能babel babel-runtime
include: getBabelPath(),
use: [{
loader: 'babel-loader',
options: {
'filename': path.join(cml.root, 'chameleon.js')
}
},
{
loader: 'interface-loader',
options: {
cmlType: type,
media,
check: chameleonConfig.check
}
}
]
}
]
},
plugins: [
new webpack.DefinePlugin({
'process.env.platform': JSON.stringify(type)
}),
new ChameleonErrorsWebpackPlugin({
cmlType: type,
showWarning: chameleonConfig.optimize && chameleonConfig.optimize.showWarning
})
]
}
if (options.cache) {
addCahceLoader(commonConfig, type);
}
if (chameleonConfig.enableGlobalCheck === true) {
commonConfig.plugins.push(
new WebpackCheckPlugin({
cmlType: type,
whiteListFile: getGlobalCheckWhiteList()
})
)
}
if (chameleonConfig.optimize && chameleonConfig.optimize.processBar) {
commonConfig.plugins.push(new ProgressBarPlugin())
}
if (chameleonConfig.optimize && chameleonConfig.optimize.circularDependency) {
commonConfig.plugins.push(new ProgressBarPlugin())
}
if (options.definePlugin) {
commonConfig.plugins.push(new webpack.DefinePlugin(options.definePlugin))
}
if (options.analysis) {
commonConfig.plugins.push(new BundleAnalyzerPlugin())
}
let devApiPrefix = `http://${config.ip}:${webServerPort}`
// 兼容旧版api
let apiPrefix = options.apiPrefix || devApiPrefix;
// 新版api 优先读取domain
// 浅拷贝不影响config中的domain
let domain = {};
if (options.domain) {
domain = {
...options.domain
}
}
if (options.media === 'dev') {
// dev模式添加domainKey参数
Object.keys(domain).forEach(key => {
if (domain[key].toLowerCase() === 'localhost') {
domain[key] = devApiPrefix;
}
domain[key] = domain[key] + '__DEV_SPLIT__' + key;
})
commonConfig.plugins.push(
new ExtraWatchWebpackPlugin({
dirs: [path.join(cml.projectRoot, 'mock/api')]
})
);
commonConfig.plugins.push(
new DuplicatePackageCheckerPlugin({
verbose: true
})
);
commonConfig.plugins.push(
new DuplicatePackageCheckerPlugin({
verbose: true
})
);
if (chameleonConfig.optimize && chameleonConfig.optimize.circularDependency) {
commonConfig.plugins.push(
new CircularDependencyPlugin({
// exclude detection of files based on a RegExp
exclude: /node_modules/,
// include specific files based on a RegExp
include: /src/,
// add errors to webpack instead of warnings
failOnError: true,
// allow import cycles that include an asyncronous import,
// e.g. via import(/* webpackMode: "weak" */ './file.js')
allowAsyncCycles: false,
// set the current working directory for displaying module paths
cwd: process.cwd()
})
);
}
}
// 兼容旧版api
commonConfig.plugins.push(new webpack.DefinePlugin({
'process.env.cmlApiPrefix': JSON.stringify(apiPrefix)
}))
Object.keys(domain).forEach(key => {
commonConfig.plugins.push(new webpack.DefinePlugin({
['process.env.domain.' + key]: JSON.stringify(domain[key])
}))
})
commonConfig.plugins.push(new webpack.DefinePlugin({
'process.env.media': JSON.stringify(options.media)
}))
if (options.minimize) {
commonConfig.plugins = commonConfig.plugins.concat([
new OptimizeCSSPlugin({
assetNameRegExp: /\.css$/,
cssProcessorOptions: { safe: true, discardComments: { removeAll: true }, autoprefixer: false }
}),
new UglifyJsPlugin({
compress: {
drop_console: chameleonConfig.optimize && chameleonConfig.optimize.dropConsole
}
})
])
}
let moduleIdType = options.moduleIdType
let moduleIdMap = {
hash: new webpack.HashedModuleIdsPlugin(),
name: new webpack.NamedModulesPlugin(),
chameleon: new ChameleonWebpackPlugin({openModuleHash: true, openChunkHash: true})
}
if (moduleIdType && moduleIdMap[moduleIdType]) {
commonConfig.plugins.push(moduleIdMap[moduleIdType])
}
let subProject = chameleonConfig.subProject;
if (subProject && subProject.length > 0) {
subProject.forEach(item => {
let npmName = cmlUtils.isString(item) ? item : item.npmName;
let packageJSON = JSON.parse(fs.readFileSync(path.resolve(cml.projectRoot, 'node_modules', npmName, 'package.json'), {encoding: 'utf-8'}));
let cmlConfig = packageJSON.cml || {};
let definePlugin = cmlConfig.definePlugin;
if (definePlugin) {
Object.keys(definePlugin).forEach(key => {
definePlugin[key] = JSON.stringify(definePlugin[key])
})
commonConfig.plugins.push(new webpack.DefinePlugin(definePlugin))
}
})
}
return commonConfig;
}