stylex-webpack 0.4.3 → 0.4.5

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/dist/index.d.ts CHANGED
@@ -49,16 +49,14 @@ interface StyleXPluginOption {
49
49
  */
50
50
  transformCss?: CSSTransformer;
51
51
  }
52
- type RegisterStyleXRules = (resourcePath: string, stylexRules: Rule[]) => void;
53
52
  declare class StyleXPlugin {
54
53
  stylexRules: Map<string, readonly Rule[]>;
55
54
  useCSSLayers: boolean;
56
55
  loaderOption: StyleXLoaderOptions;
57
56
  transformCss: CSSTransformer;
58
- private readonly _virtualModuleInstance;
59
57
  constructor({ stylexImports, useCSSLayers, stylexOption, nextjsMode, nextjsAppRouterMode, transformCss }?: StyleXPluginOption);
60
58
  apply(compiler: webpack.Compiler): void;
61
59
  }
62
60
 
63
61
  export { StyleXPlugin };
64
- export type { RegisterStyleXRules, StyleXPluginOption };
62
+ export type { StyleXPluginOption };
package/dist/index.js CHANGED
@@ -3,19 +3,28 @@
3
3
  var stylexBabelPlugin = require('@stylexjs/babel-plugin');
4
4
  var path = require('node:path');
5
5
  var process = require('node:process');
6
- var VirtualModulesPlugin = require('webpack-virtual-modules');
7
6
  var identity = require('foxts/identity');
8
7
 
9
- var version = "0.4.3";
8
+ var version = "0.4.5";
10
9
 
11
10
  const PLUGIN_NAME = 'stylex';
12
11
  const VIRTUAL_ENTRYPOINT_CSS_PATH = require.resolve('./stylex.css');
13
- const VIRTUAL_ENTRYPOINT_CSS_PATTERN = /stylex\.css/;
12
+ require.resolve('./stylex-virtual.css');
13
+ const VIRTUAL_CSS_PATTERN = /stylex\.css|stylex-virtual\.css/;
14
+ const VIRTUAL_STYLEX_CSS_DUMMY_IMPORT_PATTERN = /stylex-virtual\.css/;
14
15
  const STYLEX_CHUNK_NAME = '_stylex-webpack-generated';
15
- require.resolve('./stylex.fuck-nextjs.virtual-carrier.css');
16
- const FUCK_NEXTJS_VIRTUAL_CARRIER_PATTERN = /stylex\.fuck-nextjs\.virtual-carrier\.css/;
17
- const FUCK_NEXTJS_VIRTUAL_CARRIERCHUNK_NAME = '_stylex-fuck-nextjs-collect-stylex-rules';
18
16
  const INCLUDE_REGEXP = /\.[cm]?[jt]sx?$/;
17
+ const BUILD_INFO_STYLEX_KEY = '~stylex_webpack_stylex_rules';
18
+ // https://github.com/vercel/next.js/blob/ad6907a8a37e930639af071203f4ce49a5d69ee5/packages/next/src/shared/lib/constants.ts#L7
19
+ const NEXTJS_COMPILER_NAMES = {
20
+ client: 'client',
21
+ server: 'server',
22
+ edgeServer: 'edge-server'
23
+ };
24
+ function isNextJsCompilerName(name) {
25
+ if (name == null) return false;
26
+ return NEXTJS_COMPILER_NAMES[name] === name;
27
+ }
19
28
 
