@apployees-nx/webserver
Version:
A create-react-app inspired plugin for Nx, with SSR and PWA capabilities.
171 lines (161 loc) • 7.5 kB
text/typescript
/*******************************************************************************
* © Apployees Inc., 2019
* All Rights Reserved.
******************************************************************************/
import { IBuildWebserverBuilderOptions } from "./webserver-types";
import { getWebserverEnvironmentVariables } from "./env";
import webpack from "webpack";
import MiniCssExtractPlugin from "mini-css-extract-plugin";
import WriteFileWebpackPlugin from "write-file-webpack-plugin";
import CaseSensitivePathsPlugin from "case-sensitive-paths-webpack-plugin";
import ModuleNotFoundPlugin from "react-dev-utils/ModuleNotFoundPlugin";
import ForkTsCheckerWebpackPlugin from "fork-ts-checker-webpack-plugin";
import typescriptFormatter from "react-dev-utils/typescriptFormatter";
import WatchMissingNodeModulesPlugin from "react-dev-utils/WatchMissingNodeModulesPlugin";
import findup from "findup-sync";
import { BuilderContext } from "@angular-devkit/architect";
import ForkTsNotifier from "fork-ts-checker-notifier-webpack-plugin";
import WebpackNotifier from "webpack-notifier";
import { getNotifierOptions } from "@apployees-nx/common-build-utils";
import _ from "lodash";
import path from "path";
import HardSourceWebpackPlugin from "hard-source-webpack-plugin-fixed-hashbug";
import NodeObjectHash from "node-object-hash";
export function getPlugins(options: IBuildWebserverBuilderOptions, context: BuilderContext, isEnvClient: boolean) {
const isEnvDevelopment = options.dev;
const nodeModulesPath = findup("node_modules");
const rootPath = findup("angular.json") || findup("nx.json") || options.root;
const notifierOptions = getNotifierOptions(options);
const tsPnp = _.isString(require.resolve("pnp-webpack-plugin/ts"))
? require.resolve("pnp-webpack-plugin/ts")
: "pnp-webpack-plugin/ts";
return [
new HardSourceWebpackPlugin({
// Either an absolute path or relative to webpack's options.context.
configHash: function (webpackConfig) {
return NodeObjectHash({ sort: false }).hash(webpackConfig);
},
// Either an absolute path or relative to webpack's options.context.
cacheDirectory: path.join(
options.buildCacheFolder,
"hard-source-webpack-plugin",
context.target.project + (isEnvClient ? "-client" : "-server"),
),
environmentHash: {
root: options.root,
directories: [],
files: ["package-lock.json", "yarn.lock"],
},
// Clean up large, old caches automatically.
cachePrune: {
sizeThreshold: 500 * 1024 * 1024,
},
}),
new HardSourceWebpackPlugin.ExcludeModulePlugin([
{
test: /file-loader/,
},
{
// HardSource works with mini-css-extract-plugin but due to how
// mini-css emits assets, assets are not emitted on repeated builds with
// mini-css and hard-source together. Ignoring the mini-css loader
// modules, but not the other css loader modules, excludes the modules
// that mini-css needs rebuilt to output assets every time.
test: /mini-css-extract-plugin[\\/]dist[\\/]loader/,
},
]),
isEnvDevelopment &&
options.notifier !== false &&
new WebpackNotifier({
title: `(${isEnvClient ? "Client" : "Server"}) ${context.target.project}`,
...notifierOptions,
}),
isEnvDevelopment &&
options.notifier !== false &&
new ForkTsNotifier({
title: `(${isEnvClient ? "Client" : "Server"}) ${context.target.project}`,
...notifierOptions,
skipSuccessful: true, // always skip successful for fork otherwise we get duplicate notifications
}),
// This gives some necessary context to module not found errors, such as
// the requesting resource.
new ModuleNotFoundPlugin(rootPath),
// 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 is set to production
// during a production build.
// Otherwise React will be compiled in the very slow development mode.
// we use define here instead of EnvironmentPlugin because on the browser
// process.env needs to be completely defined (i.e. it doesn't already
// exist)
isEnvClient &&
new webpack.DefinePlugin({
NODE_ENV: options.dev ? "development" : "production",
RENDER_ENV: "client",
}),
// on the server tho, we need the EnvironmentPlugin because we do not want
// to overwrite all of process.env
!isEnvClient &&
new webpack.EnvironmentPlugin({
NODE_ENV: options.dev ? "development" : "production",
RENDER_ENV: "server",
}),
// now we define the env variables loaded from .env files into the top-level
// 'env' object.
new webpack.DefinePlugin(getWebserverEnvironmentVariables(options, context, isEnvClient).stringified as any),
// This is necessary to emit hot updates
isEnvDevelopment && new WriteFileWebpackPlugin(),
isEnvDevelopment && new webpack.HotModuleReplacementPlugin(),
// Watcher doesn't work well if you mistype casing in a path so we use
// a plugin that prints an error when you attempt to do this.
// See https://github.com/facebook/create-react-app/issues/240
isEnvDevelopment && new CaseSensitivePathsPlugin(),
// If you require a missing module and then `npm install` it, you still have
// to restart the development server for Webpack to discover it. This plugin
// makes the discovery automatic so you don't have to restart.
// See https://github.com/facebook/create-react-app/issues/186
isEnvDevelopment && new WatchMissingNodeModulesPlugin(nodeModulesPath),
!isEnvDevelopment &&
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: "static/css/[name].[contenthash:8].css",
chunkFilename: "static/css/[name].[contenthash:8].chunk.css",
}),
// Moment.js is an extremely popular library that bundles large locale files
// by default due to how Webpack interprets its code. This is a practical
// solution that requires the user to opt into importing specific locales.
// https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
// You can remove this if you don't use Moment.js:
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
// TypeScript type checking
new ForkTsCheckerWebpackPlugin({
typescript: {
configFile: options.tsConfig,
configOverwrite: {
compilerOptions: {
incremental: true,
tsBuildInfoFile: path.join(
options.buildCacheFolder,
"fork-ts-checker-webpack-plugin",
context.target.project,
(isEnvClient ? "client" : "server") + ".tsbuildinfo",
),
sourceMap: isEnvDevelopment,
},
},
mode: "write-tsbuildinfo",
},
//issue: {
// exclude: ["**/__tests__/**", "**/?(*.)(spec|test).*", "**/src/setupTests.*"]
//},
//logger: {
// infrastructure: "silent",
// issues: "silent",
//},
async: isEnvDevelopment,
// The formatter is invoked directly in WebpackDevServerUtils during development
formatter: !isEnvDevelopment ? typescriptFormatter : undefined,
}),
];
}