UNPKG

spinjs

Version:

<p align="center"><a href="#"><img width="150" src="https://rawgit.com/sysgears/spin.js/master/logo.svg"></a></p>

290 lines (264 loc) 9.98 kB
import * as path from 'path'; import * as ip from 'ip'; import * as url from 'url'; import Spin from "../Spin"; import { ConfigPlugin } from "../ConfigPlugin"; import { Builder } from "../Builder"; import requireModule from '../requireModule'; const pkg = requireModule('./package.json'); const __WINDOWS__ = /^win/.test(process.platform); const createPlugins = (builder: Builder, spin: Spin) => { const stack = builder.stack; const webpack = requireModule('webpack'); const buildNodeEnv = spin.dev ? (spin.test ? 'test' : 'development') : 'production'; let plugins = []; if (spin.dev) { plugins.push(new webpack.NamedModulesPlugin()); if (stack.hasAny(['server', 'web']) && !spin.test) { plugins.push(new webpack.HotModuleReplacementPlugin()); plugins.push(new webpack.NoEmitOnErrorsPlugin()); } } else { const uglifyOpts: any = { minimize: true }; if (stack.hasAny('angular')) { // https://github.com/angular/angular/issues/10618 uglifyOpts.mangle = { keep_fnames: true }; } plugins.push(new webpack.optimize.UglifyJsPlugin(uglifyOpts)); const loaderOpts: any = { minimize: true }; if (stack.hasAny('angular')) { loaderOpts.htmlLoader = { minimize: false // workaround for ng2 }; } plugins.push(new webpack.LoaderOptionsPlugin(loaderOpts)); plugins.push(new webpack.optimize.ModuleConcatenationPlugin()); } const backendUrl = spin.options.backendUrl.replace('{ip}', ip.address()); if (stack.hasAny('dll')) { const name = `vendor_${builder.parent.name}`; plugins = [ new webpack.DefinePlugin({ __DEV__: spin.dev, 'process.env.NODE_ENV': `"${buildNodeEnv}"`, }), new webpack.DllPlugin({ name, path: path.join(spin.options.dllBuildDir, `${name}_dll.json`), }), ]; } else { if (stack.hasAny('server')) { plugins = plugins.concat([ new webpack.BannerPlugin({ banner: 'require("source-map-support").install();', raw: true, entryOnly: false, }), new webpack.DefinePlugin({ __CLIENT__: false, __SERVER__: true, __SSR__: spin.options.ssr && !spin.test, __DEV__: spin.dev, 'process.env.NODE_ENV': `"${buildNodeEnv}"`, __BACKEND_URL__: `"${backendUrl}"`, ...spin.options.defines }), ]); } else { plugins = plugins.concat([ new webpack.DefinePlugin({ __CLIENT__: true, __SERVER__: false, __SSR__: spin.options.ssr && !spin.test, __DEV__: spin.dev, 'process.env.NODE_ENV': `"${buildNodeEnv}"`, __BACKEND_URL__: `"${backendUrl}"`, ...spin.options.defines }), ]); if (stack.hasAny('web')) { const ManifestPlugin = requireModule('webpack-manifest-plugin'); plugins.push(new ManifestPlugin({ fileName: 'assets.json', })); if (!spin.options.ssr) { const HtmlWebpackPlugin = requireModule('html-webpack-plugin'); plugins.push(new HtmlWebpackPlugin({ template: builder.htmlTemplate || path.join(__dirname, '../../html-plugin-template.ejs'), inject: 'body', })); } if (!spin.dev) { plugins.push(new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', filename: '[name].[hash].js', minChunks: function (module) { return module.resource && module.resource.indexOf(path.resolve('./node_modules')) === 0; }, })); } } } } return plugins; }; const getDepsForNode = (builder, depPlatforms) => { let deps = []; for (let key of Object.keys(pkg.dependencies)) { const val = depPlatforms[key]; if (key.indexOf('@types') !== 0 && (!val || (val.constructor === Array && val.indexOf(builder.parent.name) >= 0) || val === builder.parent.name)) { deps.push(key); } } return deps; }; let curWebpackDevPort = 3000; let webpackPortMap = {}; const createConfig = (builder: Builder, spin: Spin) => { const stack = builder.stack; const backendUrl = spin.options.backendUrl.replace('{ip}', ip.address()); const baseConfig: any = { name: builder.name, devtool: spin.dev ? '#cheap-module-source-map' : '#source-map', module: { rules: [], }, resolve: { modules: [path.join(process.cwd(), 'node_modules'), 'node_modules'], }, watchOptions: { ignored: /build/, }, bail: !spin.dev, }; const baseDevServerConfig = { hot: true, contentBase: '/', publicPath: '/', headers: { 'Access-Control-Allow-Origin': '*' }, quiet: false, noInfo: true, historyApiFallback: true, stats: { colors: true, chunkModules: false }, }; const plugins = createPlugins(builder, spin); let config = { ...baseConfig, plugins, }; if (stack.hasAny('server')) { config = { ...config, target: 'node', node: { __dirname: true, __filename: true, }, externals: [requireModule('webpack-node-externals')({ whitelist: [/(^webpack|^react-native)/] })], } } if (stack.hasAny('dll')) { const name = `vendor_${builder.parent.name}`; config = { ...config, devtool: '#cheap-module-source-map', entry: { vendor: getDepsForNode(builder, spin.depPlatforms), }, output: { filename: `${name}.[hash]_dll.js`, path: path.resolve(spin.options.dllBuildDir), library: name, }, }; } else { if (stack.hasAny('server')) { const index = []; if (spin.dev && !spin.test) { if (__WINDOWS__) { index.push('webpack/hot/poll?1000'); } else { index.push('webpack/hot/signal.js'); } } index.push('./src/server/index.js'); config = { ...config, entry: { index, }, output: { devtoolModuleFilenameTemplate: spin.dev ? '../../[resource-path]' : undefined, devtoolFallbackModuleFilenameTemplate: spin.dev ? '../../[resource-path];[hash]' : undefined, filename: '[name].js', sourceMapFilename: '[name].[chunkhash].js.map', path: path.resolve(spin.options.backendBuildDir), publicPath: '/', }, }; } else if (stack.hasAny('web')) { const { protocol, host } = url.parse(backendUrl); const backendBaseUrl = protocol + '//' + host; let webpackDevPort; if (!builder.webpackDevPort) { if (!webpackPortMap[builder.name]) { webpackPortMap[builder.name] = curWebpackDevPort++; } webpackDevPort = webpackPortMap[builder.name]; } else { webpackDevPort = builder.webpackDevPort; } config = { ...config, entry: { index: (spin.dev ? [`webpack-hot-middleware/client`] : []).concat([ './src/client/index.js', ]), }, output: { filename: '[name].[hash].js', path: path.resolve(path.join(spin.options.frontendBuildDir, 'web')), publicPath: '/', }, devServer: { ...baseDevServerConfig, port: webpackDevPort, proxy: { '!/*.hot-update.{json,js}': { target: backendBaseUrl, logLevel: 'info', }, }, }, }; } else if (stack.hasAny('react-native')) { config = { ...config, entry: { index: [ './src/mobile/index.js', ], }, output: { filename: `index.mobile.bundle`, publicPath: '/', path: path.resolve(path.join(spin.options.frontendBuildDir, builder.name)), }, devServer: { ...baseDevServerConfig, hot: false, port: stack.hasAny('android') ? 3010 : 3020, }, }; } else { throw new Error(`Unknown platform target: ${stack.platform}`); } } return config; }; export default class WebpackPlugin implements ConfigPlugin { configure(builder: Builder, spin: Spin) { const stack = builder.stack; if (stack.hasAny('webpack')) { builder.config = builder.config || {}; builder.config = spin.merge(builder.config, createConfig(builder, spin)); } } }