@stylexswc/nextjs-plugin 0.4.4 → 0.5.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,25 +4,41 @@ Next.js plugin for an unofficial
4
4
  [`napi-rs`](https://github.com/dwlad90/stylex-swc-plugin/tree/develop/crates/stylex-rs-compiler)
5
5
  compiler that includes the StyleX SWC code transformation under the hood.
6
6
 
7
+ ## Breaking Changes in v0.5.0
8
+
9
+ > [!IMPORTANT]
10
+ > The plugin API has been updated since version
11
+ > [0.5.0](https://www.npmjs.com/package/@stylexswc/swc-plugin/v/0.5.0). If
12
+ > you're upgrading from an earlier version, please note that the configuration
13
+ > options have changed. See the [Plugin Options](#plugin-options) section for
14
+ > the updated API.
15
+
7
16
  ## Overview
8
17
 
9
- This package combines two solutions to enhance your Next.js development experience with StyleX:
18
+ This package combines two solutions to enhance your Next.js development
19
+ experience with StyleX:
10
20
 
11
21
  ### StyleX SWC Plugin
12
22
 
13
- * Integrates StyleX with the SWC compiler, potentially leading to faster build times compared to using Babel.
14
- * Maintains high compatibility with official StyleX tests, ensuring a reliable experience.
15
- * Integrates seamlessly with Next.js SWC Compiler for a streamlined workflow.
23
+ - Integrates StyleX with the SWC compiler, potentially leading to faster build
24
+ times compared to using Babel.
25
+ - Maintains high compatibility with official StyleX tests, ensuring a reliable
26
+ experience.
27
+ - Integrates seamlessly with Next.js SWC Compiler for a streamlined workflow.
16
28
 
17
29
  ### Stylex NAPI-RS Compiler
18
30
 
19
- * Utilizes NAPI-RS to compile StyleX code, offering advantages over the SWC plugin approach.
20
- * Provides access to StyleX metadata and source maps, enabling advanced plugin and tool development.
31
+ - Utilizes NAPI-RS to compile StyleX code, offering advantages over the SWC
32
+ plugin approach.
33
+ - Provides access to StyleX metadata and source maps, enabling advanced plugin
34
+ and tool development.
21
35
 
22
36
  ## Why choose this approach?
23
37
 
24
- * Leverage SWC's speed: Benefit from Next.js's default SWC compiler for potentially faster build times.
25
- * Maintain StyleX familiarity: The usage of StyleX remains unchanged for developers.
38
+ - Leverage SWC's speed: Benefit from Next.js's default SWC compiler for
39
+ potentially faster build times.
40
+ - Maintain StyleX familiarity: The usage of StyleX remains unchanged for
41
+ developers.
26
42
 
27
43
  ## Installation
28
44
 
@@ -32,48 +48,85 @@ To install the package, run the following command:
32
48
  npm install --save-dev @stylexswc/nextjs-plugin
33
49
  ```
34
50
 
35
- Please install `@stylexswc/rs-compiler` if you haven't done so already:
51
+ ## Plugin Options
36
52
 
37
- ```bash
38
- npm install --save-dev @stylexswc/rs-compiler
39
- ```
53
+ ### Basic Options
40
54
 
41
- ## Usage
55
+ #### `rsOptions`
42
56
 
43
- Modify Next.js config. For example:
57
+ - Type: `Partial<StyleXOptions>`
58
+ - Optional
59
+ - Description: StyleX compiler options that will be passed to the NAPI-RS
60
+ compiler. See
61
+ [StyleX configuration docs](https://stylexjs.com/docs/api/configuration/babel-plugin/)
62
+ for details.
44
63
 
45
- ```js
46
- /** @type {import('next').NextConfig} */
47
- const stylexPlugin = require('@stylexswc/nextjs-plugin');
64
+ #### `stylexImports`
48
65
 
49
- const nextConfig = {
50
- // Configure `pageExtensions` to include MDX files
51
- pageExtensions: ['js', 'jsx', 'mdx', 'ts', 'tsx'],
52
- transpilePackages: ['@stylexjs/open-props'],
53
- // Optionally, add any other Next.js config below
54
- swcMinify: true,
55
- };
66
+ - Type: `Array<string | { as: string, from: string }>`
67
+ - Default: `['stylex', '@stylexjs/stylex']`
68
+ - Description: Specifies where StyleX will be imported from. Supports both
69
+ string paths and import aliases.
70
+
71
+ #### `useCSSLayers`
72
+
73
+ - Type: `boolean`
74
+ - Default: `false`
75
+ - Description: Enables CSS cascade layers support for better style isolation.
76
+
77
+ ### Advanced Options
78
+
79
+ #### `transformCss`
80
+
81
+ - Type: `(css: string) => string | Buffer | Promise<string | Buffer>`
82
+ - Optional
83
+ - Description: Custom CSS transformation function. Since the plugin injects CSS
84
+ after all loaders, use this to apply PostCSS or other CSS transformations.
85
+
86
+ ### Example Configuration
87
+
88
+ ```javascript
89
+ const path = require('path');
90
+ const stylexPlugin = require('@stylexswc/nextjs-plugin');
91
+ const rootDir = __dirname;
56
92
 
57
93
  module.exports = stylexPlugin({
58
- rootDir: __dirname,
59
- // Stylex RS compiler options
60
- dev: false,
61
- runtimeInjection: false,
62
- genConditionalClasses: true,
63
- treeshakeCompensation: true,
64
- unstable_moduleResolution: {
65
- type: 'commonJS',
66
- rootDir: __dirname,
94
+ // Add any StyleX options here
95
+ rsOptions: {
96
+ dev: process.env.NODE_ENV !== 'production',
97
+ useRemForFontSize: true,
98
+ aliases: {
99
+ '@/*': [path.join(rootDir, '*')],
100
+ },
101
+ unstable_moduleResolution: {
102
+ type: 'commonJS',
103
+ rootDir,
104
+ },
67
105
  },
68
- })(nextConfig);
106
+ stylexImports: ['@stylexjs/stylex', { from: './theme', as: 'tokens' }],
107
+ useCSSLayers: true,
108
+ transformCss: async css => {
109
+ const postcss = require('postcss');
110
+ const result = await postcss([require('autoprefixer')]).process(css);
111
+ return result.css;
112
+ },
113
+ })({
114
+ transpilePackages: ['@stylexjs/open-props'],
115
+ // Optionally, add any other Next.js config below
116
+ });
69
117
  ```
70
118
 
71
119
  ## Examples
72
120
 
73
- * [Example repo](https://github.com/Dwlad90/nextjs-app-dir-stylex)
74
- * [CodeSandbox with example repo](https://codesandbox.io/p/github/Dwlad90/nextjs-app-dir-stylex/main)
121
+ - [Example repo](https://github.com/Dwlad90/nextjs-app-dir-stylex)
122
+ - [CodeSandbox with example repo](https://codesandbox.io/p/github/Dwlad90/nextjs-app-dir-stylex/main)
75
123
 
76
124
  ## Documentation
77
125
 
78
- * [StyleX Documentation](https://stylexjs.com)
79
- * [NAPI-RS compiler for StyleX](https://github.com/Dwlad90/stylex-swc-plugin/tree/develop/crates/stylex-rs-compiler)
126
+ - [StyleX Documentation](https://stylexjs.com)
127
+ - [NAPI-RS compiler for StyleX](https://github.com/Dwlad90/stylex-swc-plugin/tree/develop/crates/stylex-rs-compiler)
128
+
129
+ ## Acknowledgments
130
+
131
+ This plugin was inspired by
132
+ [`stylex-webpack`](https://github.com/SukkaW/stylex-webpack).
package/dist/index.d.ts CHANGED
@@ -1,114 +1,5 @@
1
- import type { Configuration } from 'webpack';
2
- import type { NextConfig } from 'next';
3
- import type { ConfigurationContext } from 'next/dist/build/webpack/config/utils';
4
- import type { WebpackConfigContext } from 'next/dist/server/config-shared';
5
- import type { StyleXOptions } from '@stylexswc/rs-compiler';
6
- interface StylexNextJSPluginOptions extends StyleXOptions {
7
- rootDir: string;
8
- filename?: string;
9
- }
10
- declare function StylexNextJSPlugin({ rootDir, filename, ...pluginOptions }: StylexNextJSPluginOptions): (nextConfig?: NextConfig) => {
11
- transpilePackages: string[];
12
- webpack(config: Configuration & ConfigurationContext, options: WebpackConfigContext): Configuration & ConfigurationContext;
13
- exportPathMap?: (defaultMap: import("next/dist/server/config-shared").ExportPathMap, ctx: {
14
- dev: boolean;
15
- dir: string;
16
- outDir: string | null;
17
- distDir: string;
18
- buildId: string;
19
- }) => Promise<import("next/dist/server/config-shared").ExportPathMap> | import("next/dist/server/config-shared").ExportPathMap;
20
- i18n?: import("next/dist/server/config-shared").I18NConfig | null;
21
- eslint?: import("next/dist/server/config-shared").ESLintConfig;
22
- typescript?: import("next/dist/server/config-shared").TypeScriptConfig;
23
- headers?: () => Promise<import("next/dist/lib/load-custom-routes").Header[]>;
24
- rewrites?: () => Promise<import("next/dist/lib/load-custom-routes").Rewrite[] | {
25
- beforeFiles: import("next/dist/lib/load-custom-routes").Rewrite[];
26
- afterFiles: import("next/dist/lib/load-custom-routes").Rewrite[];
27
- fallback: import("next/dist/lib/load-custom-routes").Rewrite[];
28
- }>;
29
- redirects?: () => Promise<import("next/dist/lib/load-custom-routes").Redirect[]>;
30
- excludeDefaultMomentLocales?: boolean;
31
- trailingSlash?: boolean;
32
- env?: Record<string, string | undefined>;
33
- distDir?: string;
34
- cleanDistDir?: boolean;
35
- assetPrefix?: string;
36
- cacheHandler?: string | undefined;
37
- cacheMaxMemorySize?: number;
38
- useFileSystemPublicRoutes?: boolean;
39
- generateBuildId?: () => string | null | Promise<string | null>;
40
- generateEtags?: boolean;
41
- pageExtensions?: string[];
42
- compress?: boolean;
43
- analyticsId?: string;
44
- poweredByHeader?: boolean;
45
- images?: import("next/dist/shared/lib/image-config").ImageConfig;
46
- devIndicators?: {
47
- buildActivity?: boolean;
48
- buildActivityPosition?: "bottom-right" | "bottom-left" | "top-right" | "top-left";
49
- };
50
- onDemandEntries?: {
51
- maxInactiveAge?: number;
52
- pagesBufferLength?: number;
53
- };
54
- amp?: {
55
- canonicalBase?: string;
56
- };
57
- deploymentId?: string;
58
- basePath?: string;
59
- sassOptions?: {
60
- [key: string]: any;
61
- };
62
- productionBrowserSourceMaps?: boolean;
63
- optimizeFonts?: boolean;
64
- reactProductionProfiling?: boolean;
65
- reactStrictMode?: boolean | null;
66
- publicRuntimeConfig?: {
67
- [key: string]: any;
68
- };
69
- serverRuntimeConfig?: {
70
- [key: string]: any;
71
- };
72
- httpAgentOptions?: {
73
- keepAlive?: boolean;
74
- };
75
- outputFileTracing?: boolean;
76
- staticPageGenerationTimeout?: number;
77
- crossOrigin?: "anonymous" | "use-credentials";
78
- swcMinify?: boolean;
79
- compiler?: {
80
- reactRemoveProperties?: boolean | {
81
- properties?: string[];
82
- };
83
- relay?: {
84
- src: string;
85
- artifactDirectory?: string;
86
- language?: "typescript" | "javascript" | "flow";
87
- eagerEsModules?: boolean;
88
- };
89
- removeConsole?: boolean | {
90
- exclude?: string[];
91
- };
92
- styledComponents?: boolean | import("next/dist/server/config-shared").StyledComponentsConfig;
93
- emotion?: boolean | import("next/dist/server/config-shared").EmotionConfig;
94
- styledJsx?: boolean | {
95
- useLightningcss?: boolean;
96
- };
97
- };
98
- output?: "standalone" | "export";
99
- skipMiddlewareUrlNormalize?: boolean;
100
- skipTrailingSlashRedirect?: boolean;
101
- modularizeImports?: Record<string, {
102
- transform: string | Record<string, string>;
103
- preventFullImport?: boolean;
104
- skipDefaultConversion?: boolean;
105
- }>;
106
- logging?: {
107
- fetches?: {
108
- fullUrl?: boolean;
109
- };
110
- };
111
- experimental?: import("next/dist/server/config-shared").ExperimentalConfig;
112
- };
113
- export default StylexNextJSPlugin;
1
+ import type { NextConfig } from 'next/dist/server/config-shared';
2
+ import type { StyleXPluginOption } from '@stylexswc/webpack-plugin';
3
+ declare const withStyleX: (pluginOptions?: StyleXPluginOption) => (nextConfig?: NextConfig) => NextConfig;
4
+ export default withStyleX;
114
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AACvC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAC;AACjF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAC3E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAI5D,UAAU,yBAA0B,SAAQ,aAAa;IACvD,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,iBAAS,kBAAkB,CAAC,EAC1B,OAAO,EACP,QAA8B,EAC9B,GAAG,aAAa,EACjB,EAAE,yBAAyB,iBACN,UAAU;;oBAIV,aAAa,GAAG,oBAAoB,WAAW,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAkD0wjB,CAAC;6BAAsG,CAAC;;;sBAAke,CAAC;yBAA4H,CAAC;;;qBAA+H,CAAC;;;;;;;;;;;;;;;;;;iBAA8pE,CAAC;;;;;;;6BAAg6C,CAAC;sBAAoC,CAAC;;aAAoC,CAAC;;6BAA0D,CAAC;oBAA8B,CAAC;0BAAkE,CAAC;;qBAA2C,CAAC;mBAAiC,CAAC;;wBAA+C,CAAC;eAAmD,CAAC;iBAA4C,CAAC;2BAAyC,CAAC;;;;;;;;yBAA4zC,CAAC;6BAAwC,CAAC;;;eAAkD,CAAC;mBAAuB,CAAC;;;;EAJ3owB;AAED,eAAe,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAwB,MAAM,gCAAgC,CAAC;AACvF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAmFpE,QAAA,MAAM,UAAU,mBACG,kBAAkB,mBACtB,UAAU,KAAQ,UAqI9B,CAAC;AAEJ,eAAe,UAAU,CAAC"}
package/dist/index.js CHANGED
@@ -1,52 +1,198 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
2
25
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
27
  };
5
28
  Object.defineProperty(exports, "__esModule", { value: true });
6
- const custom_webpack_plugin_1 = __importDefault(require("./custom-webpack-plugin"));
29
+ const mini_css_extract_plugin_1 = __importDefault(require("next/dist/build/webpack/plugins/mini-css-extract-plugin"));
30
+ const log_1 = require("next/dist/build/output/log");
31
+ const browserslist_1 = __importDefault(require("next/dist/compiled/browserslist"));
32
+ const css_1 = require("next/dist/build/webpack/config/blocks/css");
33
+ const webpack_plugin_1 = __importStar(require("@stylexswc/webpack-plugin"));
34
+ /** Next.js' precompilation add "__esModule: true", but doesn't add an actual default exports */
35
+ const NextMiniCssExtractPlugin = mini_css_extract_plugin_1.default;
36
+ // Adopted from https://github.com/vercel/next.js/blob/1f1632979c78b3edfe59fd85d8cce62efcdee688/packages/next/build/webpack-config.ts#L60-L72
37
+ const getSupportedBrowsers = (dir, isDevelopment) => {
38
+ try {
39
+ return browserslist_1.default.loadConfig({
40
+ path: dir,
41
+ env: isDevelopment ? 'development' : 'production',
42
+ });
43
+ }
44
+ catch { }
45
+ };
46
+ const getNextMiniCssExtractPlugin = (isDev) => {
47
+ // Use own MiniCssExtractPlugin to ensure HMR works
48
+ // v9 has issues when using own plugin in production
49
+ // v10.2.1 has issues when using built-in plugin in development since it
50
+ // doesn't bundle HMR files
51
+ // v12.1.7 finaly fixes the issue by adding the missing hmr/hotModuleReplacement.js file
52
+ if (isDev) {
53
+ try {
54
+ // Check if hotModuleReplacement exists
55
+ require.resolve('next/dist/compiled/mini-css-extract-plugin/hmr/hotModuleReplacement');
56
+ return NextMiniCssExtractPlugin;
57
+ }
58
+ catch {
59
+ (0, log_1.warn)('Next.js built-in mini-css-extract-plugin is broken, will fallback to "mini-css-extract-plugin"');
60
+ return require('mini-css-extract-plugin');
61
+ }
62
+ }
63
+ // Always use Next.js built-in MiniCssExtractPlugin in production
64
+ return NextMiniCssExtractPlugin;
65
+ };
66
+ // Adopt from Next.js' getGlobalCssLoader
67
+ // https://github.com/vercel/next.js/blob/d61b0761efae09bd9cb1201ff134ed8950d9deca/packages/next/src/build/webpack/config/blocks/css/loaders/global.ts#L7
68
+ function getStyleXVirtualCssLoader(ctx, MiniCssExtractPlugin, postcss) {
69
+ const loaders = [];
70
+ // Adopt from Next.js' getClientStyleLoader
71
+ // https://github.com/vercel/next.js/blob/56d35ede8ed2ab25fa8e29583d4e81e3e76a0e29/packages/next/src/build/webpack/config/blocks/css/loaders/global.ts#L7
72
+ if (!ctx.isServer) {
73
+ // https://github.com/vercel/next.js/blob/56d35ede8ed2ab25fa8e29583d4e81e3e76a0e29/packages/next/src/build/webpack/config/blocks/css/loaders/global.ts#L18
74
+ // https://github.com/vercel/next.js/blob/56d35ede8ed2ab25fa8e29583d4e81e3e76a0e29/packages/next/src/build/webpack/config/blocks/css/loaders/client.ts#L3
75
+ loaders.push({
76
+ loader: MiniCssExtractPlugin.loader,
77
+ options: {
78
+ publicPath: `${ctx.config.assetPrefix}/_next/`,
79
+ esModule: false,
80
+ },
81
+ });
82
+ }
83
+ // We don't actually use postcss-loader or css-loader to run against
84
+ // the stylex css (which doesn't exist yet).
85
+ // We use this loader to run against the virtual dummy css.
86
+ loaders.push({
87
+ // https://github.com/vercel/next.js/blob/0572e218afe130656be53f7367bf18c4ea389f7d/packages/next/build/webpack/config/blocks/css/loaders/global.ts#L29-L38
88
+ loader: require.resolve('next/dist/build/webpack/loaders/css-loader/src'),
89
+ options: {
90
+ // https://github.com/vercel/next.js/blob/88a5f263f11cb55907f0d89a4cd53647ee8e96ac/packages/next/build/webpack/config/blocks/css/index.ts#L142-L147
91
+ postcss,
92
+ importLoaders: 1,
93
+ modules: false,
94
+ },
95
+ });
96
+ return loaders;
97
+ }
7
98
  let count = 0;
8
- function StylexNextJSPlugin({ rootDir, filename = 'stylex-bundle.css', ...pluginOptions }) {
9
- return (nextConfig = {}) => {
10
- return {
11
- ...nextConfig,
12
- transpilePackages: [...(nextConfig.transpilePackages || []), '@stylexjs/open-props'],
13
- webpack(config, options) {
14
- if (typeof nextConfig.webpack === 'function') {
15
- config = nextConfig.webpack(config, options);
16
- }
17
- const { buildId, dev, isServer } = options;
18
- console.log([
19
- '!!!GETTING WEBPACK CONFIG!!!',
20
- '======================',
21
- `Count: ${++count}`,
22
- `Build ID: ${buildId}`,
23
- `Server: ${isServer}`,
24
- `Env: ${dev ? 'dev' : 'prod'}`,
25
- ].join('\n'));
26
- if (config.optimization?.splitChunks) {
27
- config.optimization.splitChunks ||= { cacheGroups: {} };
28
- if (config.optimization.splitChunks.cacheGroups) {
29
- config.optimization.splitChunks.cacheGroups.stylex = {
30
- name: 'stylex',
31
- chunks: 'all',
32
- test: /\.css$/,
33
- enforce: true,
34
- };
35
- }
36
- }
37
- const webpackPluginOptions = {
38
- rootDir,
39
- appendTo: (name) => name.endsWith('.css'),
99
+ const withStyleX = (pluginOptions) => (nextConfig = {}) => {
100
+ return {
101
+ ...nextConfig,
102
+ webpack(config, ctx) {
103
+ if (typeof nextConfig.webpack === 'function') {
104
+ config = nextConfig.webpack(config, ctx);
105
+ }
106
+ const { buildId, dev, isServer } = ctx;
107
+ console.log([
108
+ '!!!GETTING WEBPACK CONFIG!!!',
109
+ '======================',
110
+ `Count: ${++count}`,
111
+ `Build ID: ${buildId}`,
112
+ `Server: ${isServer}`,
113
+ `Env: ${dev ? 'dev' : 'prod'}`,
114
+ ].join('\n'));
115
+ // For some reason, Next 11.0.1 has `config.optimization.splitChunks`
116
+ // set to `false` when webpack 5 is enabled.
117
+ config.optimization ||= {};
118
+ config.optimization.splitChunks ||= {};
119
+ config.optimization.splitChunks.cacheGroups ||= {};
120
+ let lazyPostCSSPromise = null;
121
+ const postcss = () => {
122
+ lazyPostCSSPromise ||= (0, css_1.lazyPostCSS)(ctx.dir, getSupportedBrowsers(ctx.dir, ctx.dev), undefined, false);
123
+ return lazyPostCSSPromise;
124
+ };
125
+ const MiniCssExtractPlugin = getNextMiniCssExtractPlugin(ctx.dev);
126
+ // Based on https://github.com/vercel/next.js/blob/88a5f263f11cb55907f0d89a4cd53647ee8e96ac/packages/next/build/webpack/config/helpers.ts#L12-L18
127
+ const cssRules = (config.module?.rules?.find(rule => typeof rule === 'object' &&
128
+ rule !== null &&
129
+ Array.isArray(rule.oneOf) &&
130
+ rule.oneOf.some(setRule => setRule &&
131
+ setRule.test instanceof RegExp &&
132
+ typeof setRule.test.test === 'function' &&
133
+ setRule.test.test('filename.css')))).oneOf;
134
+ // Here we matches virtual css file emitted by StyleXPlugin
135
+ cssRules?.unshift({
136
+ test: webpack_plugin_1.VIRTUAL_CSS_PATTERN,
137
+ use: getStyleXVirtualCssLoader(ctx, MiniCssExtractPlugin, postcss),
138
+ });
139
+ config.plugins ??= [];
140
+ // StyleX need to emit the css file on both server and client, both during the
141
+ // development and production.
142
+ // However, Next.js only add MiniCssExtractPlugin on client + production.
143
+ //
144
+ // To simplify the logic at our side, we will add MiniCssExtractPlugin based on
145
+ // the "instanceof" check (We will only add our required MiniCssExtractPlugin if
146
+ // Next.js hasn't added it yet).
147
+ // This also prevent multiple MiniCssExtractPlugin being added (which will cause
148
+ // RealContentHashPlugin to panic)
149
+ if (!config.plugins.some((plugin) => plugin instanceof MiniCssExtractPlugin)) {
150
+ // HMR reloads the CSS file when the content changes but does not use
151
+ // the new file name, which means it can't contain a hash.
152
+ const filename = ctx.dev ? 'static/css/[name].css' : 'static/css/[contenthash].css';
153
+ // Logic adopted from https://git.io/JtdBy
154
+ config.plugins.push(new MiniCssExtractPlugin({
40
155
  filename,
41
- dev,
42
- rsOptions: pluginOptions,
43
- };
44
- const stylexPlugin = new custom_webpack_plugin_1.default(webpackPluginOptions);
45
- config.plugins?.push(stylexPlugin);
46
- return config;
47
- },
48
- };
156
+ chunkFilename: filename,
157
+ // Next.js guarantees that CSS order "doesn't matter", due to imposed
158
+ // restrictions:
159
+ // 1. Global CSS can only be defined in a single entrypoint (_app)
160
+ // 2. CSS Modules generate scoped class names by default and cannot
161
+ // include Global CSS (:global() selector).
162
+ //
163
+ // While not a perfect guarantee (e.g. liberal use of `:global()`
164
+ // selector), this assumption is required to code-split CSS.
165
+ //
166
+ // As for StyleX, the CSS is always atomic (so classes are always unique),
167
+ // and StyleX Plugin will always sort the css based on media query and pseudo
168
+ // selector.
169
+ //
170
+ // If this warning were to trigger, it'd be unactionable by the user,
171
+ // but likely not valid -- so just disable it.
172
+ ignoreOrder: true,
173
+ }));
174
+ }
175
+ config.plugins.push(new webpack_plugin_1.default({
176
+ ...pluginOptions,
177
+ rsOptions: {
178
+ ...pluginOptions?.rsOptions,
179
+ dev: ctx.dev,
180
+ },
181
+ // Enforce nextjsMode to true
182
+ nextjsMode: true,
183
+ async transformCss(css) {
184
+ const { postcssWithPlugins } = await postcss();
185
+ const result = await postcssWithPlugins.process(css);
186
+ if (typeof pluginOptions?.transformCss === 'function') {
187
+ return pluginOptions.transformCss(result.css);
188
+ }
189
+ return result.css;
190
+ },
191
+ }));
192
+ return config;
193
+ },
49
194
  };
50
- }
51
- exports.default = StylexNextJSPlugin;
52
- module.exports = StylexNextJSPlugin;
195
+ };
196
+ exports.default = withStyleX;
197
+ module.exports = withStyleX;
198
+ module.exports.default = withStyleX;
package/package.json CHANGED
@@ -1,25 +1,24 @@
1
1
  {
2
2
  "name": "@stylexswc/nextjs-plugin",
3
3
  "description": "Stylex NextJS plugin with NAPI-RS compiler",
4
- "version": "0.4.4",
4
+ "version": "0.5.0-rc.2",
5
5
  "config": {
6
6
  "scripty": {
7
7
  "path": "../../scripts/packages"
8
8
  }
9
9
  },
10
- "dependencies": {
11
- "@stylexjs/babel-plugin": "^0.9.3"
12
- },
13
10
  "devDependencies": {
14
11
  "@babel/types": "^7.23.9",
15
- "@stylexswc/eslint-config": "0.4.4",
16
- "@stylexswc/rs-compiler": "0.4.4",
17
- "@stylexswc/typescript-config": "0.4.4",
12
+ "@stylexswc/eslint-config": "0.5.0-rc.2",
13
+ "@stylexswc/rs-compiler": "0.5.0-rc.2",
14
+ "@stylexswc/typescript-config": "0.5.0-rc.2",
15
+ "@stylexswc/webpack-plugin": "0.5.0-rc.2",
18
16
  "@types/babel__core": "^7.20.5",
19
17
  "@types/node": "^22.5.1",
20
- "next": "^14.2.10",
21
- "react": "^18.2.0",
22
- "react-dom": "^18.2.0",
18
+ "next": "^15.0.4",
19
+ "postcss": "^8.4.49",
20
+ "react": "^19.0.0",
21
+ "react-dom": "^19.0.0",
23
22
  "webpack": "^5.94.0"
24
23
  },
25
24
  "files": [
@@ -34,7 +33,7 @@
34
33
  "license": "MIT",
35
34
  "main": "dist/index.js",
36
35
  "peerDependencies": {
37
- "next": ">=14.0.1"
36
+ "next": ">=15.0.0"
38
37
  },
39
38
  "private": false,
40
39
  "publishConfig": {
@@ -1,4 +0,0 @@
1
- import type { LoaderContext } from 'webpack';
2
- export declare const PLUGIN_NAME = "stylex";
3
- export type SupplementedLoaderContext<Options = unknown> = LoaderContext<Options> & {};
4
- //# sourceMappingURL=constants.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE7C,eAAO,MAAM,WAAW,WAAW,CAAC;AAEpC,MAAM,MAAM,yBAAyB,CAAC,OAAO,GAAG,OAAO,IAAI,aAAa,CAAC,OAAO,CAAC,GAAG,EAEnF,CAAC"}
package/dist/constants.js DELETED
@@ -1,4 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PLUGIN_NAME = void 0;
4
- exports.PLUGIN_NAME = 'stylex';
@@ -1,16 +0,0 @@
1
- import type * as webpack from 'webpack';
2
- import { type SupplementedLoaderContext } from './constants';
3
- export type WebpackLoaderOptions = {
4
- /**
5
- * Please never use this feature, it will be removed without further notice.
6
- */
7
- stylexPlugin?: {
8
- transformCode: (code: string, filePath: string, logger?: ReturnType<webpack.Compiler['getInfrastructureLogger']>) => Promise<{
9
- code: string;
10
- map: string;
11
- }>;
12
- };
13
- };
14
- declare function stylexLoader(this: SupplementedLoaderContext<WebpackLoaderOptions>, inputCode: string): void;
15
- export default stylexLoader;
16
- //# sourceMappingURL=custom-webpack-loader.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"custom-webpack-loader.d.ts","sourceRoot":"","sources":["../src/custom-webpack-loader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,OAAO,MAAM,SAAS,CAAC;AAExC,OAAO,EAAe,KAAK,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAE1E,MAAM,MAAM,oBAAoB,GAAG;IACjC;;OAEG;IACH,YAAY,CAAC,EAAE;QACb,aAAa,EAAE,CACb,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,QAAQ,CAAC,yBAAyB,CAAC,CAAC,KAC7D,OAAO,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KAC7C,CAAC;CACH,CAAC;AAIF,iBAAS,YAAY,CAAC,IAAI,EAAE,yBAAyB,CAAC,oBAAoB,CAAC,EAAE,SAAS,EAAE,MAAM,QAc7F;AAED,eAAe,YAAY,CAAC"}
@@ -1,15 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const constants_1 = require("./constants");
4
- function stylexLoader(inputCode) {
5
- const callback = this.async();
6
- const { stylexPlugin } = this.getOptions();
7
- const logger = this._compiler?.getInfrastructureLogger(constants_1.PLUGIN_NAME);
8
- stylexPlugin?.transformCode(inputCode, this.resourcePath, logger).then(({ code, map }) => {
9
- callback(null, code, map);
10
- }, (error) => {
11
- callback(error);
12
- });
13
- }
14
- exports.default = stylexLoader;
15
- module.exports = stylexLoader;
@@ -1,23 +0,0 @@
1
- import type { Compiler } from 'webpack';
2
- import type { StyleXOptions } from '@stylexswc/rs-compiler';
3
- declare class StylexPlugin {
4
- filesInLastRun: any;
5
- filePath?: string | null;
6
- dev: boolean;
7
- appendTo: any;
8
- filename?: string;
9
- stylexImports: any[];
10
- useCSSLayers: any;
11
- rsOptions: StyleXOptions;
12
- constructor({ dev, appendTo, filename, stylexImports, useCSSLayers, rsOptions, }?: any);
13
- apply(compiler: Compiler): void;
14
- transformCode(inputCode: string, filename: string, logger: any): Promise<{
15
- code: string;
16
- map: string | undefined;
17
- } | {
18
- code: string;
19
- map?: undefined;
20
- }>;
21
- }
22
- export default StylexPlugin;
23
- //# sourceMappingURL=custom-webpack-plugin.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"custom-webpack-plugin.d.ts","sourceRoot":"","sources":["../src/custom-webpack-plugin.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,QAAQ,EAAgB,MAAM,SAAS,CAAC;AAEtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAc5D,cAAM,YAAY;IAChB,cAAc,EAAE,GAAG,CAAQ;IAC3B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAQ;IAChC,GAAG,EAAE,OAAO,CAAC;IACb,QAAQ,EAAE,GAAG,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,GAAG,EAAE,CAAC;IACrB,YAAY,EAAE,GAAG,CAAC;IAClB,SAAS,EAAE,aAAa,CAAC;gBAEb,EACV,GAAgB,EAChB,QAAQ,EACR,QAAsD,EACtD,aAA8C,EAC9C,YAAoB,EACpB,SAAc,GACf,GAAE,GAAQ;IAUX,KAAK,CAAC,QAAQ,EAAE,QAAQ;IA2FlB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG;;;;;;;CA0CrE;AACD,eAAe,YAAY,CAAC"}
@@ -1,143 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- const path_1 = __importDefault(require("path"));
7
- const babel_plugin_1 = __importDefault(require("@stylexjs/babel-plugin"));
8
- const rs_compiler_1 = require("@stylexswc/rs-compiler");
9
- const webpack_1 = __importDefault(require("webpack"));
10
- const promises_1 = __importDefault(require("fs/promises"));
11
- const { NormalModule, Compilation } = webpack_1.default;
12
- const PLUGIN_NAME = 'stylex';
13
- const IS_DEV_ENV = process.env.NODE_ENV === 'development';
14
- const { RawSource, ConcatSource } = webpack_1.default.sources;
15
- const stylexRules = {};
16
- const cssFiles = new Set();
17
- const compilers = new Set();
18
- class StylexPlugin {
19
- filesInLastRun = null;
20
- filePath = null;
21
- dev;
22
- appendTo;
23
- filename;
24
- stylexImports;
25
- useCSSLayers;
26
- rsOptions;
27
- constructor({ dev = IS_DEV_ENV, appendTo, filename = appendTo == null ? 'stylex.css' : undefined, stylexImports = ['stylex', '@stylexjs/stylex'], useCSSLayers = false, rsOptions = {}, } = {}) {
28
- this.dev = dev;
29
- this.appendTo = appendTo;
30
- this.filename = filename;
31
- this.stylexImports = stylexImports;
32
- this.useCSSLayers = useCSSLayers;
33
- this.rsOptions = rsOptions;
34
- }
35
- apply(compiler) {
36
- compiler.hooks.make.tap(PLUGIN_NAME, compilation => {
37
- // Apply loader to JS modules.
38
- NormalModule.getCompilationHooks(compilation).loader.tap(PLUGIN_NAME, (_, module) => {
39
- if (
40
- // JavaScript (and Flow) modules
41
- /\.jsx?/.test(path_1.default.extname(module.resource)) ||
42
- // TypeScript modules
43
- /\.tsx?/.test(path_1.default.extname(module.resource))) {
44
- // We use .unshift() and not .push() like original babel plugin
45
- // because we want to run other transformations first, e.g. custom SWC plugins.
46
- module.loaders.unshift({
47
- loader: path_1.default.resolve(__dirname, 'custom-webpack-loader.js'),
48
- options: { stylexPlugin: this },
49
- ident: null,
50
- type: null,
51
- });
52
- }
53
- if (
54
- // JavaScript (and Flow) modules
55
- /\.css/.test(path_1.default.extname(module.resource))) {
56
- cssFiles.add(module.resource);
57
- }
58
- });
59
- const getStyleXRules = () => {
60
- if (Object.keys(stylexRules).length === 0) {
61
- return null;
62
- }
63
- // Take styles for the modules that were included in the last compilation.
64
- const allRules = Object.keys(stylexRules)
65
- .map(filename => stylexRules[filename])
66
- .flat()
67
- .filter((rule) => !!rule);
68
- return babel_plugin_1.default.processStylexRules(allRules, this.useCSSLayers);
69
- };
70
- if (this.appendTo) {
71
- compilation.hooks.processAssets.tap({
72
- name: PLUGIN_NAME,
73
- stage: Compilation.PROCESS_ASSETS_STAGE_PRE_PROCESS, // see below for more stages
74
- }, assets => {
75
- const cssFileName = Object.keys(assets).find(typeof this.appendTo === 'function'
76
- ? this.appendTo
77
- : filename => filename.endsWith(this.appendTo));
78
- const stylexCSS = getStyleXRules();
79
- if (cssFileName && stylexCSS != null) {
80
- this.filePath = path_1.default.join(process.cwd(), '.next', cssFileName);
81
- const updatedSource = new ConcatSource(new RawSource(assets[cssFileName]?.source() || ''), new RawSource(stylexCSS));
82
- compilation.updateAsset(cssFileName, updatedSource);
83
- compilers.add(compiler);
84
- }
85
- });
86
- }
87
- else {
88
- // Consume collected rules and emit the stylex CSS asset
89
- compilation.hooks.additionalAssets.tap(PLUGIN_NAME, () => {
90
- try {
91
- const collectedCSS = getStyleXRules();
92
- if (collectedCSS && this.filename) {
93
- console.log('emitting asset', this.filename, collectedCSS);
94
- compilation.emitAsset(this.filename, new RawSource(collectedCSS));
95
- promises_1.default.writeFile(this.filename, collectedCSS).then(() => console.log('wrote file', this.filename));
96
- }
97
- }
98
- catch (e) {
99
- compilation.errors.push(e);
100
- }
101
- });
102
- }
103
- });
104
- }
105
- // This function is not called by Webpack directly.
106
- // Instead, `NormalModule.getCompilationHooks` is used to inject a loader
107
- // for JS modules. The loader than calls this function.
108
- async transformCode(inputCode, filename, logger) {
109
- const originalSource = inputCode;
110
- if (inputCode.includes('Welcome to my MDX page'))
111
- console.log('originalSource: ', originalSource);
112
- if (this.stylexImports.some(importName => originalSource.includes(importName))) {
113
- let result = (0, rs_compiler_1.transform)(filename, inputCode, this.rsOptions);
114
- const metadata = result?.metadata;
115
- const code = result.code;
116
- const map = result.map;
117
- if (metadata.stylex != null && metadata.stylex.length > 0) {
118
- const oldRules = stylexRules[filename] || [];
119
- stylexRules[filename] = metadata.stylex;
120
- logger.debug(`Read stylex styles from ${filename}:`, metadata.stylex);
121
- const oldClassNames = new Set(oldRules.map(rule => rule[0]));
122
- const newClassNames = new Set(metadata.stylex.map(rule => rule[0]));
123
- // If there are any new classNames in the output we need to recompile
124
- // the CSS bundle.
125
- if (oldClassNames.size !== newClassNames.size ||
126
- [...newClassNames].some(className => !oldClassNames.has(className)) ||
127
- filename.endsWith('.stylex.ts') ||
128
- filename.endsWith('.stylex.tsx') ||
129
- filename.endsWith('.stylex.js')) {
130
- compilers.forEach(compiler => {
131
- cssFiles.forEach(cssFile => {
132
- compiler.watchFileSystem.watcher.fileWatchers.get(cssFile).watcher.emit('change');
133
- });
134
- });
135
- }
136
- }
137
- return { code, map };
138
- }
139
- return { code: inputCode };
140
- }
141
- }
142
- exports.default = StylexPlugin;
143
- module.exports = StylexPlugin;
package/dist/types.d.ts DELETED
@@ -1,9 +0,0 @@
1
- export type PluginRule = {
2
- class_name: string;
3
- style: {
4
- ltr: string;
5
- rtl?: null | string;
6
- };
7
- priority: number;
8
- };
9
- //# sourceMappingURL=types.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,IAAI,GAAG,MAAM,CAAA;KAAE,CAAC;IAC5C,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC"}
package/dist/types.js DELETED
@@ -1,2 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });