UNPKG

@stylexswc/nextjs-plugin

Version:

StyleX NextJS plugin with NAPI-RS compiler

228 lines (227 loc) 11.6 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const mini_css_extract_plugin_1 = __importDefault(require("next/dist/build/webpack/plugins/mini-css-extract-plugin")); const log_1 = require("next/dist/build/output/log"); const browserslist_1 = __importDefault(require("next/dist/compiled/browserslist")); const css_1 = require("next/dist/build/webpack/config/blocks/css"); const webpack_plugin_1 = __importStar(require("@stylexswc/webpack-plugin")); /** Next.js' precompilation add "__esModule: true", but doesn't add an actual default exports */ const NextMiniCssExtractPlugin = mini_css_extract_plugin_1.default; // Adopted from https://github.com/vercel/next.js/blob/1f1632979c78b3edfe59fd85d8cce62efcdee688/packages/next/build/webpack-config.ts#L60-L72 const getSupportedBrowsers = (dir, isDevelopment) => { try { return browserslist_1.default.loadConfig({ path: dir, env: isDevelopment ? 'development' : 'production', }); } catch { // Ignore } }; const getNextMiniCssExtractPlugin = (isDev) => { // Use own MiniCssExtractPlugin to ensure HMR works // v9 has issues when using own plugin in production // v10.2.1 has issues when using built-in plugin in development since it // doesn't bundle HMR files // v12.1.7 finaly fixes the issue by adding the missing hmr/hotModuleReplacement.js file if (isDev) { try { // Check if hotModuleReplacement exists require.resolve('next/dist/compiled/mini-css-extract-plugin/hmr/hotModuleReplacement'); return NextMiniCssExtractPlugin; } catch { (0, log_1.warn)('Next.js built-in mini-css-extract-plugin is broken, will fallback to "mini-css-extract-plugin"'); // eslint-disable-next-line @typescript-eslint/no-require-imports return require('mini-css-extract-plugin'); } } // Always use Next.js built-in MiniCssExtractPlugin in production return NextMiniCssExtractPlugin; }; // Adopt from Next.js' getGlobalCssLoader // https://github.com/vercel/next.js/blob/d61b0761efae09bd9cb1201ff134ed8950d9deca/packages/next/src/build/webpack/config/blocks/css/loaders/global.ts#L7 function getStyleXVirtualCssLoader(ctx, MiniCssExtractPlugin, postcss) { const loaders = []; // Adopt from Next.js' getClientStyleLoader // https://github.com/vercel/next.js/blob/56d35ede8ed2ab25fa8e29583d4e81e3e76a0e29/packages/next/src/build/webpack/config/blocks/css/loaders/global.ts#L7 if (!ctx.isServer) { // https://github.com/vercel/next.js/blob/56d35ede8ed2ab25fa8e29583d4e81e3e76a0e29/packages/next/src/build/webpack/config/blocks/css/loaders/global.ts#L18 // https://github.com/vercel/next.js/blob/56d35ede8ed2ab25fa8e29583d4e81e3e76a0e29/packages/next/src/build/webpack/config/blocks/css/loaders/client.ts#L3 loaders.push({ // eslint-disable-next-line @typescript-eslint/no-explicit-any loader: MiniCssExtractPlugin.loader, options: { publicPath: `${ctx.config.assetPrefix}/_next/`, esModule: false, }, }); } // We don't actually use postcss-loader or css-loader to run against // the stylex css (which doesn't exist yet). // We use this loader to run against the virtual dummy css. loaders.push({ // https://github.com/vercel/next.js/blob/0572e218afe130656be53f7367bf18c4ea389f7d/packages/next/build/webpack/config/blocks/css/loaders/global.ts#L29-L38 loader: require.resolve('next/dist/build/webpack/loaders/css-loader/src'), options: { // https://github.com/vercel/next.js/blob/88a5f263f11cb55907f0d89a4cd53647ee8e96ac/packages/next/build/webpack/config/blocks/css/index.ts#L142-L147 postcss, importLoaders: 1, modules: false, }, }); return loaders; } let count = 0; const withStyleX = (pluginOptions) => (nextConfig = {}) => { return { ...nextConfig, webpack(config, ctx) { if (typeof nextConfig.webpack === 'function') { config = nextConfig.webpack(config, ctx); } const { buildId, dev, isServer } = ctx; if (pluginOptions?.rsOptions?.debug || process.env.STYLEX_DEBUG) { console.log([ '!!!GETTING WEBPACK CONFIG!!!', '======================', `Count: ${++count}`, `Build ID: ${buildId}`, `Server: ${isServer}`, `Env: ${dev ? 'dev' : 'prod'}`, ].join('\n')); } // For some reason, Next 11.0.1 has `config.optimization.splitChunks` // set to `false` when webpack 5 is enabled. config.optimization ||= {}; config.optimization.splitChunks ||= {}; config.optimization.splitChunks.cacheGroups ||= {}; const extractCSS = pluginOptions?.extractCSS ?? true; config.plugins ??= []; let lazyPostCSSPromise = null; const postcss = () => { lazyPostCSSPromise ||= (0, css_1.lazyPostCSS)(ctx.dir, getSupportedBrowsers(ctx.dir, ctx.dev), undefined, false); return lazyPostCSSPromise; }; if (extractCSS) { const MiniCssExtractPlugin = getNextMiniCssExtractPlugin(ctx.dev); // Based on https://github.com/vercel/next.js/blob/88a5f263f11cb55907f0d89a4cd53647ee8e96ac/packages/next/build/webpack/config/helpers.ts#L12-L18 const cssRules = (config.module?.rules?.find(rule => typeof rule === 'object' && rule !== null && Array.isArray(rule.oneOf) && rule.oneOf.some(setRule => setRule && setRule.test instanceof RegExp && typeof setRule.test.test === 'function' && setRule.test.test('filename.css')))).oneOf; // Here we matches virtual css file emitted by StyleXPlugin cssRules?.unshift({ test: webpack_plugin_1.VIRTUAL_CSS_PATTERN, use: getStyleXVirtualCssLoader(ctx, MiniCssExtractPlugin, postcss), }); // StyleX need to emit the css file on both server and client, both during the // development and production. // However, Next.js only add MiniCssExtractPlugin on client + production. // // To simplify the logic at our side, we will add MiniCssExtractPlugin based on // the "instanceof" check (We will only add our required MiniCssExtractPlugin if // Next.js hasn't added it yet). // This also prevent multiple MiniCssExtractPlugin being added (which will cause // RealContentHashPlugin to panic) if (!config.plugins.some((plugin) => plugin instanceof MiniCssExtractPlugin)) { // 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 = ctx.dev ? 'static/css/[name].css' : 'static/css/[contenthash].css'; // Logic adopted from https://git.io/JtdBy config.plugins.push(new MiniCssExtractPlugin({ filename, chunkFilename: filename, // Next.js guarantees that CSS order "doesn't matter", due to imposed // 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). // // While not a perfect guarantee (e.g. liberal use of `:global()` // selector), this assumption is required to code-split CSS. // // As for StyleX, the CSS is always atomic (so classes are always unique), // and StyleX Plugin will always sort the css based on media query and pseudo // selector. // // If this warning were to trigger, it'd be unactionable by the user, // but likely not valid -- so just disable it. ignoreOrder: true, })); } } config.plugins.push(new webpack_plugin_1.default({ ...pluginOptions, rsOptions: { ...pluginOptions?.rsOptions, dev: ctx.dev, }, // Enforce nextjsMode to true nextjsMode: true, ...(extractCSS ? { async transformCss(css, filePath) { const { postcssWithPlugins } = await postcss(); const result = await postcssWithPlugins.process(css, { from: filePath, map: { inline: false, annotation: false, }, }); if (typeof pluginOptions?.transformCss === 'function') { return pluginOptions.transformCss(result.css, filePath); } return result.css; }, } : { transformCss: undefined }), })); return config; }, }; }; exports.default = withStyleX; module.exports = withStyleX; module.exports.default = withStyleX;