Skip to content

Commit db40c7e

Browse files
feat(import): add <import> filter support (options.import)
1 parent 00e5686 commit db40c7e

File tree

8 files changed

+306
-25
lines changed

8 files changed

+306
-25
lines changed

README.md

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,52 @@ If your application includes many HTML Components or certain HTML Components are
9393
</div>
9494
```
9595

96+
#### `{Boolean}`
97+
98+
**webpack.config.js**
99+
```js
100+
{
101+
loader: 'html-loader',
102+
options: {
103+
import: false
104+
}
105+
}
106+
```
107+
108+
#### `{String}`
109+
110+
**webpack.config.js**
111+
```js
112+
{
113+
loader: 'html-loader',
114+
options: {
115+
import: 'filter'
116+
}
117+
}
118+
```
119+
120+
#### `{RegExp}`
121+
96122
**webpack.config.js**
97123
```js
98124
{
99125
loader: 'html-loader',
100126
options: {
101-
import: // TODO add URL filter method (#158)
127+
import: /filter/
128+
}
129+
}
130+
```
131+
132+
#### `{Function}`
133+
134+
**webpack.config.js**
135+
```js
136+
{
137+
loader: 'html-loader',
138+
options: {
139+
import (url) {
140+
return /filter/.test(url)
141+
}
102142
}
103143
}
104144
```

src/index.js

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import schema from './options.json';
1111
import LoaderError from './lib/Error';
1212

1313
// Loader Defaults
14-
const defaults = {
14+
const DEFAULTS = {
1515
url: true,
1616
import: true,
1717
minimize: false,
@@ -20,7 +20,11 @@ const defaults = {
2020

2121
export default function loader(html, map, meta) {
2222
// Loader Options
23-
const options = Object.assign(defaults, getOptions(this));
23+
const options = Object.assign(
24+
{},
25+
DEFAULTS,
26+
getOptions(this)
27+
);
2428

2529
validateOptions(schema, options, 'HTML Loader');
2630
// Make the loader async
@@ -30,14 +34,21 @@ export default function loader(html, map, meta) {
3034
// HACK add Module.type
3135
this._module.type = 'text/html';
3236

33-
const template = options.template
37+
options.template = options.template
3438
? typeof options.template === 'string' ? options.template : '_'
3539
: false;
3640

3741
const plugins = [];
38-
39-
if (options.url) plugins.push(urls());
40-
if (options.import) plugins.push(imports({ template }));
42+
43+
// HTML URL Plugin
44+
if (options.url) {
45+
plugins.push(urls());
46+
}
47+
48+
// HTML IMPORT Plugin
49+
if (options.import) {
50+
plugins.push(imports(options));
51+
}
4152
// TODO(michael-ciniawsky)
4253
// <imports src=""./file.html"> aren't minified (#160)
4354
if (options.minimize) plugins.push(minifier());
@@ -55,7 +66,7 @@ export default function loader(html, map, meta) {
5566
let imports = messages[1];
5667

5768
// TODO(michael-ciniawsky) revisit
58-
// Ensure to cleanup/reset messages
69+
// HACK Ensure to cleanup/reset messages
5970
// during recursive resolving of imports
6071
messages.length = 0;
6172

@@ -66,6 +77,7 @@ export default function loader(html, map, meta) {
6677
.map((url) => `import ${url} from '${urls[url]}';`)
6778
.join('\n');
6879
}
80+
6981
// <import src="./file.html">
7082
// => import HTML__IMPORT__${idx} from './file.html';
7183
if (imports) {
@@ -75,7 +87,7 @@ export default function loader(html, map, meta) {
7587
}
7688

7789
html = options.template
78-
? `function (${template}) { return \`${html}\`; }`
90+
? `function (${options.template}) { return \`${html}\`; }`
7991
: `\`${html}\``;
8092

8193
const result = [

src/lib/plugins/import.js

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,56 @@
11
/* eslint-disable */
2-
// External URL (Protocol URL)
3-
const TEST_URL = /^\w+:\/\//;
2+
// External URL (Protocol)
3+
const URL = /^\w+:\/\//;
4+
const TAGS = [
5+
{ tag: 'import' },
6+
{ tag: 'include' }
7+
]
48

5-
// TODO(michael-ciniawsky)
6-
// add filter method for urls (e.g `options.import`) (#158)
79
const filter = (url, options) => {
8-
return TEST_URL.test(url) || url.startsWith('//');
10+
if (URL.test(url)) {
11+
return true;
12+
}
13+
14+
if (url.startsWith('//')) {
15+
return true;
16+
}
17+
18+
if (typeof options.import === 'string') {
19+
return url.includes(options.import);
20+
}
21+
22+
if (options.import instanceof RegExp) {
23+
return options.import.test(url);
24+
}
25+
26+
if (typeof options.import === 'function') {
27+
return options.import(url);
28+
}
29+
30+
return false;
931
};
1032

1133
export default function(options = {}) {
1234
return function(tree) {
1335
let idx = 0;
36+
37+
// HTML Imports
1438
const imports = {};
1539

16-
tree.match([{ tag: 'import' }, { tag: 'include' }], (node) => {
40+
tree.match(TAGS, (node) => {
1741
if (node.attrs && node.attrs.src) {
1842
// Remove <import>/<include> tag
1943
node.tag = false;
2044

21-
// TODO(michael-ciniawky)
22-
// add warning about invalid use of external urls within <import> (#?)
45+
// Ignore external && filtered URLs
46+
if (filter(node.attrs.src, options)) {
47+
return;
48+
}
2349

24-
// Ignore external && filtered urls
25-
if (filter(node.attrs.src, options)) return;
26-
// Add url to messages.imports
50+
// Add URL to result.messages.imports
2751
imports[`HTML__IMPORT__${idx}`] = node.attrs.src;
28-
// Add content placeholders to HTML
52+
53+
// Add URL content placeholders to HTML
2954
node.content = options.template
3055
? '${' + `HTML__IMPORT__${idx}(${options.template})` + '}'
3156
: '${' + `HTML__IMPORT__${idx}` + '}';
@@ -36,7 +61,7 @@ export default function(options = {}) {
3661
return node;
3762
});
3863

39-
// Add imports to result.messages
64+
// Add HTML Imports to result.messages
4065
tree.messages.push(imports);
4166

4267
return tree;

src/options.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,12 @@
55
"type": "boolean"
66
},
77
"import": {
8-
"type": "boolean"
8+
"anyOf": [
9+
{ "type": "string" },
10+
{ "type": "boolean" },
11+
{ "instanceof": "RegExp" },
12+
{ "instanceof": "Function" }
13+
]
914
},
1015
"template": {
1116
"type": [ "boolean", "string" ]
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<!-- Ignore -->
2+
<import src="//file.html"></import>
3+
<import src="//cdn.com/file.html"></import>
4+
<import src="http://cdn.com/file.html"></import>
5+
<import src="https://cdn.com/file.html"></import>
6+
<!-- Transform -->
7+
<import src="./1.html"></import>
8+
<import src="/2.html"></import>
9+
<!-- Filter (Ignore) -->
10+
<import src="./filter/1.html"></import>
11+
<import src="/filter/2.html"></import>
12+
<!-- Transform -->
13+
<include src="./1.html"></include>
14+
<include src="/2.html"></include>
15+
<!-- Filter (Ignore) -->
16+
<include src="./filter/1.html"></include>
17+
<include src="/filter/2.html"></include>
18+
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import html from './fixture.html';
2+
3+
export default html;

test/options/__snapshots__/import.test.js.snap

Lines changed: 113 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,31 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3-
exports[`Options import {Boolean} 1`] = `
3+
exports[`Options import {Boolean} - false 1`] = `
4+
"// HTML
5+
export default \`<!DOCTYPE html>
6+
<html lang=\\"en\\">
7+
<head>
8+
<meta charset=\\"utf-8\\">
9+
<title>HTML Loader</title>
10+
</head>
11+
<body>
12+
<!-- Ignore -->
13+
<import src=\\"//file.html\\"></import>
14+
<import src=\\"//cdn.com/file.html\\"></import>
15+
<import src=\\"http://cdn.com/file.html\\"></import>
16+
<import src=\\"https://cdn.com/file.html\\"></import>
17+
<!-- Transform -->
18+
<import src=\\"./1.html\\"></import>
19+
<import src=\\"/2.html\\"></import>
20+
21+
<include src=\\"./1.html\\"></include>
22+
<include src=\\"/2.html\\"></include>
23+
</body>
24+
</html>
25+
\`"
26+
`;
27+
28+
exports[`Options import {Boolean} - true - default 1`] = `
429
"// HTML Imports
530
import HTML__IMPORT__0 from './1.html';
631
import HTML__IMPORT__1 from '/2.html';
@@ -28,5 +53,92 @@ export default \`<!DOCTYPE html>
2853
\${HTML__IMPORT__3}
2954
</body>
3055
</html>
56+
\`"
57+
`;
58+
59+
exports[`Options import {Function} 1`] = `
60+
"// HTML Imports
61+
import HTML__IMPORT__0 from './1.html';
62+
import HTML__IMPORT__1 from '/2.html';
63+
import HTML__IMPORT__2 from './1.html';
64+
import HTML__IMPORT__3 from '/2.html';
65+
66+
// HTML
67+
export default \`<!-- Ignore -->
68+
69+
70+
71+
72+
<!-- Transform -->
73+
\${HTML__IMPORT__0}
74+
\${HTML__IMPORT__1}
75+
<!-- Filter (Ignore) -->
76+
77+
78+
<!-- Transform -->
79+
\${HTML__IMPORT__2}
80+
\${HTML__IMPORT__3}
81+
<!-- Filter (Ignore) -->
82+
83+
84+
85+
\`"
86+
`;
87+
88+
exports[`Options import {RegExp} 1`] = `
89+
"// HTML Imports
90+
import HTML__IMPORT__0 from './1.html';
91+
import HTML__IMPORT__1 from '/2.html';
92+
import HTML__IMPORT__2 from './1.html';
93+
import HTML__IMPORT__3 from '/2.html';
94+
95+
// HTML
96+
export default \`<!-- Ignore -->
97+
98+
99+
100+
101+
<!-- Transform -->
102+
\${HTML__IMPORT__0}
103+
\${HTML__IMPORT__1}
104+
<!-- Filter (Ignore) -->
105+
106+
107+
<!-- Transform -->
108+
\${HTML__IMPORT__2}
109+
\${HTML__IMPORT__3}
110+
<!-- Filter (Ignore) -->
111+
112+
113+
114+
\`"
115+
`;
116+
117+
exports[`Options import {String} 1`] = `
118+
"// HTML Imports
119+
import HTML__IMPORT__0 from './1.html';
120+
import HTML__IMPORT__1 from '/2.html';
121+
import HTML__IMPORT__2 from './1.html';
122+
import HTML__IMPORT__3 from '/2.html';
123+
124+
// HTML
125+
export default \`<!-- Ignore -->
126+
127+
128+
129+
130+
<!-- Transform -->
131+
\${HTML__IMPORT__0}
132+
\${HTML__IMPORT__1}
133+
<!-- Filter (Ignore) -->
134+
135+
136+
<!-- Transform -->
137+
\${HTML__IMPORT__2}
138+
\${HTML__IMPORT__3}
139+
<!-- Filter (Ignore) -->
140+
141+
142+
31143
\`"
32144
`;

0 commit comments

Comments
 (0)