From 2217aa8a99a6682e4ff4e41df5c5aace711ac4a4 Mon Sep 17 00:00:00 2001 From: SukkaW Date: Sat, 17 Dec 2022 09:52:14 +0800 Subject: [PATCH 1/4] First attempt to support Next.js 13 --- next.js | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/next.js b/next.js index d2a11f8..3af3afe 100644 --- a/next.js +++ b/next.js @@ -3,6 +3,9 @@ const { } = 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'); @@ -50,7 +53,7 @@ const getNextMiniCssExtractPlugin = isDev => { return NextMiniCssExtractPlugin; }; -function getStyle9VirtualCssLoader(options, MiniCssExtractPlugin) { +function getStyle9VirtualCssLoader(options, MiniCssExtractPlugin, hasAppDir) { const outputLoaders = [ { loader: cssLoader, @@ -69,6 +72,7 @@ function getStyle9VirtualCssLoader(options, MiniCssExtractPlugin) { outputLoaders.unshift({ // Logic adopted from https://git.io/JfD9r ...getClientStyleLoader({ + hasAppDir, // 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 @@ -87,15 +91,28 @@ function getStyle9VirtualCssLoader(options, MiniCssExtractPlugin) { module.exports = (pluginOptions = {}) => (nextConfig = {}) => { return { ...nextConfig, - webpack(config, options) { - const outputCSS = !options.isServer; + webpack(config, ctx) { + const findPagesDirResult = findPagesDir(ctx.dir); + // 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.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 +121,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 +156,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, hasAppDir) }); if (outputCSS) { @@ -165,7 +182,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'; From ac1667b07bec430c1faeded2bb0532a23eedfdcc Mon Sep 17 00:00:00 2001 From: SukkaW Date: Sat, 17 Dec 2022 11:06:57 +0800 Subject: [PATCH 2/4] Fix nextConfig.experimental.appDir reading --- next.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/next.js b/next.js index 3af3afe..a13d4a5 100644 --- a/next.js +++ b/next.js @@ -98,7 +98,7 @@ module.exports = (pluginOptions = {}) => (nextConfig = {}) => { // 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.appDir && + !!(nextConfig.experimental && nextConfig.experimental.appDir) && !!(findPagesDirResult && findPagesDirResult.appDir); const outputCSS = hasAppDir From d835897f2e4c0aba6d5ec6749a9053b48c58d3c8 Mon Sep 17 00:00:00 2001 From: SukkaW Date: Thu, 22 Dec 2022 14:58:27 +0800 Subject: [PATCH 3/4] Fix findPagesDir requires 2 arguments --- next.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/next.js b/next.js index a13d4a5..43d17f8 100644 --- a/next.js +++ b/next.js @@ -92,7 +92,11 @@ module.exports = (pluginOptions = {}) => (nextConfig = {}) => { return { ...nextConfig, webpack(config, ctx) { - const findPagesDirResult = findPagesDir(ctx.dir); + 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 From b97974fb7598bf9df18bf29e46fa0d7497c8d1d8 Mon Sep 17 00:00:00 2001 From: SukkaW Date: Thu, 22 Dec 2022 18:56:12 +0800 Subject: [PATCH 4/4] Simplify Next.js plugin to prevent breaking in the future --- next.js | 63 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 31 insertions(+), 32 deletions(-) diff --git a/next.js b/next.js index 43d17f8..679bca0 100644 --- a/next.js +++ b/next.js @@ -1,6 +1,3 @@ -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; @@ -52,40 +49,42 @@ 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, hasAppDir) { - 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) - ) - } - } - ]; +function getStyle9VirtualCssLoader(options, MiniCssExtractPlugin) { + 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({ - hasAppDir, - // 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 = {}) => { @@ -160,7 +159,7 @@ module.exports = (pluginOptions = {}) => (nextConfig = {}) => { // Here we matches virtual css file emitted by Style9Plugin cssRules.unshift({ test: /\.style9.css$/, - use: getStyle9VirtualCssLoader(ctx, MiniCssExtractPlugin, hasAppDir) + use: getStyle9VirtualCssLoader(ctx, MiniCssExtractPlugin) }); if (outputCSS) {