20
29
  function _define_property(obj, key, value) {
21
30
  if (key in obj) {
@@ -31,7 +40,7 @@ function _define_property(obj, key, value) {
31
40
  return obj;
32
41
  }
33
42
  const stylexLoaderPath = require.resolve('./stylex-loader');
34
- const fuckNextjsVirtualLoaderPath = require.resolve('./stylex-fuck-nextjs-virtual-carrier-loader');
43
+ const stylexVirtualCssLoaderPath = require.resolve('./stylex-virtual-css-loader');
35
44
  function getStyleXRules(stylexRules, useCSSLayers) {
36
45
  if (stylexRules.size === 0) {
37
46
  return null;
@@ -50,24 +59,14 @@ class StyleXPlugin {
50
59
  '"optimization.splitChunks" should be enabled for "stylex-webpack" to function properly.'
51
60
  ].join(' '));
52
61
  }
53
- this._virtualModuleInstance.apply(compiler);
54
62
  (_compiler_options_optimization_splitChunks = compiler.options.optimization.splitChunks).cacheGroups ?? (_compiler_options_optimization_splitChunks.cacheGroups = {});
55
63
  compiler.options.optimization.splitChunks.cacheGroups[STYLEX_CHUNK_NAME] = {
56
64
  name: STYLEX_CHUNK_NAME,
57
- test: VIRTUAL_ENTRYPOINT_CSS_PATTERN,
65
+ test: VIRTUAL_CSS_PATTERN,
58
66
  type: 'css/mini-extract',
59
67
  chunks: 'all',
60
68
  enforce: true
61
69
  };
62
- if (this.loaderOption.nextjsMode) {
63
- compiler.options.optimization.splitChunks.cacheGroups[FUCK_NEXTJS_VIRTUAL_CARRIERCHUNK_NAME] = {
64
- name: FUCK_NEXTJS_VIRTUAL_CARRIERCHUNK_NAME,
65
- test: FUCK_NEXTJS_VIRTUAL_CARRIER_PATTERN,
66
- type: 'css/mini-extract',
67
- chunks: 'all',
68
- enforce: true
69
- };
70
- }
71
70
  // const IS_RSPACK = Object.prototype.hasOwnProperty.call(compiler.webpack, 'rspackVersion');
72
71
  // stylex-loader adds virtual css import (which triggers virtual-loader)
73
72
  // This prevents "stylex.virtual.css" files from being tree shaken by forcing
@@ -83,6 +82,12 @@ class StyleXPlugin {
83
82
  }
84
83
  });
85
84
  });
85
+ // compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
86
+ // compilation.dependencyTemplates.set(
87
+ // CssLocalIdentifierDependency,
88
+ // new CssLocalIdentifierDependency.Template()
89
+ // );
90
+ // });
86
91
  const { Compilation, NormalModule, sources } = compiler.webpack;
87
92
  const { RawSource } = sources;
