Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HMR for client CSS imports #39916

Merged
merged 5 commits into from Aug 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
305 changes: 180 additions & 125 deletions packages/next/build/webpack/config/blocks/css/index.ts
Expand Up @@ -201,59 +201,121 @@ export const css = curry(async function css(

// CSS Modules support must be enabled on the server and client so the class
// names are available for SSR or Prerendering.
fns.push(
loader({
oneOf: [
markRemovable({
// 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.
test: regexCssModules,
// CSS Modules are only supported in the user's application. We're
// not yet allowing CSS imports _within_ `node_modules`.
issuer: {
and: [
{
or: [ctx.rootDirectory, regexClientEntry],
},
if (ctx.experimental.appDir && !ctx.isProduction) {
fns.push(
loader({
oneOf: [
markRemovable({
// 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.
test: regexCssModules,
// CSS Modules are only supported in the user's application. We're
// not yet allowing CSS imports _within_ `node_modules`.
issuer: {
and: [
{
or: [ctx.rootDirectory, regexClientEntry],
},
],
not: [/node_modules/],
},
use: [
require.resolve('../../../loaders/next-flight-css-dev-loader'),
...getCssModuleLoader(ctx, lazyPostCSSInitializer),
],
not: [/node_modules/],
},
use: getCssModuleLoader(ctx, lazyPostCSSInitializer),
}),
],
})
)
fns.push(
loader({
oneOf: [
// Opt-in support for Sass (using .scss or .sass extensions).
markRemovable({
// 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: {
and: [ctx.rootDirectory],
not: [/node_modules/],
},
use: getCssModuleLoader(
ctx,
lazyPostCSSInitializer,
sassPreprocessors
),
}),
],
})
)
}),
],
})
)
fns.push(
loader({
oneOf: [
// Opt-in support for Sass (using .scss or .sass extensions).
markRemovable({
// 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: {
and: [ctx.rootDirectory],
not: [/node_modules/],
},
use: [
require.resolve('../../../loaders/next-flight-css-dev-loader'),
...getCssModuleLoader(
ctx,
lazyPostCSSInitializer,
sassPreprocessors
),
],
}),
],
})
)
} else {
fns.push(
loader({
oneOf: [
markRemovable({
// 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.
test: regexCssModules,
// CSS Modules are only supported in the user's application. We're
// not yet allowing CSS imports _within_ `node_modules`.
issuer: {
and: [
{
or: [ctx.rootDirectory, regexClientEntry],
},
],
not: [/node_modules/],
},
use: getCssModuleLoader(ctx, lazyPostCSSInitializer),
}),
],
})
)
fns.push(
loader({
oneOf: [
// Opt-in support for Sass (using .scss or .sass extensions).
markRemovable({
// 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: {
and: [ctx.rootDirectory],
not: [/node_modules/],
},
use: getCssModuleLoader(
ctx,
lazyPostCSSInitializer,
sassPreprocessors
),
}),
],
})
)
}

if (!ctx.experimental.appDir) {
// Throw an error for CSS Modules used outside their supported scope
Expand All @@ -280,6 +342,7 @@ export const css = curry(async function css(
loader({
oneOf: [
markRemovable({
sideEffects: true,
test: [regexCssGlobal, regexSassGlobal],
use: require.resolve(
'../../../loaders/next-flight-css-dev-loader'
Expand All @@ -301,38 +364,7 @@ export const css = curry(async function css(
)
}
} else {
fns.push(
loader({
oneOf: [
markRemovable({
// 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,
// 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: ctx.experimental.craCompat
? undefined
: {
and: [ctx.rootDirectory],
not: [/node_modules/],
},
use: getGlobalCssLoader(ctx, lazyPostCSSInitializer),
}),
],
})
)

if (ctx.customAppFile) {
if (ctx.experimental.appDir) {
fns.push(
loader({
oneOf: [
Expand All @@ -343,8 +375,10 @@ export const css = curry(async function css(
// See https://github.com/webpack/webpack/issues/6571
sideEffects: true,
test: regexCssGlobal,
issuer: { and: [ctx.customAppFile] },
use: getGlobalCssLoader(ctx, lazyPostCSSInitializer),
use: [
require.resolve('../../../loaders/next-flight-css-dev-loader'),
...getGlobalCssLoader(ctx, lazyPostCSSInitializer),
],
}),
],
})
Expand All @@ -353,25 +387,17 @@ export const css = curry(async function css(
loader({
oneOf: [
markRemovable({
// 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,
lazyPostCSSInitializer,
sassPreprocessors
),
sideEffects: false,
test: regexCssModules,
use: [
require.resolve('../../../loaders/next-flight-css-dev-loader'),
...getCssModuleLoader(ctx, lazyPostCSSInitializer),
],
}),
],
})
)
}

if (ctx.experimental.appDir) {
} else {
fns.push(
loader({
oneOf: [
Expand All @@ -382,36 +408,65 @@ export const css = curry(async function css(
// See https://github.com/webpack/webpack/issues/6571
sideEffects: true,
test: regexCssGlobal,
issuer: {
and: [
{
or: [
{ and: [ctx.rootDirectory, /\.(js|mjs|jsx|ts|tsx)$/] },
regexClientEntry,
],
// 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: ctx.experimental.craCompat
? undefined
: {
and: [ctx.rootDirectory],
not: [/node_modules/],
},
],
},
use: getGlobalCssLoader(ctx, lazyPostCSSInitializer),
}),
],
})
)
fns.push(
loader({
oneOf: [
markRemovable({
sideEffects: false,
test: regexCssModules,
issuer: {
and: [ctx.rootDirectory, /\.(js|mjs|jsx|ts|tsx)$/],
or: [regexClientEntry],
},
use: getCssModuleLoader(ctx, lazyPostCSSInitializer),
}),
],
})
)

if (ctx.customAppFile) {
fns.push(
loader({
oneOf: [
markRemovable({
// 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, lazyPostCSSInitializer),
}),
],
})
)
fns.push(
loader({
oneOf: [
markRemovable({
// 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,
lazyPostCSSInitializer,
sassPreprocessors
),
}),
],
})
)
}
}
}

Expand Down
Expand Up @@ -40,8 +40,10 @@ export function getClientStyleLoader({
const MiniCssExtractPlugin =
require('../../../../plugins/mini-css-extract-plugin').default
return {
// @ts-ignore: TODO: remove when webpack 5 is stable
loader: MiniCssExtractPlugin.loader,
options: { publicPath: `${assetPrefix}/_next/`, esModule: false },
options: {
publicPath: `${assetPrefix}/_next/`,
esModule: false,
},
}
}