@apployees-nx/webserver
Version:
A create-react-app inspired plugin for Nx, with SSR and PWA capabilities.
233 lines (224 loc) • 9.01 kB
text/typescript
/*******************************************************************************
* © Apployees Inc., 2019
* All Rights Reserved.
******************************************************************************/
import { IBuildWebserverBuilderOptions } from "../common/webserver-types";
import { getAssetsUrl } from "../common/env";
import postcssNormalize from "postcss-normalize";
import {
cssModuleRegex,
cssRegex,
getCSSModuleLocalIdent,
lessModuleRegex,
lessRegex,
sassModuleRegex,
sassRegex,
} from "../common/common-loaders";
import _ from "lodash";
import MiniCssExtractPlugin from "mini-css-extract-plugin";
import NodeSass from "node-sass";
export function getClientLoaders(options: IBuildWebserverBuilderOptions) {
const isEnvDevelopment: boolean = options.dev;
const isEnvProduction = !isEnvDevelopment;
const shouldUseSourceMap = isEnvDevelopment && options.sourceMapForStyles;
// 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.
// In development, we always serve from the root. This makes config easier.
const publicPath = getAssetsUrl(options);
// 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.startsWith(".");
let styleOrExtractLoader;
if (isEnvDevelopment) {
styleOrExtractLoader = _.isString(require.resolve("style-loader"))
? require.resolve("style-loader")
: "style-loader";
} else {
styleOrExtractLoader = {
loader: MiniCssExtractPlugin.loader,
// css is located in `static/css`, use '../../' to locate index.html folder
// in production `paths.publicUrlOrPath` can be a relative path
options: shouldUseRelativeAssetPaths ? { publicPath: "../../" } : {},
};
}
// common function to get style loaders
const getStyleLoaders = (isForModule: boolean, cssOptions?) => {
cssOptions = isForModule
? {
...cssOptions,
localsConvention: "dashesOnly",
modules: {
localIdentName: getCSSModuleLocalIdent(isEnvDevelopment),
},
}
: cssOptions;
return [
// For some reason, webpack is unable to load the CSS
// files that are extracted from this MiniCssExtractPlugin
// Therefore, for now, we are going to use style-loader regardless of dev or production mode,
// and comment out MiniCssExtractPlugin
styleOrExtractLoader,
{
loader: _.isString(require.resolve("css-loader")) ? require.resolve("css-loader") : "css-loader",
options: {
...cssOptions,
sourceMap: shouldUseSourceMap,
},
},
{
// Options for PostCSS as we reference these options twice
// Adds vendor prefixing based on your specified browser support in
// package.json
loader: _.isString(require.resolve("postcss-loader")) ? require.resolve("postcss-loader") : "postcss-loader",
options: {
// Necessary for external CSS imports to work
// https://github.com/facebook/create-react-app/issues/2677
ident: "postcss",
plugins: () => [
require("postcss-flexbugs-fixes"),
require("postcss-preset-env")({
autoprefixer: {
flexbox: "no-2009",
},
stage: 3,
}),
// Adds PostCSS Normalize as the reset css with default options,
// so that it honors browserslist config in package.json
// which in turn let's users customize the target behavior as per their needs.
postcssNormalize(),
],
sourceMap: shouldUseSourceMap,
},
},
].filter(Boolean);
};
return [
// "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$/, /\.woff$/, /\.woff2$/, /\.avi$/, /\.mp4$/, /\.mp3$/],
loader: _.isString(require.resolve("url-loader")) ? require.resolve("url-loader") : "url-loader",
options: {
limit: options.imageInlineSizeLimit,
name: "static/media/[name].[hash:8].[ext]",
},
},
// "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 MiniCSSExtractPlugin to extract that CSS
// to a file, but in development "style" loader enables hot editing
// of CSS.
// By default we support CSS Modules with the extension .module.css
{
test: cssRegex,
exclude: cssModuleRegex,
use: getStyleLoaders(false, {
importLoaders: 1,
sourceMap: shouldUseSourceMap,
}),
// Don't consider CSS imports dead code even if the
// containing package claims to have no side effects.
// Remove this when webpack adds a warning or an error for this.
// See https://github.com/webpack/webpack/issues/6571
sideEffects: true,
},
// Adds support for CSS Modules (https://github.com/css-modules/css-modules)
// using the extension .module.css
{
test: cssModuleRegex,
use: getStyleLoaders(true, {
importLoaders: 1,
}),
},
// Opt-in support for SASS (using .scss or .sass extensions).
// By default we support SASS Modules with the
// extensions .module.scss or .module.sass
{
test: sassRegex,
exclude: sassModuleRegex,
use: getStyleLoaders(false, {
importLoaders: 2,
}).concat({
loader: _.isString(require.resolve("sass-loader")) ? require.resolve("sass-loader") : "sass-loader",
options: {
implementation: NodeSass,
sourceMap: shouldUseSourceMap,
},
}),
// Don't consider CSS imports dead code even if the
// containing package claims to have no side effects.
// Remove this when webpack adds a warning or an error for this.
// See https://github.com/webpack/webpack/issues/6571
sideEffects: true,
},
// Adds support for CSS Modules, but using SASS
// using the extension .module.scss or .module.sass
{
test: sassModuleRegex,
use: getStyleLoaders(true, {
importLoaders: 2,
}).concat({
loader: _.isString(require.resolve("sass-loader")) ? require.resolve("sass-loader") : "sass-loader",
options: {
implementation: NodeSass,
sourceMap: shouldUseSourceMap,
},
}),
},
// Opt-in support for LESS (using .less).
// By default we support LESS Modules with the
// extensions .module.LESS
{
test: lessRegex,
exclude: lessModuleRegex,
use: getStyleLoaders(false, {
importLoaders: 2,
}).concat({
loader: _.isString(require.resolve("less-loader")) ? require.resolve("less-loader") : "less-loader",
options: {
sourceMap: shouldUseSourceMap,
javascriptEnabled: true,
modifyVars: options.lessStyleVariables_calculated,
},
}),
// Don't consider CSS imports dead code even if the
// containing package claims to have no side effects.
// Remove this when webpack adds a warning or an error for this.
// See https://github.com/webpack/webpack/issues/6571
sideEffects: true,
},
// Adds support for CSS Modules, but using less
// using the extension .module.less
{
test: lessModuleRegex,
use: getStyleLoaders(true, {
importLoaders: 2,
}).concat({
loader: _.isString(require.resolve("less-loader")) ? require.resolve("less-loader") : "less-loader",
options: {
sourceMap: shouldUseSourceMap,
javascriptEnabled: true,
modifyVars: options.lessStyleVariables_calculated,
},
}),
},
// "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.
{
loader: _.isString(require.resolve("file-loader")) ? require.resolve("file-loader") : "file-loader",
// Exclude `js` files to keep "css" loader working as it injects
// its runtime that would otherwise be processed through "file" loader.
// Also exclude `html` and `json` extensions so they get processed
// by webpacks internal loaders.
exclude: [/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
options: {
name: "static/media/[name].[hash:8].[ext]",
},
},
];
}