slightlyfaulty / stylus-native-loader

✒️ A super fast Stylus loader for Webpack that leverages the built-in power of Stylus.
MIT License
16 stars 1 forks source link
loader stylus stylus-loader webpack

Stylus Webpack

Version Node.js Version Size Downloads Maintained

stylus-native-loader

A super fast Stylus loader for Webpack that leverages the built-in power of Stylus. Pre-configured for modern development.

Unlike other Stylus loaders available that make use of Webpack's resolver for import path resolution (which comes with several limitations and increased overhead), stylus-native-loader relies on the powerful "native" @import/require capabilities of Stylus.

The result is a highly configurable, lean Stylus loader with near-baseline build speeds and unhindered @import/require functionality (with Webpack alias support) 🥳

Why use this instead of stylus-loader?

Benchmarks

Build times for the vuejs.org Stylus source code, sorted from fastest to slowest.

Min Max Average Overhead
stylus (no Webpack) 93.26ms 98.72ms 95.97ms
stylus-native-loader 101.72ms 107.23ms 104.37ms +8.76%
stylus-loader 167.18ms 201.92ms 180.69ms +88.28%

Getting started

To begin, install stylus-native-loader and stylus:

pnpm add -D stylus-native-loader stylus
# or
npm i -D stylus-native-loader stylus

Then add the loader to your webpack.config.js. For example, a minimal configuration might look like this:

module.exports = {
  module: {
    rules: [
      {
        test: /\.styl$/,
        use: ['style-loader', 'css-loader', 'stylus-native-loader'],
      },
    ],
  },
}

Configuration by example

Below is an example webpack.config.js using all stylus-native-loader options. None are required.

const path = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

module.exports = {
  // Any "original source" option excluding "eval" enables source map generation
  // @see https://webpack.js.org/configuration/devtool/
  devtool: 'source-map',

  resolve: {
    // All aliases are used for Stylus @import and @require path resolution
    // See `alias` loader option below for adding Stylus-specific aliases
    alias: {
      // A standard alias that matches the first segment of an import path
      // Note: Tilde (~) is not required, but is convention for stylesheet aliases
      // Maps @import '~styl/*' to '/path/to/src/styl/*'
      '~styl': path.join(__dirname, 'src/styl'),

      // An "exact match" alias (i.e. will only match @import 'mixins')
      // @see https://webpack.js.org/configuration/resolve/#resolvealias
      // Maps @import 'mixins' to '/path/to/src/styl/mixins'
      'mixins$': path.join(__dirname, 'src/styl/mixins'),
    },
  },

  module: {
    rules: [
      {
        test: /\.styl$/,
        use: [
          {
            // Extracts CSS to a separate file
            loader: MiniCssExtractPlugin.loader
          },
          {
            // Translates CSS into CommonJS
            loader: 'css-loader',
            options: {
              // Required for Stylus source map output
              sourceMap: true,
            }
          },
          {
            // Compiles Stylus to CSS
            loader: 'stylus-native-loader',
            options: {
              /**
               * Specify Stylus plugins to use. Plugins can be passed as
               * strings instead of importing them in your Webpack config.
               *
               * @type {string|Function|(string|Function)[]}
               * @default []
               */
              use: 'nib',

              /**
               * Add path(s) to the import lookup paths.
               *
               * @type {string|string[]}
               * @default []
               */
              include: path.join(__dirname, 'src/styl/config'),

              /**
               * Import the specified Stylus files/paths.
               *
               * @type {string|string[]}
               * @default []
               */
              import: [
                'nib',
                path.join(__dirname, 'src/styl/mixins'),
              ],

              /**
               * Define Stylus variables or functions.
               *
               * @type {Object}
               * @default {}
               */
              define: {
                '$development': process.env.NODE_ENV === 'development',
                '$hashvars': { foo: 'bar', bar: 'baz' },
              },

              /**
               * The 3rd parameter of the Stylus `define()` method.
               * Controls whether object literals are converted into
               * hashes (true) or lists/expressions (false).
               *
               * @type {boolean}
               * @default true
               */
              defineRaw: true,

              /**
               * Include regular CSS on @import.
               *
               * @type {boolean}
               * @default false
               */
              includeCSS: false,

              /**
               * Resolve relative url()'s inside imported files.
               *
               * @see https://stylus-lang.com/docs/js.html#stylusresolveroptions
               *
               * @type {boolean|Object|'nocheck'}
               * @default false
               */
              resolveUrl: 'nocheck',

              /**
               * Aliases used for @import and @require path resolution.
               * If set, webpack `resolve.alias` config is ignored.
               *
               * @type {Object|false}
               * @default `resolve.alias`
               */
              alias: {
                'mixins': path.join(__dirname, 'src/styl/mixins'),
              },

              /**
               * Non-alias imports beginning with tilde (~) are resolved
               * using `require.resolve()` to find the module's base path.
               *
               * @example @import '~nib' resolves to '/path/to/node_modules/nib'
               *
               * @type {boolean}
               * @default true
               */
              resolveTilde: true,

              /**
               * Toggle built-in Stylus/Nib vendor prefixing.
               * Disabled by default (prefer PostCSS Autoprefixer).
               *
               * @type {boolean}
               * @default false
               */
              vendors: false,

              /**
               * Toggle/configure source map generation.
               * Set according to `devtool` config value by default.
               *
               * @see https://stylus-lang.com/docs/sourcemaps.html
               *
               * @type {boolean|Object}
               * @default `devtool`|false
               */
              sourceMap: {
                // Toggle loading of source file contents into source map
                content: true,
                // All other Stylus "sourcemap" options can be set if needed
              },

              /**
               * Toggle watching directories for changes. Allows new files to
               * be detected in watch mode when using glob imports.
               *
               * @type {boolean}
               * @default true
               */
              watchDirs: true,

              /**
               * Callback that triggers right before Stylus compiles,
               * allowing access to the Stylus JS API and loader context.
               * 
               * Note: This is experimental and may prevent proper caching
               * in Webpack. Use at your own risk.
               *
               * @see https://stylus-lang.com/docs/js.html
               *
               * @type {Function}
               * @default undefined
               *
               * @param {Renderer} renderer The stylus renderer instance
               * @param {Object} context The loader context object
               * @param {Object} options The unified stylus options object
               */
              beforeCompile(renderer, context, options) {
                renderer.define('expression', { foo: 'bar', bar: 'baz' })
                renderer.define('hash', { foo: 'bar', bar: 'baz' }, true)
              },

              /**
               * Stylus has several other options/configurations that can be
               * set here and will be passed directly to the Stylus compiler.
               *
               * @see https://stylus-lang.com/docs/js.html
               * @see https://stylus-lang.com/docs/executable.html
               */
            },
          },
        ],
      },
    ],
  },
}

License

MIT