UNPKG

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
/** * @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; }