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
text/typescript
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));
}
}
}