You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

288 lines
11 KiB

const path = require("path");
const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const CaseSensitivePathsPlugin = require("case-sensitive-paths-webpack-plugin");
const WatchMissingNodeModulesPlugin = require("react-dev-utils/WatchMissingNodeModulesPlugin");
// const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const MinifyPlugin = require("babel-minify-webpack-plugin");
const cssnext = require("postcss-cssnext");
const { getClientEnvironment } = require("../env");
const paths = require("../paths");
// Webpack uses `publicPath` to determine where the app is being served from.
// It requires a trailing slash, or the file assets will get an incorrect path.
const publicPath = paths.publicPath;
// `publicUrl` is just like `publicPath`, but we will provide it to our app
// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
// Omit trailing slash as %PUBLIC_URL%/xyz looks better than %PUBLIC_URL%xyz.
const publicUrl = paths.publicUrl.slice(0, -1);
// Source maps are resource heavy and can cause out of memory issue for large source files.
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false';
// Some apps do not use client-side routing with pushState.
// For these, "homepage" can be set to "." to enable relative asset paths.
const shouldUseRelativeAssetPaths = publicPath === "./";
// Get environment variables to inject into our app.
const environ = getClientEnvironment(publicUrl);
// Note: defined here because it will be used more than once.
const cssFilename = "static/css/[name].[contenthash:8].css";
const postCssConfig = {
loader: require.resolve("postcss-loader"),
options: {
// Necessary for external CSS imports to work
// https://github.com/facebookincubator/create-react-app/issues/2677
ident: "postcss",
plugins: () => [
require("postcss-flexbugs-fixes"),
cssnext({
browsers: [
">1%",
"last 4 versions",
"Firefox ESR",
"not ie < 9", // React doesn"t support IE8 anyway
],
flexbox: "no-2009",
}),
],
},
};
const sassConfig = {
loader: require.resolve("sass-loader"),
options: {},
};
const rules = (env) => {
// "postcss" loader applies autoprefixer to our CSS.
// "css" loader resolves paths in CSS and adds assets as dependencies.
// "style" loader turns CSS into JS modules that inject <style> tags.
// In production, we use a plugin to extract that CSS to a file, but
// in development "style" loader enables hot editing of CSS.
const cssRule = (env === "dev") ?
{
test: /\.css$/,
use: [
require.resolve("style-loader"),
{
loader: require.resolve("css-loader"),
options: {
importLoaders: 1,
},
},
postCssConfig,
],
} :
{
test: /\.css$/,
loader: ExtractTextPlugin.extract({
fallback: require.resolve('style-loader'),
use: [
{
loader: require.resolve('css-loader'),
options: {
importLoaders: 1,
minimize: true,
sourceMap: shouldUseSourceMap,
},
},
postCssConfig
],
// ExtractTextPlugin expects the build output to be flat.
// (See https://github.com/webpack-contrib/extract-text-webpack-plugin/issues/27)
// However, our output is structured with css, js and media folders.
// To have this structure working with relative paths, we have to use custom options.
publicPath: shouldUseRelativeAssetPaths ?
Array(cssFilename.split("/").length).join("../") :
publicPath,
})
};
const sassRule = (env === "dev") ?
{
test: /\.scss$/,
use: [
require.resolve("style-loader"),
{
loader: require.resolve("css-loader"),
options: {
importLoaders: 1,
},
},
sassConfig,
],
} :
{
test: /\.css$/,
loader: ExtractTextPlugin.extract({
fallback: require.resolve('style-loader'),
use: [
{
loader: require.resolve('css-loader'),
options: {
importLoaders: 1,
minimize: true,
sourceMap: shouldUseSourceMap,
},
},
sassConfig
],
// ExtractTextPlugin expects the build output to be flat.
// (See https://github.com/webpack-contrib/extract-text-webpack-plugin/issues/27)
// However, our output is structured with css, js and media folders.
// To have this structure working with relative paths, we have to use custom options.
publicPath: shouldUseRelativeAssetPaths ?
Array(cssFilename.split("/").length).join("../") :
publicPath,
})
};
return [
{
test: /\.tsx?$/,
enforce: "pre",
loader: require.resolve("tslint-loader"),
options: { typeCheck: true, tsConfigFile: paths.appTsConfig },
},
{
// "oneOf" will traverse all following loaders until one will
// match the requirements. when no loader matches it will fall
// back to the "file" loader at the end of the loader list.
oneOf: [
// "url" loader works like "file" loader except that it embeds assets
// smaller than specified limit in bytes as data urls to avoid requests.
// a missing `test` is equivalent to a match.
{
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
loader: require.resolve("url-loader"),
options: {
limit: 10000,
name: "static/media/[name].[hash:8].[ext]",
},
},
cssRule,
sassRule,
// Process TypeScript with TSC.
{
test: /\.tsx?$/, use: {
loader: "awesome-typescript-loader",
options: { configFileName: paths.appTsConfig }
},
},
// "file" loader makes sure those assets get served by WebpackDevServer.
// When you `import` an asset, you get its (virtual) filename.
// In production, they would get copied to the `build` folder.
// This loader doesn"t use a "test" so it will catch all modules
// that fall through the other loaders.
{
// Exclude `js` files to keep "css" loader working as it injects
// it"s runtime that would otherwise processed through "file" loader.
// Also exclude `html` and `json` extensions so they get processed
// by webpacks internal loaders.
exclude: [/\.js$/, /\.html$/, /\.json$/],
loader: require.resolve("file-loader"),
options: {
name: "static/media/[name].[hash:8].[ext]",
},
},
],
},
];
}
const getConfig = module.exports = (env) => {
const isProd = (env === "prod");
const isDev = (env === "dev");
// Assert this just to be safe.
// Development builds of React are slow and not intended for production.
if (isProd && environ.stringified["process.env"].NODE_ENV !== '"production"') {
throw new Error("Production builds must have NODE_ENV=production.");
}
const plugins = [
new HtmlWebpackPlugin({
inject: true,
template: paths.appHtml,
minify: isProd ? {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
} : undefined,
}),
// Makes some environment variables available to the JS code, for example:
// if (process.env.NODE_ENV === "production") { ... }. See `./env.js`.
// It is absolutely essential that NODE_ENV was set to production here.
// Otherwise React will be compiled in the very slow development mode.
new webpack.DefinePlugin(environ.stringified),
new CaseSensitivePathsPlugin(),
// Note: this won"t work without ExtractTextPlugin.extract(..) in `loaders`.
isProd && new ExtractTextPlugin({
filename: cssFilename,
}),
// TODO: doesn't work with typescript target: es6
// isProd && new UglifyJsPlugin({
// sourceMap: shouldUseSourceMap,
// }),
isProd && new MinifyPlugin({}, {
sourceMap: shouldUseSourceMap,
}),
isDev && new webpack.NamedModulesPlugin(),
isDev && new webpack.HotModuleReplacementPlugin(),
isDev && new WatchMissingNodeModulesPlugin(paths.nodeModulesDir),
].filter(Boolean);
return {
bail: isProd,
devtool: shouldUseSourceMap ?
isProd ? "source-map" : "eval-source-map" :
false,
entry: [
isDev && require.resolve("react-hot-loader/patch"),
isDev && require.resolve("react-dev-utils/webpackHotDevClient"),
require.resolve("./polyfills"),
paths.appEntry,
].filter(Boolean),
output: {
path: paths.appBuildDir,
pathinfo: isDev,
filename: isProd ?
'static/js/[name].[chunkhash:8].js' :
"static/js/bundle.js",
chunkFilename: isProd ?
'static/js/[name].[chunkhash:8].chunk.js' :
"static/js/[name].chunk.js",
publicPath: publicPath,
devtoolModuleFilenameTemplate: isDev ?
(info) =>
"webpack://" + path.resolve(info.absoluteResourcePath).replace(/\\/g, "/") : undefined,
},
resolve: {
extensions: [".ts", ".tsx", ".js", ".json"],
alias: {
"@app": paths.appDir,
"@common": paths.commonDir,
}
},
module: {
rules: rules(env),
},
plugins: plugins,
devServer: {
hot: true,
port: 8081,
proxy: [{
context: ["/api"], // TODO: update when there is actually an api
target: paths.publicUrl,
}],
},
}
};