stylex-webpack 0.3.0 → 0.3.1

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 ADDED
@@ -0,0 +1,228 @@
1
+ # stylex-webpack
2
+
3
+ [First introduced at React Conf 2020 by Frank Yan](https://www.youtube.com/watch?v=9JZHodNR184), [stylex](https://stylexjs.com/) is a framework agnostic CSS-in-JS system with near-zero runtime, ahead-of-time compiler, atomic CSS extraction that powers [Facebook.com](https://www.facebook.com).
4
+
5
+ `stylex-webpack` is an alternative webpack plugin and loader for stylex.
6
+
7
+ ## Motivation
8
+
9
+ stylex provides a CSS-in-JS compiler, which means you will write your CSS in your JavaScript/JSX/TSX. But unlike other CSS-in-JS solutions that collect and process styles inside the browser, stylex will read your source code, collect your style and transform your JS/JSX/TSX, stripping runtime calls as much as possible (making the value of `className` a static string literal), and output CSS elsewhere.
10
+
11
+ stylex does provide a webpack plugin. Under the hood, it will traverse through the source code, collect styles, and emit a new CSS asset during the webpack compilation. However, it has some limitations:
12
+
13
+ - stylex's official Next.js setup requires a `.babelrc` file, which disables Next.js' built-in SWC compiler.
14
+ - stylex's official Next.js plugin requires a CSS asset to pre-exist so that it can append the extracted CSS to it.
15
+
16
+ I start this project as a Proof of Concept, to see if it is possible to make a webpack plugin for ststylex that doesn't disable Next.js' SWC compiler. I have already made [a similar webpack plugin for style9](https://github.com/sukkaw/style9-webpack), which is also an AoT atomic CSS-in-JS system that is inspired by stylex.
17
+
18
+ Unlike stylex's official webpack plugin, `stylex-webpack` requires you have setup `css-loader` and `MiniCssExtractPlugin` in your webpack configuration, just like your normal CSS based webpack project. `stylex-webpack`'s built-in loader will generate a virtual CSS import containing a dummy CSS rule. This allows the `MiniCssExtractPlugin` to collect those virtual CSS imports and emit a CSS asset, which `stylex-webpack` will later inject the actual extracted CSS into.
19
+
20
+ ## Installation
21
+
22
+ ```sh
23
+ # npm
24
+ npm i style9-webpack
25
+ # Yarn
26
+ yarn add style9-webpack
27
+ # pnpm
28
+ pnpm add style9-webpack
29
+ ```
30
+
31
+ ## Usage
32
+
33
+ ### Webpack
34
+
35
+ ```js
36
+ // webpack.config.js
37
+ const { StyleXPlugin } = require('stylex-webpack');
38
+ const MiniCssExtractPlugin = require('mini-css-extract-plugin');
39
+
40
+ module.exports = {
41
+ module: {
42
+ rules: [
43
+ // Just like your normal CSS setup, a css-loader and MiniCssExtractPlugin.loader
44
+ {
45
+ test: /\.css$/i,
46
+ use: [MiniCssExtractPlugin.loader, 'css-loader']
47
+ }
48
+ ]
49
+ },
50
+ plugins: [
51
+ new StyleXPlugin({
52
+ // stylex-webpack options goes here, see the following section for more details
53
+ }),
54
+ new MiniCssExtractPlugin(),
55
+ new CssMinimizerPlugin()
56
+ // You can also use `LightningCssMinifyPlugin` from `lightningcss-loader`
57
+ // to replace CssMinimizerPlugin for faster CSS minification
58
+ // https://github.com/fz6m/lightningcss-loader
59
+ ]
60
+ };
61
+ ```
62
+
63
+ ### Next.js
64
+
65
+ ```js
66
+ // next.config.js
67
+ const { withStyleX } = require('stylex-webpack/next');
68
+
69
+ module.exports = withStyle9({
70
+ // stylex-webpack options goes here, see the following section for more details
71
+ })({
72
+ // Your Next.js config goes here.
73
+ reactStrictMode: true
74
+ });
75
+ ```
76
+
77
+ ## Options
78
+
79
+ ### webpack
80
+
81
+ ```ts
82
+ new StyleXPlugin({
83
+ // stylex-webpack options
84
+
85
+ /**
86
+ * stylex options passed to stylex babel plugin
87
+ *
88
+ * @see https://stylexjs.com/docs/api/configuration/babel-plugin/
89
+ */
90
+ stylexOption: {
91
+ dev: process.env.NODE_ENV === 'development',
92
+ test: process.env.NODE_ENV === 'test'
93
+ // Check the stylex documentation for more options
94
+ },
95
+ /* Specify where stylex will be imported from
96
+ * This overrides `importSources` in the `stylexOption` above
97
+ *
98
+ * @default ['stylex', '@stylexjs/stylex']
99
+ */
100
+ stylexImports: ['stylex', '@stylexjs/stylex'],
101
+ /**
102
+ * Whether to use CSS layers
103
+ *
104
+ * @default false
105
+ */
106
+ useCSSLayers?: boolean,
107
+ /**
108
+ * Enable other CSS transformation
109
+ *
110
+ * Since stylex-webpack's loader only emit virtual CSS imports with dummy rules,
111
+ * while the actual CSS is injected by the plugin after all loaders, you can not
112
+ * use postcss-loader + PostCSS plugins. You can manually transform the CSS here.
113
+ */
114
+ transformCss(css) {
115
+ const postcss = require('postcss');
116
+ const autoprefixer = require('autoprefixer');
117
+ /**
118
+ * It is a known issue that stylex won't sort your at-rules and media queries.
119
+ *
120
+ * https://github.com/facebook/stylex/issues/455
121
+ * https://github.com/facebook/stylex/issues/517
122
+ *
123
+ * For now, it is recommended to use postcss-sort-media-queries as a workaround.
124
+ */
125
+ const sortMediaQueries = require('postcss-sort-media-queries');
126
+
127
+ return postcss([
128
+ autoprefixer({
129
+ // autoprefixer options
130
+ }),
131
+ sortMediaQueries({
132
+ sort: 'mobile-first'
133
+ })
134
+ ]).process(css, { from: undefined }).css;
135
+
136
+ // If you don't use custom PostCSS plugins (like `postcss-sort-media-queries`
137
+ // mentioned above), only downleveling CSS syntax using autoprefixer, you can
138
+ // also use LightningCSS. It is a Rust-based CSS transformer and minifier that
139
+ // has built-in downleveling support.
140
+ const browserslist = require('browserslist');
141
+ const { transform, browserslistToTargets } = require('lightningcss');
142
+ return transform({
143
+ code: Buffer.from(css),
144
+ targets: browserslistToTargets(browserslist('>= 0.25%'))
145
+ }).code;
146
+
147
+ // If you don't need to transform CSS at all, you can just return the input as-is as well.
148
+ return css;
149
+ }
150
+ });
151
+ ```
152
+
153
+ ### Next.js
154
+
155
+ ```ts
156
+ withStyleX({
157
+ // The same options as the webpack plugin, but with a few differences
158
+ stylexOption: {
159
+ /**
160
+ * You don't have to specify `dev` here. `stylex-webpack` will automatically read
161
+ * Next.js building mode and set `dev` accordingly.
162
+ */
163
+ // dev: process.env.NODE_ENV === 'development',
164
+ },
165
+ /**
166
+ * You don't have to specify `transformCss` here. `stylex-webpack` will automatically
167
+ * read your PostCSS configuration and apply it here, just like how Next.js does.
168
+ *
169
+ * Under the hood, `withStyleX` uses Next.js built-in PostCSS config reader to
170
+ * maintain the consistency with Next.js' built-in PostCSS support.
171
+ */
172
+ // transformCss(css) {}
173
+ })
174
+ ```
175
+
176
+ It is recommended to use `postcss-sort-media-queries` as a workaround for stylex's known issue with sorting at-rules and media queries. You can configure it in your PostCSS configuration file, and `stylex-webpack` will automatically apply your PostCSS configuration to the extracted CSS just like Next.js' built-in PostCSS support.
177
+
178
+ ```js
179
+ // postcss.config.js
180
+
181
+ /** @type {Record<'plugins', import('postcss').AcceptedPlugin[]>} */
182
+ module.exports = {
183
+ plugins: [
184
+ [
185
+ require.resolve('postcss-sort-media-queries'),
186
+ {
187
+ sort: 'mobile-first' // default value
188
+ }
189
+ ],
190
+
191
+ // Next.js will disable its built-in default PostCSS configuration you
192
+ // create `postcss.config.js`, which you can add it back:
193
+
194
+ /* --- Start of Next.js built-in default PostCSS configuration --- */
195
+ require.resolve('next/dist/compiled/postcss-flexbugs-fixes'),
196
+ [
197
+ require.resolve('next/dist/compiled/postcss-preset-env'),
198
+ {
199
+ browsers: ['defaults'],
200
+ autoprefixer: {
201
+ // Disable legacy flexbox support
202
+ flexbox: 'no-2009'
203
+ },
204
+ // Enable CSS features that have shipped to the
205
+ // web platform, i.e. in 2+ browsers unflagged.
206
+ stage: 3,
207
+ features: {
208
+ 'custom-properties': false
209
+ }
210
+ }
211
+ ]
212
+ /* --- End of Next.js built-in default PostCSS configuration --- */
213
+ ]
214
+ };
215
+ ```
216
+
217
+ ## Author
218
+
219
+ **stylex-webpack** © [Sukka](https://github.com/SukkaW), Released under the [MIT](./LICENSE) License.<br>
220
+ Authored and maintained by Sukka with help from contributors ([list](https://github.com/SukkaW/stylex-webpack/graphs/contributors)).
221
+
222
+ > [Personal Website](https://skk.moe) · [Blog](https://blog.skk.moe) · GitHub [@SukkaW](https://github.com/SukkaW) · Telegram Channel [@SukkaChannel](https://t.me/SukkaChannel) · Twitter [@isukkaw](https://twitter.com/isukkaw) · Mastodon [@sukka@acg.mn](https://acg.mn/@sukka) · Keybase [@sukka](https://keybase.io/sukka)
223
+
224
+ <p align="center">
225
+ <a href="https://github.com/sponsors/SukkaW/">
226
+ <img src="https://sponsor.cdn.skk.moe/sponsors.svg"/>
227
+ </a>
228
+ </p>
package/dist/index.d.ts CHANGED
@@ -9,6 +9,11 @@ interface StyleXLoaderOptions {
9
9
 
10
10
  type CSSTransformer = (css: string) => string | Buffer | Promise<string | Buffer>;
11
11
  interface StyleXPluginOption {
12
+ /**
13
+ * stylex options passed to stylex babel plugin
14
+ *
15
+ * @see https://stylexjs.com/docs/api/configuration/babel-plugin/
16
+ */
12
17
  stylexOption?: Partial<Options>;
13
18
  /**
14
19
  * Specify where stylex will be imported from
package/dist/index.js CHANGED
@@ -7,6 +7,7 @@ const PLUGIN_NAME = 'stylex';
7
7
  const VIRTUAL_CSS_PATH = require.resolve('./stylex.virtual.css');
8
8
  const VIRTUAL_CSS_PATTERN = /stylex\.virtual\.css/;
9
9
  const STYLEX_CHUNK_NAME = '_stylex-webpack-generated';
10
+ const INCLUDE_REGEXP = /\.[cm]?[jt]sx?$/;
10
11
 
11
12
  function _define_property(obj, key, value) {
12
13
  if (key in obj) {
@@ -71,8 +72,7 @@ class StyleXPlugin {
71
72
  compiler.hooks.make.tap(PLUGIN_NAME, (compilation)=>{
72
73
  NormalModule.getCompilationHooks(compilation).loader.tap(PLUGIN_NAME, (loaderContext, mod)=>{
73
74
  const extname = path.extname(mod.matchResource || mod.resource);
74
- if (// JavaScript (and Flow) modules
75
- /\.jsx?/.test(extname) || /\.tsx?/.test(extname)) {
75
+ if (INCLUDE_REGEXP.test(extname)) {
76
76
  loaderContext.StyleXWebpackContextKey = {
77
77
  registerStyleXRules: (resourcePath, stylexRules)=>{
78
78
  this.stylexRules.set(resourcePath, stylexRules);
@@ -113,7 +113,7 @@ class StyleXPlugin {
113
113
  const cssModulesInStylexChunk = compilation.chunkGraph.getChunkModulesIterableBySourceType(stylexChunk, 'css/mini-extract');
114
114
  // we only re-collect stylex rules if we can found css in the stylex chunk
115
115
  if (cssModulesInStylexChunk) {
116
- this.stylexRules = new Map();
116
+ this.stylexRules.clear();
117
117
  for (const cssModule of cssModulesInStylexChunk){
118
118
  const stringifiedStylexRule = cssModule._identifier.split('!').pop()?.split('?').pop();
119
119
  if (!stringifiedStylexRule) {
@@ -128,7 +128,7 @@ class StyleXPlugin {
128
128
  }
129
129
  }
130
130
  // Let's find the css file that belongs to the stylex chunk
131
- const cssAssetDetails = Object.entries(assets).find(([assetName])=>stylexChunk.files.has(assetName) && assetName.includes(STYLEX_CHUNK_NAME));
131
+ const cssAssetDetails = Object.entries(assets).find(([assetName])=>stylexChunk.files.has(assetName) && assetName.endsWith('.css'));
132
132
  if (!cssAssetDetails) {
133
133
  return;
134
134
  }
package/dist/next.d.ts CHANGED
@@ -3,6 +3,11 @@ import { Options } from '@stylexjs/babel-plugin';
3
3
 
4
4
  type CSSTransformer = (css: string) => string | Buffer | Promise<string | Buffer>;
5
5
  interface StyleXPluginOption {
6
+ /**
7
+ * stylex options passed to stylex babel plugin
8
+ *
9
+ * @see https://stylexjs.com/docs/api/configuration/babel-plugin/
10
+ */
6
11
  stylexOption?: Partial<Options>;
7
12
  /**
8
13
  * Specify where stylex will be imported from
package/dist/next.js CHANGED
@@ -144,6 +144,9 @@ const withStyleX = (pluginOptions)=>(nextConfig = {})=>{
144
144
  async transformCss (css) {
145
145
  const { postcssWithPlugins } = await postcss();
146
146
  const result = await postcssWithPlugins.process(css);
147
+ if (pluginOptions?.transformCss) {
148
+ return pluginOptions.transformCss(result.css);
149
+ }
147
150
  return result.css;
148
151
  }
149
152
  }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stylex-webpack",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "The another Webpack Plugin for Facebook's StyleX",
5
5
  "homepage": "https://github.com/SukkaW/style9-webpack#readme",
6
6
  "repository": {