diff --git a/README.md b/README.md
index 5f4f5a54..1a5c79ef 100644
--- a/README.md
+++ b/README.md
@@ -114,23 +114,41 @@ module.exports = {
## Options
-| Name | Type | Default | Description |
-| :-----------------------------------------: | :-----------------: | :-------------: | :------------------------------------------ |
-| **[`url`](#url)** | `{Boolean}` | `true` | Enable/Disable `url()` handling |
-| **[`import`](#import)** | `{Boolean}` | `true` | Enable/Disable @import handling |
-| **[`modules`](#modules)** | `{Boolean\|String}` | `false` | Enable/Disable CSS Modules and setup mode |
-| **[`localIdentName`](#localidentname)** | `{String}` | `[hash:base64]` | Configure the generated ident |
-| **[`sourceMap`](#sourcemap)** | `{Boolean}` | `false` | Enable/Disable Sourcemaps |
-| **[`camelCase`](#camelcase)** | `{Boolean\|String}` | `false` | Export Classnames in CamelCase |
-| **[`importLoaders`](#importloaders)** | `{Number}` | `0` | Number of loaders applied before CSS loader |
-| **[`exportOnlyLocals`](#exportonlylocals)** | `{Boolean}` | `false` | Export only locals |
+| Name | Type | Default | Description |
+| :-----------------------------------------: | :-------------------: | :-------------: | :------------------------------------------ |
+| **[`url`](#url)** | `{Boolean\|Function}` | `true` | Enable/Disable `url()` handling |
+| **[`import`](#import)** | `{Boolean}` | `true` | Enable/Disable @import handling |
+| **[`modules`](#modules)** | `{Boolean\|String}` | `false` | Enable/Disable CSS Modules and setup mode |
+| **[`localIdentName`](#localidentname)** | `{String}` | `[hash:base64]` | Configure the generated ident |
+| **[`sourceMap`](#sourcemap)** | `{Boolean}` | `false` | Enable/Disable Sourcemaps |
+| **[`camelCase`](#camelcase)** | `{Boolean\|String}` | `false` | Export Classnames in CamelCase |
+| **[`importLoaders`](#importloaders)** | `{Number}` | `0` | Number of loaders applied before CSS loader |
+| **[`exportOnlyLocals`](#exportonlylocals)** | `{Boolean}` | `false` | Export only locals |
### `url`
-Type: `Boolean`
+Type: `Boolean|Function`
Default: `true`
-Enable/disable `url()` resolving. Absolute `urls` are not resolving by default.
+Control `url()` resolving. Absolute `urls` are not resolving by default.
+
+Examples resolutions:
+
+```
+url(image.png) => require('./image.png')
+url(./image.png) => require('./image.png')
+```
+
+To import assets from a `node_modules` path (include `resolve.modules`) and for `alias`, prefix it with a `~`:
+
+```
+url(~module/image.png) => require('module/image.png')
+url(~aliasDirectory/image.png) => require('otherDirectory/image.png')
+```
+
+#### `Boolean`
+
+Enable/disable `url()` resolving.
**webpack.config.js**
@@ -150,18 +168,27 @@ module.exports = {
};
```
-Examples resolutions:
+#### `Function`
-```
-url(image.png) => require('./image.png')
-url(./image.png) => require('./image.png')
-```
-
-To import assets from a `node_modules` path (include `resolve.modules`) and for `alias`, prefix it with a `~`:
+Allow to filter `url()`. All filtered `url()` will not be resolved.
-```
-url(~module/image.png) => require('module/image.png')
-url(~aliasDirectory/image.png) => require('otherDirectory/image.png')
+```js
+module.exports = {
+ module: {
+ rules: [
+ {
+ test: /\.css$/,
+ loader: 'css-loader',
+ options: {
+ url: (url, resourcePath) => {
+ // `url()` with `img.png` stay untouched
+ return url.includes('img.png');
+ },
+ },
+ },
+ ],
+ },
+};
```
### `import`
diff --git a/src/index.js b/src/index.js
index bb13e4b2..2413e360 100644
--- a/src/index.js
+++ b/src/index.js
@@ -26,6 +26,7 @@ import {
getImportPrefix,
placholderRegExps,
dashesCamelCase,
+ getFilter,
} from './utils';
import Warning from './Warning';
import CssSyntaxError from './CssSyntaxError';
@@ -66,9 +67,6 @@ export default function loader(content, map, meta) {
}
}
- const resolveImport = options.import !== false;
- const resolveUrl = options.url !== false;
-
const plugins = [];
if (options.modules) {
@@ -100,14 +98,16 @@ export default function loader(content, map, meta) {
);
}
- if (resolveImport) {
+ if (options.import !== false) {
plugins.push(importParser());
}
- if (resolveUrl) {
+ if (options.url !== false) {
plugins.push(
urlParser({
- filter: (value) => isUrlRequest(value),
+ filter: getFilter(options.url, this.resourcePath, (value) =>
+ isUrlRequest(value)
+ ),
})
);
}
diff --git a/src/options.json b/src/options.json
index ba1448e4..8aa4f818 100644
--- a/src/options.json
+++ b/src/options.json
@@ -2,7 +2,14 @@
"additionalProperties": false,
"properties": {
"url": {
- "type": "boolean"
+ "anyOf": [
+ {
+ "type": "boolean"
+ },
+ {
+ "instanceof": "Function"
+ }
+ ]
},
"import": {
"type": "boolean"
diff --git a/src/utils.js b/src/utils.js
index e07acd17..ab0b7a00 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -61,4 +61,24 @@ function getLocalIdent(loaderContext, localIdentName, localName, options) {
.replace(/^((-?[0-9])|--)/, '_$1');
}
-export { getImportPrefix, getLocalIdent, placholderRegExps, dashesCamelCase };
+function getFilter(filter, resourcePath, defaultFilter = null) {
+ return (content) => {
+ if (defaultFilter && !defaultFilter(content)) {
+ return false;
+ }
+
+ if (typeof filter === 'function') {
+ return !filter(content, resourcePath);
+ }
+
+ return true;
+ };
+}
+
+export {
+ getImportPrefix,
+ getLocalIdent,
+ placholderRegExps,
+ dashesCamelCase,
+ getFilter,
+};
diff --git a/test/__snapshots__/errors.test.js.snap b/test/__snapshots__/errors.test.js.snap
index 14050475..3b06b7c0 100644
--- a/test/__snapshots__/errors.test.js.snap
+++ b/test/__snapshots__/errors.test.js.snap
@@ -4,6 +4,8 @@ exports[`validation 1`] = `
"CSS Loader Invalid Options
options.url should be boolean
+options.url should pass \\"instanceof\\" keyword validation
+options.url should match some schema in anyOf
"
`;
diff --git a/test/__snapshots__/url-option.test.js.snap b/test/__snapshots__/url-option.test.js.snap
index 1de17c8d..cebb3c16 100644
--- a/test/__snapshots__/url-option.test.js.snap
+++ b/test/__snapshots__/url-option.test.js.snap
@@ -1,5 +1,274 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
+exports[`url option Function: errors 1`] = `Array []`;
+
+exports[`url option Function: module (evaluated) 1`] = `
+Array [
+ Array [
+ 2,
+ ".bar {
+ background: url(/webpack/public/path/img-from-imported.png);
+}
+",
+ "",
+ ],
+ Array [
+ 1,
+ ".class {
+ background: url('./img.png');
+}
+
+.class {
+ background: url(\\"./img.png\\");
+}
+
+.class {
+ background: url(./img.png);
+}
+
+.class {
+ background: url(\\"./img.png#hash\\");
+}
+
+.class {
+ background: url(
+ \\"./img.png\\"
+ );
+}
+
+.class {
+ background: green url( './img.png' ) xyz;
+}
+
+.class {
+ background: green url( \\"./img.png\\" ) xyz;
+}
+
+.class {
+ background: green url( ./img.png ) xyz;
+}
+
+.class {
+ background: green url(~package/img.png) url(./other-img.png) xyz;
+}
+
+.class {
+ background: green url( \\"./img img.png\\" ) xyz;
+}
+
+.class {
+ background: green url( './img img.png' ) xyz;
+}
+
+.class {
+ background: green url(/img.png) xyz;
+}
+
+.class {
+ background: green url(data:image/png;base64,AAA) url(http://example.com/image.jpg) url(//example.com/image.png) xyz;
+}
+
+.class {
+ background-image: url(\\"data:image/svg+xml;charset=utf-8,\\");
+}
+
+.class {
+ background-image: url(\\"data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2042%2026%27%20fill%3D%27%2523007aff%27%3E%3Crect%20width%3D%274%27%20height%3D%274%27%2F%3E%3Crect%20x%3D%278%27%20y%3D%271%27%20width%3D%2734%27%20height%3D%272%27%2F%3E%3Crect%20y%3D%2711%27%20width%3D%274%27%20height%3D%274%27%2F%3E%3Crect%20x%3D%278%27%20y%3D%2712%27%20width%3D%2734%27%20height%3D%272%27%2F%3E%3Crect%20y%3D%2722%27%20width%3D%274%27%20height%3D%274%27%2F%3E%3Crect%20x%3D%278%27%20y%3D%2723%27%20width%3D%2734%27%20height%3D%272%27%2F%3E%3C%2Fsvg%3E\\");
+}
+
+.class {
+ filter: url('data:image/svg+xml;charset=utf-8,#filter');
+}
+
+.class {
+ filter: url('data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D%5C%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%5C%22%3E%3Cfilter%20id%3D%5C%22filter%5C%22%3E%3CfeGaussianBlur%20in%3D%5C%22SourceAlpha%5C%22%20stdDeviation%3D%5C%220%5C%22%20%2F%3E%3CfeOffset%20dx%3D%5C%221%5C%22%20dy%3D%5C%222%5C%22%20result%3D%5C%22offsetblur%5C%22%20%2F%3E%3CfeFlood%20flood-color%3D%5C%22rgba(255%2C255%2C255%2C1)%5C%22%20%2F%3E%3CfeComposite%20in2%3D%5C%22offsetblur%5C%22%20operator%3D%5C%22in%5C%22%20%2F%3E%3CfeMerge%3E%3CfeMergeNode%20%2F%3E%3CfeMergeNode%20in%3D%5C%22SourceGraphic%5C%22%20%2F%3E%3C%2FfeMerge%3E%3C%2Ffilter%3E%3C%2Fsvg%3E%23filter');
+}
+
+.highlight {
+ filter: url(#highlight);
+}
+
+.highlight {
+ filter: url('#line-marker');
+}
+
+@font-face {
+ src: url(/webpack/public/path/font.woff) format('woff'),
+ url(/webpack/public/path/font.woff2) format('woff2'),
+ url(/webpack/public/path/font.eot) format('eot'),
+ url(/webpack/public/path/font.ttf) format('truetype'),
+ url(\\"/webpack/public/path/font with spaces.eot\\") format(\\"embedded-opentype\\"),
+ url(/webpack/public/path/font.svg#svgFontName) format('svg'),
+ url(/webpack/public/path/font.woff2) format('woff2'),
+ url(/webpack/public/path/font.eot?#iefix) format('embedded-opentype'),
+ url(\\"/webpack/public/path/font with spaces.eot?#iefix\\") format('embedded-opentype');
+}
+
+@media (min-width: 500px) {
+ body {
+ background: url(\\"./img.png\\");
+ }
+}
+
+a {
+ content: \\"do not use url(path)\\";
+}
+
+b {
+ content: 'do not \\"use\\" url(path)';
+}
+
+@keyframes anim {
+ background: green url('./img.png') xyz;
+}
+
+.a {
+ background-image: -webkit-image-set(url(/webpack/public/path/img1x.png) 1x, url(/webpack/public/path/img2x.png) 2x)
+}
+
+.a {
+ background-image: image-set(url(/webpack/public/path/img1x.png) 1x, url(/webpack/public/path/img2x.png) 2x)
+}
+
+.class {
+ background: green url() xyz;
+}
+
+.class {
+ background: green url('') xyz;
+}
+
+.class {
+ background: green url(\\"\\") xyz;
+}
+
+.class {
+ background: green url(' ') xyz;
+}
+
+.class {
+ background: green url(
+
+ ) xyz;
+}
+
+.class {
+ background: green url(https://raw.githubusercontent.com/webpack/media/master/logo/icon.png) xyz;
+}
+
+.class {
+ background: green url(//raw.githubusercontent.com/webpack/media/master/logo/icon.png) xyz;
+}
+
+.class {
+ background: url(\\"./img.png?foo\\");
+}
+
+.class {
+ background: url(\\"./img.png?foo=bar\\");
+}
+
+.class {
+ background: url(\\"./img.png?foo=bar#hash\\");
+}
+
+.class {
+ background: url(\\"./img.png?foo=bar#hash\\");
+}
+
+.class {
+ background: url(\\"./img.png?\\");
+}
+
+.class {
+ background-image: url('./img.png') url(\\"data:image/svg+xml;charset=utf-8,\\") url('./img.png');
+}
+
+.class {
+ background: ___CSS_LOADER_URL___;
+ background: ___CSS_LOADER_URL___INDEX___;
+ background: ___CSS_LOADER_URL___99999___;
+ background: ___CSS_LOADER_IMPORT___;
+ background: ___CSS_LOADER_IMPORT___INDEX___;
+ background: ___CSS_LOADER_IMPORT___99999___;
+}
+
+.pure-url {
+ background: url(/webpack/public/path/img-simple.png);
+}
+
+.not-resolved {
+ background: url('/img-simple.png');
+}
+
+.above-below {
+ background: url(/webpack/public/path/img-simple.png);
+}
+
+.tilde {
+ background: url('~package/img.png');
+}
+
+.aliases {
+ background: url('~aliasesImg/img.png') ;
+}
+",
+ "",
+ ],
+]
+`;
+
+exports[`url option Function: module 1`] = `
+"exports = module.exports = require(\\"../../../src/runtime/api.js\\")(false);
+// Imports
+exports.i(require(\\"-!../../../src/index.js??ref--4-0!./imported.css\\"), \\"\\");
+var urlEscape = require(\\"../../../src/runtime/url-escape.js\\");
+var ___CSS_LOADER_URL___0___ = urlEscape(require(\\"./font.woff\\"));
+var ___CSS_LOADER_URL___1___ = urlEscape(require(\\"./font.woff2\\"));
+var ___CSS_LOADER_URL___2___ = urlEscape(require(\\"./font.eot\\"));
+var ___CSS_LOADER_URL___3___ = urlEscape(require(\\"package/font.ttf\\"));
+var ___CSS_LOADER_URL___4___ = urlEscape(require(\\"./font with spaces.eot\\"));
+var ___CSS_LOADER_URL___5___ = urlEscape(require(\\"./font.svg\\") + \\"#svgFontName\\");
+var ___CSS_LOADER_URL___6___ = urlEscape(require(\\"./font.woff2?foo=bar\\"));
+var ___CSS_LOADER_URL___7___ = urlEscape(require(\\"./font.eot\\") + \\"?#iefix\\");
+var ___CSS_LOADER_URL___8___ = urlEscape(require(\\"./font with spaces.eot\\") + \\"?#iefix\\");
+var ___CSS_LOADER_URL___9___ = urlEscape(require(\\"./img1x.png\\"));
+var ___CSS_LOADER_URL___10___ = urlEscape(require(\\"./img2x.png\\"));
+var ___CSS_LOADER_URL___11___ = urlEscape(require(\\"./img-simple.png\\"));
+var ___CSS_LOADER_URL___12___ = urlEscape(require(\\"../url/img-simple.png\\"));
+
+// Module
+exports.push([module.id, \\".class {\\\\n background: url('./img.png');\\\\n}\\\\n\\\\n.class {\\\\n background: url(\\\\\\"./img.png\\\\\\");\\\\n}\\\\n\\\\n.class {\\\\n background: url(./img.png);\\\\n}\\\\n\\\\n.class {\\\\n background: url(\\\\\\"./img.png#hash\\\\\\");\\\\n}\\\\n\\\\n.class {\\\\n background: url(\\\\n \\\\\\"./img.png\\\\\\"\\\\n );\\\\n}\\\\n\\\\n.class {\\\\n background: green url( './img.png' ) xyz;\\\\n}\\\\n\\\\n.class {\\\\n background: green url( \\\\\\"./img.png\\\\\\" ) xyz;\\\\n}\\\\n\\\\n.class {\\\\n background: green url( ./img.png ) xyz;\\\\n}\\\\n\\\\n.class {\\\\n background: green url(~package/img.png) url(./other-img.png) xyz;\\\\n}\\\\n\\\\n.class {\\\\n background: green url( \\\\\\"./img img.png\\\\\\" ) xyz;\\\\n}\\\\n\\\\n.class {\\\\n background: green url( './img img.png' ) xyz;\\\\n}\\\\n\\\\n.class {\\\\n background: green url(/img.png) xyz;\\\\n}\\\\n\\\\n.class {\\\\n background: green url(data:image/png;base64,AAA) url(http://example.com/image.jpg) url(//example.com/image.png) xyz;\\\\n}\\\\n\\\\n.class {\\\\n background-image: url(\\\\\\"data:image/svg+xml;charset=utf-8,\\\\\\");\\\\n}\\\\n\\\\n.class {\\\\n background-image: url(\\\\\\"data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2042%2026%27%20fill%3D%27%2523007aff%27%3E%3Crect%20width%3D%274%27%20height%3D%274%27%2F%3E%3Crect%20x%3D%278%27%20y%3D%271%27%20width%3D%2734%27%20height%3D%272%27%2F%3E%3Crect%20y%3D%2711%27%20width%3D%274%27%20height%3D%274%27%2F%3E%3Crect%20x%3D%278%27%20y%3D%2712%27%20width%3D%2734%27%20height%3D%272%27%2F%3E%3Crect%20y%3D%2722%27%20width%3D%274%27%20height%3D%274%27%2F%3E%3Crect%20x%3D%278%27%20y%3D%2723%27%20width%3D%2734%27%20height%3D%272%27%2F%3E%3C%2Fsvg%3E\\\\\\");\\\\n}\\\\n\\\\n.class {\\\\n filter: url('data:image/svg+xml;charset=utf-8,#filter');\\\\n}\\\\n\\\\n.class {\\\\n filter: url('data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D%5C%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%5C%22%3E%3Cfilter%20id%3D%5C%22filter%5C%22%3E%3CfeGaussianBlur%20in%3D%5C%22SourceAlpha%5C%22%20stdDeviation%3D%5C%220%5C%22%20%2F%3E%3CfeOffset%20dx%3D%5C%221%5C%22%20dy%3D%5C%222%5C%22%20result%3D%5C%22offsetblur%5C%22%20%2F%3E%3CfeFlood%20flood-color%3D%5C%22rgba(255%2C255%2C255%2C1)%5C%22%20%2F%3E%3CfeComposite%20in2%3D%5C%22offsetblur%5C%22%20operator%3D%5C%22in%5C%22%20%2F%3E%3CfeMerge%3E%3CfeMergeNode%20%2F%3E%3CfeMergeNode%20in%3D%5C%22SourceGraphic%5C%22%20%2F%3E%3C%2FfeMerge%3E%3C%2Ffilter%3E%3C%2Fsvg%3E%23filter');\\\\n}\\\\n\\\\n.highlight {\\\\n filter: url(#highlight);\\\\n}\\\\n\\\\n.highlight {\\\\n filter: url('#line-marker');\\\\n}\\\\n\\\\n@font-face {\\\\n src: url(\\" + ___CSS_LOADER_URL___0___ + \\") format('woff'),\\\\n url(\\" + ___CSS_LOADER_URL___1___ + \\") format('woff2'),\\\\n url(\\" + ___CSS_LOADER_URL___2___ + \\") format('eot'),\\\\n url(\\" + ___CSS_LOADER_URL___3___ + \\") format('truetype'),\\\\n url(\\" + ___CSS_LOADER_URL___4___ + \\") format(\\\\\\"embedded-opentype\\\\\\"),\\\\n url(\\" + ___CSS_LOADER_URL___5___ + \\") format('svg'),\\\\n url(\\" + ___CSS_LOADER_URL___6___ + \\") format('woff2'),\\\\n url(\\" + ___CSS_LOADER_URL___7___ + \\") format('embedded-opentype'),\\\\n url(\\" + ___CSS_LOADER_URL___8___ + \\") format('embedded-opentype');\\\\n}\\\\n\\\\n@media (min-width: 500px) {\\\\n body {\\\\n background: url(\\\\\\"./img.png\\\\\\");\\\\n }\\\\n}\\\\n\\\\na {\\\\n content: \\\\\\"do not use url(path)\\\\\\";\\\\n}\\\\n\\\\nb {\\\\n content: 'do not \\\\\\"use\\\\\\" url(path)';\\\\n}\\\\n\\\\n@keyframes anim {\\\\n background: green url('./img.png') xyz;\\\\n}\\\\n\\\\n.a {\\\\n background-image: -webkit-image-set(url(\\" + ___CSS_LOADER_URL___9___ + \\") 1x, url(\\" + ___CSS_LOADER_URL___10___ + \\") 2x)\\\\n}\\\\n\\\\n.a {\\\\n background-image: image-set(url(\\" + ___CSS_LOADER_URL___9___ + \\") 1x, url(\\" + ___CSS_LOADER_URL___10___ + \\") 2x)\\\\n}\\\\n\\\\n.class {\\\\n background: green url() xyz;\\\\n}\\\\n\\\\n.class {\\\\n background: green url('') xyz;\\\\n}\\\\n\\\\n.class {\\\\n background: green url(\\\\\\"\\\\\\") xyz;\\\\n}\\\\n\\\\n.class {\\\\n background: green url(' ') xyz;\\\\n}\\\\n\\\\n.class {\\\\n background: green url(\\\\n \\\\n ) xyz;\\\\n}\\\\n\\\\n.class {\\\\n background: green url(https://raw.githubusercontent.com/webpack/media/master/logo/icon.png) xyz;\\\\n}\\\\n\\\\n.class {\\\\n background: green url(//raw.githubusercontent.com/webpack/media/master/logo/icon.png) xyz;\\\\n}\\\\n\\\\n.class {\\\\n background: url(\\\\\\"./img.png?foo\\\\\\");\\\\n}\\\\n\\\\n.class {\\\\n background: url(\\\\\\"./img.png?foo=bar\\\\\\");\\\\n}\\\\n\\\\n.class {\\\\n background: url(\\\\\\"./img.png?foo=bar#hash\\\\\\");\\\\n}\\\\n\\\\n.class {\\\\n background: url(\\\\\\"./img.png?foo=bar#hash\\\\\\");\\\\n}\\\\n\\\\n.class {\\\\n background: url(\\\\\\"./img.png?\\\\\\");\\\\n}\\\\n\\\\n.class {\\\\n background-image: url('./img.png') url(\\\\\\"data:image/svg+xml;charset=utf-8,\\\\\\") url('./img.png');\\\\n}\\\\n\\\\n.class {\\\\n background: ___CSS_LOADER_URL___;\\\\n background: ___CSS_LOADER_URL___INDEX___;\\\\n background: ___CSS_LOADER_URL___99999___;\\\\n background: ___CSS_LOADER_IMPORT___;\\\\n background: ___CSS_LOADER_IMPORT___INDEX___;\\\\n background: ___CSS_LOADER_IMPORT___99999___;\\\\n}\\\\n\\\\n.pure-url {\\\\n background: url(\\" + ___CSS_LOADER_URL___11___ + \\");\\\\n}\\\\n\\\\n.not-resolved {\\\\n background: url('/img-simple.png');\\\\n}\\\\n\\\\n.above-below {\\\\n background: url(\\" + ___CSS_LOADER_URL___12___ + \\");\\\\n}\\\\n\\\\n.tilde {\\\\n background: url('~package/img.png');\\\\n}\\\\n\\\\n.aliases {\\\\n background: url('~aliasesImg/img.png') ;\\\\n}\\\\n\\", \\"\\"]);
+
+"
+`;
+
+exports[`url option Function: warnings 1`] = `
+Array [
+ "ModuleWarning: Module Warning (from \`replaced original path\`):
+Warning
+
+(120:3) Unable to find uri in 'background: green url() xyz'",
+ "ModuleWarning: Module Warning (from \`replaced original path\`):
+Warning
+
+(124:3) Unable to find uri in 'background: green url('') xyz'",
+ "ModuleWarning: Module Warning (from \`replaced original path\`):
+Warning
+
+(128:3) Unable to find uri in 'background: green url(\\"\\") xyz'",
+ "ModuleWarning: Module Warning (from \`replaced original path\`):
+Warning
+
+(132:3) Unable to find uri in 'background: green url(' ') xyz'",
+ "ModuleWarning: Module Warning (from \`replaced original path\`):
+Warning
+
+(136:3) Unable to find uri in 'background: green url(
+ ) xyz'",
+]
+`;
+
exports[`url option false: errors 1`] = `Array []`;
exports[`url option false: module (evaluated) 1`] = `
diff --git a/test/errors.test.js b/test/errors.test.js
index 09b602f5..4a6d4a21 100644
--- a/test/errors.test.js
+++ b/test/errors.test.js
@@ -22,6 +22,7 @@ it('validation', () => {
expect(() => validate({ url: true })).not.toThrow();
expect(() => validate({ url: false })).not.toThrow();
+ expect(() => validate({ url: () => {} })).not.toThrow();
expect(() => validate({ url: 'true' })).toThrowErrorMatchingSnapshot();
expect(() => validate({ import: true })).not.toThrow();
diff --git a/test/url-option.test.js b/test/url-option.test.js
index 0a556763..6893d35a 100644
--- a/test/url-option.test.js
+++ b/test/url-option.test.js
@@ -54,4 +54,31 @@ describe('url option', () => {
);
});
});
+
+ it('Function', async () => {
+ const config = {
+ loader: {
+ options: {
+ url: (url, resourcePath) => {
+ expect(typeof resourcePath === 'string').toBe(true);
+
+ return url.includes('img.png');
+ },
+ },
+ },
+ };
+ const testId = './url/url.css';
+ const stats = await webpack(testId, config);
+ const { modules } = stats.toJson();
+ const module = modules.find((m) => m.id === testId);
+
+ expect(module.source).toMatchSnapshot('module');
+ expect(evaluated(module.source, modules)).toMatchSnapshot(
+ 'module (evaluated)'
+ );
+ expect(normalizeErrors(stats.compilation.warnings)).toMatchSnapshot(
+ 'warnings'
+ );
+ expect(normalizeErrors(stats.compilation.errors)).toMatchSnapshot('errors');
+ });
});