2020-03-29 01:43:59 +01:00
|
|
|
import curry from 'next/dist/compiled/lodash.curry'
|
2019-12-09 21:08:15 +01:00
|
|
|
import path from 'path'
|
2020-01-22 21:26:51 +01:00
|
|
|
import webpack, { Configuration } from 'webpack'
|
2019-12-13 21:23:28 +01:00
|
|
|
import MiniCssExtractPlugin from '../../../plugins/mini-css-extract-plugin'
|
2020-01-13 13:48:24 +01:00
|
|
|
import { loader, plugin } from '../../helpers'
|
2019-12-09 21:08:15 +01:00
|
|
|
import { ConfigurationContext, ConfigurationFn, pipe } from '../../utils'
|
2020-01-22 16:50:27 +01:00
|
|
|
import { getCssModuleLoader, getGlobalCssLoader } from './loaders'
|
2019-12-27 19:35:37 +01:00
|
|
|
import {
|
2020-01-14 20:53:42 +01:00
|
|
|
getCustomDocumentError,
|
2019-12-27 19:35:37 +01:00
|
|
|
getGlobalImportError,
|
|
|
|
getGlobalModuleImportError,
|
|
|
|
getLocalModuleImportError,
|
|
|
|
} from './messages'
|
2019-12-09 21:08:15 +01:00
|
|
|
import { getPostCssPlugins } from './plugins'
|
|
|
|
|
2020-01-22 21:26:51 +01:00
|
|
|
// RegExps for all Style Sheet variants
|
|
|
|
const regexLikeCss = /\.(css|scss|sass)$/
|
|
|
|
|
|
|
|
// RegExps for Style Sheets
|
2020-01-22 15:50:30 +01:00
|
|
|
const regexCssGlobal = /(?<!\.module)\.css$/
|
|
|
|
const regexCssModules = /\.module\.css$/
|
|
|
|
|
2020-01-22 21:26:51 +01:00
|
|
|
// RegExps for Syntactically Awesome Style Sheets
|
|
|
|
const regexSassGlobal = /(?<!\.module)\.(scss|sass)$/
|
|
|
|
const regexSassModules = /\.module\.(scss|sass)$/
|
|
|
|
|
2019-12-09 21:08:15 +01:00
|
|
|
export const css = curry(async function css(
|
|
|
|
ctx: ConfigurationContext,
|
|
|
|
config: Configuration
|
|
|
|
) {
|
2020-05-23 15:37:48 +02:00
|
|
|
const { prependData: sassPrependData, ...sassOptions } = ctx.sassOptions
|
|
|
|
|
2020-01-22 21:26:51 +01:00
|
|
|
const sassPreprocessors: webpack.RuleSetUseItem[] = [
|
|
|
|
// First, process files with `sass-loader`: this inlines content, and
|
|
|
|
// compiles away the proprietary syntax.
|
|
|
|
{
|
|
|
|
loader: require.resolve('sass-loader'),
|
|
|
|
options: {
|
|
|
|
// Source maps are required so that `resolve-url-loader` can locate
|
|
|
|
// files original to their source directory.
|
|
|
|
sourceMap: true,
|
2020-05-23 15:37:48 +02:00
|
|
|
sassOptions,
|
|
|
|
prependData: sassPrependData,
|
2020-01-22 21:26:51 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
// Then, `sass-loader` will have passed-through CSS imports as-is instead
|
|
|
|
// of inlining them. Because they were inlined, the paths are no longer
|
|
|
|
// correct.
|
|
|
|
// To fix this, we use `resolve-url-loader` to rewrite the CSS
|
|
|
|
// imports to real file paths.
|
|
|
|
{
|
|
|
|
loader: require.resolve('resolve-url-loader'),
|
|
|
|
options: {
|
|
|
|
// Source maps are not required here, but we may as well emit
|
|
|
|
// them.
|
|
|
|
sourceMap: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
]
|
|
|
|
|
2019-12-13 21:23:28 +01:00
|
|
|
const fns: ConfigurationFn[] = [
|
|
|
|
loader({
|
|
|
|
oneOf: [
|
|
|
|
{
|
|
|
|
// Impossible regex expression
|
|
|
|
test: /a^/,
|
|
|
|
loader: 'noop-loader',
|
|
|
|
options: { __next_css_remove: true },
|
|
|
|
},
|
|
|
|
],
|
|
|
|
}),
|
|
|
|
]
|
2019-12-09 21:08:15 +01:00
|
|
|
|
2019-12-31 00:53:35 +01:00
|
|
|
const postCssPlugins = await getPostCssPlugins(
|
|
|
|
ctx.rootDirectory,
|
2020-01-07 22:59:58 +01:00
|
|
|
ctx.isProduction,
|
2019-12-31 00:53:35 +01:00
|
|
|
// TODO: In the future, we should stop supporting old CSS setups and
|
|
|
|
// unconditionally inject ours. When that happens, we should remove this
|
|
|
|
// function argument.
|
|
|
|
true
|
|
|
|
)
|
2020-01-14 20:53:42 +01:00
|
|
|
|
|
|
|
// CSS cannot be imported in _document. This comes before everything because
|
|
|
|
// global CSS nor CSS modules work in said file.
|
|
|
|
fns.push(
|
|
|
|
loader({
|
|
|
|
oneOf: [
|
|
|
|
{
|
2020-01-22 21:26:51 +01:00
|
|
|
test: regexLikeCss,
|
2020-01-14 20:53:42 +01:00
|
|
|
// Use a loose regex so we don't have to crawl the file system to
|
|
|
|
// find the real file name (if present).
|
2020-06-17 17:00:29 +02:00
|
|
|
issuer: /pages[\\/]_document\./,
|
2020-01-14 20:53:42 +01:00
|
|
|
use: {
|
|
|
|
loader: 'error-loader',
|
|
|
|
options: {
|
|
|
|
reason: getCustomDocumentError(),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
})
|
|
|
|
)
|
|
|
|
|
2019-12-11 11:46:12 +01:00
|
|
|
// CSS Modules support must be enabled on the server and client so the class
|
2020-07-19 06:38:20 +02:00
|
|
|
// names are available for SSR or Prerendering.
|
2019-12-11 11:46:12 +01:00
|
|
|
fns.push(
|
|
|
|
loader({
|
|
|
|
oneOf: [
|
|
|
|
{
|
|
|
|
// CSS Modules should never have side effects. This setting will
|
|
|
|
// allow unused CSS to be removed from the production build.
|
|
|
|
// We ensure this by disallowing `:global()` CSS at the top-level
|
|
|
|
// via the `pure` mode in `css-loader`.
|
|
|
|
sideEffects: false,
|
|
|
|
// CSS Modules are activated via this specific extension.
|
2020-01-22 15:50:30 +01:00
|
|
|
test: regexCssModules,
|
2019-12-11 11:46:12 +01:00
|
|
|
// CSS Modules are only supported in the user's application. We're
|
|
|
|
// not yet allowing CSS imports _within_ `node_modules`.
|
|
|
|
issuer: {
|
2020-06-17 17:00:29 +02:00
|
|
|
and: [ctx.rootDirectory],
|
|
|
|
not: [/node_modules/],
|
2019-12-11 11:46:12 +01:00
|
|
|
},
|
2020-01-22 16:50:27 +01:00
|
|
|
use: getCssModuleLoader(ctx, postCssPlugins),
|
2019-12-11 11:46:12 +01:00
|
|
|
},
|
|
|
|
],
|
|
|
|
})
|
|
|
|
)
|
2020-04-25 21:00:41 +02:00
|
|
|
fns.push(
|
|
|
|
loader({
|
|
|
|
oneOf: [
|
|
|
|
// Opt-in support for Sass (using .scss or .sass extensions).
|
|
|
|
{
|
|
|
|
// Sass Modules should never have side effects. This setting will
|
|
|
|
// allow unused Sass to be removed from the production build.
|
|
|
|
// We ensure this by disallowing `:global()` Sass at the top-level
|
|
|
|
// via the `pure` mode in `css-loader`.
|
|
|
|
sideEffects: false,
|
|
|
|
// Sass Modules are activated via this specific extension.
|
|
|
|
test: regexSassModules,
|
|
|
|
// Sass Modules are only supported in the user's application. We're
|
|
|
|
// not yet allowing Sass imports _within_ `node_modules`.
|
|
|
|
issuer: {
|
2020-06-17 17:00:29 +02:00
|
|
|
and: [ctx.rootDirectory],
|
|
|
|
not: [/node_modules/],
|
2020-01-22 21:26:51 +01:00
|
|
|
},
|
2020-04-25 21:00:41 +02:00
|
|
|
use: getCssModuleLoader(ctx, postCssPlugins, sassPreprocessors),
|
|
|
|
},
|
|
|
|
],
|
|
|
|
})
|
|
|
|
)
|
2019-12-11 11:46:12 +01:00
|
|
|
|
|
|
|
// Throw an error for CSS Modules used outside their supported scope
|
|
|
|
fns.push(
|
|
|
|
loader({
|
|
|
|
oneOf: [
|
|
|
|
{
|
2020-09-10 19:45:30 +02:00
|
|
|
test: [regexCssModules, regexSassModules],
|
2019-12-11 11:46:12 +01:00
|
|
|
use: {
|
|
|
|
loader: 'error-loader',
|
|
|
|
options: {
|
2019-12-27 19:35:37 +01:00
|
|
|
reason: getLocalModuleImportError(),
|
2019-12-11 11:46:12 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
})
|
|
|
|
)
|
|
|
|
|
2019-12-09 21:08:15 +01:00
|
|
|
if (ctx.isServer) {
|
|
|
|
fns.push(
|
|
|
|
loader({
|
2020-01-22 15:50:30 +01:00
|
|
|
oneOf: [
|
2020-01-22 21:26:51 +01:00
|
|
|
{
|
2020-09-10 19:45:30 +02:00
|
|
|
test: [regexCssGlobal, regexSassGlobal],
|
2020-03-30 03:37:41 +02:00
|
|
|
use: require.resolve('next/dist/compiled/ignore-loader'),
|
2020-01-22 21:26:51 +01:00
|
|
|
},
|
2020-01-22 15:50:30 +01:00
|
|
|
],
|
2019-12-09 21:08:15 +01:00
|
|
|
})
|
|
|
|
)
|
2020-09-10 19:45:30 +02:00
|
|
|
} else {
|
2019-12-09 21:08:15 +01:00
|
|
|
fns.push(
|
|
|
|
loader({
|
|
|
|
oneOf: [
|
|
|
|
{
|
|
|
|
// A global CSS import always has side effects. Webpack will tree
|
|
|
|
// shake the CSS without this option if the issuer claims to have
|
|
|
|
// no side-effects.
|
|
|
|
// See https://github.com/webpack/webpack/issues/6571
|
|
|
|
sideEffects: true,
|
2020-01-22 15:50:30 +01:00
|
|
|
test: regexCssGlobal,
|
2020-09-10 19:45:30 +02:00
|
|
|
// We only allow Global CSS to be imported anywhere in the
|
|
|
|
// application if it comes from node_modules. This is a best-effort
|
|
|
|
// heuristic that makes a safety trade-off for better
|
|
|
|
// interoperability with npm packages that require CSS. Without
|
|
|
|
// this ability, the component's CSS would have to be included for
|
|
|
|
// the entire app instead of specific page where it's required.
|
|
|
|
include: { and: [/node_modules/] },
|
|
|
|
// Global CSS is only supported in the user's application, not in
|
|
|
|
// node_modules.
|
|
|
|
issuer: {
|
|
|
|
and: [ctx.rootDirectory],
|
|
|
|
not: [/node_modules/],
|
|
|
|
},
|
2020-01-22 16:50:27 +01:00
|
|
|
use: getGlobalCssLoader(ctx, postCssPlugins),
|
2019-12-09 21:08:15 +01:00
|
|
|
},
|
|
|
|
],
|
|
|
|
})
|
|
|
|
)
|
2020-09-10 19:45:30 +02:00
|
|
|
|
|
|
|
if (ctx.customAppFile) {
|
|
|
|
fns.push(
|
|
|
|
loader({
|
|
|
|
oneOf: [
|
|
|
|
{
|
|
|
|
// A global CSS import always has side effects. Webpack will tree
|
|
|
|
// shake the CSS without this option if the issuer claims to have
|
|
|
|
// no side-effects.
|
|
|
|
// See https://github.com/webpack/webpack/issues/6571
|
|
|
|
sideEffects: true,
|
|
|
|
test: regexCssGlobal,
|
|
|
|
issuer: { and: [ctx.customAppFile] },
|
|
|
|
use: getGlobalCssLoader(ctx, postCssPlugins),
|
|
|
|
},
|
|
|
|
],
|
|
|
|
})
|
|
|
|
)
|
|
|
|
fns.push(
|
|
|
|
loader({
|
|
|
|
oneOf: [
|
|
|
|
{
|
|
|
|
// A global Sass import always has side effects. Webpack will tree
|
|
|
|
// shake the Sass without this option if the issuer claims to have
|
|
|
|
// no side-effects.
|
|
|
|
// See https://github.com/webpack/webpack/issues/6571
|
|
|
|
sideEffects: true,
|
|
|
|
test: regexSassGlobal,
|
|
|
|
issuer: { and: [ctx.customAppFile] },
|
|
|
|
use: getGlobalCssLoader(ctx, postCssPlugins, sassPreprocessors),
|
|
|
|
},
|
|
|
|
],
|
|
|
|
})
|
|
|
|
)
|
|
|
|
}
|
2019-12-09 21:08:15 +01:00
|
|
|
}
|
|
|
|
|
2019-12-27 19:35:37 +01:00
|
|
|
// Throw an error for Global CSS used inside of `node_modules`
|
|
|
|
fns.push(
|
|
|
|
loader({
|
|
|
|
oneOf: [
|
|
|
|
{
|
2020-09-10 19:45:30 +02:00
|
|
|
test: [regexCssGlobal, regexSassGlobal],
|
2020-06-17 17:00:29 +02:00
|
|
|
issuer: { and: [/node_modules/] },
|
2019-12-27 19:35:37 +01:00
|
|
|
use: {
|
|
|
|
loader: 'error-loader',
|
|
|
|
options: {
|
|
|
|
reason: getGlobalModuleImportError(),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
})
|
|
|
|
)
|
|
|
|
|
2019-12-09 21:08:15 +01:00
|
|
|
// Throw an error for Global CSS used outside of our custom <App> file
|
|
|
|
fns.push(
|
|
|
|
loader({
|
|
|
|
oneOf: [
|
|
|
|
{
|
2020-09-10 19:45:30 +02:00
|
|
|
test: [regexCssGlobal, regexSassGlobal],
|
2019-12-09 21:08:15 +01:00
|
|
|
use: {
|
|
|
|
loader: 'error-loader',
|
|
|
|
options: {
|
|
|
|
reason: getGlobalImportError(
|
|
|
|
ctx.customAppFile &&
|
|
|
|
path.relative(ctx.rootDirectory, ctx.customAppFile)
|
|
|
|
),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
})
|
|
|
|
)
|
|
|
|
|
2019-12-26 23:17:37 +01:00
|
|
|
if (ctx.isClient) {
|
|
|
|
// Automatically transform references to files (i.e. url()) into URLs
|
|
|
|
// e.g. url(./logo.svg)
|
|
|
|
fns.push(
|
|
|
|
loader({
|
|
|
|
oneOf: [
|
|
|
|
{
|
|
|
|
// This should only be applied to CSS files
|
2020-06-17 17:00:29 +02:00
|
|
|
issuer: regexLikeCss,
|
2019-12-26 23:17:37 +01:00
|
|
|
// Exclude extensions that webpack handles by default
|
|
|
|
exclude: [/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
|
|
|
|
use: {
|
|
|
|
// `file-loader` always emits a URL reference, where `url-loader`
|
|
|
|
// might inline the asset as a data URI
|
2020-03-30 03:30:55 +02:00
|
|
|
loader: require.resolve('next/dist/compiled/file-loader'),
|
2019-12-26 23:17:37 +01:00
|
|
|
options: {
|
|
|
|
// Hash the file for immutable cacheability
|
|
|
|
name: 'static/media/[name].[hash].[ext]',
|
|
|
|
},
|
2019-12-09 21:08:15 +01:00
|
|
|
},
|
|
|
|
},
|
2019-12-26 23:17:37 +01:00
|
|
|
],
|
|
|
|
})
|
|
|
|
)
|
|
|
|
}
|
2019-12-09 21:08:15 +01:00
|
|
|
|
2020-01-13 13:48:24 +01:00
|
|
|
if (ctx.isClient && ctx.isProduction) {
|
|
|
|
// Extract CSS as CSS file(s) in the client-side production bundle.
|
|
|
|
fns.push(
|
|
|
|
plugin(
|
2020-07-15 19:56:27 +02:00
|
|
|
// @ts-ignore webpack 5 compat
|
2020-01-13 13:48:24 +01:00
|
|
|
new MiniCssExtractPlugin({
|
|
|
|
filename: 'static/css/[contenthash].css',
|
|
|
|
chunkFilename: 'static/css/[contenthash].css',
|
2020-01-22 15:50:30 +01:00
|
|
|
// Next.js guarantees that CSS order "doesn't matter", due to imposed
|
2020-01-13 13:48:24 +01:00
|
|
|
// restrictions:
|
|
|
|
// 1. Global CSS can only be defined in a single entrypoint (_app)
|
|
|
|
// 2. CSS Modules generate scoped class names by default and cannot
|
|
|
|
// include Global CSS (:global() selector).
|
|
|
|
//
|
2020-01-22 15:50:30 +01:00
|
|
|
// While not a perfect guarantee (e.g. liberal use of `:global()`
|
|
|
|
// selector), this assumption is required to code-split CSS.
|
|
|
|
//
|
2020-01-13 13:48:24 +01:00
|
|
|
// If this warning were to trigger, it'd be unactionable by the user,
|
2020-09-10 19:45:30 +02:00
|
|
|
// but likely not valid -- so we disable it.
|
2020-01-13 13:48:24 +01:00
|
|
|
ignoreOrder: true,
|
|
|
|
})
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2019-12-09 21:08:15 +01:00
|
|
|
const fn = pipe(...fns)
|
|
|
|
return fn(config)
|
|
|
|
})
|