diff --git a/next.js b/next.js index d2a11f8..679bca0 100644 --- a/next.js +++ b/next.js @@ -1,8 +1,8 @@ -const { - getClientStyleLoader -} = require('next/dist/build/webpack/config/blocks/css/loaders/client'); const NextMiniCssExtractPlugin = require('next/dist/build/webpack/plugins/mini-css-extract-plugin') .default; + +const { findPagesDir } = require('next/dist/lib/find-pages-dir'); + const { browserslist } = require('next/dist/compiled/browserslist'); const { lazyPostCSS } = require('next/dist/build/webpack/config/blocks/css'); @@ -49,53 +49,73 @@ const getNextMiniCssExtractPlugin = isDev => { // Always use Next.js built-in MiniCssExtractPlugin in production return NextMiniCssExtractPlugin; }; +// Adopt from Next.js' getGlobalCssLoader +// https://github.com/vercel/next.js/blob/0572e218afe130656be53f7367bf18c4ea389f7d/packages/next/build/webpack/config/blocks/css/loaders/global.ts#L7 function getStyle9VirtualCssLoader(options, MiniCssExtractPlugin) { - const outputLoaders = [ - { - loader: cssLoader, - options: { - // A simplify version of https://github.com/vercel/next.js/blob/88a5f263f11cb55907f0d89a4cd53647ee8e96ac/packages/next/build/webpack/config/blocks/css/index.ts#L142-L147 - postcss: () => - lazyPostCSS( - options.dir, - getSupportedBrowsers(options.dir, options.dev) - ) - } - } - ]; + const loaders = []; + // Adopt from Next.js' getClientStyleLoader + // https://github.com/vercel/next.js/blob/0572e218afe130656be53f7367bf18c4ea389f7d/packages/next/build/webpack/config/blocks/css/loaders/client.ts#L4 if (!options.isServer) { - outputLoaders.unshift({ - // Logic adopted from https://git.io/JfD9r - ...getClientStyleLoader({ - // In development model Next.js uses style-loader, which inserts each - // CSS file as its own style tag, which means the CSS won't be sorted - // and causes issues with determinism when using media queries and - // pseudo selectors. Setting isDevelopment means MiniCssExtractPlugin is - // used instead. - isDevelopment: false, - assetPrefix: options.config.assetPrefix - }), - loader: MiniCssExtractPlugin.loader + loaders.push({ + loader: MiniCssExtractPlugin.loader, + options: { + publicPath: `${options.assetPrefix}/_next/`, + esModule: false + } }); } - return outputLoaders; + loaders.push({ + // https://github.com/vercel/next.js/blob/0572e218afe130656be53f7367bf18c4ea389f7d/packages/next/build/webpack/config/blocks/css/loaders/global.ts#L29-L38 + loader: cssLoader, + options: { + // https://github.com/vercel/next.js/blob/88a5f263f11cb55907f0d89a4cd53647ee8e96ac/packages/next/build/webpack/config/blocks/css/index.ts#L142-L147 + postcss: () => + lazyPostCSS( + options.dir, + getSupportedBrowsers(options.dir, options.dev) + ), + importLoaders: 1, + modules: false + } + }); + // We don't need postcss loader here, as style9 always produce vanilla css, + // and only perform sort/merge afterwards + + return loaders; } module.exports = (pluginOptions = {}) => (nextConfig = {}) => { return { ...nextConfig, - webpack(config, options) { - const outputCSS = !options.isServer; + webpack(config, ctx) { + const findPagesDirResult = findPagesDir( + ctx.dir, + nextConfig.experimental && nextConfig.experimental.appDir + ); + + // https://github.com/vercel/next.js/blob/1fb4cad2a8329811b5ccde47217b4a6ae739124e/packages/next/build/index.ts#L336 + // https://github.com/vercel/next.js/blob/1fb4cad2a8329811b5ccde47217b4a6ae739124e/packages/next/build/webpack-config.ts#L626 + // https://github.com/vercel/next.js/pull/43916 + const hasAppDir = + // on Next.js 12, findPagesDirResult is a string. on Next.js 13, findPagesDirResult is an object + !!(nextConfig.experimental && nextConfig.experimental.appDir) && + !!(findPagesDirResult && findPagesDirResult.appDir); + + const outputCSS = hasAppDir + ? // When there is appDir, always output css, even on server (React Server Component is also server) + true + : // There is no appDir, do not output css on server build + !ctx.isServer; // The style9 compiler must run on source code, which means it must be // configured as the last loader in webpack so that it runs before any // other transformation. if (typeof nextConfig.webpack === 'function') { - config = nextConfig.webpack(config, options); + config = nextConfig.webpack(config, ctx); } // For some reason, Next 11.0.1 has `config.optimization.splitChunks` @@ -104,7 +124,7 @@ module.exports = (pluginOptions = {}) => (nextConfig = {}) => { cacheGroups: {} }; - const MiniCssExtractPlugin = getNextMiniCssExtractPlugin(options.dev); + const MiniCssExtractPlugin = getNextMiniCssExtractPlugin(ctx.dev); config.module.rules.push({ test: /\.(tsx|ts|js|mjs|jsx)$/, @@ -139,7 +159,7 @@ module.exports = (pluginOptions = {}) => (nextConfig = {}) => { // Here we matches virtual css file emitted by Style9Plugin cssRules.unshift({ test: /\.style9.css$/, - use: getStyle9VirtualCssLoader(options, MiniCssExtractPlugin) + use: getStyle9VirtualCssLoader(ctx, MiniCssExtractPlugin) }); if (outputCSS) { @@ -165,7 +185,7 @@ module.exports = (pluginOptions = {}) => (nextConfig = {}) => { ) { // HMR reloads the CSS file when the content changes but does not use // the new file name, which means it can't contain a hash. - const filename = options.dev + const filename = ctx.dev ? 'static/css/[name].css' : 'static/css/[contenthash].css';