Skip to content

Commit c9e5365

Browse files
squash: add LoaderError && init test refactor
1 parent e3c509e commit c9e5365

File tree

9 files changed

+312
-302
lines changed

9 files changed

+312
-302
lines changed

src/Error.js

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
class LoaderError extends Error {
2+
constructor(err) {
3+
super(err);
4+
5+
this.name = 'HTML Loader';
6+
this.message = `\n\n${this.name}\n\n`;
7+
8+
if (err.name === 'AttributesError') {
9+
this.message += `[option.attrs] ${err.message}\n`;
10+
} else {
11+
this.message += `${err.message}\n`;
12+
}
13+
14+
this.stack = false;
15+
}
16+
}
17+
18+
export default LoaderError;

src/index.js

+77-70
Original file line numberDiff line numberDiff line change
@@ -1,126 +1,134 @@
1-
/* eslint-disable */
2-
import loaderUtils from "loader-utils";
1+
/* eslint-disable
2+
import/order,
3+
import/first,
4+
no-undefined,
5+
no-param-reassign,
6+
no-useless-escape,
7+
*/
8+
import LoaderError from './Error';
9+
import loaderUtils from 'loader-utils';
310
import validateOptions from 'schema-utils';
411

5-
import url from "url";
6-
import attrs from "./lib/attrs";
7-
import minifier from "html-minifier";
12+
import url from 'url';
13+
import attrs from './lib/attrs';
14+
import minifier from 'html-minifier';
815

916
const schema = require('./options');
1017

1118
function randomIdent() {
12-
return "xxxHTMLLINKxxx" + Math.random() + Math.random() + "xxx";
19+
return `xxxHTMLLINKxxx${Math.random()}${Math.random()}xxx`;
1320
}
1421

