@stylexswc/webpack-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
@@ -36,8 +36,13 @@ const config = (env, argv) => ({
36
36
  },
37
37
  plugins: [
38
38
  new StylexPlugin({
39
- filename: 'styles.[contenthash].css',
40
- dev: argv.mode === 'development',
39
+ // ... Other StyleX options
40
+ transformCss: css => {
41
+ // Transform CSS here, for example, using PostCSS
42
+ const transformedCSS = css;
43
+
44
+ return transformedCSS;
45
+ },
41
46
  }),
42
47
  ],
43
48
  cache: true,
@@ -47,9 +52,78 @@ module.exports = config;
47
52
  ```
48
53
 
49
54
  ## Plugin Options
50
- The options are similar like `@stylexjs/babel-plugin` and can be found [here](https://stylexjs.com/docs/api/configuration/babel-plugin/)
55
+
56
+ ### Basic Options
57
+
58
+ #### `rsOptions`
59
+
60
+ * Type: `Partial<StyleXOptions>`
61
+ * Optional
62
+ * Description: StyleX compiler options that will be passed to the NAPI-RS
63
+ compiler. See
64
+ [StyleX configuration docs](https://stylexjs.com/docs/api/configuration/babel-plugin/)
65
+ for details.
66
+
67
+ #### `stylexImports`
68
+
69
+ * Type: `Array<string | { as: string, from: string }>`
70
+ * Default: `['stylex', '@stylexjs/stylex']`
71
+ * Description: Specifies where StyleX will be imported from. Supports both
72
+ string paths and import aliases.
73
+
74
+ #### `useCSSLayers`
75
+
76
+ * Type: `boolean`
77
+ * Default: `false`
78
+ * Description: Enables CSS cascade layers support for better style isolation.
79
+
80
+ #### `nextjsMode`
81
+
82
+ * Type: `boolean`
83
+ * Default: `false`
84
+ * Description: Enables Next.js-specific optimizations and compatibility
85
+ features.
86
+
87
+ ### Advanced Options
88
+
89
+ #### `transformCss`
90
+
91
+ * Type: `(css: string) => string | Buffer | Promise<string | Buffer>`
92
+ * Optional
93
+ * Description: Custom CSS transformation function. Since the plugin injects CSS
94
+ after all loaders, use this to apply PostCSS or other CSS transformations.
95
+
96
+ ### Example Configuration
97
+
98
+ ```javascript
99
+ const StylexPlugin = require('@stylexswc/webpack-plugin');
100
+
101
+ module.exports = {
102
+ plugins: [
103
+ new StylexPlugin({
104
+ rsOptions: {
105
+ dev: process.env.NODE_ENV !== 'production',
106
+ useRemForFontSize: true,
107
+ },
108
+ stylexImports: ['@stylexjs/stylex', { from: './theme', as: 'tokens' }],
109
+ useCSSLayers: true,
110
+ nextjsMode: false,
111
+ transformCss: async css => {
112
+ const postcss = require('postcss');
113
+ const result = await postcss([require('autoprefixer')]).process(css);
114
+ return result.css;
115
+ },
116
+ }),
117
+ ],
118
+ };
119
+ ```
51
120
 
52
121
  ## Documentation
53
122
 
54
123
  * [StyleX Documentation](https://stylexjs.com)
55
124
  * [NAPI-RS compiler for StyleX](https://github.com/Dwlad90/stylex-swc-plugin/tree/develop/crates/stylex-rs-compiler)
125
+
126
+ ## Acknowledgments
127
+
128
+ This plugin was inspired by
129
+ [`stylex-webpack`](https://github.com/SukkaW/stylex-webpack).
@@ -1,4 +1,7 @@
1
- import type { LoaderContext } from 'webpack';
2
1
  export declare const PLUGIN_NAME = "stylex";
3
- export type SupplementedLoaderContext<Options = unknown> = LoaderContext<Options> & {};
2
+ export declare const VIRTUAL_CSS_PATH: string;
3
+ export declare const VIRTUAL_CSS_PATTERN: RegExp;
4
+ export declare const STYLEX_CHUNK_NAME = "_stylex-webpack-generated";
5
+ export declare const INCLUDE_REGEXP: RegExp;
6
+ export declare const IS_DEV_ENV: boolean;
4
7
  //# sourceMappingURL=constants.d.ts.map
@@ -1 +1 @@
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"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW,WAAW,CAAC;AACpC,eAAO,MAAM,gBAAgB,QAA0C,CAAC;AACxE,eAAO,MAAM,mBAAmB,QAAyB,CAAC;AAC1D,eAAO,MAAM,iBAAiB,8BAA8B,CAAC;AAC7D,eAAO,MAAM,cAAc,QAAoB,CAAC;AAChD,eAAO,MAAM,UAAU,SAAyC,CAAC"}
package/dist/constants.js CHANGED
@@ -1,4 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PLUGIN_NAME = void 0;
3
+ exports.IS_DEV_ENV = exports.INCLUDE_REGEXP = exports.STYLEX_CHUNK_NAME = exports.VIRTUAL_CSS_PATTERN = exports.VIRTUAL_CSS_PATH = exports.PLUGIN_NAME = void 0;
4
4
  exports.PLUGIN_NAME = 'stylex';
5
+ exports.VIRTUAL_CSS_PATH = require.resolve('./stylex.virtual.css');
6
+ exports.VIRTUAL_CSS_PATTERN = /stylex\.virtual\.css/;
7
+ exports.STYLEX_CHUNK_NAME = '_stylex-webpack-generated';
8
+ exports.INCLUDE_REGEXP = /\.[cm]?[jt]sx?$/;
9
+ exports.IS_DEV_ENV = process.env.NODE_ENV === 'development';
package/dist/index.d.ts CHANGED
@@ -1,2 +1,16 @@
1
- export {};
1
+ import { VIRTUAL_CSS_PATTERN } from './constants';
2
+ import type webpack from 'webpack';
3
+ import type { Rule as StyleXRule } from '@stylexjs/babel-plugin';
4
+ import type { CSSTransformer, StyleXPluginOption, StyleXWebpackLoaderOptions } from './types';
5
+ export type RegisterStyleXRules = (resourcePath: string, stylexRules: StyleXRule[]) => void;
6
+ export default class StyleXPlugin {
7
+ stylexRules: Map<string, readonly StyleXRule[]>;
8
+ useCSSLayers: boolean;
9
+ loaderOption: StyleXWebpackLoaderOptions;
10
+ transformCss: CSSTransformer;
11
+ constructor({ stylexImports, useCSSLayers, rsOptions, nextjsMode, transformCss, transformer, }?: StyleXPluginOption);
12
+ apply(compiler: webpack.Compiler): void;
13
+ }
14
+ export { VIRTUAL_CSS_PATTERN };
15
+ export type { StyleXPluginOption } from './types';
2
16
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAML,mBAAmB,EACpB,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,EAAE,IAAI,IAAI,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,KAAK,EACV,cAAc,EACd,kBAAkB,EAClB,0BAA0B,EAE3B,MAAM,SAAS,CAAC;AAkBjB,MAAM,MAAM,mBAAmB,GAAG,CAAC,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,KAAK,IAAI,CAAC;AAE5F,MAAM,CAAC,OAAO,OAAO,YAAY;IAC/B,WAAW,qCAA4C;IACvD,YAAY,EAAE,OAAO,CAAC;IAEtB,YAAY,EAAE,0BAA0B,CAAC;IAEzC,YAAY,EAAE,cAAc,CAAC;gBAEjB,EACV,aAA8C,EAC9C,YAAoB,EACpB,SAAc,EACd,UAAkB,EAClB,YAAgC,EAChC,WAA2B,GAC5B,GAAE,kBAAuB;IAmB1B,KAAK,CAAC,QAAQ,EAAE,OAAO,CAAC,QAAQ;CA2JjC;AAED,OAAO,EAAE,mBAAmB,EAAE,CAAC;AAE/B,YAAY,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC"}
package/dist/index.js CHANGED
@@ -3,154 +3,161 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- const path_1 = __importDefault(require("path"));
6
+ exports.VIRTUAL_CSS_PATTERN = void 0;
7
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 { NormalModule, Compilation } = webpack_1.default;
11
- const PLUGIN_NAME = 'stylex';
12
- const IS_DEV_ENV = process.env.NODE_ENV === 'development';
13
- const { RawSource, ConcatSource } = webpack_1.default.sources;
14
- class StylexPlugin {
15
- filesInLastRun = null;
16
- filePath = null;
17
- dev;
18
- appendTo;
19
- filename;
20
- stylexImports;
8
+ const path_1 = __importDefault(require("path"));
9
+ const constants_1 = require("./constants");
10
+ Object.defineProperty(exports, "VIRTUAL_CSS_PATTERN", { enumerable: true, get: function () { return constants_1.VIRTUAL_CSS_PATTERN; } });
11
+ const stylexLoaderPath = require.resolve('./stylex-loader');
12
+ const stylexVirtualLoaderPath = require.resolve('./stylex-virtual-css-loader');
13
+ const getStyleXRules = (stylexRules, useCSSLayers) => {
14
+ if (stylexRules.size === 0) {
15
+ return null;
16
+ }
17
+ // Take styles for the modules that were included in the last compilation.
18
+ const allRules = Array.from(stylexRules.values()).flat();
19
+ return babel_plugin_1.default.processStylexRules(allRules, useCSSLayers);
20
+ };
21
+ const identityTransfrom = css => css;
22
+ class StyleXPlugin {
23
+ stylexRules = new Map();
21
24
  useCSSLayers;
22
- rsOptions;
23
- stylexRules = {};
24
- constructor({ dev = IS_DEV_ENV, appendTo, filename = appendTo == null ? 'stylex.css' : undefined, stylexImports = ['stylex', '@stylexjs/stylex'], useCSSLayers = false, rsOptions = {}, } = {}) {
25
- this.dev = dev;
26
- this.appendTo = appendTo;
27
- this.filename = filename;
28
- this.stylexImports = stylexImports;
25
+ loaderOption;
26
+ transformCss;
27
+ constructor({ stylexImports = ['stylex', '@stylexjs/stylex'], useCSSLayers = false, rsOptions = {}, nextjsMode = false, transformCss = identityTransfrom, transformer = 'rs-compiler', } = {}) {
29
28
  this.useCSSLayers = useCSSLayers;
30
- this.rsOptions = rsOptions;
29
+ this.loaderOption = {
30
+ stylexImports,
31
+ rsOptions: {
32
+ dev: constants_1.IS_DEV_ENV,
33
+ useRemForFontSize: true,
34
+ runtimeInjection: false,
35
+ genConditionalClasses: true,
36
+ treeshakeCompensation: true,
37
+ importSources: stylexImports,
38
+ ...rsOptions,
39
+ },
40
+ nextjsMode,
41
+ transformer,
42
+ };
43
+ this.transformCss = transformCss;
31
44
  }
32
45
  apply(compiler) {
33
- compiler.hooks.make.tap(PLUGIN_NAME, compilation => {
46
+ // If splitChunk is enabled, we create a dedicated chunk for stylex css
47
+ if (!compiler.options.optimization.splitChunks) {
48
+ throw new Error([
49
+ 'You don\'t have "optimization.splitChunks" enabled.',
50
+ '"optimization.splitChunks" should be enabled for "@stylexswc/webpack-plugin" to function properly.',
51
+ ].join(' '));
52
+ }
53
+ compiler.options.optimization.splitChunks.cacheGroups ??= {};
54
+ compiler.options.optimization.splitChunks.cacheGroups[constants_1.STYLEX_CHUNK_NAME] = {
55
+ name: constants_1.STYLEX_CHUNK_NAME,
56
+ test: constants_1.VIRTUAL_CSS_PATTERN,
57
+ type: 'css/mini-extract',
58
+ chunks: 'all',
59
+ enforce: true,
60
+ };
61
+ // stylex-loader adds virtual css import (which triggers virtual-loader)
62
+ // This prevents "stylex.virtual.css" files from being tree shaken by forcing
63
+ // "sideEffects" setting.
64
+ compiler.hooks.normalModuleFactory.tap(constants_1.PLUGIN_NAME, nmf => {
65
+ nmf.hooks.createModule.tap(constants_1.PLUGIN_NAME, createData => {
66
+ const modPath = createData.matchResource ?? createData.resourceResolveData?.path;
67
+ if (modPath === constants_1.VIRTUAL_CSS_PATH) {
68
+ createData.settings ??= {};
69
+ createData.settings.sideEffects = true;
70
+ }
71
+ });
72
+ });
73
+ const { Compilation, NormalModule, sources } = compiler.webpack;
74
+ const { RawSource, ConcatSource } = sources;
75
+ compiler.hooks.make.tap(constants_1.PLUGIN_NAME, compilation => {
34
76
  // Apply loader to JS modules.
35
- NormalModule.getCompilationHooks(compilation).loader.tap(PLUGIN_NAME, (loaderContext, module) => {
36
- if (
37
- // .js, .jsx, .mjs, .cjs, .ts, .tsx, .mts, .cts
38
- /\.[mc]?[jt]sx?$/.test(path_1.default.extname(module.resource))) {
39
- // It might make sense to use .push() here instead of .unshift()
40
- // Webpack usually runs loaders in reverse order and we want to ideally run
41
- // our loader before anything else.
42
- module.loaders.unshift({
43
- loader: path_1.default.resolve(__dirname, 'loader.js'),
44
- options: { stylexPlugin: this },
77
+ NormalModule.getCompilationHooks(compilation).loader.tap(constants_1.PLUGIN_NAME, (loaderContext, mod) => {
78
+ const extname = path_1.default.extname(mod.matchResource || mod.resource);
79
+ if (constants_1.INCLUDE_REGEXP.test(extname)) {
80
+ loaderContext.StyleXWebpackContextKey = {
81
+ registerStyleXRules: (resourcePath, stylexRules) => {
82
+ this.stylexRules.set(resourcePath, stylexRules);
83
+ },
84
+ };
85
+ // We use .unshift() and not .push() like original webpack plugin
86
+ // because we want to transpile theme imports first,
87
+ // else it will be unused imports, that will be removed by tree shaking,
88
+ // and to run other transformations first, e.g. custom SWC plugins.
89
+ mod.loaders.unshift({
90
+ loader: stylexLoaderPath,
91
+ options: this.loaderOption,
92
+ ident: null,
93
+ type: null,
94
+ });
95
+ }
96
+ if (constants_1.VIRTUAL_CSS_PATTERN.test(mod.matchResource || mod.resource)) {
97
+ mod.loaders.push({
98
+ loader: stylexVirtualLoaderPath,
99
+ options: {},
45
100
  ident: null,
46
101
  type: null,
47
102
  });
48
103
  }
49
104
  });
50
- // Make a list of all modules that were included in the last compilation.
51
- // This might need to be tweaked if not all files are included after a change
52
- compilation.hooks.finishModules.tap(PLUGIN_NAME, modules => {
53
- this.filesInLastRun = [...modules.values()].map(m => m.resource);
54
- });
55
- const getStyleXRules = () => {
56
- const { stylexRules } = this;
57
- if (Object.keys(stylexRules).length === 0) {
58
- return null;
105
+ compilation.hooks.processAssets.tapPromise({
106
+ name: constants_1.PLUGIN_NAME,
107
+ stage: Compilation.PROCESS_ASSETS_STAGE_PRE_PROCESS,
108
+ }, async (assets) => {
109
+ // on previous step, we create a "stylex" chunk to hold all virtual stylex css
110
+ // the chunk contains all css chunks generated by mini-css-extract-plugin
111
+ const stylexChunk = compilation.namedChunks.get(constants_1.STYLEX_CHUNK_NAME);
112
+ if (stylexChunk == null) {
113
+ return;
59
114
  }
60
- // Take styles for the modules that were included in the last compilation.
61
- const allRules = Object.keys(stylexRules)
62
- .filter(filename => this.filesInLastRun == null ? true : this.filesInLastRun.includes(filename))
63
- .map(filename => stylexRules[filename])
64
- .flat()
65
- .filter(Boolean);
66
- return babel_plugin_1.default.processStylexRules(allRules, this.useCSSLayers);
67
- };
68
- if (this.appendTo) {
69
- compilation.hooks.processAssets.tap({
70
- name: PLUGIN_NAME,
71
- stage: Compilation.PROCESS_ASSETS_STAGE_PRE_PROCESS, // see below for more stages
72
- }, assets => {
73
- const cssFileName = Object.keys(assets).find(typeof this.appendTo === 'function'
74
- ? this.appendTo
75
- : filename => filename.endsWith(this.appendTo));
76
- if (cssFileName) {
77
- const cssAsset = assets[cssFileName];
78
- const stylexCSS = getStyleXRules();
79
- if (stylexCSS != null && cssAsset) {
80
- assets[cssFileName] = new ConcatSource(cssAsset, new RawSource(stylexCSS));
115
+ // Collect stylex rules from module instead of self maintained map
116
+ if (this.loaderOption.nextjsMode) {
117
+ const cssModulesInStylexChunk = compilation.chunkGraph.getChunkModulesIterableBySourceType(stylexChunk, 'css/mini-extract');
118
+ // we only re-collect stylex rules if we can found css in the stylex chunk
119
+ if (cssModulesInStylexChunk) {
120
+ this.stylexRules.clear();
121
+ for (const cssModule of cssModulesInStylexChunk) {
122
+ const stringifiedStylexRule = cssModule._identifier
123
+ .split('!')
124
+ .pop()
125
+ ?.split('?')
126
+ .pop();
127
+ if (!stringifiedStylexRule) {
128
+ continue;
129
+ }
130
+ const params = new URLSearchParams(stringifiedStylexRule);
131
+ const stylex = params.get('stylex');
132
+ if (stylex != null) {
133
+ this.stylexRules.set(cssModule.identifier(), JSON.parse(stylex));
134
+ }
81
135
  }
82
136
  }
83
- });
84
- }
85
- else {
86
- // We'll emit an asset ourselves. This comes with some complications in from Webpack.
87
- // If the filename contains replacement tokens, like [contenthash], we need to
88
- // process those tokens ourselves. Webpack does provide a way to reuse the configured
89
- // hashing functions. We'll take advantage of that to process tokens.
90
- const getContentHash = (source) => {
91
- const { outputOptions } = compilation;
92
- const { hashDigest, hashDigestLength, hashFunction, hashSalt } = outputOptions;
93
- if (!hashFunction) {
94
- throw new Error('webpack output options must have a hashFunction');
95
- }
96
- const hash = compiler.webpack.util.createHash(hashFunction);
97
- if (hashSalt) {
98
- hash.update(hashSalt);
99
- }
100
- hash.update(source);
101
- const fullContentHash = hash.digest(hashDigest);
102
- return fullContentHash.toString().slice(0, hashDigestLength);
103
- };
104
- // Consume collected rules and emit the stylex CSS asset
105
- compilation.hooks.processAssets.tap({
106
- name: PLUGIN_NAME,
107
- stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL,
108
- }, () => {
109
- try {
110
- const collectedCSS = getStyleXRules();
111
- if (collectedCSS && this.filename) {
112
- // build up a content hash for the rules using webpack's configured hashing functions
113
- const contentHash = getContentHash(collectedCSS);
114
- // pretend to be a chunk so we can reuse the webpack routine to process the filename and do token replacement
115
- // see https://github.com/webpack/webpack/blob/main/lib/Compilation.js#L4733
116
- // see https://github.com/webpack/webpack/blob/main/lib/TemplatedPathPlugin.js#L102
117
- const data = {
118
- filename: this.filename,
119
- contentHash: contentHash,
120
- chunk: {
121
- id: this.filename,
122
- name: path_1.default.parse(this.filename).name,
123
- hash: contentHash,
124
- },
125
- };
126
- const { path: hashedPath, info: assetsInfo } = compilation.getPathWithInfo(data.filename, data);
127
- compilation.emitAsset(hashedPath, new RawSource(collectedCSS), assetsInfo);
128
- }
129
- }
130
- catch (e) {
131
- compilation.errors.push(e);
132
- }
133
- });
134
- }
137
+ }
138
+ // Let's find the css file that belongs to the stylex chunk
139
+ const cssAssetDetails = Object.entries(assets).filter(([assetName]) => stylexChunk.files.has(assetName) && assetName.endsWith('.css'));
140
+ if (cssAssetDetails.length === 0) {
141
+ return;
142
+ }
143
+ if (cssAssetDetails.length > 1) {
144
+ console.warn('[stylex-webpack] Multiple CSS assets found for the stylex chunk. This should not happen. Please report this issue.');
145
+ }
146
+ const stylexAsset = cssAssetDetails[0];
147
+ const stylexCSS = getStyleXRules(this.stylexRules, this.useCSSLayers);
148
+ if (stylexCSS == null) {
149
+ return;
150
+ }
151
+ const finalCss = await this.transformCss(stylexCSS);
152
+ const cssAsset = stylexAsset?.[0];
153
+ if (cssAsset) {
154
+ compilation.updateAsset(cssAsset, source => new ConcatSource(source, new RawSource(finalCss)));
155
+ }
156
+ });
135
157
  });
136
158
  }
137
- // This function is not called by Webpack directly.
138
- // Instead, `NormalModule.getCompilationHooks` is used to inject a loader
139
- // for JS modules. The loader than calls this function.
140
- async transformCode(inputCode, filename, logger) {
141
- if (this.stylexImports.some(importName => inputCode.includes(importName))) {
142
- const originalSource = inputCode;
143
- let result = (0, rs_compiler_1.transform)(filename, originalSource, this.rsOptions);
144
- const metadata = result?.metadata;
145
- const code = result.code;
146
- const map = result.map;
147
- if (metadata.stylex != null && metadata.stylex.length > 0) {
148
- this.stylexRules[filename] = metadata.stylex;
149
- logger.debug(`Read stylex styles from ${filename}:`, metadata.stylex);
150
- }
151
- return { code, map };
152
- }
153
- return { code: inputCode };
154
- }
155
159
  }
156
- module.exports = StylexPlugin;
160
+ exports.default = StyleXPlugin;
161
+ module.exports = StyleXPlugin;
162
+ module.exports.default = StyleXPlugin;
163
+ module.exports.VIRTUAL_CSS_PATTERN = constants_1.VIRTUAL_CSS_PATTERN;
@@ -0,0 +1,3 @@
1
+ import type { InputCode, SourceMap, StyleXWebpackLoaderOptions, SupplementedLoaderContext } from './types';
2
+ export default function stylexLoader(this: SupplementedLoaderContext<StyleXWebpackLoaderOptions>, inputCode: InputCode, inputSourceMap: SourceMap): Promise<void>;
3
+ //# sourceMappingURL=stylex-loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stylex-loader.d.ts","sourceRoot":"","sources":["../src/stylex-loader.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,SAAS,EACT,SAAS,EACT,0BAA0B,EAC1B,yBAAyB,EAC1B,MAAM,SAAS,CAAC;AAEjB,wBAA8B,YAAY,CACxC,IAAI,EAAE,yBAAyB,CAAC,0BAA0B,CAAC,EAC3D,SAAS,EAAE,SAAS,EACpB,cAAc,EAAE,SAAS,iBA8F1B"}
@@ -0,0 +1,65 @@
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
+ exports.default = stylexLoader;
7
+ const constants_1 = require("./constants");
8
+ const loader_utils_1 = __importDefault(require("loader-utils"));
9
+ const utils_1 = require("./utils");
10
+ async function stylexLoader(inputCode, inputSourceMap) {
11
+ const callback = this.async();
12
+ const { stylexImports, rsOptions, nextjsMode, transformer } = this.getOptions();
13
+ const logger = this._compiler?.getInfrastructureLogger(constants_1.PLUGIN_NAME);
14
+ if (!inputCode) {
15
+ if (!this.resourcePath.includes('empty')) {
16
+ logger?.warn(`@stylexswc/webpack-plugin: inputCode is empty for resource ${this.resourcePath}`);
17
+ }
18
+ return callback(null, inputCode, inputSourceMap);
19
+ }
20
+ const stringifiedInputCode = typeof inputCode === 'string' ? inputCode : inputCode.toString();
21
+ // bail out early if the input doesn't contain stylex imports
22
+ if (!stylexImports?.some(importName => typeof importName === 'string'
23
+ ? stringifiedInputCode.includes(importName)
24
+ : stringifiedInputCode.includes(importName.as) ||
25
+ stringifiedInputCode.includes(importName.from))) {
26
+ return callback(null, stringifiedInputCode, inputSourceMap);
27
+ }
28
+ if (!(0, utils_1.isSupplementedLoaderContext)(this)) {
29
+ return callback(new Error('stylex-loader: loader context is not SupplementedLoaderContext!'));
30
+ }
31
+ try {
32
+ const { code, map, metadata } = (0, utils_1.generateStyleXOutput)(this.resourcePath, stringifiedInputCode, rsOptions, transformer);
33
+ // If metadata.stylex doesn't exist at all, we only need to return the transformed code
34
+ if (!metadata ||
35
+ !('stylex' in metadata) ||
36
+ metadata.stylex == null ||
37
+ !metadata.stylex.length) {
38
+ logger?.debug(`No stylex styles generated from ${this.resourcePath}`);
39
+ return callback(null, code ?? undefined, map ?? undefined);
40
+ }
41
+ logger?.debug(`Read stylex styles from ${this.resourcePath}:`, metadata.stylex);
42
+ this.StyleXWebpackContextKey.registerStyleXRules(this.resourcePath, metadata.stylex);
43
+ const serializedStyleXRules = JSON.stringify(metadata.stylex);
44
+ const urlParams = new URLSearchParams({
45
+ from: this.resourcePath,
46
+ stylex: serializedStyleXRules,
47
+ });
48
+ if (!nextjsMode) {
49
+ // Normal webpack mode
50
+ // We generate a virtual css file that looks like it is relative to the source
51
+ const virtualFileName = loader_utils_1.default.interpolateName(this, '[path][name].[hash:base64:8].stylex.virtual.css', { content: serializedStyleXRules });
52
+ const virtualCssRequest = (0, utils_1.stringifyRequest)(this, `${virtualFileName}!=!${constants_1.VIRTUAL_CSS_PATH}?${urlParams.toString()}`);
53
+ const postfix = `\nimport ${virtualCssRequest};`;
54
+ return callback(null, code + postfix, map ?? undefined);
55
+ }
56
+ // Next.js App Router doesn't support inline matchResource and inline loaders
57
+ // So we adapt Next.js' "external" css import approach instead
58
+ const virtualCssRequest = (0, utils_1.stringifyRequest)(this, `${constants_1.VIRTUAL_CSS_PATH}?${urlParams.toString()}`);
59
+ const postfix = `\nimport ${virtualCssRequest};`;
60
+ return callback(null, code + postfix, map ?? undefined);
61
+ }
62
+ catch (error) {
63
+ return callback(error);
64
+ }
65
+ }
@@ -0,0 +1,4 @@
1
+ import type webpack from 'webpack';
2
+ import { InputCode, SourceMap } from './types';
3
+ export default function (this: webpack.LoaderContext<unknown>, inputCode: InputCode, inputSourceMap?: SourceMap): void;
4
+ //# sourceMappingURL=stylex-virtual-css-loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stylex-virtual-css-loader.d.ts","sourceRoot":"","sources":["../src/stylex-virtual-css-loader.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAE/C,MAAM,CAAC,OAAO,WACZ,IAAI,EAAE,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,EACpC,SAAS,EAAE,SAAS,EACpB,cAAc,CAAC,EAAE,SAAS,QA6C3B"}
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = default_1;
4
+ const loader_utils_1 = require("loader-utils");
5
+ function default_1(inputCode, inputSourceMap) {
6
+ const callback = this.async();
7
+ const data = new URLSearchParams(this.resourceQuery.slice(1));
8
+ try {
9
+ const stylex = data.get('stylex');
10
+ if (stylex == null) {
11
+ callback(null, inputCode, inputSourceMap);
12
+ return;
13
+ }
14
+ const isProd = this._compiler?.options.mode === 'production';
15
+ // If we got stylex in the virtual css import, we need to disable the cache
16
+ // to fix HMR and Next.js navigation
17
+ this.cacheable(isProd);
18
+ if (isProd) {
19
+ // In production, we don't need to generate dummy css
20
+ return callback(null, '');
21
+ }
22
+ // @ts-expect-error - since v3 getHashDigest supports xxhash64
23
+ // https://github.com/webpack/loader-utils?tab=readme-ov-file#interpolatename
24
+ const hash = (0, loader_utils_1.getHashDigest)(Buffer.from(stylex), 'xxhash64', 'base62', 32);
25
+ const css = `
26
+ /*
27
+ * Temporary CSS placeholder - @stylexswc/webpack-plugin
28
+ * This will be replaced with actual CSS during asset injection
29
+ *
30
+ * StyleX Bundle ID: ${hash}
31
+ * Generated Rules:
32
+ * ${JSON.stringify(JSON.parse(stylex), null, 4)}
33
+ *
34
+ * Note: This content is for development reference only
35
+ */
36
+ `;
37
+ callback(null, css);
38
+ }
39
+ catch (e) {
40
+ callback(e);
41
+ }
42
+ }
@@ -0,0 +1,9 @@
1
+ /*
2
+ * Virtual placeholder file for StyleX CSS extraction
3
+ *
4
+ * Webpack requires physical files on disk for virtual sources.
5
+ * This empty file serves as the target for extracted CSS.
6
+ *
7
+ * Note: Filename "stylex.virtual.css" is intentionally chosen
8
+ * to avoid conflicts with other loaders.
9
+ */
@@ -0,0 +1,67 @@
1
+ import type { StyleXOptions } from '@stylexswc/rs-compiler';
2
+ import type { LoaderContext } from 'webpack';
3
+ import type webpack from 'webpack';
4
+ import type { RegisterStyleXRules } from '.';
5
+ type AsyncFnParams = Parameters<ReturnType<LoaderContext<unknown>['async']>>;
6
+ export type InputCode = AsyncFnParams['1'];
7
+ export type SourceMap = AsyncFnParams['2'];
8
+ export type CSSTransformer = (css: string) => string | Buffer | Promise<string | Buffer>;
9
+ export interface StyleXPluginOption extends Pick<StyleXWebpackLoaderOptions, 'transformer'> {
10
+ /**
11
+ * stylex options passed to stylex babel plugin
12
+ *
13
+ * @see https://stylexjs.com/docs/api/configuration/babel-plugin/
14
+ */
15
+ rsOptions?: Partial<StyleXOptions>;
16
+ /**
17
+ * Specify where stylex will be imported from
18
+ *
19
+ * @default ['stylex', '@stylexjs/stylex']
20
+ */
21
+ stylexImports?: StyleXOptions['importSources'];
22
+ /**
23
+ * Whether to use CSS layers
24
+ *
25
+ * @default false
26
+ */
27
+ useCSSLayers?: boolean;
28
+ /**
29
+ * Next.js Mode
30
+ *
31
+ * @default false
32
+ */
33
+ nextjsMode?: boolean;
34
+ /**
35
+ * Enable other CSS transformation
36
+ *
37
+ * Since @stylexswc/webpack-plugin only inject CSS after all loaders, you can not use postcss-loader.
38
+ * With this you can incovate `postcss()` here.
39
+ */
40
+ transformCss?: CSSTransformer;
41
+ }
42
+ export type StyleXWebpackLoaderOptions = {
43
+ stylexImports: StyleXOptions['importSources'];
44
+ rsOptions: Partial<StyleXOptions>;
45
+ nextjsMode: boolean;
46
+ /**
47
+ * Specify the transformer to transform StyleX code
48
+ *
49
+ * @default 'rs-compiler'
50
+ */
51
+ transformer?: 'rs-compiler' | 'swc';
52
+ };
53
+ export type SupplementedLoaderContext<Options = unknown> = webpack.LoaderContext<Options> & {
54
+ StyleXWebpackContextKey: {
55
+ registerStyleXRules: RegisterStyleXRules;
56
+ };
57
+ };
58
+ export type SWCPluginRule = {
59
+ class_name: string;
60
+ style: {
61
+ ltr: string;
62
+ rtl?: null | string;
63
+ };
64
+ priority: number;
65
+ };
66
+ export {};
67
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7C,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,GAAG,CAAC;AAE7C,KAAK,aAAa,GAAG,UAAU,CAAC,UAAU,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AAE7E,MAAM,MAAM,SAAS,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;AAC3C,MAAM,MAAM,SAAS,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;AAE3C,MAAM,MAAM,cAAc,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;AACzF,MAAM,WAAW,kBAAmB,SAAQ,IAAI,CAAC,0BAA0B,EAAE,aAAa,CAAC;IACzF;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;IACnC;;;;OAIG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;IAC/C;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,cAAc,CAAC;CAC/B;AACD,MAAM,MAAM,0BAA0B,GAAG;IACvC,aAAa,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;IAC9C,SAAS,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;IAClC,UAAU,EAAE,OAAO,CAAC;IAEpB;;;;OAIG;IACH,WAAW,CAAC,EAAE,aAAa,GAAG,KAAK,CAAC;CACrC,CAAC;AAEF,MAAM,MAAM,yBAAyB,CAAC,OAAO,GAAG,OAAO,IAAI,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,GAAG;IAC1F,uBAAuB,EAAE;QACvB,mBAAmB,EAAE,mBAAmB,CAAC;KAC1C,CAAC;CACH,CAAC;AAGF,MAAM,MAAM,aAAa,GAAG;IAC1B,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 ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,8 @@
1
+ import stylexPlugin from '@stylexswc/rs-compiler';
2
+ import type webpack from 'webpack';
3
+ import type { StyleXWebpackLoaderOptions, SupplementedLoaderContext } from './types';
4
+ import type { StyleXTransformResult } from '@stylexswc/rs-compiler';
5
+ export declare function stringifyRequest(loaderContext: webpack.LoaderContext<unknown>, request: string): string;
6
+ export declare const isSupplementedLoaderContext: <T>(context: webpack.LoaderContext<T>) => context is SupplementedLoaderContext<T>;
7
+ export declare function generateStyleXOutput(resourcePath: string, inputSource: string, rsOptions: Partial<stylexPlugin.StyleXOptions>, transformer: StyleXWebpackLoaderOptions['transformer']): StyleXTransformResult;
8
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,wBAAwB,CAAC;AAElD,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,EAAiB,0BAA0B,EAAE,yBAAyB,EAAE,MAAM,SAAS,CAAC;AACpG,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAEpE,wBAAgB,gBAAgB,CAAC,aAAa,EAAE,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,MAAM,UAI9F;AAED,eAAO,MAAM,2BAA2B,GAAI,CAAC,WAClC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,KAChC,OAAO,IAAI,yBAAyB,CAAC,CAAC,CAExC,CAAC;AAEF,wBAAgB,oBAAoB,CAClC,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,OAAO,CAAC,YAAY,CAAC,aAAa,CAAC,EAC9C,WAAW,EAAE,0BAA0B,CAAC,aAAa,CAAC,GACrD,qBAAqB,CA4BvB"}
package/dist/utils.js ADDED
@@ -0,0 +1,35 @@
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
+ exports.isSupplementedLoaderContext = void 0;
7
+ exports.stringifyRequest = stringifyRequest;
8
+ exports.generateStyleXOutput = generateStyleXOutput;
9
+ const rs_compiler_1 = __importDefault(require("@stylexswc/rs-compiler"));
10
+ function stringifyRequest(loaderContext, request) {
11
+ return JSON.stringify(loaderContext.utils.contextify(loaderContext.context || loaderContext.rootContext, request));
12
+ }
13
+ const isSupplementedLoaderContext = (context) => {
14
+ return Object.prototype.hasOwnProperty.call(context, 'StyleXWebpackContextKey');
15
+ };
16
+ exports.isSupplementedLoaderContext = isSupplementedLoaderContext;
17
+ function generateStyleXOutput(resourcePath, inputSource, rsOptions, transformer) {
18
+ if (transformer === 'swc') {
19
+ const metadata = { stylex: [] };
20
+ let metadataStr = '[]';
21
+ const code = inputSource.replace(/\/\/*__stylex_metadata_start__(?<metadata>.+)__stylex_metadata_end__/, (...args) => {
22
+ metadataStr = args.at(-1)?.metadata.split('"__stylex_metadata_end__')[0];
23
+ return '';
24
+ });
25
+ try {
26
+ metadata.stylex = JSON.parse(metadataStr)?.map((rule) => [rule.class_name, rule.style, rule.priority]);
27
+ }
28
+ catch (e) {
29
+ console.error('Error parsing StylexX metadata', e);
30
+ }
31
+ const map = undefined;
32
+ return { code, map, metadata };
33
+ }
34
+ return rs_compiler_1.default.transform(resourcePath, inputSource, rsOptions);
35
+ }
package/package.json CHANGED
@@ -1,22 +1,25 @@
1
1
  {
2
2
  "name": "@stylexswc/webpack-plugin",
3
3
  "description": "Stylex webpack 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
10
  "dependencies": {
11
- "@stylexjs/babel-plugin": "^0.9.3"
11
+ "@stylexjs/babel-plugin": "^0.9.3",
12
+ "loader-utils": "^3.3.1"
12
13
  },
13
14
  "devDependencies": {
14
15
  "@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",
16
+ "@stylexswc/eslint-config": "0.5.0-rc.2",
17
+ "@stylexswc/rs-compiler": "0.5.0-rc.2",
18
+ "@stylexswc/typescript-config": "0.5.0-rc.2",
18
19
  "@types/babel__core": "^7.20.5",
20
+ "@types/loader-utils": "^2.0.6",
19
21
  "@types/node": "^22.5.1",
22
+ "mini-css-extract-plugin": "^2.9.2",
20
23
  "webpack": "^5.94.0"
21
24
  },
22
25
  "files": [
package/dist/loader.d.ts DELETED
@@ -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=loader.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../src/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,QAa7F;AAED,eAAe,YAAY,CAAC"}
package/dist/loader.js DELETED
@@ -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;