88
93
  const meta = JSON.stringify({
@@ -90,25 +95,14 @@ class StyleXPlugin {
90
95
  packageVersion: version,
91
96
  opt: this.loaderOption
92
97
  });
98
+ const logger = compiler.getInfrastructureLogger(PLUGIN_NAME);
93
99
  // Apply loader to JS modules
94
100
  compiler.hooks.make.tap(PLUGIN_NAME, (compilation)=>{
95
101
  compilation.hooks.chunkHash.tap(PLUGIN_NAME, (_, hash)=>hash.update(meta));
96
- NormalModule.getCompilationHooks(compilation).loader.tap(PLUGIN_NAME, (loaderContext, mod)=>{
102
+ NormalModule.getCompilationHooks(compilation).loader.tap(PLUGIN_NAME, (_loaderContext, mod)=>{
97
103
  const extname = path.extname(mod.matchResource || mod.resource);
98
- if (this.loaderOption.nextjsMode && FUCK_NEXTJS_VIRTUAL_CARRIER_PATTERN.test(mod.matchResource || mod.resource)) {
99
- mod.loaders.push({
100
- loader: fuckNextjsVirtualLoaderPath,
101
- options: {},
102
- ident: null,
103
- type: null
104
- });
105
- }
104
+ logger.debug(`attaching stylex-loader to ${mod.matchResource || mod.resource}`);
106
105
  if (INCLUDE_REGEXP.test(extname)) {
107
- loaderContext.StyleXWebpackContextKey = {
108
- registerStyleXRules: (resourcePath, stylexRules)=>{
109
- this.stylexRules.set(resourcePath, stylexRules);
110
- }
111
- };
112
106
  // We use .push() here instead of .unshift()
113
107
  // Webpack usually runs loaders in reverse order and we want to ideally run
114
108
  // our loader before anything else.
@@ -118,45 +112,78 @@ class StyleXPlugin {
118
112
  ident: null,
119
113
  type: null
120
114
  });
115
+ } else if (VIRTUAL_STYLEX_CSS_DUMMY_IMPORT_PATTERN.test(mod.matchResource || mod.resource)) {
116
+ mod.loaders.push({
117
+ loader: stylexVirtualCssLoaderPath,
118
+ ident: null,
119
+ type: null
120
+ });
121
121
  }
122
122
  });
123
123
  compilation.hooks.processAssets.tapPromise({
124
124
  name: PLUGIN_NAME,
125
125
  stage: Compilation.PROCESS_ASSETS_STAGE_PRE_PROCESS
126
126
  }, async (assets)=>{
127
- if (this.loaderOption.nextjsMode) {
128
- // Collect stylex rules from module instead of self maintained map
129
- // on previous step, we create a "stylex" chunk to hold all virtual stylex css
130
- // the chunk contains all css chunks generated by mini-css-extract-plugin
131
- // TODO-RSPACK: once again, rspack doesn't have this
132
- const fuckNextjsChunk = compilation.namedChunks.get(FUCK_NEXTJS_VIRTUAL_CARRIERCHUNK_NAME);
133
- if (fuckNextjsChunk) {
134
- const cssModulesInFuckNextjsChunk = compilation.chunkGraph.getChunkModulesIterableBySourceType(fuckNextjsChunk, 'css/mini-extract');
135
- // we only re-collect stylex rules if we can found css in the stylex chunk
136
- if (cssModulesInFuckNextjsChunk) {
137
- for (const cssModule of cssModulesInFuckNextjsChunk){
138
- if (!('_identifier' in cssModule) || typeof cssModule._identifier !== 'string') {
139
- continue;
140
- }
141
- const stringifiedStylexRule = cssModule._identifier.split('!').pop()?.split('?').pop();
142
- if (!stringifiedStylexRule) {
143
- continue;
144
- }
145
- const params = new URLSearchParams(stringifiedStylexRule);
146
- const stylex = params.get('stylex');
147
- const from = params.get('from');
148
- if (stylex != null && from != null) {
149
- this.stylexRules.set(from, JSON.parse(stylex));
150
- }
151
- }
127
+ compilation.modules.forEach((mod)=>{
128
+ if (mod.buildInfo && BUILD_INFO_STYLEX_KEY in mod.buildInfo) {
129
+ const stylexBuildInfo = mod.buildInfo[BUILD_INFO_STYLEX_KEY];
130
+ if (typeof stylexBuildInfo === 'object' && stylexBuildInfo != null && 'resourcePath' in stylexBuildInfo && 'stylexRules' in stylexBuildInfo && typeof stylexBuildInfo.resourcePath === 'string') {
131
+ logger.debug(`collecting stylex rules from ${stylexBuildInfo.resourcePath}'s build info`);
132
+ this.stylexRules.set(stylexBuildInfo.resourcePath, stylexBuildInfo.stylexRules);
152
133
  }
153
- // Let's find the css file that belongs to the fuck-next.js chunk to remove it
154
- const fuckNextjsChunkCssAssetNames = Object.keys(assets).filter((assetName)=>fuckNextjsChunk.files.has(assetName) && assetName.endsWith('.css'));
155
- if (fuckNextjsChunkCssAssetNames.length > 0) {
156
- for (const assetName of fuckNextjsChunkCssAssetNames){
157
- compilation.deleteAsset(assetName);
158
- }
134
+ }
135
+ });
136
+ if (this.loaderOption.nextjsMode && this.loaderOption.nextjsAppRouterMode && isNextJsCompilerName(compiler.name)) {
137
+ /**
138
+ * Next.js call webpack compiler through "runCompiler": https://github.com/vercel/next.js/blob/c0c75e4aaa8ece2c9e789e2e3f150d7487b60bbc/packages/next/src/build/compiler.ts#L39
139
+ *
140
+ * The "runCompiler" funtion is invoked by "webpackBuildImpl" function: https://github.com/vercel/next.js/blob/ad6907a8a37e930639af071203f4ce49a5d69ee5/packages/next/src/build/webpack-build/impl.ts#L203
141
+ *
142
+ * The "webpackBuildImpl" function accepts "compilerName" parameter, and is invoked by "webpackBuild" function: https://github.com/vercel/next.js/blob/c0c75e4aaa8ece2c9e789e2e3f150d7487b60bbc/packages/next/src/build/webpack-build/index.ts#L124
143
+ * When build worker is enabled, the "compilerName" parameter is set to either "client", "server" or "edge-server". If build worker is disabled,
144
+ * the "compilerName" parameter is always "null".
145
+ *
146
+ * When build worker is disabled, the multi-stage build is managed by "webpackBuildImpl" function itself: https://github.com/vercel/next.js/blob/ad6907a8a37e930639af071203f4ce49a5d69ee5/packages/next/src/build/webpack-build/impl.ts#L203
147
+ * It will first run "server" compiler, then "edge-server" compiler, and finally "client" compiler.
148
+ *
149
+ * The "webpackBuildImpl" function is invoked by "webpackBuild" function: https://github.com/vercel/next.js/blob/c0c75e4aaa8ece2c9e789e2e3f150d7487b60bbc/packages/next/src/build/webpack-build/index.ts#L124
150
+ *
151
+ * If build worker is enabled, the multi-stage build is managed by the build entrypoint, and the "client", "server" and "edge-server" compilerName
152
+ * is passed to "webpackBuildImpl" through "webpackBuild" function:
153
+ * https://github.com/vercel/next.js/blob/c0c75e4aaa8ece2c9e789e2e3f150d7487b60bbc/packages/next/src/build/index.ts#L905
154
+ * https://github.com/vercel/next.js/blob/c0c75e4aaa8ece2c9e789e2e3f150d7487b60bbc/packages/next/src/build/index.ts#L1796
155
+ *
156
+ * Note that, if a custom webpack config is provided, Next.js will always disable build worker: https://github.com/vercel/next.js/blob/c0c75e4aaa8ece2c9e789e2e3f150d7487b60bbc/packages/next/src/build/index.ts#L1723
157
+ * We will not take that as an assumption. We already overwrite "nextConfig.experimental.webpackBuildWorker" to false in the Next.js plugin.
158
+ *
159
+ * Now all compiler instances are running in the same process, we can use a global variable to track stylex rules from different compilers.
160
+ *
161
+ * Back to "runCompiler". "runCompiler" accepts webpack configurations which is created by "getBaseWebpackConfig" function: https://github.com/vercel/next.js/blob/ad6907a8a37e930639af071203f4ce49a5d69ee5/packages/next/src/build/webpack-build/impl.ts#L128
162
+ *
163
+ * Inside "getBaseWebpackConfig" function, there is a "buildConfiguration" function: https://github.com/vercel/next.js/blob/ad6907a8a37e930639af071203f4ce49a5d69ee5/packages/next/src/build/webpack-config.ts#L2464
164
+ * Inside "buildConfiguration" function there is a curried "base" function: https://github.com/vercel/next.js/blob/c0c75e4aaa8ece2c9e789e2e3f150d7487b60bbc/packages/next/src/build/webpack/config/index.ts#L73
165
+ * Inside the "base" function, the compiler name is attached to the webpack configuration: https://github.com/vercel/next.js/blob/c0c75e4aaa8ece2c9e789e2e3f150d7487b60bbc/packages/next/src/build/webpack/config/blocks/base.ts#L24
166
+ */ if (compiler.name === NEXTJS_COMPILER_NAMES.server || compiler.name === NEXTJS_COMPILER_NAMES.edgeServer) {
167
+ var _globalThis;
168
+ ((_globalThis = globalThis).__stylex_nextjs_global_registry__ ?? (_globalThis.__stylex_nextjs_global_registry__ = new Map())).set(compiler.name, this.stylexRules);
169
+ // we don't need to do anything more in server/edge compiler, no CSS generation is needed
170
+ return;
171
+ }
172
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- type safe
173
+ if (compiler.name === NEXTJS_COMPILER_NAMES.client) {
174
+ const globalRegistry = globalThis.__stylex_nextjs_global_registry__;
175
+ if (globalRegistry != null) {
176
+ // now we merge all collected rules from other compilers
177
+ globalRegistry.forEach((rules)=>{
178
+ rules.forEach((rule, resourcePath)=>{
179
+ if (!this.stylexRules.has(resourcePath)) {
180
+ this.stylexRules.set(resourcePath, rule);
181
+ }
182
+ });
183
+ });
159
184
  }
185
+ } else {
186
+ compiler.name;
160
187
  }
161
188
  }
162
189
  const stylexCSS = getStyleXRules(this.stylexRules, this.useCSSLayers);
@@ -164,28 +191,20 @@ class StyleXPlugin {
164
191
  return;
165
192
  }
166
193
  const finalCss = await this.transformCss(stylexCSS);
167
- if (compiler.options.mode === 'development' && this.loaderOption.nextjsAppRouterMode) {
168
- // In development mode, a.k.a. HMR
169
- /**
170
- * Now we write final CSS to virtual module, which acts like `stylex-webpack/stylex.css` has been
171
- * updated locally on the disk, and Next.js and webpack will have no choice but to update the global css
172
- */ this._virtualModuleInstance.writeModule(VIRTUAL_ENTRYPOINT_CSS_PATH, finalCss.toString());
173
- } else {
174
- const stylexChunk = compilation.namedChunks.get(STYLEX_CHUNK_NAME);
175
- if (!stylexChunk) return;
176
- // Let's find the css file that belongs to the stylex chunk
177
- const stylexChunkCssAssetNames = Object.keys(assets).filter((assetName)=>stylexChunk.files.has(assetName) && assetName.endsWith('.css'));
178
- if (stylexChunkCssAssetNames.length === 0) {
179
- return;
180
- }
181
- if (stylexChunkCssAssetNames.length > 1) {
182
- console.warn('[stylex-webpack] Multiple CSS assets found for the stylex chunk. This should not happen. Please report this issue.');
183
- }
184
- const stylexAssetName = stylexChunkCssAssetNames[0];
185
- compilation.updateAsset(stylexAssetName, ()=>new RawSource(finalCss), {
186
- minimized: false
187
- });
194
+ const stylexChunk = compilation.namedChunks.get(STYLEX_CHUNK_NAME);
195
+ if (!stylexChunk) return;
196
+ // Let's find the css file that belongs to the stylex chunk
197
+ const stylexChunkCssAssetNames = Object.keys(assets).filter((assetName)=>stylexChunk.files.has(assetName) && assetName.endsWith('.css'));
198
+ if (stylexChunkCssAssetNames.length === 0) {
199
+ return;
200
+ }
201
+ if (stylexChunkCssAssetNames.length > 1) {
202
+ logger.warn('Multiple CSS assets found for the stylex chunk. This should not happen. Please report this issue.');
188
203
  }
204
+ const stylexAssetName = stylexChunkCssAssetNames[0];
205
+ compilation.updateAsset(stylexAssetName, ()=>new RawSource(finalCss), {
206
+ minimized: false
207
+ });
189
208
  });
190
209
  });
191
210
  }
@@ -197,7 +216,6 @@ class StyleXPlugin {
197
216
  _define_property(this, "useCSSLayers", void 0);
198
217
  _define_property(this, "loaderOption", void 0);
199
218
  _define_property(this, "transformCss", void 0);
200
- _define_property(this, "_virtualModuleInstance", new VirtualModulesPlugin());
201
219
  this.useCSSLayers = useCSSLayers;
202
220
  this.loaderOption = {
203
221
  stylexImports,
package/dist/next.js CHANGED
@@ -7,8 +7,8 @@ var css = require('next/dist/build/webpack/config/blocks/css');
7
7
  var index = require('./index');
8
8
 
9
9
  require.resolve('./stylex.css');
10
- const VIRTUAL_ENTRYPOINT_CSS_PATTERN = /stylex\.css/;
11
- require.resolve('./stylex.fuck-nextjs.virtual-carrier.css');
10
+ require.resolve('./stylex-virtual.css');
11
+ const VIRTUAL_CSS_PATTERN = /stylex\.css|stylex-virtual\.css/;
12
12
 
13
13
  /** Next.js' precompilation add "__esModule: true", but doesn't add an actual default exports */ // @ts-expect-error -- Next.js fucks something up
14
14
  const NextMiniCssExtractPlugin = nextMiniCssExtractPluginExports.default;
@@ -74,7 +74,8 @@ function getStyleXVirtualCssLoader(ctx, MiniCssExtractPlugin, postcss) {
74
74
  return loaders;
75
75
  }
76
76
  function withStyleX(pluginOptions) {
77
- return (nextConfig = {})=>({
77
+ return (nextConfig = {})=>{
78
+ const config = {
78
79
  ...nextConfig,
79
80
  webpack (config, ctx) {
80
81
  var // For some reason, Next 11.0.1 has `config.optimization.splitChunks`
@@ -98,7 +99,7 @@ function withStyleX(pluginOptions) {
98
99
  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;
99
100
  // Here we matches virtual css file emitted by StyleXPlugin
100
101
  cssRules?.unshift({
101
- test: VIRTUAL_ENTRYPOINT_CSS_PATTERN,
102
+ test: VIRTUAL_CSS_PATTERN,
102
103
  use: getStyleXVirtualCssLoader(ctx, MiniCssExtractPlugin, postcss)
103
104
  });
104
105
  (_config1 = config).plugins ?? (_config1.plugins = []);
@@ -147,7 +148,10 @@ function withStyleX(pluginOptions) {
147
148
  },
148
149
  async transformCss (css) {
149
150
  const { postcssWithPlugins } = await postcss();
150
- const result = await postcssWithPlugins.process(css);
151
+ // add from: undefined to avoid source map warning
152
+ const result = await postcssWithPlugins.process(css, {
153
+ from: undefined
154
+ });
151
155
  if (pluginOptions?.transformCss) {
152
156
  return pluginOptions.transformCss(result.css);
153
157
  }
@@ -156,7 +160,16 @@ function withStyleX(pluginOptions) {
156
160
  }));
157
161
  return config;
158
162
  }
159
- });
163
+ };
164
+ // https://github.com/vercel/next.js/blob/ad6907a8a37e930639af071203f4ce49a5d69ee5/packages/next/src/build/index.ts#L1723
165
+ // Actually, if a custom webpack config is provided, Next.js will always disable parallel bundling
166
+ // But we should not take that as an assumption, so we just warn and disable the options
167
+ if (config.experimental?.webpackBuildWorker) {
168
+ log.warn('[stylex-webpack] "experimental.webpackBuildWorker" is not supported with "stylex-webpack", the option will be disabled.');
169
+ config.experimental.webpackBuildWorker = false;
170
+ }
171
+ return config;
172
+ };
160
173
  }
161
174
 
162
175
  exports.withStyleX = withStyleX;
@@ -2,13 +2,11 @@
2
2
 
3
3
  var core = require('@babel/core');
4
4
  var stylexBabelPlugin = require('@stylexjs/babel-plugin');
5
+ var guard = require('foxts/guard');
5
6
 
6
7
  require.resolve('./stylex.css');
7
- const FUCK_NEXTJS_VIRTUAL_CARRIER_PATH = require.resolve('./stylex.fuck-nextjs.virtual-carrier.css');
8
- function isSupplementedLoaderContext(context) {
9
- // eslint-disable-next-line prefer-object-has-own -- target older
10
- return Object.prototype.hasOwnProperty.call(context, 'StyleXWebpackContextKey');
11
- }
8
+ const VIRTUAL_STYLEX_CSS_DUMMY_IMPORT_PATH = require.resolve('./stylex-virtual.css');
9
+ const BUILD_INFO_STYLEX_KEY = '~stylex_webpack_stylex_rules';
12
10
 
13
11
  function stringifyRequest(loaderContext, request) {
14
12
  return JSON.stringify(loaderContext.utils.contextify(loaderContext.context || loaderContext.rootContext, request));
@@ -17,14 +15,11 @@ function stringifyRequest(loaderContext, request) {
17
15
  const PLUGIN_NAME = 'stylex';
18
16
  async function stylexLoader(inputCode, inputSourceMap) {
19
17
  const callback = this.async();
20
- const { stylexImports, stylexOption, nextjsMode, nextjsAppRouterMode } = this.getOptions();
18
+ const { stylexImports, stylexOption } = this.getOptions();
21
19
  // bail out early if the input doesn't contain stylex imports
22
20
  if (!stylexImports.some((importName)=>inputCode.includes(importName))) {
23
21
  return callback(null, inputCode, inputSourceMap);
24
22
  }
25
- if (!isSupplementedLoaderContext(this)) {
26
- return callback(new Error('stylex-loader: loader context is not SupplementedLoaderContext!'));
27
- }
28
23
  try {
29
24
  const { code, map, metadata } = await core.transformAsync(inputCode, {
30
25
  babelrc: false,
@@ -51,22 +46,18 @@ async function stylexLoader(inputCode, inputSourceMap) {
51
46
  }
52
47
  // this.stylexRules[filename] = metadata.stylex;
53
48
  logger?.debug(`Read stylex styles from ${this.resourcePath}:`, metadata.stylex);
54
- // TODO-RSPACK: doesn't support custom loader context
55
- // Find a better way to register stylex rules to the compiler instance
56
- this.StyleXWebpackContextKey.registerStyleXRules(this.resourcePath, metadata.stylex);
57
- // Next.js Pages Router doesn't need CSS import in every page.
58
- if (nextjsMode && nextjsAppRouterMode) {
59
- // Next.js App Router doesn't support inline matchResource and inline loaders
60
- // So we adapt Next.js' "external" css import approach instead
61
- const urlParams = new URLSearchParams({
62
- from: this.resourcePath,
63
- stylex: JSON.stringify(metadata.stylex) // color: #fff is not url safe, let's get through JSON.stringify
64
- });
65
- const virtualCssRequest = stringifyRequest(this, `${FUCK_NEXTJS_VIRTUAL_CARRIER_PATH}?${urlParams.toString()}`);
66
- const postfix = `\nimport ${virtualCssRequest};`;
67
- return callback(null, code + postfix, map ?? undefined);
68
- }
69
- return callback(null, code ?? undefined, map ?? undefined);
49
+ guard.nullthrow(this._module?.buildInfo, '[stylex-webpack] Expected "this._module.buildInfo" to be defined')[BUILD_INFO_STYLEX_KEY] = {
50
+ resourcePath: this.resourcePath,
51
+ stylexRules: metadata.stylex
52
+ };
53
+ // Add a dummy virtual import that will be picked up by virtual dummy import loader to add fake CSS to invalidate HMR
54
+ const urlParams = new URLSearchParams({
55
+ from: this.resourcePath,
56
+ stylex: JSON.stringify(metadata.stylex) // color: #fff is not url safe, let's get through JSON.stringify
57
+ });
58
+ const virtualCssRequest = stringifyRequest(this, `${VIRTUAL_STYLEX_CSS_DUMMY_IMPORT_PATH}?${urlParams.toString()}`);
59
+ const postfix = `\nimport ${virtualCssRequest};`;
60
+ return callback(null, code + postfix, map ?? undefined);
70
61
  } catch (error) {
71
62
  return callback(error);
72
63
  }
@@ -1,11 +1,16 @@
1
1
  'use strict';
2
2
 
3
3
  var loaderUtils = require('loader-utils');
4
- var tsDedent = require('ts-dedent');
5
4
 
6
5
  // prefer loader-utils over self-implemented hash function to utilize caching + bulk hashing
7
- function stylexFuckNextjsVirtualCarrierLoader(inputCode, inputSourceMap) {
6
+ function stylexVirtualCssLoader(inputCode, inputSourceMap) {
8
7
  const callback = this.async();
8
+ this.cacheable(false);
9
+ if (this._compiler?.options.mode === 'production') {
10
+ // In development mode, we don't need to generate the virtual CSS content
11
+ callback(null, inputCode, inputSourceMap);
12
+ return;
13
+ }
9
14
  const data = new URLSearchParams(this.resourceQuery.slice(1));
10
15
  try {
11
16
  const stylex = data.get('stylex');
@@ -15,14 +20,11 @@ function stylexFuckNextjsVirtualCarrierLoader(inputCode, inputSourceMap) {
15
20
  }
16
21
  // @ts-expect-error -- getHashDigest supports string & xxhash64
17
22
  const hash = loaderUtils.getHashDigest(stylex, 'xxhash64', 'base62', 32);
18
- const code = tsDedent.dedent`
19
- /** stylex rules: ${stylex} */
20
- .stylex-fuck-nextjs-${hash} {}
21
- `;
23
+ const code = `/** stylex rules: ${stylex} */\n.stylex-hashed-${hash} {}`;
22
24
  callback(null, inputCode + '\n' + code);
23
25
  } catch (e) {
24
26
  callback(e);
25
27
  }
26
28
  }
27
29
 
28
- module.exports = stylexFuckNextjsVirtualCarrierLoader;
30
+ module.exports = stylexVirtualCssLoader;
@@ -0,0 +1,7 @@
1
+ /*
2
+ * This is a noop file that is meant to be imported via a dummy import by stylex webpack loader
3
+ *
4
+ * This has to be an actual file as webpack requires a file to exist on disk
5
+ *
6
+ * In production build this should have been minimized away, if not please report an issue.
7
+ */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stylex-webpack",
3
- "version": "0.4.3",
3
+ "version": "0.4.5",
4
4
  "description": "The another Webpack Plugin for Facebook's StyleX",
5
5
  "homepage": "https://github.com/SukkaW/style9-webpack#readme",
6
6
  "repository": {
@@ -29,7 +29,7 @@
29
29
  "./stylex.css": "./dist/stylex.css"
30
30
  },
31
31
  "scripts": {
32
- "prebuild": "rimraf dist",
32
+ "prebuild": "premove dist",
33
33
  "build": "rollup -c rollup.config.ts --configPlugin swc3 --bundleConfigAsCjs",
34
34
  "lint": "eslint --format=sukka .",
35
35
  "test": "mocha --require @swc-node/register --require mocha-expect-snapshot test/index.ts",
@@ -49,50 +49,48 @@
49
49
  "author": "Sukka <https://skk.moe>",
50
50
  "license": "MIT",
51
51
  "dependencies": {
52
- "@babel/core": "^7.28.4",
52
+ "@babel/core": "^7.28.5",
53
53
  "@babel/plugin-syntax-jsx": "^7.27.1",
54
54
  "@babel/plugin-syntax-typescript": "^7.27.1",
55
55
  "@types/webpack": "^5.28.5",
56
- "foxts": "^3.13.0",
57
- "loader-utils": "^3.3.1",
58
- "ts-dedent": "^2.2.0",
59
- "webpack-virtual-modules": "^0.6.2"
56
+ "foxts": "^5.0.0",
57
+ "loader-utils": "^3.3.1"
60
58
  },
61
59
  "devDependencies": {
62
- "@eslint-sukka/node": "^7.0.2",
60
+ "@eslint-sukka/node": "^8.0.2",
63
61
  "@rollup/plugin-json": "^6.1.0",
64
62
  "@swc-node/register": "^1.11.1",
65
- "@swc/core": "^1.13.5",
63
+ "@swc/core": "^1.15.0",
66
64
  "@types/babel__core": "^7.20.5",
67
65
  "@types/loader-utils": "^3.0.0",
68
66
  "@types/mocha": "^10.0.10",
69
- "@types/node": "^22.18.3",
70
- "browserslist": "^4.26.0",
71
- "bumpp": "^10.2.3",
67
+ "@types/node": "^22.19.0",
68
+ "browserslist": "^4.27.0",
69
+ "bumpp": "^10.3.1",
72
70
  "css-loader": "^7.1.2",
73
- "eslint": "^9.35.0",
74
- "eslint-config-sukka": "^7.0.2",
75
- "eslint-formatter-sukka": "^7.0.2",
76
- "expect": "^30.1.2",
77
- "memfs": "^4.39.0",
71
+ "eslint": "^9.39.1",
72
+ "eslint-config-sukka": "^8.0.2",
73
+ "eslint-formatter-sukka": "^8.0.2",
74
+ "expect": "^30.2.0",
75
+ "memfs": "^4.50.0",
78
76
  "mini-css-extract-plugin": "^2.9.4",
79
- "mocha": "^11.7.2",
77
+ "mocha": "^11.7.5",
80
78
  "mocha-expect-snapshot": "^8.0.0",
81
- "next": "^15.5.3",
79
+ "next": "^15.5.6",
82
80
  "postcss": "^8.5.6",
83
- "rimraf": "^6.0.1",
84
- "rollup": "^4.50.1",
81
+ "premove": "^4.0.0",
82
+ "rollup": "^4.52.5",
85
83
  "rollup-plugin-copy": "^3.5.0",
86
84
  "rollup-plugin-dts": "^6.2.3",
87
85
  "rollup-plugin-swc3": "^0.12.1",
88
86
  "swc-loader": "^0.2.6",
89
- "typescript": "^5.9.2",
90
- "webpack": "^5.101.3"
87
+ "typescript": "^5.9.3",
88
+ "webpack": "^5.102.1"
91
89
  },
92
90
  "peerDependencies": {
93
91
  "@stylexjs/babel-plugin": "*"
94
92
  },
95
- "packageManager": "pnpm@10.16.0",
93
+ "packageManager": "pnpm@10.20.0",
96
94
  "pnpm": {
97
95
  "overrides": {
98
96
  "eslint>chalk": "npm:picocolors@^1.1.1"
@@ -1,15 +0,0 @@
1
- /*
2
- * This is a noop file specifically for Next.js app dir
3
- *
4
- * This has to be an actual file as webpack requires a file to exist on disk
5
- *
6
- * Due to how Next.js stupidly handles server code and client code in two different webpack namespaces,
7
- * the client compiler instance can't have access to the server code, including styles registered in the server code.
8
- *
9
- * In order for the client bundle to collect all the styles, we use this virtual noop file as a bridge. This file doesn't
10
- * do anything, but it will become a carrier, holding collected style rules. It is done by appending a fake import with
11
- * a url query using the "stylex-loader".
12
- *
13
- * Later in webpack's "processAsset" phase, we collect these imports from chunk information, extract those url query from
14
- * module identifiers, collect style rules for later CSS generation.
15
- */