0.3.2 • Published 1 month ago

stylex-webpack v0.3.2

Weekly downloads
-
License
MIT
Repository
github
Last release
1 month ago

stylex-webpack

First introduced by Frank Yan at React Conf 2020, StyleX framework agnostic CSS-in-JS system with near-zero runtime, ahead-of-time compiler, atomic CSS extraction that powers Facebook and Instagram.

Motivation

stylex offers a CSS-in-JS compiler, allowing you to write CSS in your JavaScript/JSX/TSX. However, unlike other CSS-in-JS solutions that gather and process styles within 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.

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 does come with some limitations:

  • StyleX's official Next.js setup requires a .babelrc file, which disables Next.js' built-in SWC compiler.
  • StyleX's official Next.js plugin requires a CSS asset to pre-exist so that it can append the extracted CSS to it.

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, which is also an AoT atomic CSS-in-JS system that is inspired by StyleX.

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 at the processAssets stage.

Installation

# npm
npm i stylex-webpack
# Yarn
yarn add stylex-webpack
# pnpm
pnpm add stylex-webpack

Usage

Webpack

// webpack.config.js
const { StyleXPlugin } = require('stylex-webpack');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
  module: {
    rules: [
      // Just like your normal CSS setup, a css-loader and MiniCssExtractPlugin.loader
      {
        test: /\.css$/i,
        use: [MiniCssExtractPlugin.loader, 'css-loader']
      }
    ]
  },
  plugins: [
    new StyleXPlugin({
      // stylex-webpack options goes here, see the following section for more details
    }),
    new MiniCssExtractPlugin(),
    new CssMinimizerPlugin()
    // You can also use `LightningCssMinifyPlugin` from `lightningcss-loader`
    // to replace CssMinimizerPlugin for faster CSS minification
    // https://github.com/fz6m/lightningcss-loader
  ]
};

Next.js

// next.config.js
const { withStyleX } = require('stylex-webpack/next');

module.exports = withStyleX({
  // stylex-webpack options goes here, see the following section for more details
})({
  // Your Next.js config goes here.
  reactStrictMode: true
});

Options

webpack

new StyleXPlugin({
  // stylex-webpack options

  /**
   * stylex options passed to stylex babel plugin
   *
   * @see https://stylexjs.com/docs/api/configuration/babel-plugin/
   */
  stylexOption: {
    dev: process.env.NODE_ENV === 'development',
    test: process.env.NODE_ENV === 'test'
    // Check the stylex documentation for more options
  },
  /* Specify where stylex will be imported from
   * This overrides `importSources` in the `stylexOption` above
   *
   * @default ['stylex', '@stylexjs/stylex']
   */
  stylexImports: ['stylex', '@stylexjs/stylex'],
  /**
   * Whether to use CSS layers
   *
   * @default false
   */
  useCSSLayers?: boolean,
  /**
   * Enable other CSS transformation
   *
   * Since stylex-webpack's loader only emit virtual CSS imports with dummy rules,
   * while the actual CSS is injected by the plugin after all loaders, you can not
   * use postcss-loader + PostCSS plugins. You can manually transform the CSS here.
   */
  transformCss(css) {
    const postcss = require('postcss');
    const autoprefixer = require('autoprefixer');
    /**
     * It is a known issue that stylex won't sort your at-rules and media queries.
     *
     * https://github.com/facebook/stylex/issues/455
     * https://github.com/facebook/stylex/issues/517
     *
     * For now, it is recommended to use postcss-sort-media-queries as a workaround.
     */
    const sortMediaQueries = require('postcss-sort-media-queries');

    return postcss([
      autoprefixer({
        // autoprefixer options
      }),
      sortMediaQueries({
        sort: 'mobile-first'
      })
    ]).process(css, { from: undefined }).css;

    // If you don't use custom PostCSS plugins (like `postcss-sort-media-queries`
    // mentioned above), only downleveling CSS syntax using autoprefixer, you can
    // also use LightningCSS. It is a Rust-based CSS transformer and minifier that
    // has built-in downleveling support.
    const browserslist = require('browserslist');
    const { transform, browserslistToTargets } = require('lightningcss');
    return transform({
      code: Buffer.from(css),
      targets: browserslistToTargets(browserslist('>= 0.25%'))
    }).code;

    // If you don't need to transform CSS at all, you can just return the input as-is as well.
    return css;
  }
});

Next.js

withStyleX({
  // The same options as the webpack plugin, but with a few differences
  stylexOption: {
    /**
     * You don't have to specify `dev` here. `stylex-webpack` will automatically read
     * Next.js building mode and set `dev` accordingly.
     */
    // dev: process.env.NODE_ENV === 'development',
  },
  /**
   * You don't have to specify `transformCss` here. `stylex-webpack` will automatically
   * read your PostCSS configuration and apply it here, just like how Next.js does.
   *
   * Under the hood, `withStyleX` uses Next.js built-in PostCSS config reader to
   * maintain the consistency with Next.js' built-in PostCSS support.
   */
  // transformCss(css) {}
})

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.

// postcss.config.js

/** @type {Record<'plugins', import('postcss').AcceptedPlugin[]>} */
module.exports = {
  plugins: [
    [
      require.resolve('postcss-sort-media-queries'),
      {
        sort: 'mobile-first' // default value
      }
    ],

    // Next.js will disable its built-in default PostCSS configuration you
    // create `postcss.config.js`, which you can add it back:

    /* --- Start of Next.js built-in default PostCSS configuration --- */
    require.resolve('next/dist/compiled/postcss-flexbugs-fixes'),
    [
      require.resolve('next/dist/compiled/postcss-preset-env'),
      {
        browsers: ['defaults'],
        autoprefixer: {
          // Disable legacy flexbox support
          flexbox: 'no-2009'
        },
        // Enable CSS features that have shipped to the
        // web platform, i.e. in 2+ browsers unflagged.
        stage: 3,
        features: {
          'custom-properties': false
        }
      }
    ]
    /* --- End of Next.js built-in default PostCSS configuration --- */
  ]
};

Author

stylex-webpack © Sukka, Released under the MIT License. Authored and maintained by Sukka with help from contributors (list).

Personal Website · Blog · GitHub @SukkaW · Telegram Channel @SukkaChannel · Twitter @isukkaw · Mastodon @sukka@acg.mn · Keybase @sukka

0.3.2

1 month ago

0.3.1

1 month ago

0.2.1-beta.7

1 month ago

0.3.0

1 month ago

0.3.1-beta.0

1 month ago

0.2.1-beta.6

3 months ago

0.2.1-beta.2

3 months ago

0.2.1-beta.3

3 months ago

0.2.1-beta.0

3 months ago

0.2.1-beta.1

3 months ago

0.2.1-beta.4

3 months ago

0.2.1-beta.5

3 months ago

0.2.0

3 months ago

0.1.1-beta.5

3 months ago

0.1.1-beta.4

3 months ago

0.1.1-beta.3

3 months ago

0.1.1-beta.2

3 months ago

0.1.1-beta.1

3 months ago