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

First attempt to support Next.js 13 appDir #83

Closed
wants to merge 4 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 55 additions & 35 deletions next.js
Original file line number Diff line number Diff line change
@@ -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');

Expand Down Expand Up @@ -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`
Expand All @@ -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)$/,
Expand Down Expand Up @@ -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) {
Expand All @@ -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';

Expand Down