Skip to content

Commit 9732f2f

Browse files
committed
Initial commit
0 parents  commit 9732f2f

File tree

10 files changed

+295
-0
lines changed

10 files changed

+295
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules

.npmignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules
2+
test

.travis.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
language: node_js
2+
node_js:
3+
- 0.6
4+
- 0.8
5+
- 0.10

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# html loader for webpack
2+
3+
## License
4+
5+
MIT (http://www.opensource.org/licenses/mit-license.php)

index.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
MIT License http://www.opensource.org/licenses/mit-license.php
3+
Author Tobias Koppers @sokra
4+
*/
5+
var htmlMinifier = require("html-minifier");
6+
var attrParse = require("./lib/attributesParser");
7+
var SourceNode = require("source-map").SourceNode;
8+
var loaderUtils = require("loader-utils");
9+
10+
function randomIdent() {
11+
return "xxxHTMLLINKxxx" + Math.random() + Math.random() + "xxx";
12+
};
13+
14+
15+
module.exports = function(content) {
16+
this.cacheable && this.cacheable();
17+
var links = attrParse(content);
18+
links.reverse();
19+
var data = {};
20+
content = [content];
21+
links.forEach(function(link) {
22+
if(/^data:|^(https?:)?\/\//.test(link.value)) return;
23+
do {
24+
var ident = randomIdent();
25+
} while(data[ident]);
26+
data[ident] = link.value;
27+
var x = content.pop();
28+
content.push(x.substr(link.start + link.length));
29+
content.push(ident);
30+
content.push(x.substr(0, link.start));
31+
});
32+
content.reverse();
33+
return "module.exports = " + JSON.stringify(content.join("")).replace(/xxxHTMLLINKxxx[0-9\.]+xxx/g, function(match) {
34+
if(!data[match]) return match;
35+
return '" + require(' + JSON.stringify(urlToRequire(data[match])) + ') + "';
36+
}) + ";";
37+
}
38+
39+
function urlToRequire(url) {
40+
if(/^~/.test(url))
41+
return url.substring(1);
42+
else
43+
return "./"+url;
44+
}
45+

lib/attributesParser.js

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
MIT License http://www.opensource.org/licenses/mit-license.php
3+
Author Tobias Koppers @sokra
4+
*/
5+
var Parser = require("./fastparse");
6+
7+
var RELEVANT_TAG_ATTRS = [
8+
"img src",
9+
"link href",
10+
"script src",
11+
];
12+
13+
function isRelevantTagAttr(tag, attr) {
14+
return RELEVANT_TAG_ATTRS.indexOf(tag + " " + attr) >= 0;
15+
}
16+
17+
var parser = new Parser({
18+
outside: {
19+
"<!--.*?-->": true,
20+
"<![CDATA[.*?]]>": true,
21+
"<[!\\?].*?>": true,
22+
"<\/[^>]+>": true,
23+
"<([a-zA-Z\\-:]+)\\s*": function(match, tagName) {
24+
this.currentTag = tagName;
25+
return "inside";
26+
},
27+
"[^<]+": true
28+
},
29+
inside: {
30+
"\\s+": true, // eat up whitespace
31+
">": "outside", // end of attributes
32+
"(([a-zA-Z\\-]+)\\s*=\\s*\")([^\"]*)\"": function(match, strUntilValue, name, value, index) {
33+
if(!isRelevantTagAttr(this.currentTag, name)) return;
34+
this.links.push({
35+
start: index + strUntilValue.length,
36+
length: value.length,
37+
value: value
38+
});
39+
},
40+
"(([a-zA-Z\\-]+)\\s*=\\s*)([^\\s>]+)": function(match, strUntilValue, name, value, index) {
41+
if(!isRelevantTagAttr(this.currentTag, name)) return;
42+
this.links.push({
43+
start: index + strUntilValue.length,
44+
length: value.length,
45+
value: value
46+
});
47+
},
48+
"[a-zA-Z\-]+": true, // attribute without value
49+
"[^>]+": true // catch parsing errors
50+
}
51+
});
52+
53+
54+
module.exports = function parse(html) {
55+
return parser.parse("outside", html, {
56+
currentTag: null,
57+
links: []
58+
}).links;
59+
};

lib/fastparse.js

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/*
2+
MIT License http://www.opensource.org/licenses/mit-license.php
3+
Author Tobias Koppers @sokra
4+
*/
5+
6+
/*
7+
8+
states = {
9+
state1: {
10+
"aa(\d)": function(match) {
11+
match == ["aa4", "4"];
12+
return "state2"
13+
},
14+
"a": "state2",
15+
"b": true
16+
}
17+
}
18+
*/
19+
20+
function Parser(states, options) {
21+
this.options = options || {};
22+
this.states = this.compileStates(states);
23+
};
24+
25+
Parser.prototype.compileStates = function(states) {
26+
var result = {};
27+
Object.keys(states).forEach(function(name) {
28+
result[name] = this.compileState(states[name]);
29+
}, this);
30+
return result;
31+
};
32+
33+
Parser.prototype.compileState = function(state) {
34+
var regExps = Object.keys(state).map(function(str) {
35+
return {
36+
groups: Parser.getGroupCount(str),
37+
regExp: str,
38+
value: state[str]
39+
};
40+
});
41+
var total = regExps.map(function(r) {
42+
return "(" + r.regExp + ")";
43+
}).join("|");
44+
var actions = [];
45+
var pos = 1;
46+
regExps.forEach(function(r) {
47+
var fn;
48+
if(typeof r.value === "function") {
49+
fn = r.value;
50+
} else if(typeof r.value === "string") {
51+
fn = createReturningFunction(r.value);
52+
} else {
53+
fn = ignoreFunction;
54+
}
55+
actions.push({
56+
name: r.regExp,
57+
fn: fn,
58+
pos: pos,
59+
pos2: pos + r.groups + 1
60+
});
61+
pos += r.groups + 1;
62+
});
63+
return {
64+
regExp: new RegExp(total, "g"),
65+
actions: actions
66+
}
67+
};
68+
69+
Parser.getGroupCount = function(regExpStr) {
70+
return new RegExp("(" + regExpStr + ")|^$").exec("").length - 2;
71+
};
72+
73+
Parser.prototype.parse = function(initialState, string, context) {
74+
var currentState = initialState;
75+
var currentIndex = 0;
76+
for(;;) {
77+
var state = this.states[currentState];
78+
var regExp = state.regExp;
79+
regExp.lastIndex = currentIndex;
80+
var match = regExp.exec(string);
81+
if(!match) return context;
82+
var actions = state.actions;
83+
currentIndex = state.regExp.lastIndex;
84+
for(var i = 0; i < actions.length; i++) {
85+
var action = actions[i];
86+
if(match[action.pos]) {
87+
var ret = action.fn.apply(context, Array.prototype.slice.call(match, action.pos, action.pos2).concat([state.regExp.lastIndex - match[0].length, match[0].length]));
88+
if(ret) currentState = ret;
89+
break;
90+
}
91+
}
92+
}
93+
};
94+
95+
module.exports = Parser;
96+
97+
function ignoreFunction() {}
98+
99+
function createReturningFunction(value) {
100+
return function() {
101+
return value;
102+
};
103+
}

package.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"name": "html-loader",
3+
"version": "0.1.0",
4+
"author": "Tobias Koppers @sokra",
5+
"description": "html loader module for webpack",
6+
"dependencies": {
7+
"html-minifier": "0.5.x",
8+
"source-map": "0.1.x",
9+
"loader-utils": "0.2.x"
10+
},
11+
"devDependencies": {
12+
"mocha": "1.17.x",
13+
"should": "3.1.x"
14+
},
15+
"scripts": {
16+
"test": "mocha --reporter spec"
17+
},
18+
"repository": {
19+
"type": "git",
20+
"url": "git@github.com:webpack/html-loader.git"
21+
},
22+
"licenses": [
23+
{
24+
"type": "MIT",
25+
"url": "http://www.opensource.org/licenses/mit-license.php"
26+
}
27+
]
28+
}

test/loaderTest.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
var should = require("should");
2+
3+
var loader = require("../");
4+
5+
describe("loader", function() {
6+
it("should convert to requires", function() {
7+
loader.call({}, 'Text <img src="image.png"><script src="~bootstrap"> Text').should.be.eql(
8+
'module.exports = "Text <img src=\\"" + require("./image.png") + "\\"><script src=\\"" + require("bootstrap") + "\\"> Text";'
9+
);
10+
});
11+
});

test/parserTest.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
var should = require("should");
2+
3+
var attrParse = require("../lib/attributesParser");
4+
5+
function test(name, html, result) {
6+
it("should parse " + name, function() {
7+
attrParse(html).map(function(match) { return match.value }).should.be.eql(result);
8+
});
9+
}
10+
11+
describe("parser", function() {
12+
test("normal", 'Text <img src="image.png"><img src="image2.png">', ["image.png", "image2.png"]);
13+
test("whitespace", 'T ex t <img \t src = "image.png" > <img\t\nsrc\n=\n"image2.png"\n>', ["image.png", "image2.png"]);
14+
test("whitespace2", 'Text < img src="image.png" >', []);
15+
test("wrong <", 'Text <<img src="image.png">', ["image.png"]);
16+
test("wrong >", 'Text ><img src="image.png">', ["image.png"]);
17+
test("no quot", '<img src=image.png>', ["image.png"]);
18+
test("first tag", '<img src="image.png">', ["image.png"]);
19+
test("comment", '<!--<img src="image.png">-->', []);
20+
test("comment2", '<!--<!--<img src="image.png">-->', []);
21+
test("comment3", '<!--><img src="image.png">-->', []);
22+
test("comment4", '<!----><img src="image.png">-->', ["image.png"]);
23+
test("tags", '<img src="image.png"><script src="script.js"></script><link type="stylesheet" href="style.css">', ["image.png", "script.js", "style.css"]);
24+
test("cdata", '<![CDATA[<img src="image.png">]]><img src="image2.png">', ["image2.png"]);
25+
test("doctype", '<!doctype html><img src="image.png">', ["image.png"]);
26+
});
27+
28+
describe("locations", function() {
29+
it("should report correct locations", function() {
30+
attrParse('<img src= "image.png">').should.be.eql([{
31+
start: 12,
32+
length: 9,
33+
value: "image.png"
34+
}]);
35+
});
36+
});

0 commit comments

Comments
 (0)