15-
export default function loader (html) {
16-
var options = loaderUtils.getOptions(this) || {};
22+
export default function loader(html) {
23+
const options = loaderUtils.getOptions(this) || {};
1724

1825
validateOptions(schema, options, 'HTML Loader');
1926

20-
var attributes = ["img:src"];
27+
let attributes = ['img:src'];
2128

22-
if (options.attrs !== undefined) {
23-
if (typeof options.attrs === "string") attributes = options.attrs.split(" ");
29+
if (options.attrs === undefined) {
30+
if (typeof options.attrs === 'string') attributes = options.attrs.split(' ');
2431
else if (Array.isArray(options.attrs)) attributes = options.attrs;
25-
else if (options.attrs === false) attributes = [];
26-
else throw new Error("Invalid value to options parameter attrs");
32+
else if (options.attrs === false) attributes = [];
33+
else {
34+
throw new LoaderError({
35+
name: 'AttributesError',
36+
message: 'Invalid attribute value found',
37+
});
38+
}
2739
}
2840

29-
var root = options.root;
41+
const { root } = options;
3042

31-
var links = attrs(html, (tag, attr) => {
32-
return attributes.indexOf(tag + ":" + attr) >= 0;
43+
// eslint-disable-next-line
44+
const links = attrs(html, (tag, attr) => {
45+
return attributes.indexOf(`${tag}:${attr}`) >= 0;
3346
});
3447

3548
links.reverse();
3649

37-
var data = {};
50+
const data = {};
3851

3952
html = [html];
4053

4154
links.forEach((link) => {
42-
if(!loaderUtils.isUrlRequest(link.value, root)) return;
55+
if (!loaderUtils.isUrlRequest(link.value, root)) return;
4356

44-
var uri = url.parse(link.value);
57+
const uri = url.parse(link.value);
4558

4659
if (uri.hash !== null && uri.hash !== undefined) {
4760
uri.hash = null;
4861

4962
link.value = uri.format();
5063
link.length = link.value.length;
5164
}
52-
53-
do {
54-
var ident = randomIdent();
55-
} while(data[ident]);
56-
65+
// eslint-disable-next-line
66+
var ident;
67+
do { ident = randomIdent(); } while (data[ident]);
5768
data[ident] = link.value;
5869

59-
var x = html.pop();
70+
const item = html.pop();
6071

61-
html.push(x.substr(link.start + link.length));
72+
html.push(item.substr(link.start + link.length));
6273
html.push(ident);
63-
html.push(x.substr(0, link.start));
74+
html.push(item.substr(0, link.start));
6475
});
6576

66-
html.reverse();
67-
68-
html = html.join("");
77+
html = html.reverse().join('');
6978

7079
if (options.interpolate === 'require') {
71-
var regex = /\$\{require\([^)]*\)\}/g;
80+
const regex = /\$\{require\([^)]*\)\}/g;
81+
// eslint-disable-next-line
7282
var result;
7383

74-
var reqList = [];
84+
const requires = [];
7585

76-
while(result = regex.exec(html)) {
77-
reqList.push({
78-
length : result[0].length,
79-
start : result.index,
80-
value : result[0]
81-
})
86+
// eslint-disable-next-line
87+
while (result = regex.exec(html)) {
88+
requires.push({
89+
length: result[0].length,
90+
start: result.index,
91+
value: result[0],
92+
});
8293
}
8394

84-
reqList.reverse();
95+
requires.reverse();
8596

8697
html = [html];
8798

88-
reqList.forEach((link) => {
89-
var x = html.pop();
99+
requires.forEach((link) => {
100+
const item = html.pop();
101+
// eslint-disable-next-line
102+
var ident
103+
do { ident = randomIdent(); } while (data[ident]);
104+
data[ident] = link.value.substring(11, link.length - 3);
90105

91-
do {
92-
var ident = randomIdent();
93-
} while(data[ident]);
94-
95-
data[ident] = link.value.substring(11,link.length - 3)
96-
97-
html.push(x.substr(link.start + link.length));
106+
html.push(item.substr(link.start + link.length));
98107
html.push(ident);
99-
html.push(x.substr(0, link.start));
108+
html.push(item.substr(0, link.start));
100109
});
101110

102-
html.reverse();
103-
html = html.join("");
111+
html = html.reverse().join('');
104112
}
105113

106-
if (typeof options.minimize === "boolean" ? options.minimize : this.minimize) {
107-
var minimizeOptions = Object.assign({}, options);
114+
if (typeof options.minimize === 'boolean' ? options.minimize : this.minimize) {
115+
const minimizeOptions = Object.assign({}, options);
108116

109117
[
110-
"removeComments",
111-
"removeCommentsFromCDATA",
112-
"removeCDATASectionsFromCDATA",
113-
"collapseWhitespace",
114-
"conservativeCollapse",
115-
"removeAttributeQuotes",
116-
"useShortDoctype",
117-
"keepClosingSlash",
118-
"minifyJS",
119-
"minifyCSS",
120-
"removeScriptTypeAttributes",
121-
"removeStyleTypeAttributes",
118+
'removeComments',
119+
'removeCommentsFromCDATA',
120+
'removeCDATASectionsFromCDATA',
121+
'collapseWhitespace',
122+
'conservativeCollapse',
123+
'removeAttributeQuotes',
124+
'useShortDoctype',
125+
'keepClosingSlash',
126+
'minifyJS',
127+
'minifyCSS',
128+
'removeScriptTypeAttributes',
129+
'removeStyleTypeAttributes',
122130
].forEach((name) => {
123-
if (typeof minimizeOptions[name] === "undefined") {
131+
if (typeof minimizeOptions[name] === 'undefined') {
124132
minimizeOptions[name] = true;
125133
}
126134
});
@@ -129,7 +137,7 @@ export default function loader (html) {
129137
}
130138

131139
// TODO
132-
// Support exporting a template function
140+
// #120 - Support exporting a template function
133141
//
134142
// import template from 'file.html'
135143
//
@@ -141,8 +149,7 @@ export default function loader (html) {
141149
}
142150

143151
return `export default ${html.replace(/xxxHTMLLINKxxx[0-9\.]+xxx/g, (match) => {
144-
if(!data[match]) return match;
145-
return '" + require(' + JSON.stringify(loaderUtils.urlToRequest(data[match], root)) + ') + "';
152+
if (!data[match]) return match;
153+
return `"require('${JSON.stringify(loaderUtils.urlToRequest(data[match], root))}')"`;
146154
})};`;
147-
148155
}

src/lib/attrs.js

+24-26
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,40 @@
1-
/* eslint-disable */
2-
import Parser from "fastparse";
1+
/* eslint-disable no-useless-escape, no-dupe-keys */
2+
import Parser from 'fastparse';
33

4-
function matcher (match, strUntilValue, name, value, index) {
4+
function matcher(match, strUntilValue, name, value, index) {
55
if (!this.isRelevant(this.tag, name)) return;
66

77
this.results.push({
88
start: index + strUntilValue.length,
99
length: value.length,
10-
value: value
10+
value,
1111
});
12-
};
12+
}
1313

1414
const parser = new Parser({
1515
outside: {
16-
"<!--.*?-->": true,
17-
"<![CDATA[.*?]]>": true,
18-
"<[!\\?].*?>": true,
19-
"<\/[^>]+>": true,
20-
"<([a-zA-Z\\-:]+)\\s*": function (match, tag) {
21-
this.tag = tag;
22-
return "inside";
23-
}
16+
'<!--.*?-->': true,
17+
'<![CDATA[.*?]]>': true,
18+
'<[!\\?].*?>': true,
19+
'<\/[^>]+>': true,
20+
'<([a-zA-Z\\-:]+)\\s*': function tag(match, name) {
21+
this.tag = name;
22+
return 'inside';
23+
},
2424
},
2525
inside: {
26-
"\\s+": true, // eat up whitespace
27-
">": "outside", // end of attributes
28-
"(([0-9a-zA-Z\\-:]+)\\s*=\\s*\")([^\"]*)\"": matcher,
29-
"(([0-9a-zA-Z\\-:]+)\\s*=\\s*\')([^\']*)\'": matcher,
30-
"(([0-9a-zA-Z\\-:]+)\\s*=\\s*)([^\\s>]+)": matcher
31-
}
26+
// eat up whitespace
27+
'\\s+': true,
28+
// end of attributes
29+
'>': 'outside',
30+
'(([0-9a-zA-Z\\-:]+)\\s*=\\s*\")([^\"]*)\'': matcher,
31+
'(([0-9a-zA-Z\\-:]+)\\s*=\\s*\")([^\"]*)\'': matcher,
32+
'(([0-9a-zA-Z\\-:]+)\\s*=\\s*)([^\\s>]+)': matcher,
33+
},
3234
});
3335

34-
export default function parse (html, isRelevant) {
36+
export default function parse(html, isRelevant) {
3537
return parser
36-
.parse("outside", html, {
37-
tag: null,
38-
results: [],
39-
isRelevant
40-
})
38+
.parse('outside', html, { tag: null, results: [], isRelevant })
4139
.results;
42-
};
40+
}

test/Errors.test.js

+21-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,30 @@
1+
/* eslint-disable
2+
no-eval,
3+
import/order,
4+
arrow-parens,
5+
prefer-destructuring,
6+
*/
17
import loader from '../src/';
28

9+
import webpack from './webpack';
10+
import { stats } from './helpers';
11+
312
describe('Errors', () => {
4-
test('- Options - ValidationError', () => {
13+
test('ValidationError', () => {
514
const err = () => loader.call({ query: { root: 1 } }, '<html></html>');
615

716
expect(err).toThrow();
817
expect(err).toThrowErrorMatchingSnapshot();
918
});
19+
20+
test('LoaderError', () => {
21+
const config = {};
22+
23+
return webpack('index.js', config)
24+
.then((result) => stats(result))
25+
.then(({ loader }) => {
26+
expect(() => eval(loader.err)).toThrowErrorMatchingSnapshot();
27+
})
28+
.catch((err) => err);
29+
});
1030
});

test/__snapshots__/Errors.test.js.snap

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3-
exports[`Errors - Options - ValidationError 1`] = `
3+
exports[`Errors ValidationError 1`] = `
44
"Validation Error
55
66
HTML Loader Invalid Options
+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3-
exports[`HTML Loader - Should export as ES2015 Module 1`] = `"export default \\"<p>Hello world!</p>\\";"`;
3+
exports[`HTML Loader Should process HTML with defaults 1`] = `"throw new Error(\\"Module build failed: \\\\n\\\\nHTML Loader\\\\n\\\\n[option.attrs] Invalid attribute value found\\\\n\\");"`;

test/helpers/index.js

+5-11
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,13 @@
11
/* eslint-disable */
22
export function stats ({ compilation }) {
3-
console.log(compilation);
43
return {
54
compilation,
6-
// assets: Object.keys(compilation.assets).map((asset) => {
7-
// if (/map/.test(asset)) return asset;
8-
// return compilation.assets[asset].sourceAndMap();
9-
// }),
105
loader: {
11-
// err: compilation.modules.errors,
12-
src: compilation.modules.map((module) => module._source._value),
13-
// map: compilation.modules.map((module) => module._source._sourceMap),
14-
// meta: compilation.modules.map((module) => module.meta)
6+
err: compilation.modules.map((module) => module.error)[0],
7+
src: compilation.modules.map((module) => module._source._value)[0],
8+
map: compilation.modules.map((module) => module._source._sourceMap)[0],
159
},
16-
// errors: compilation.errors,
17-
// warnings: compilation.warnings
10+
errors: compilation.errors,
11+
warnings: compilation.warnings
1812
};
1913
}

0 commit comments

Comments
 (0)