roadhogx
Version:
Cli tool for serve and build react app, based on roadhog, support JSON pattern config, support more features
301 lines (282 loc) • 6.85 kB
JavaScript
/**
* @Author: eason
* @Date: 2017-06-28T10:28:21+08:00
* @Email: uniquecolesmith@gmail.com
* @Last modified by: eason
* @Last modified time: 2017-07-15T16:45:11+08:00
* @License: MIT
* @Copyright: Eason(uniquecolesmith@gmail.com)
*/
import webpack from 'webpack';
import autoprefixer from 'autoprefixer';
import { existsSync } from 'fs';
import path from 'path';
import ExtractTextPlugin from 'extract-text-webpack-plugin';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import CopyWebpackPlugin from 'copy-webpack-plugin';
import normalizeDefine from '../utils/normalizeDefine';
export function getBabelOptions(config) {
return {
babelrc: false,
presets: [
require.resolve('babel-preset-es2015'),
require.resolve('babel-preset-react'),
require.resolve('babel-preset-react-optimize'),
require.resolve('babel-preset-stage-0'),
].concat(config.extraBabelPresets || []),
plugins: [
require.resolve('babel-plugin-add-module-exports'),
require.resolve('babel-plugin-react-require'),
require.resolve('babel-plugin-transform-decorators-legacy'),
].concat(config.extraBabelPlugins || []),
cacheDirectory: true,
};
}
export const baseSvgLoader = {
test: /\.svg$/,
loader: 'file',
options: {
name: 'static/[name].[hash:8].[ext]',
},
};
export const spriteSvgLoader = {
test: /\.(svg)$/i,
loader: 'svg-sprite',
};
export const defaultDevtool = '#cheap-module-eval-source-map';
export function getResolve(config, paths) {
return {
resolve: {
modules: [
paths.ownNodeModules,
paths.appNodeModules,
],
extensions: [
...(config.extraResolveExtensions || []),
'.web.js', '.web.jsx', '.web.ts', '.web.tsx',
'.js', '.json', '.jsx', '.ts', '.tsx',
],
alias: {
'@': paths.appSrc, // eslint-disable-line
components: path.join(paths.appSrc, 'components'),
models: path.join(paths.appSrc, 'models'),
routes: path.join(paths.appSrc, 'routes'),
services: path.join(paths.appSrc, 'services'),
assets: path.join(paths.appSrc, 'assets'),
utils: path.join(paths.appSrc, 'utils'),
...Object.entries(config.alias || {})
.reduce(
(t, [name, value]) => Object.assign(t, { [name]: value }),
{},
),
},
},
resolveLoader: {
modules: [
paths.ownNodeModules,
paths.appNodeModules,
],
moduleExtensions: ['-loader'],
},
};
}
export function getFirstRules({ paths, babelOptions, config }) {
return [
{
exclude: [
/\.html$/,
/\.(js|jsx)$/,
/\.(css|less|scss|sass)$/,
/\.json$/,
/\.svg$/,
/\.tsx?$/,
],
loader: 'url',
options: {
limit: 10000,
name: 'static/[name].[hash:8].[ext]',
},
},
{
test: /\.(js|jsx)$/,
include: (config && config.include && Array.isArray(config.include))
? [paths.appSrc, ...(config.include.map(t => paths.resolveApp(t)))]
: paths.appSrc,
loader: 'babel',
options: babelOptions,
},
];
}
export function getLastRules({ paths, babelOptions }) {
return [
/*
{
test: /\.html$/,
loader: 'file',
options: {
name: '[name].[ext]',
},
},
*/
{
test: /\.tsx?$/,
include: paths.appSrc,
use: [
{
loader: 'babel',
options: babelOptions,
},
{
loader: 'awesome-typescript',
options: {
transpileOnly: true,
},
},
],
},
];
}
export function getCSSRules(env, { paths, cssLoaders, theme, config }) {
const rules = [
{
test: /\.css$/,
include: (config && config.include && Array.isArray(config.include))
? [paths.appSrc, ...(config.include.map(t => paths.resolveApp(t)))]
: paths.appSrc,
use: [
'style',
...cssLoaders.own,
],
},
{
test: /\.less$/,
include: paths.appSrc,
use: [
'style',
...cssLoaders.own,
{
loader: 'less',
options: {
modifyVars: theme,
},
},
],
},
{
test: /\.scss$/,
include: paths.appSrc,
use: [
'style',
...cssLoaders.own,
{
loader: 'sass',
},
],
},
{
test: /\.css$/,
include: paths.appNodeModules,
use: [
'style',
...cssLoaders.nodeModules,
],
},
{
test: /\.less$/,
include: paths.appNodeModules,
use: [
'style',
...cssLoaders.nodeModules,
{
loader: 'less',
options: {
modifyVars: theme,
},
},
],
},
{
test: /\.scss$/,
include: paths.appNodeModules,
use: [
'style',
...cssLoaders.nodeModules,
{
loader: 'sass',
},
],
},
];
if (env === 'production') {
rules.forEach((rule) => {
rule.use = ExtractTextPlugin.extract({
fallback: 'style',
use: rule.use.slice(1),
});
});
}
return rules;
}
export const node = {
fs: 'empty',
net: 'empty',
tls: 'empty',
};
export function getCommonPlugins({ config, paths, appBuild, NODE_ENV }) {
const ret = [];
// ref: https://zhuanlan.zhihu.com/p/27980441
// ret.push(new webpack.optimize.ModuleConcatenationPlugin());
let defineObj = {
'process.env': {
NODE_ENV: JSON.stringify(NODE_ENV),
},
};
if (config.define) {
defineObj = {
...defineObj,
...normalizeDefine(config.define),
};
}
ret.push(new webpack.DefinePlugin(defineObj));
if (existsSync(paths.appPublic)) {
ret.push(new CopyWebpackPlugin([
{
from: paths.appPublic,
to: appBuild,
},
]));
}
if (config.multipage) {
ret.push(new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
// filename: '[name].js',
}));
}
ret.push(new webpack.LoaderOptionsPlugin({
options: {
context: __dirname,
postcss: [
autoprefixer(config.autoprefixer || {
browsers: [
'>1%',
'last 4 versions',
'Firefox ESR',
'not ie < 9', // React doesn't support IE8 anyway
],
}),
...(config.extraPostCSSPlugins ? config.extraPostCSSPlugins : []),
],
},
}));
if (config.provide) {
ret.push(new webpack.ProvidePlugin(config.provide));
}
const htmlPath = path.join(paths.appPublic, config.html || 'index.html');
if (existsSync(htmlPath)) {
ret.push(new HtmlWebpackPlugin({
template: htmlPath,
inject: true,
}));
}
return ret;
}