Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,21 +57,21 @@ You may need to specify loaders for images in your configuration (recommended `f

| Name | Type | Default | Description |
| :-------------------------------: | :-----------------: | :------------------------------------------: | :--------------------------------------- |
| **[`attrs`](#attrs)** | `{Array\|String}` | `['img:src']` | Enables/Disables attributes handling |
| **[`attributes`](#attributes)** | `{Array\|String}` | `['img:src']` | Enables/Disables attributes handling |
| **[`root`](#root)** | `{String}` | `undefiend` | Allow to handle root-relative attributes |
| **[`interpolate`](#interpolate)** | `{Boolean\|String}` | `false` | Allow to use expressions in HTML syntax |
| **[`minimize`](#minimize)** | `{Boolean\|Object}` | `true` in production mode, otherwise `false` | Tell `html-loader` to minimize HTML |
| **[`esModule`](#esmodule)** | `{Boolean}` | `false` | Use ES modules syntax |

### `attrs`
### `attributes`

Type: `Array|String`
Default: `['img:src']`

You can specify which tag-attribute combination should be processed by this loader via the query parameter `attrs`.
Pass an array or a space-separated list of `<tag>:<attribute>` combinations. (Default: `attrs=img:src`)
You can specify which tag-attribute combination should be processed by this loader via the query parameter `attributes`.
Pass an array or a space-separated list of `<tag>:<attribute>` combinations. (Default: `attributes=img:src`)

If you use `<custom-elements>`, and lots of them make use of a `custom-src` attribute, you don't have to specify each combination `<tag>:<attribute>`: just specify an empty tag like `attrs=:custom-src` and it will match every element.
If you use `<custom-elements>`, and lots of them make use of a `custom-src` attribute, you don't have to specify each combination `<tag>:<attribute>`: just specify an empty tag like `attributes=:custom-src` and it will match every element.

**webpack.config.js**

Expand All @@ -83,7 +83,7 @@ module.exports = {
test: /\.html$/i,
loader: 'html-loader',
options: {
attrs: [':data-src'],
attributes: [':data-src'],
},
},
],
Expand Down Expand Up @@ -323,20 +323,20 @@ require('html-loader!./file.html');
```

```js
require('html-loader?attrs=img:data-src!./file.html');
require('html-loader?attributes=img:data-src!./file.html');

// => '<img src="image.png" data-src="data:image/png;base64,..." >'
```

```js
require('html-loader?attrs=img:src img:data-src!./file.html');
require('html-loader?attrs[]=img:src&attrs[]=img:data-src!./file.html');
require('html-loader?attributes=img:src img:data-src!./file.html');
require('html-loader?attributes[]=img:src&attributes[]=img:data-src!./file.html');

// => '<img src="http://cdn.example.com/49eba9f/a992ca.png" data-src="data:image/png;base64,..." >'
```

```js
require('html-loader?-attrs!./file.html');
require('html-loader?-attributes!./file.html');

// => '<img src="image.jpg" data-src="image2x.png" >'
```
Expand Down
5 changes: 1 addition & 4 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import validateOptions from 'schema-utils';

import { GET_URL_CODE, IDENT_REGEX, REQUIRE_REGEX } from './constants';
import {
getAttributes,
getExportsString,
getLinks,
getUniqueIdent,
Expand All @@ -30,9 +29,7 @@ export default function htmlLoader(source) {

let content = source.toString();

const attributes = getAttributes(options);
const links = getLinks(content, attributes);

const links = getLinks(content, options.attributes);
const data = new Map();

let offset = 0;
Expand Down
8 changes: 6 additions & 2 deletions src/options.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
{
"type": "object",
"properties": {
"attrs": {
"anyOf": [{ "type": "array" }, { "type": "string" }]
"attributes": {
"anyOf": [
{ "type": "boolean" },
{ "type": "array" },
{ "type": "string" }
]
},
"root": {
"type": "string"
Expand Down
48 changes: 28 additions & 20 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,42 +4,42 @@ function randomIdent() {
return `xxxHTMLLINKxxx${Math.random()}${Math.random()}xxx`;
}

export function getAttributes(options) {
if (typeof options.attrs !== 'undefined') {
if (typeof options.attrs === 'string') {
return options.attrs.split(' ');
export function getTagsAndAttributes(attributes) {
const defaultAttributes = ['img:src'];

if (typeof attributes !== 'undefined') {
if (typeof attributes === 'string') {
return attributes.split(' ');
}

if (Array.isArray(options.attrs)) {
return options.attrs;
if (Array.isArray(attributes)) {
return attributes;
}

if (options.attrs === false) {
if (attributes === false) {
return [];
}

throw new Error('Invalid value to options parameter attrs');
}

return ['img:src'];
}
if (attributes === true) {
return defaultAttributes;
}

export function getExportsString(options) {
if (options.esModule) {
return 'export default ';
throw new Error('Invalid value to options parameter attrs');
}

return 'module.exports = ';
return defaultAttributes;
}

export function getLinks(content, attributes) {
return parseAttributes(content, (tag, attr) => {
const res = attributes.find((a) => {
const tagsAndAttributes = getTagsAndAttributes(attributes);

return parseAttributes(content, (tag, attribute) => {
const res = tagsAndAttributes.find((a) => {
if (a.startsWith(':')) {
return attr === a.slice(1);
return attribute === a.slice(1);
}

return `${tag}:${attr}` === a;
return `${tag}:${attribute}` === a;
});

return Boolean(res);
Expand All @@ -56,6 +56,14 @@ export function getUniqueIdent(data) {
return ident;
}

export function getExportsString(options) {
if (options.esModule) {
return 'export default ';
}

return 'module.exports = ';
}

export function replaceLinkWithIdent(source, link, ident, offset = 0) {
return (
source.substr(0, link.start + offset) +
Expand Down
102 changes: 102 additions & 0 deletions test/attributes-option.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import loader from '../src';
import { GET_URL_CODE } from '../src/constants';

describe("'attributes' option", () => {
it('should work with a "string" notation', () => {
const result = loader.call(
{
mode: 'development',
query: '?attributes=script:src',
},
'Text <script src="script.js"><img src="image.png">'
);

expect(result).toBe(
`${GET_URL_CODE}module.exports = "Text <script src=\\"" + __url__(require("./script.js")) + "\\"><img src=\\"image.png\\">";`
);
});

it('should work with multiple a "string" notations', () => {
const result = loader.call(
{
mode: 'development',
query: '?attributes=script:src img:src',
},
'Text <script src="script.js"><img src="image.png">'
);

expect(result).toBe(
`${GET_URL_CODE}module.exports = "Text <script src=\\"" + __url__(require("./script.js")) + "\\"><img src=\\"" + __url__(require("./image.png")) + "\\">";`
);
});

it('should work with an "array" notations', () => {
const result = loader.call(
{
mode: 'development',
query: '?attributes[]=img:src',
},
'Text <script src="script.js"><img src="image.png">'
);

expect(result).toBe(
`${GET_URL_CODE}module.exports = "Text <script src=\\"script.js\\"><img src=\\"" + __url__(require("./image.png")) + "\\">";`
);
});

it('should work with multiple an "array" notations', () => {
const result = loader.call(
{
mode: 'development',
query: '?attributes[]=script:src&attributes[]=img:src',
},
'Text <script src="script.js"><img src="image.png">'
);

expect(result).toBe(
`${GET_URL_CODE}module.exports = "Text <script src=\\"" + __url__(require("./script.js")) + "\\"><img src=\\"" + __url__(require("./image.png")) + "\\">";`
);
});

it('should work with a custom attribute', () => {
const result = loader.call(
{
mode: 'development',
query: '?attributes[]=:custom-src',
},
'Text <custom-element custom-src="image1.png"><custom-img custom-src="image2.png"/></custom-element>'
);

expect(result).toBe(
`${GET_URL_CODE}module.exports = "Text <custom-element custom-src=\\"" + __url__(require("./image1.png")) + "\\"><custom-img custom-src=\\"" + __url__(require("./image2.png")) + "\\"/></custom-element>";`
);
});

it('should work with a "boolean" notation', () => {
const result = loader.call(
{
mode: 'development',
query: '?attributes=false',
},
'Text <script src="script.js"><img src="image.png">'
);

expect(result).toBe(
`${GET_URL_CODE}module.exports = "Text <script src=\\"script.js\\"><img src=\\"image.png\\">";`
);
});

it('should work with a "boolean" notation', () => {
const result = loader.call(
{
mode: 'development',
query: '?attributes=true',
},
'Text <script src="script.js"><img src="image.png">'
);

expect(result).toBe(
`${GET_URL_CODE}module.exports = "Text <script src=\\"script.js\\"><img src=\\"" + __url__(require("./image.png")) + "\\">";`
);
});
});
71 changes: 6 additions & 65 deletions test/loader.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,71 +15,6 @@ describe('loader', () => {
);
});

it('should accept attrs from query', () => {
const result = loader.call(
{
mode: 'development',
query: '?attrs=script:src',
},
'Text <script src="script.js"><img src="image.png">'
);

expect(result).toBe(
`${GET_URL_CODE}module.exports = "Text <script src=\\"" + __url__(require("./script.js")) + "\\"><img src=\\"image.png\\">";`
);
});
it('should accept attrs from query (space separated)', () => {
const result = loader.call(
{
mode: 'development',
query: '?attrs=script:src img:src',
},
'Text <script src="script.js"><img src="image.png">'
);

expect(result).toBe(
`${GET_URL_CODE}module.exports = "Text <script src=\\"" + __url__(require("./script.js")) + "\\"><img src=\\"" + __url__(require("./image.png")) + "\\">";`
);
});
it('should accept attrs from query (multiple)', () => {
const result = loader.call(
{
mode: 'development',
query: '?attrs[]=script:src&attrs[]=img:src',
},
'Text <script src="script.js"><img src="image.png">'
);

expect(result).toBe(
`${GET_URL_CODE}module.exports = "Text <script src=\\"" + __url__(require("./script.js")) + "\\"><img src=\\"" + __url__(require("./image.png")) + "\\">";`
);
});
it('should accept :attribute (empty tag) from query', () => {
const result = loader.call(
{
mode: 'development',
query: '?attrs[]=:custom-src',
},
'Text <custom-element custom-src="image1.png"><custom-img custom-src="image2.png"/></custom-element>'
);

expect(result).toBe(
`${GET_URL_CODE}module.exports = "Text <custom-element custom-src=\\"" + __url__(require("./image1.png")) + "\\"><custom-img custom-src=\\"" + __url__(require("./image2.png")) + "\\"/></custom-element>";`
);
});
it('should accept :attribute (empty tag) from query and not collide with similar attributes', () => {
const result = loader.call(
{
mode: 'development',
query: '?attrs[]=:custom-src',
},
'Text <custom-element custom-src="image1.png" custom-src-other="other.png"><custom-img custom-src="image2.png"/></custom-element>'
);

expect(result).toBe(
`${GET_URL_CODE}module.exports = "Text <custom-element custom-src=\\"" + __url__(require("./image1.png")) + "\\" custom-src-other=\\"other.png\\"><custom-img custom-src=\\"" + __url__(require("./image2.png")) + "\\"/></custom-element>";`
);
});
it('should not make bad things with templates', () => {
const result = loader.call(
{ mode: 'development' },
Expand Down Expand Up @@ -112,6 +47,7 @@ describe('loader', () => {
`${GET_URL_CODE}module.exports = "Text <img src=\\"/image.png\\">";`
);
});

it('should accept root from query', () => {
const result = loader.call(
{
Expand All @@ -125,6 +61,7 @@ describe('loader', () => {
`${GET_URL_CODE}module.exports = "Text <img src=\\"" + __url__(require("/test/image.png")) + "\\">";`
);
});

it('should ignore hash fragments in URLs', () => {
const result = loader.call(
{ mode: 'development' },
Expand All @@ -135,6 +72,7 @@ describe('loader', () => {
`${GET_URL_CODE}module.exports = "<img src=\\"" + __url__(require("./icons.svg")) + "#hash\\">";`
);
});

it("should ignore anchor with 'mailto:' in the href attribute", () => {
const result = loader.call(
{ mode: 'development' },
Expand Down Expand Up @@ -170,6 +108,7 @@ describe('loader', () => {
`${GET_URL_CODE}module.exports = "<img src=\\"" + ("Hello " + (1 + 1)) + "\\">";`
);
});

it('should not change handling of quotes when interpolation is enabled', () => {
const result = loader.call(
{
Expand All @@ -183,6 +122,7 @@ describe('loader', () => {
`${GET_URL_CODE}module.exports = "<script>{\\\"json\\\": \\\"with \\\\\\\"quotes\\\\\\\" in value\\\"}</script>";`
);
});

it('should enable interpolations when using interpolate=require flag and only require function be translate', () => {
const result = loader.call(
{
Expand All @@ -196,6 +136,7 @@ describe('loader', () => {
`${GET_URL_CODE}module.exports = "<a href=\\"\${list.href}\\"><img src=\\"" + __url__(require("./test.jpg")) + "\\" /></a>";`
);
});

it('should export as es6 default export', () => {
const result = loader.call(
{
Expand Down