UNPKG

webpack-config-spaceship

Version:
223 lines (210 loc) 6.45 kB
const path = require('path'); const pathExists = require('path-exists'); const fs = require('fs'); const findUp = require('find-up'); const merge = require('lodash.merge'); const autoprefixer = require('autoprefixer'); const findBabelConfig = require('find-babel-config'); const webpack = require('webpack'); const ExtractTextPlugin = require('extract-text-webpack-plugin'); const spaceshipBabelConfig = require('babel-preset-spaceship'); const { ENV } = require('./constants'); const paths = require('./paths'); const { defaultFilter, identity, getAssetsFromStats } = require('./utils'); function getWebpackConfig(opts = {}) { if (!opts.entry) { throw new Error('Required option "entry" is missing.'); } const options = merge({ module: {}, output: {}, }, opts); const cwd = options.cwd || process.cwd(); const contextPath = options.context || paths.contextPath; const outputPath = options.output.path || paths.outputPath; const publicPath = options.output.publicPath || `${process.env.HOST}/static`; const statsPath = path.join(cwd, options.statsPath || path.join(contextPath, './webpack.stats.json')); const manifestPath = path.join(cwd, options.manifestPath || path.join(contextPath, './webpack.manifest.json')); const pkgPath = findUp.sync('package.json', { cwd }); const pkg = require(pkgPath); // eslint-disable-line global-require, import/no-dynamic-require const babelConfig = merge( { plugins: ['react-hot-loader/babel'] }, spaceshipBabelConfig, findBabelConfig.sync(cwd).config ); const rules = [ { test: /\.(js|jsx)$/, exclude: /node_modules/, use: [ { loader: 'babel-loader', options: Object.assign( { babelrc: false, cacheDirectory: true, forceEnv: 'webpack', }, babelConfig ), }, ], }, { test: /\.json$/, loader: 'json-loader' }, { test: /\.html$/, loader: 'html-loader' }, { exclude: [ /\.html$/, /\.(js|jsx)$/, /\.(css|scss)$/, /\.json$/, /\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/, /\.svg$/, ], loader: 'file-loader', }, { test: [ /\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/, /\.svg$/, ], loader: 'url-loader', options: { limit: 10000, }, }, { test: /\.(css|scss)$/, loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: [ { loader: 'css-loader', options: { sourceMap: true, minimize: ENV !== 'development', importLoaders: 1, }, }, { loader: 'sass-loader', options: { sourceMap: true, minimize: ENV !== 'development', includePaths: [ `${cwd}/${contextPath}`, `${cwd}/node_modules`, ], }, }, { loader: 'postcss-loader', options: { ident: 'postcss', // https://webpack.js.org/guides/migrating/#complex-options plugins() { return [ autoprefixer({ browsers: [ 'last 2 versions', 'Firefox ESR', ], }), ]; }, }, }, ], }), }, ]; const entry = typeof options.entry === 'string' ? { main: options.entry } : options.entry; if (options.vendor !== false) { let vendor = ['babel-polyfill', 'whatwg-fetch']; if (Array.isArray(entry.vendor)) { vendor = entry.vendor.concat(vendor); } else if (typeof entry.vendor === 'string') { vendor.unshift(entry.vendor); } else if (pathExists.sync(`${cwd}/${contextPath}/vendor.js`)) { vendor.unshift('./vendor.js'); } else if (pathExists.sync(`${cwd}/${contextPath}/vendor.jsx`)) { vendor.unshift('./vendor.jsx'); } entry.vendor = vendor; } const chunks = Object.keys(entry); let plugins = [ new webpack.optimize.OccurrenceOrderPlugin(true), (ENV === 'development') && new webpack.HotModuleReplacementPlugin(), new webpack.NamedModulesPlugin(), new webpack.EnvironmentPlugin({ NODE_ENV: ENV, VERSION: pkg.version, }), (options.stylesheets !== false) && new ExtractTextPlugin('[name].[hash].css'), new webpack.IgnorePlugin(/^\.\/locale$/, [/moment$/]), (options.common !== false) && new webpack.optimize.CommonsChunkPlugin({ name: 'common', chunks: chunks.filter(options.filterCommonChunks || defaultFilter), }), (ENV === 'production') && new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false, }, sourceMap: true, }), function saveWebpackStats() { this.plugin('done', webpackStats => { const stats = webpackStats.toJson(); fs.writeFileSync( statsPath, JSON.stringify(stats, null, 2) ); const manifest = getAssetsFromStats(stats); fs.writeFileSync( manifestPath, JSON.stringify(manifest, null, 2) ); }); }, ]; plugins = plugins.filter(plugin => Boolean(plugin)); const filterRule = options.filterRule || defaultFilter; const mapRule = options.mapRule || identity; const filterPlugin = options.filterPlugin || defaultFilter; const mapPlugin = options.mapPlugin || identity; return merge({}, { entry, output: { filename: '[name].[hash].js', sourceMapFilename: '[file].map', path: path.join(cwd, outputPath), pathinfo: true, publicPath, }, module: { rules: rules.filter(filterRule).map(mapRule), }, plugins: plugins.filter(filterPlugin).map(mapPlugin), resolve: { extensions: ['.js', '.json', '.jsx'], modules: [ 'node_modules', path.join(cwd, contextPath), ], }, context: path.join(cwd, contextPath), devServer: { hot: true, contentBase: path.join(cwd, outputPath), publicPath, }, devtool: (ENV === 'production' ? 'source-map' : 'cheap-module-source-map'), }, options.merge || {}); } module.exports = getWebpackConfig;