UNPKG

13.3 kBMarkdownView Raw
1# Resolve URL Loader
2
3[![NPM](https://nodei.co/npm/resolve-url-loader.png)](https://www.npmjs.com/package/resolve-url-loader)
4
5A **webpack loader** that rewrites relative paths in url() statements based on the original source file.
6
7## Why?
8
9> **TL;DR** Making Sass work with a feature based project structure
10
11With webpack you can import a `.scss` file (or some other compile-to-css file) and have a loader take care of the transpilation. With **Sass** (at least) this file can include a whole tree of source files into a single output.
12
13We can imagine a virtual `.css` file at the location the original `.scss` import. Webpack expects any **assets** found in this CSS to be relative to the original imported file.
14
15For projects with a **feature based structure** this will be a problem, since you will want to **co-locate** your assets with your `.scss` partials.
16
17**Example** - webpack imports `index.scss` which includes feature `foo`.
18
19| files | content |
20|------------------------------------|------------------------|
21|src / | |
22|  index.scss | `@import features/foo` |
23|  features / | |
24|    _foo.scss | `url(bar.png)` |
25|    bar.png | |
26
27Intuatively we want the assets in partial `_foo.scss` relative to the partial, meaning `url(bar.png)`.
28
29However webpack's `css-loader` will encounter `url(bar.png)` and expect to find `src/bar.png`. This is **not** the correct location and the build will fail.
30
31Thankfully `resolve-url-loader` provides the "url rewriting" that Sass is missing. Use it _after_ the transpiler (such as [sass-loader](https://www.npmjs.com/package/sass-loader)). It makes use of the [source-map](http://www.mattzeunert.com/2016/02/14/how-do-source-maps-work.html) to find the original source file and rewrite `url()` statements.
32
33In our example it rewrites `url(bar.png)` to `url(features/bar.png)` as required.
34
35## Version 3
36
37**Features**
38
39* Use `postcss` parser by default. This is long overdue as the old `rework` parser doesn't cope with modern css.
40
41* Lots of automated tests running actual webpack builds. If you have an interesting use-case let me know.
42
43**Breaking Changes**
44* Multiple options changed or deprecated.
45* Removed file search "magic" in favour of `join` option.
46* Errors always fail and are no longer swallowed.
47* Processing absolute asset paths requires `root` option to be set.
48
49**Migrating**
50
51Initially set option `engine: 'rework'` for parity with your existing build. Once working you can remove this option **or** set `engine: 'postcss'` explicitly.
52
53Retain `keepQuery` option if you are already using it.
54
55The `root` option now has a different meaning. Previously it limited file search. Now it is the base path for absolute or root-relative URIs, consistent with `css-loader`. If you are already using it you can probably remove it.
56
57If you build on Windows platform **and** your content contains absolute asset paths, then `css-loader` could fail. The `root` option here may fix the URIs before they get to `css-loader`. Try to leave it unspecified, otherwise (windows only) set to empty string `root: ''`.
58
59## Getting started
60
61### Install
62
63via npm
64
65```bash
66npm install resolve-url-loader --save-dev
67```
68
69via yarn
70
71```bash
72yarn add resolve-url-loader --dev
73```
74
75### Configure Webpack
76
77The typical use case is `resolve-url-loader` between `sass-loader` and `css-loader`.
78
79**:warning: IMPORTANT**
80* **source-maps required** for loaders preceding `resolve-url-loader` (regardless of `devtool`).
81* Always use **full loader package name** (don't omit `-loader`) otherwise you can get errors that are hard to debug.
82
83
84``` javascript
85rules: [
86 {
87 test: /\.scss$/,
88 use: [
89 ...
90 {
91 loader: 'css-loader',
92 options: {...}
93 }, {
94 loader: 'resolve-url-loader',
95 options: {...}
96 }, {
97 loader: 'sass-loader',
98 options: {
99 sourceMap: true,
100 sourceMapContents: false
101 }
102 }
103 ]
104 },
105 ...
106]
107```
108
109Refer to `test` directory for full webpack configurations (as used in automated tests).
110
111## Options
112
113| option | type | default | | description |
114|-------------|----------------------------|-------------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
115| `engine` | `'rework'`<br/>`'postcss'` | `'postcss'` | | The css parser engine. |
116| `sourceMap` | boolean | `false` | | Generate a source-map. |
117| `keepQuery` | boolean | `false` | | Keep query-string and/or hash suffixes.<br/>e.g. `url('./MyFont.eot?#iefix')`<br/>Be aware downstream loaders may remove query-string or hash. |
118| `removeCR` | boolean | `false` | | Convert orphan CR to whitespace (postcss only).<br/>See known issues below. |
119| `debug` | boolean | `false` | | Display debug information. |
120| `silent` | boolean | `false` | | Do **not** display warnings. |
121| `root` | string | _unset_ | | Similar to the (now defunct) option in `css-loader`.<br/>This string, possibly empty, is prepended to absolute URIs.<br/>Absolute URIs are only processed if this option is set. |
122| `join` | function | _inbuilt_ | advanced | Custom join function.<br/>Use custom javascript to fix asset paths on a per-case basis.<br/>Refer to the default implementation for more information. |
123| `absolute` | boolean | `false` | useless | Forces URIs to be output as absolute file paths.<br/>This is retained for historical compatibility but is likely to be removed in the future, so let me know if you use it. |
124
125## How it works
126
127A [rework](https://github.com/reworkcss/rework) or [postcss](https://postcss.org/) process is run on incoming CSS.
128
129Each `url()` statement may imply an asset or may not. Generally only relative URIs are considered. However if `root` is specified then absolute or root-relative URIs are considered.
130
131For each URI considered, the incomming source-map is consulted to determine the original file where the `url()` was specified. This becomes the `base` argument to the `join` function, whose default implementation is something like the following pseudocode.
132
133```javascript
134join(uri, base?) =>
135 compose(path.normalize, path.join)(base || options.join, uri);
136```
137
138Note that for absolute `uri` then the `base` is absent. In the default implementation the `root` option is used instead.
139
140Full file search has been discontinued in version 3, however it is possible to specify a custom `join` function.
141
142There is the added complexity that `base` may be an iterator. However `resolve-url-loader` exports some useful functions that makes a custom `join` easier.
143
144Following `join` the URI has become an absolute path. Back-slashes are then converted to forward-slashes and the path is made relative to the initial resource being considered.
145
146Use the `debug` option to see verbose information from the `join` function.
147
148## Limitations / Known-issues
149
150### Mixins
151
152Where `url()` statements are created in a mixin the source file may then be the mixin file, and not the file calling the mixin. Obviously this is **not** the desired behaviour.
153
154Ensure this is indeed the problem as there are many ways to misconfigure webpack. Try inlining the mixin and check that everything works correctly. However ultimately you will need to work around this.
155
156### Compatiblity
157
158Tested `macOS` and `Windows` for `node 6.x`.
159
160All `webpack1`-`webpack4` with contemporaneous loaders/plugins.
161
162Refer to `test` directory for full webpack configurations (as used in automated tests).
163
164Some edge cases with `libsass` on `Windows` (see below).
165
166### Engines
167
168The `engine:postcss` is by far the more reliable option.
169
170The `engine:rework` option is retained for historical compatibility but is likely to be removed in the future, so let me know if you use it.
171
172If you need production css source-map it is best to avoid the combination `webpack4` with `engine:rework`. Tests show a systematic flaw in the outgoing source-map mappings.
173
174### Absolute URIs
175
176By "absolute URIs" we more correctly mean assets with root-relative URLs or absolute file paths.
177
178> Absolute paths are **not** processed by default
179
180These are **not** processed unless a `root` is specified.
181
182However recall that any paths that _are_ processed will have windows back-slash converted to posix forward-slash. This can be useful since some webpack loaders can choke on windows paths. By using `root: ''` then `resolve-url-loader` effectively does nothing to absolute paths except change the back-slash.
183
184It can also be useful to process absolute URIs if you have a custom `join` function and want to process all paths. However this is perhaps better done with some separate `postcss` plugin.
185
186### Windows line breaks
187
188Normal windows linebreaks are `CRLF`. But sometimes libsass will output single `CR` characters.
189
190This problem is specific to multiline declarations. Refer to the [libsass bug #2693](https://github.com/sass/libsass/issues/2693).
191
192If you have _any_ such multiline declarations preceding `url()` statements it will fail your build.
193
194Libsass doesn't consider these orphan `CR` to be newlines but `postcss` engine does. The result being an offset in source-map line-numbers which crashes `resolve-url-loader`.
195
196```
197Module build failed: Error: resolve-url-loader: CSS error
198 source-map information is not available at url() declaration
199```
200
201Some users find the node-sass `linefeed` option solves the problem.
202
203**Solutions**
204* Try the node-sass [linefeed](https://github.com/sass/node-sass#linefeed--v300) option by way of `sass-loader`.
205
206**Work arounds**
207* Enable `removeCR` option [here](#option).
208* Remove linebreaks in declarations.
209
210**Diagnosis**
2111. Run a stand-alone sass build `npx node-sass index.scss output.css`
2122. Use a hex editor to check line endings `Format-Hex output.css`
2133. Expect `0DOA` (or desired) line endings. Single `0D` confirms this problem.
214
215## Getting help
216
217Webpack is difficult to configure but extremely rewarding.
218
219> Remove this loader and make sure it is not a problem with a different loader in your config (most often the case)
220
221I am happy for you to **raise an issue** to ask a question regarding this package. However ensure you follow the check-list first.
222
223Currently I am **not** [dogfooding](https://en.wikipedia.org/wiki/Eating_your_own_dog_food) this loader in my own work. I may rely on you being able to isolate the problem in a simple example project and to help debug.
224
225I am happy this loader helps so many people. Open-source is provided as-is so please try not project your frustrations. There are some really great people who follow this project who can help.
226
227### Issues
228
229Before raising a new issue:
230
231* Remove this loader and make sure it is not a problem with a different loader in your config (most often the case).
232* Check [stack overflow](http://stackoverflow.com/search?q=resolve-url-loader) for an answer.
233* Review [previous issues](/issues?utf8=%E2%9C%93&q=is%3Aissue) that may be similar.
234* Be prepared to create a **simple open-source project** that exhibits your problem, should the solution not be immediately obvious to us.
235* (ideally) Debug some code and let me know where the problem sits.
236
237### Pull requests
238
239I am happy to take **pull requests**, however:
240
241* Ensure your change is **backwards compatible** - not all users will be using the same version of Webpack or SASS that you do.
242* Follow the **existing code style**. I know it is old but it maintains backwards compatibility.
243* Uncomon use-cases/fixes should be opt-in per a new **option**.
244* Do **not** overwrite existing variables with new values. I would prefer your change variable names elsewhere if necessary.
245* Add **comments** that describe why the code is necessary - i.e. what edge case are we solving. Otherwise we may rewrite later and break your use-case.