Skip to content

🎨 A stylus loader for webpack with fixed relative imports.

License

Notifications You must be signed in to change notification settings

walmartlabs/stylus-relative-loader

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

stylus-relative-loader

A stylus loader for webpack, with fixed imports.

NPM version

vs. stylus-loader

Relative imports

With stylus-loader (which inherits a lot of behavior from Stylus itself), relative path imports like ./variables and ./color, don't necessarily resolve to the path relative to the file in which the import is found. In fact, they have the same meaning as just importing variables and color. This means the paths are resolved using the Stylus "context" – in effect, you could get the file at the requested relative path, a file in some ancestor directory, a node module, or a file that happens to have the same name within another module.

For example:

styles
├── a
│   ├── color.styl
│   └── index.styl
├── b
│   ├── color.styl
│   └── index.styl
├── color.styl
└── index.styl

Let's say styles/index.styl contains:

@import './a';
@import './b';
@import './color';

...while a/index.styl and b/index.styl both contain:

@import './color';

With vanilla stylus-loader, the output would be ONLY the contents of styles/color.styl, repeated 3 times. Despite the ./color imports in a and b explicitly calling for a/color.styl and b/color.styl to be included, they won't be – merely because another relative path was imported with the same name.

If you're using very modularized styles (say, some of your imports come from node_modules) this behavior can spell big trouble. You basically have to ensure that all Stylus filenames in your dependency tree are unique, otherwise some styles/variables/mixins might go missing!

stylus-relative-loader fixes this issue by patching relative imports to all resolve as if they were full absolute paths. That means you'd get all of color.styl, a/color.styl, and b/color.styl above.

Dynamic imports

stylus-relative-loader supports dynamic imports, such as those in conditional branches or with $variable interpolation in the path. stylus-loader does not support these and sometimes emits confusing error messages as a result.

This is possible because the static import analysis step in stylus-relative-loader exists purely to increase performance, and is not a requirement for import resolution like it is in stylus-loader. Our general strategy is to invoke the Stylus renderer, and when any "unresolved" imports are encountered, we stop rendering, resolved any queued up imports, then try rendering again from the beginning. This makes stylus-relative-loader slower than stylus-loader if you have many dynamic imports, but has the benefit of correctness. Be careful if you have Stylus plugins with expensive side-effects.

New precacheImportVariables query option

If you are using the above dynamic import feature and have a lot of Stylus imports, it can balloon your build time due to the multiple render attempts that will be necessary.

If you tend to use the same variable in a lot of dynamic imports, and there are only a few potential values, you can optimize dynamic import discovery by providing the precacheImportVariables option. Before rendering, while the Stylus import tree is being discovered (by stylus-relative-loader) and resolved (by webpack), these values will be used to discover more potential imports, eliminating the need for additional render attempts if the value at render time happens to match.

Note that this option doesn't affect the output; the variables in your imports can still have any value at render time – but your build will potentially be much faster.

{
  precacheImportVariables: [
    { $tenant: "foo" },
    { $tenant: "bar" },
    { $tenant: "baz" }
  ]
}

This behavior currently only works for @import and @require syntax using variables like so:

@import $foo;
@import "~my-style-lib/styl/" + $foo;
@require "./styles/" + $foo + "/more/" + $bar;

Status of this fork

There are no doubt people depending on the behavior described above, using it as a feature, not a bug. We'd love it if this behavior were adopted upstream, as we don't intend to fully support this fork for a wide audience in the long-term.

Usage

var css = require('!raw!stylus-relative!./file.styl'); // Just the CSS
var css = require('!css!stylus-relative!./file.styl'); // CSS with processed url(...)s

See css-loader to see the effect of processed url(...)s.

Or within the webpack config:

module: {
  loaders: [{
    test: /\.styl$/,
    loader: 'css-loader!stylus-relative-loader?paths=node_modules/bootstrap-stylus/stylus/'
  }]
}

Then you can: var css = require('./file.styl');.

Use in tandem with the style-loader to add the css rules to your document:

module: {
  loaders: [
    { test: /\.styl$/, loader: 'style-loader!css-loader!stylus-relative-loader' }
  ]
}

and then require('./file.styl'); will compile and add the CSS to your page.

stylus-relative-loader can also take advantage of webpack's resolve options. With the default options it'll find files in web_modules as well as node_modules, make sure to prefix any lookup in node_modules with ~. For example if you have a styles package lookup files in it like @import '~styles/my-styles. It can also find stylus files without having the extension specified in the @import and index files in folders if webpack is configured for stylus's file extension.

module: {
  resolve: {
    extensions: ['', '.js', '.styl']
  }
}

will let you have an index.styl file in your styles package and require('styles') or @import '~styles' it. It also lets you load a stylus file from a package installed in node_modules or if you add a modulesDirectories, like modulesDirectories: ['node_modules', 'web_modules', 'bower_components'] option you could load from a folder like bower_components. To load files from a relative path leave off the ~ and @import 'relative-styles/my-styles'; it.

Be careful though not to use the extensions configuration for two types of in one folder. If a folder has a index.js and a index.styl and you @import './that-folder', it'll end up importing a javascript file into your stylus.

Stylus Plugins

You can also use stylus plugins by adding an extra stylus section to your webpack.config.js.

var stylus_plugin = require('stylus_plugin');
module: {
  loaders: [
    { test: /\.styl$/, loader: 'style-loader!css-loader!stylus-relative-loader' }
  ]
},
stylus: {
  use: [stylus_plugin()]
}

Using nib with stylus

The easiest way of enabling nib is to import it in the stylus options:

stylus: {
  use: [require('nib')()],
  import: ['~nib/lib/nib/index.styl']
}

where ~ resolves to node_modules/

Install

npm install stylus-relative-loader stylus --save-dev

Important: in order to have ability use any stylus package version, it won't be installed automatically. So it's required to add it to package.json along with stylus-relative-loader.

Testing

npm test
open http://localhost:8080/test/

Contributing

In lieu of a formal styleguide, take care to maintain the existing coding style.

Release History

  • See the Releases page for newer releases.
  • 3.0.0 - New import resolver supporting dynamic import paths.
  • 2.1.1 - Sync with stylus-loader 2.1.1:

    Support Node 6 (@yyx990803), Test in webpack 1 and 2 (@phyllisstein)

  • 2.1.0 - Initial release tracking [email protected], with fixed relative imports.
  • Find pre-fork releases of stylus-loader at https://github.com/shama/stylus-loader/releases

About

🎨 A stylus loader for webpack with fixed relative imports.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • JavaScript 96.2%
  • CSS 3.2%
  • HTML 0.6%