Last commit july 5th

This commit is contained in:
2024-07-05 13:46:23 +02:00
parent dad0d86e8c
commit b0e4dfbb76
24982 changed files with 2621219 additions and 413 deletions

View File

@@ -0,0 +1,397 @@
# Advanced Features
All the advanced features of this loader involve customising the `join` option.
Jump to the **"how to"** section -
* [How to: change precedence of source locations](#how-to-change-precedence-of-source-locations)
* [How to: fallback to a theme or other global directory](#how-to-fallback-to-a-theme-or-other-global-directory)
* [How to: fallback to some other asset file](#how-to-fallback-to-some-other-asset-file)
* [How to: perform a file-system search for an asset](#how-to-perform-a-file-system-search-for-an-asset)
## What is the "join" function?
The "join" function determines how CSS URIs are combined with one of the possible base paths the algorithm has identified.
⚠️ **IMPORTANT** - First read how the [algorithm](./how-it-works.md#algorithm) works.
The "join" function is a higher-order function created using the `options` and `loader` reference. That gives a function that accepts a single `item` and synchronously returns an absolute asset path to substitute back into the original CSS.
```javascript
(options:{}, loader:{}) =>
(item:{ uri:string, query: string, isAbsolute: boolean, bases:{} }) =>
string | null
```
Where the `bases` are absolute directory paths `{ subString, value, property, selector }` per the [algorithm](./how-it-works.md#algorithm). Note that returning `null` implies no substitution, the original relative `uri` is retained.
The job of the "join" function is to consider possible locations for the asset based on the `bases` and determine which is most appropriate. This implies some order of precedence in these locations and some file-system operation to determine if the asset there.
The default implementation is suitable for most users but can be customised per the `join` option.
A custom `join` function from scratch is possible but we've provided some [building blocks](#building-blocks) to make the task easier.
## Building blocks
There are a number of utilities (defined in [`lib/join-function/index.js`](../lib/join-function/index.js)) to help construct a custom "join" function . These are conveniently re-exported as properties of the loader.
These utilities are used to create the `defaultJoin` as follows.
```javascript
const {
createJoinFunction,
createJoinImplementation,
defaultJoinGenerator,
} = require('resolve-url-loader');
// create a join function equivalent to "defaultJoin"
const myJoinFn = createJoinFunction(
'myJoinFn',
createJoinImplementation(defaultJoinGenerator),
});
```
🤓 If you have some very specific behaviour in mind you can specify your own implementation. This gives full control but still gives you `debug` logging for free.
```javascript
createJoinFunction = (name:string, implementation: function): function
```
For each item, the implementation needs to make multiple attempts at locating the asset. It has mixed concerns of itentifying locations to search and then evaluating those locates one by one.
👉 However its recommended to instead use `createJoinImplementation` to create the `implementation` using the `generator` concept.
```javascript
createJoinImplementation = (generator: function*): function
```
The `generator` has the single concern of identifying locations to search. The work of searching these locations is done by `createJoinImplementation`. Overall this means less boilerplate code for you to write.
Don't worry, you don't need to use `function*` semantics for the `generator` unless you want to.
## Simple customisation
It is relatively simple to change the precedence of values (from the [algorithm](./how-it-works.md#algorithm)) or add further locations to search for an asset. To do this we use `createJoinImplementation` and write a custom `generator`.
See the reference or jump directly to the [examples](#how-to-change-precedence-of-source-locations).
### Reference
The `generator` identifies `[base:string,uri:string]` tuples describing locations to search for an asset. It does **not** return the final asset path.
You may lazily generate tuples as `Iterator`. Refer to this [guide on Iterators and Generators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators).
```javascript
generator = function* (item: {}, options: {}, loader: {}): Iterator<[string,string]>
```
Or it can be simpler to write a function that returns `Array` and convert it to a generator using `asGenerator`.
```javascript
generator = asGenerator( function (item: {}, options: {}, loader: {}): Array<string> )
```
```javascript
generator = asGenerator( function (item: {}, options: {}, loader: {}): Array<[string,string]> )
```
When using `asGenerator` you may return elements as either `base:string` **or** `[base:string,uri:string]` tuples.
<details>
<summary>Arguments</summary>
* `item` consist of -
* `uri: string` is the argument to the `url()` as it appears in the source file.
* `query: string` is any query or hash string starting with `?` or `#` that suffixes the `uri`
* `isAbsolute: boolean` flag indicates whether the URI is considered an absolute file or root relative path by webpack's definition. Absolute URIs are only processed if the `root` option is specified.
* `bases: {}` are a hash where the keys are the sourcemap evaluation locations in the [algorithm](./how-it-works.md#algorithm) and the values are absolute paths that the sourcemap reports. These directories might not actually exist.
* `options` consist of -
* All documented options for the loader.
* Any other values you include in the loader configuration for your own purposes.
* `loader` consists of the webpack loader API, useful items include -
* `fs: {}` the virtual file-system from Webpack.
* `resourcePath: string` the source file currently being processed.
* returns an `Iterator` with elements of `[base:string,uri:string]` either intrinsically or by using `asGenerator`.
</details>
<details>
<summary>FAQ</summary>
* **Why a tuple?**
The primary pupose of this loader is to find the correct `base` path for your `uri`. By returning a list of paths to search we can better generate `debug` logging.
That said there are cases where you might want to amend the `uri`. The solution is to make each element a tuple of `base` and `uri` representing a potential location to find the asset.
If you're interested only in the `base` path and don't intend to vary the `uri` then the `asGenerator` utility saves you having to create repetative tuples (and from using `function*` semantics).
* **Can I vary the `query` using the tuple?**
No. We don't support amending the `query` in the final value. If you would like this enhancement please open an issue.
* **What about duplicate or falsey elements?**
The `createJoinImplementation` will eliminate any invalid elements regardless of whether you use `Array` or `Iterator`. This makes it possible to `&&` elements inline with a predicate value.
If you use `Array` then `asGenerator` will also remove duplicates.
* **When should I use `function*`?**
If you need lazy generation of values then you may return `Iterator` or use `function*` semantics. Refer to [this guide on Iterators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators).
But in most cases, when the values are known apriori, simply returning `Array` has simpler semantics making `asGenerator` preferable.
* **Why is this generator so complicated?**
The join function must make multiple attempts to join a `base` and `uri` and check that the file exists using webpack `fs`.
The `generator` is focussed on identifying locations to search. It is a more scalable concept where you wish to search many places. The traditional use case for the custom "join" function is a file-system search so the `generator` was designed to make this possible.
If you prefer a less abstract approach consider a full `implementation` per the [full customisation](#full-customisation) approach.
</details>
### How to: change precedence of source locations
Source-map sampling is limited to the locations defined in the [algorithm](./how-it-works.md#algorithm). You can't change these locations but you can preference them in a different order.
This example shows the default order which you can easily amend. Absolute URIs are rare in most projects but can be handled for completeness.
**Using `asGenerator`**
```javascript
const {
createJoinFunction,
createJoinImplementation,
asGenerator,
defaultJoinGenerator,
} = require('resolve-url-loader');
// order source-map sampling location by your preferred precedence (matches defaultJoinGenerator)
const myGenerator = asGenerator(
({ isAbsolute, bases: { substring, value, property, selector} }, { root }) =>
isAbsolute ? [root] : [subString, value, property, selector]
);
const myJoinFn = createJoinFunction(
'myJoinFn',
createJoinImplementation(myGenerator),
);
```
**Notes**
* The implementation is the default behaviour, so if you want this precedence do **not** customise the `join` option.
* Absolute URIs generally use the base path given in the `root` option as shown.
* The `asGenerator` utility allows us to return simple `Array<string>` of potential base paths.
### How to: fallback to a theme or other global directory
Additional locations can be added by decorating the default generator. This is popular for adding some sort of "theme" directory containing assets.
This example appends a static theme directory as a fallback location where the asset might reside. Absolute URIs are rare in most projects but can be handled for completeness.
**Using `asGenerator`**
```javascript
const path = require('path');
const {
createJoinFunction,
createJoinImplementation,
asGenerator,
defaultJoinGenerator,
} = require('resolve-url-loader');
const myThemeDirectory = path.resolve(...);
// call default generator then append any additional paths
const myGenerator = asGenerator(
(item, ...rest) => [
...defaultJoinGenerator(item, ...rest),
item.isAbsolute ? null : myThemeDirectory,
]
);
const myJoinFn = createJoinFunction(
'myJoinFn',
createJoinImplementation(myGenerator),
);
```
**Notes**
* By spreading the result of `defaultJoinGenerator` we are first trying the default behaviour. If that is unsuccessful we then try the theme location.
* It's assumed that theming doesn't apply to absolute URIs. Since falsey elements are ignored we can easily `null` the additional theme element inline as shown.
* The `asGenerator` utility allows us to return simple `Array<string>` of potential base paths.
### How to: fallback to some other asset file
Lets imagine we don't have high quality files for all our assets and must sometimes use a lower quality format. For each item we need to try the `uri` with different file extensions. We can do this by returning tuples of `[base:string,uri:string]`.
In this example we prefer the `.svg` asset we are happy to use any available `.png` or `.jpg` instead.
**Using `asGenerator`**
```javascript
const {
createJoinFunction,
createJoinImplementation,
asGenerator,
defaultJoinGenerator,
} = require('resolve-url-loader');
// call default generator then pair different variations of uri with each base
const myGenerator = asGenerator(
(item, ...rest) => {
const defaultTuples = [...defaultJoinGenerator(item, ...rest)];
return /\.svg$/.test(item.uri)
? ['.svg', '.png', 'jpg'].flatMap((ext) =>
defaultTuples.flatMap(([base, uri]) =>
[base, uri.replace(/\.svg$/, ext)]
})
)
: defaultTuples;
}
);
const myJoinFn = createJoinFunction(
'myJoinFn',
createJoinImplementation(myGenerator),
);
```
**Using `function*`**
```javascript
const {
createJoinFunction,
createJoinImplementation,
defaultJoinGenerator,
} = require('resolve-url-loader');
// call default generator then pair different variations of uri with each base
const myGenerator = function* (item, ...rest) {
if (/\.svg$/.test(item.uri)) {
for (let ext of ['.svg', '.png', 'jpg']) {
for (let [base, uri] of defaultJoinGenerator(item, ...rest)) {
yield [base, uri.replace(/\.svg$/, ext)];
}
}
} else {
for (let value of defaultJoinGenerator(item, ...rest)) {
yield value;
}
}
}
const myJoinFn = createJoinFunction(
'myJoinFn',
createJoinImplementation(myGenerator),
);
```
**Notes**
* Existing generators such as `defaultJoinGenerator` will always return `[string,string]` tuples so we can destruture `base` and `uri` values with confidence.
* This implementation attempts all extensions for a given `base` before moving to the next `base`. Obviously we may change the nesting and instead do the oposite, attempt all bases for a single extension before moving on to the next extension
* The `asGenerator` utility allows us to return `Array<[string, string]>` but is **not** needed when we use `function*` semantics.
### How to: perform a file-system search for an asset
⚠️ **IMPORTANT** - This example is indicative only and is **not** advised.
When this loader was originally released it was very common for packages be broken to the point that a full file search was needed to locate assets referred to in CSS. While this was not performant some users really liked it. By customising the `generator` we can once again lazily search the file-system.
In this example we search the parent directories of the base paths, continuing upwards until we hit a package boundary. Absolute URIs are rare in most projects but can be handled for completeness.
**Using `function*`**
```javascript
const path = require('path');
const {
createJoinFunction,
createJoinImplementation,
webpackExistsSync
} = require('resolve-url-loader');
// search up from the initial base path until you hit a package boundary
const myGenerator = function* (
{ uri, isAbsolute, bases: { substring, value, property, selector } },
{ root, attempts = 1e3 },
{ fs },
) {
if (isAbsolute) {
yield [root, uri];
} else {
for (let base of [subString, value, property, selector]) {
for (let isDone = false, i = 0; !isDone && i < attempts; i++) {
yield [base, uri];
// unfortunately fs.existsSync() is not present so we must shim it
const maybePkg = path.normalize(path.join(base, 'package.json'));
try {
isDone = fs.statSync(maybePkg).isFile();
} catch (error) {
isDone = false;
}
base = base.split(/(\\\/)/).slice(0, -2).join('');
}
}
}
};
const myJoinFn = createJoinFunction(
'myJoinFn',
createJoinImplementation(myGenerator),
);
```
**Notes**
* This implementation is nether tested nor robust, it would need further safeguards to avoid searching the entire file system.
* By using `function*` the generator is lazy. We only walk the file-system directory tree as necessary.
* The webpack file-system is provided by the `enhanced-resolver-plugin` and does **not** contain `fs.existsSync()`. We must use `fs.statsSync()` instead and catch any error where the file isn't present.
* You may set additional `options` when you configure the loader in webpack and then access them in your `generator`. In this case we add an `attempts` option to limit the file search.
## Full customisation
The `createJoinFunction` can give you full control over how the `base` and `uri` are joined to create an absolute file path **and** the definitiion of success for that combination.
It provides additional logging when using `debug` option so is a better choice then writing a "join" function from scratch.
Limited documentation is given here since it is rare to require a full customisation. Refer to the source code for further information.
### Reference
The `implementation` synchronously returns the final asset path or some fallback value. It makes a number of attempts to search for the given item and returns an element describing each attempt.
```javascript
implementation = function (item: {}, options: {}, loader: {}):
Array<{
base : string,
uri : string,
joined : string,
isSuccess : boolean,
isFallback: boolean,
}>
```
<details>
<summary>Arguments</summary>
* `item` consist of -
* `uri: string` is the argument to the `url()` as it appears in the source file.
* `query: string` is any string starting with `?` or `#` that suffixes the `uri`
* `isAbsolute: boolean` flag indicates whether the URI is considered an absolute file or root relative path by webpack's definition. Absolute URIs are only processed if the `root` option is specified.
* `bases: {}` are a hash where the keys are the sourcemap evaluation locations in the [algorithm](./how-it-works.md#algorithm) and the values are absolute paths that the sourcemap reports. These directories might not actually exist.
* `options` consist of -
* All documented options for the loader.
* Any other values you include in the loader configuration for your own purposes.
* `loader` consists of the webpack loader API, useful items include -
* `fs: {}` the virtual file-system from Webpack.
* `resourcePath: string` the source file currently being processed.
* returns an array of attempts that were made in resolving the URI -
* `base` the base path
* `uri` the uri path
* `joined` the absolute path created from the joining the `base` and `uri` paths.
* `isSuccess` indicates the asset was found and that `joined` should be the final result
* `isFallback` indicates the asset was not found but that `joined` kis suitable as a fallback value
</details>

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -0,0 +1 @@
# Contributing

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -0,0 +1,159 @@
# How it works
## The problem
The `resolve-url-loader` is typically used where SASS source files are transpiled to CSS. CSS being a format that webpack can readily ingest. So let's look at a basic example where the structure is basically CSS but is composed using SASS features.
Working backwards, this is the final CSS we are after. Just a single rule with a single declaration.
```css
.cool {
background-image: url(cool.png);
}
```
When using SASS it's common for rules to come from different [partials](https://sass-lang.com/documentation/at-rules/import#partials), and for declarations to be composed using mixins and functions. Consider this more complicated project with imported files.
<img src="detailed-problem.svg" alt="the detailed problem" width="363px" height="651px">
All the subdirectories here contributed something to the rule, so we could reasonably place the asset in any of them. And any of these locations might be the "correct" to our way of thinking.
There could actually be a separate `cool.png` in each of the subdirectories! 🤯 In that case, which one gets used?
The answer: none. 😞 Webpack expects asset paths to be relative to the root SASS file `src/styles.scss`. So for the CSS `url(cool.png)` it will look for `src/cool.png` which is not present. 💥
All our assets are in subdirecties `src/foo/cool.png` or `src/foo/bar/cool.png` or `src/foo/bar/baz/cool.png`. We need to re-write the `url()` to point to the one we intend. But right now that's pretty ambiguous.
Worse still, Webpack doesn't know any of these nested SCSS files were part of the SASS composition. Meaing it doesn't know there _are_ nested directories in the first place. How do we rewite to something we don't know about?
**The problem:** How to identify contributing directectories and look for the asset in those directories in some well-defined priority order?
**The crux:** How to identify what contributed to the SASS compilation, internally and post factum, but from within Webpack? 😫
## The solution
Sourcemaps! 😃
Wait, don't run away! Sourcemaps might sound scary, but they solve our problem reasonably well. 👍
The SASS compiler source-map can tell us which original SCSS file contributed each character in the resulting CSS.
The SASS source-map is also something we can access from within Webpack.
### concept
Continuing with the example let's compile SASS on the command line. You can do this several different ways but I prefer [npx](https://blog.npmjs.org/post/162869356040/introducing-npx-an-npm-package-runner).
```sh
> npx node-sass src/styles.scss --output . --output-style expanded --source-map true
```
Using the experimental `sourcemap-to-string` package (also in this repository) we can visualise the SASS source on the left vs the output CSS on the right.
```
src/styles.scss
-------------------------------------------------------------------------------
src/foo/_partial.scss
-------------------------------------------------------------------------------
3:01 .cool░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 1:01 .cool░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
3:06 ░░░░░ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 1:06 ░░░░░ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░
3:07 ░░░░░░{⏎ 1:07 ░░░░░░{⏎
@include cool-background-image;⏎ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
}░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-:-- ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 3:02 ░⏎
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ⏎
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ /*# sourceMappingURL=styles.css.ma
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ p */░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
src/foo/bar/_mixins.scss
-------------------------------------------------------------------------------
4:03 ░░background-image░░░░░░░░░░░░░░░░ 2:03 ░░background-image░░░░░░░░░░░░░░░░
4:19 ░░░░░░░░░░░░░░░░░░: get-url("cool" 2:19 ░░░░░░░░░░░░░░░░░░: ░░░░░░░░░░░░░░
);⏎ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
}⏎ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
src/foo/bar/baz/_functions.scss
-------------------------------------------------------------------------------
2:11 ░░░░░░░░░░url(#░░░░░░░░░░░░░░░░░░░ 2:21 ░░░░░░░░░░░░░░░░░░░░url(cool.png)░
2:16 ░░░░░░░░░░░░░░░{$temp}.png);⏎ 2:34 ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░;
}░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ⏎
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ }░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
```
As expected, the pure CSS portions are essentially the same in the source and the output.
Meanwhile the indirect `@mixin` and `funtion` substitutes values into the output. But we can still clearly see where in the source that final value originated from.
### algorithm
Now we know the original SCSS sources we can use a CSS parser such as `postcss` to process all the declaration values that contain `url()` and rewrite any file paths we find there.
1. Enumerate all declaration values
2. Split the value into path substrings
3. Evaluate the source-map at that location, find the original source file
4. Rebase the path to that original source file.
For our example, this algorithm will always give us the asset located in the `baz` subdirectory. Clearly evaluating the source-map at just one location is not enough. Any of the directories that contributed source files to the rule-set might be considered the "correct" place to store the asset and all these files contributed different parts of the rule-set, not just the declaration value.
We stop short of evaluating the source-map for _every characer_ in the rule-set and instead we chose a small number of meaningful points.
| | label | sampling location | in the example | implies asset |
|---|-----------|------------------------------------------|---------------------------|--------------------------------------------------|
| 1 | subString | start of **argument** to the `url()` | `c` in `cool.png` | `src/foo/bar/baz/cool.png` |
| 2 | value | start of **value** in the declaration | `u` in `url(...)` | `src/foo/bar/baz/cool.png` |
| 3 | property | start of **property** in the declaration | `b` in `background-image` | `src/foo/bar/cool.png` |
| 4 | selector | start of **selector** in the rule-set | `.` in `.selector` | `src/foo/cool.png` |
These locations are tested in order. If an asset of the correct filename is found then we break and use that result.
Note it is a quirk of the example that the `value` and `subString` locations imply the same file. In a more complex example this may not be true.
If necessary the order can be customised or a custom file search (starting at each location) be implemented. Refer to the [advanced features](advanced-features.md).
### webpack
To operate on the `sass-loader` output, both **CSS** and **source-map**, we introduce `resolve-url-loader` containing the algorithm above.
The `resolve-url-loader` rewrites asset paths found in `url()` notation using the `postcss` parser.
This webpack configuration outlines some important points.
```javascript
rules: [
{
test: /\.scss$/,
use: [
{
loader: 'css-loader' // <-- assets are identified here
}, {
loader: 'resolve-url-loader' // <-- receives CSS and source-map from SASS compile
}, {
loader: 'sass-loader',
options: {
sourceMap: true, // <-- IMPORTANT!
sourceMapContents: false
}
}
],
},
...
{
test: /\.png$/, // <-- assets needs their own loader configuration
use: [ ... ]
}
]
```
Its essential to explicitly configure the `sass-loader` for `sourceMap: true`. That way we definitely get a sourcemap from upstream SASS loader all the time, not just in developement mode or where `devtool` is used.
Once the CSS reaches the `css-loader` webpack becomes aware of each of the asset files and will try to separately load and process them. You will need more Webpack configuration to make that work. Refer to the [troubleshooting docs](troubleshooting.md) before raising an issue.
### beyond...?
The implementation here is limited to the webpack loader but it's plausible the algorithm could be realised as a `postcss` plugin in isolation using the [root.input.map](https://postcss.org/api/#postcss-input) property to access the incomming source-map.
As a separate plugin it could be combined with other plugins in a single `postcss-loader` step. Processing multiple plugins together in this way without reparsing would arguably be more efficient.
However as a Webpack loader we have full access to the loader API and the virtual file-system. This means maximum compatibility with `webpack-dev-server` and the rest of the Webpack ecosystem.

View File

@@ -0,0 +1,71 @@
# Troubleshooting
Webpack is difficult to configure simply because it is so powerful. If you face a problem it is important to raise it in the right place.
Possibly whatever problem you are facing is _not_ an issue with this loader, so please work this list before raising an issue.
**Working with a framework**
1. Check to see if that framework is still using an older version with the `rework` engine. This will not support modern CSS and is the source of most problems. Usually there is an existing issue raised in that framework and there may be workarounds there.
2. Hack the framework code in your `node_modules` to diagose the root cause.
**Creating your own webpack config**
1. Do the checklist at the top of the page - do you _need_ to use this loader?
2. Read and understand the detail on [how the loader works](how-it-works.md).
3. Check the known-issues below.
4. Use the `debug` option to see where the loader is looking for your assets.
5. Temporarily remove this loader and use non-relative asset paths to check if the problem is something else.
6. Check [stack overflow](http://stackoverflow.com/search?q=resolve-url-loader) for an answer.
7. Review [previous issues](/issues?utf8=%E2%9C%93&q=is%3Aissue) that may be similar.
8. Try to recreate the problem with a minimum breaking example project.
I'm happy this loader helps so many people. Open-source is provided as-is and I'm currently **not** [dogfooding](https://en.wikipedia.org/wiki/Eating_your_own_dog_food) this loader in my own work, so please try not project your frustrations. There are some really great people who follow this project who can help.
## Known issues
### Support for `image-set()`
Right now this loader only rewrites `url()` statements.
If you need other statements processed, such as `image-set()`, then please upvote [issue #119](issues/119).
### Absolute URIs
By "absolute URIs" we more correctly mean assets with root-relative URLs or absolute file paths. These paths are **not** processed unless a `root` is specified.
However 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 windows backslash.
**💡 Protip** In **windows** if your downstream loaders are choking on windows paths using `root: ''` can help.
Also it be useful to process absolute URIs if you have a custom `join` function and want to process all the paths. Although this is perhaps better done with some separate `postcss` plugin.
### Windows line breaks
Normal windows linebreaks are `CRLF`. But sometimes libsass will output single `CR` characters.
This problem is specific to multiline declarations. Refer to the [libsass bug #2693](https://github.com/sass/libsass/issues/2693).
If you have _any_ such multiline declarations preceding `url()` statements it will fail your build.
Libsass 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`.
```
Module build failed: Error: resolve-url-loader: error processing CSS
source-map information is not available at url() declaration
```
Some users find the node-sass `linefeed` option solves the problem.
**Solutions**
* Try the node-sass [linefeed](https://github.com/sass/node-sass#linefeed--v300) option by way of `sass-loader`.
**Work arounds**
* Enable `removeCR` option [here](../README.md#options) (enabled by default on Window OS).
* Remove linebreaks in declarations in your `.scss` sources.
**Diagnosis**
1. Run a stand-alone sass build `npx node-sass index.scss output.css`.
2. Use a hex editor to check line endings `Format-Hex output.css`.
3. Expect `0DOA` (or desired) line endings. Single `0D` confirms this problem.