Skip to content

Commit c26670f

Browse files
committed
Initial commit
0 parents  commit c26670f

File tree

13 files changed

+390
-0
lines changed

13 files changed

+390
-0
lines changed

.gitignore

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

README.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# useref
2+
3+
Parse build blocks in HTML files to replace references
4+
5+
Extracted from the grunt plugin [grunt-useref](https://github.com/pajtai/grunt-useref).
6+
7+
## Installation
8+
9+
npm install useref
10+
11+
## Usage
12+
13+
useref = require('useref')
14+
var result = useref(inputHtml)
15+
// result = [ replacedHtml, { type: { id: [ replacedFiles] }} ]
16+
17+
18+
The build block syntax is `build:type id`. Valid types are `js` and `css`
19+
20+
<html>
21+
<head>
22+
<!-- build:css css/combined.css -->
23+
<link href="css/one.css" rel="stylesheet">
24+
<link href="css/two.css" rel="stylesheet">
25+
<!-- endbuild -->
26+
</head>
27+
<body>
28+
<!-- build:js scripts/combined.js -->
29+
<script type="text/javascript" src="scripts/one.js"></script>
30+
<script type="text/javascript" src="scripts/two.js"></script>
31+
<!-- endbuild -->
32+
</body>
33+
</html>
34+
35+
The module would be used with the above sample HTML as follows:
36+
37+
var result = useref(sampleHtml)
38+
39+
// [
40+
// resultHtml,
41+
// {
42+
// css: {
43+
// 'css/combined.css': [ 'css/one.css', 'css/two.css' ]
44+
// },
45+
// js: {
46+
// 'scripts/combined.js': [ 'css/one.js', 'css/two.js' ]
47+
// }
48+
// }
49+
// ]
50+
51+
52+
The resulting HTML would be:
53+
54+
<html>
55+
<head>
56+
<link rel="stylesheet" href="css/combined.css"/>
57+
</head>
58+
<body>
59+
<script src="scripts/combined.js"></script>
60+
</body>
61+
</html>

package.json

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"name": "useref",
3+
"version": "0.0.1",
4+
"description": "Parse build blocks in HTML files to replace references",
5+
"main": "src/index.js",
6+
"directories": {
7+
"test": "test"
8+
},
9+
"scripts": {
10+
"test": "mocha test"
11+
},
12+
"keywords": [
13+
"build",
14+
"blocks",
15+
"build",
16+
"comments",
17+
"replace",
18+
"scripts",
19+
"link",
20+
"ref"
21+
],
22+
"author": "Manuel Cabral (m.cabral@digisfera.pt)",
23+
"license": "ISC",
24+
"dependencies": {
25+
},
26+
"devDependencies": {
27+
"chai": "~1.8.1",
28+
"mocha": "~1.17.0"
29+
}
30+
}

src/index.js

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
/*global module:false, require:false */
2+
// From https://raw.github.com/yeoman/yeoman/master/cli/tasks/usemin.js
3+
// which is rooted in: https://raw.github.com/h5bp/node-build-script/master/tasks/usemin.js
4+
5+
'use strict';
6+
7+
var fs = require('fs'),
8+
path = require('path');
9+
10+
// start build pattern: <!-- build:[target] output -->
11+
// $1 is the type, $2 is the file
12+
var regbuild = /<!--\s*build:(\w+)\s*(.+)\s*-->/;
13+
14+
// end build pattern -- <!-- endbuild -->
15+
var regend = /<!--\s*endbuild\s*-->/;
16+
17+
18+
module.exports = function (content) {
19+
var blocks = getBlocks(content);
20+
21+
content = updateReferences(blocks, content);
22+
23+
var replaced = compactContent(blocks);
24+
25+
return [ content, replaced ];
26+
};
27+
28+
29+
// Returns a hash object of all the directives for the given html. Results is
30+
// of the following form:
31+
//
32+
// {
33+
// 'css/site.css ':[
34+
// ' <!-- build:css css/site.css -->',
35+
// ' <link rel="stylesheet" href="css/style.css">',
36+
// ' <!-- endbuild -->'
37+
// ],
38+
// 'js/head.js ': [
39+
// ' <!-- build:js js/head.js -->',
40+
// ' <script src="js/libs/modernizr-2.5.3.min.js"></script>',
41+
// ' <!-- endbuild -->'
42+
// ],
43+
// 'js/site.js ': [
44+
// ' <!-- build:js js/site.js -->',
45+
// ' <script src="js/plugins.js"></script>',
46+
// ' <script src="js/script.js"></script>',
47+
// ' <!-- endbuild -->'
48+
// ]
49+
// }
50+
//
51+
function getBlocks(body) {
52+
var lines = body.replace(/\r\n/g, '\n').split(/\n/),
53+
block = false,
54+
sections = {},
55+
last;
56+
57+
lines.forEach(function (l) {
58+
var build = l.match(regbuild),
59+
endbuild = regend.test(l);
60+
61+
if (build) {
62+
block = true;
63+
sections[[build[1], build[2].trim()].join(':')] = last = [];
64+
}
65+
66+
// switch back block flag when endbuild
67+
if (block && endbuild) {
68+
last.push(l);
69+
block = false;
70+
}
71+
72+
if (block && last) {
73+
last.push(l);
74+
}
75+
});
76+
77+
// sections is an array of lines starting with the build block comment opener,
78+
// including all the references and including the build block comment closer.
79+
return sections;
80+
}
81+
82+
83+
// Helpers
84+
// -------
85+
var helpers = {
86+
// useref and useref:* are used with the blocks parsed from directives
87+
useref : function (content, block, target, type) {
88+
target = target || 'replace';
89+
90+
return helpers['useref_' + type](content, block, target);
91+
},
92+
93+
useref_css : function (content, block, target) {
94+
var linefeed = /\r\n/g.test(content) ? '\r\n' : '\n';
95+
var indent = (block.split(linefeed)[0].match(/^\s*/) || [])[0];
96+
return content.replace(block, indent + '<link rel="stylesheet" href="' + target + '"\/>');
97+
},
98+
99+
useref_js : function (content, block, target) {
100+
var linefeed = /\r\n/g.test(content) ? '\r\n' : '\n';
101+
var indent = (block.split(linefeed)[0].match(/^\s*/) || [])[0];
102+
return content.replace(block, indent + '<script src="' + target + '"></script>');
103+
}
104+
};
105+
106+
function updateReferences(blocks, content) {
107+
108+
// Determine the linefeed from the content
109+
var linefeed = /\r\n/g.test(content) ? '\r\n' : '\n';
110+
111+
// handle blocks
112+
Object.keys(blocks).forEach(function (key) {
113+
var block = blocks[key].join(linefeed),
114+
parts = key.split(':'),
115+
type = parts[0],
116+
target = parts[1];
117+
118+
content = helpers.useref(content, block, target, type);
119+
});
120+
121+
return content;
122+
}
123+
124+
function compactContent(blocks) {
125+
126+
var result = {};
127+
128+
Object.keys(blocks).forEach(function (dest) {
129+
// Lines are the included scripts w/o the use blocks
130+
var lines = blocks[dest].slice(1, -1),
131+
parts = dest.split(':'),
132+
type = parts[0],
133+
// output is the useref block file
134+
output = parts[1];
135+
136+
// parse out the list of assets to handle, and update the grunt config accordingly
137+
var assets = lines.map(function (tag) {
138+
139+
// The asset is the string of the referenced source file
140+
var asset = (tag.match(/(href|src)=["']([^'"]+)["']/) || [])[2];
141+
142+
// Allow white space and comment in build blocks by checking if this line has an asset or not
143+
if (asset) { return asset; }
144+
145+
}).reduce(function (a, b) {
146+
b = ( b ? b.split(',') : '');
147+
return b ? a.concat(b) : a;
148+
}, []);
149+
150+
151+
result[type] = result[type] || {}
152+
result[type][output] = assets
153+
});
154+
155+
return result;
156+
}
157+
158+

test/test.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
var expect = require('chai').expect;
2+
var fs = require('fs');
3+
var path = require('path');
4+
var useRef = require('../src/index');
5+
6+
function djoin(p) {
7+
return path.normalize(path.join(__dirname, p));
8+
}
9+
function fread(f) {
10+
return fs.readFileSync(f, { encoding: 'utf-8'});
11+
}
12+
13+
14+
describe('html-ref-replace', function() {
15+
16+
it('should replace reference in css block and return replaced files', function() {
17+
var result = useRef(fread(djoin('testfiles/01.html')));
18+
expect(result[0]).to.equal(fread(djoin('testfiles/01-expected.html')));
19+
expect(result[1]).to.eql({ css: { '/css/combined.css': [ '/css/one.css', '/css/two.css' ] }});
20+
});
21+
22+
23+
it('should replace reference in js block and return replaced files', function() {
24+
var result = useRef(fread(djoin('testfiles/02.html')));
25+
expect(result[0]).to.equal(fread(djoin('testfiles/02-expected.html')));
26+
expect(result[1]).to.eql({ js: { 'scripts/combined.concat.min.js': [ 'scripts/this.js', 'scripts/that.js' ] }});
27+
});
28+
29+
it('should handle comments and whitespace in blocks', function() {
30+
var result = useRef(fread(djoin('testfiles/03.html')));
31+
expect(result[0]).to.equal(fread(djoin('testfiles/03-expected.html')));
32+
expect(result[1]).to.eql({ js: { 'scripts/combined.concat.min.js': [ 'scripts/this.js', 'scripts/that.js' ] }});
33+
});
34+
35+
it('should handle multiple blocks', function() {
36+
var result = useRef(fread(djoin('testfiles/04.html')));
37+
expect(result[0]).to.equal(fread(djoin('testfiles/04-expected.html')));
38+
expect(result[1]).to.eql({
39+
js: {
40+
'scripts/combined.concat.min.js': [ 'scripts/this.js', 'scripts/that.js' ],
41+
'scripts/combined2.concat.min.js': [ 'scripts/anotherone.js', 'scripts/yetonemore.js' ]
42+
},
43+
css: {
44+
'/css/combined.css': [ '/css/one.css', '/css/two.css' ],
45+
'/css/combined2.css': [ '/css/three.css', '/css/four.css' ]
46+
}
47+
});
48+
})
49+
});

test/testfiles/01-expected.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<html>
2+
<head>
3+
<link rel="stylesheet" href="/css/combined.css"/>
4+
</head>
5+
</html>

test/testfiles/01.html

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<html>
2+
<head>
3+
<!-- build:css /css/combined.css -->
4+
<link href="/css/one.css" rel="stylesheet">
5+
<link href="/css/two.css" rel="stylesheet">
6+
<!-- endbuild -->
7+
</head>
8+
</html>

test/testfiles/02-expected.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<html>
2+
<head></head>
3+
<body>
4+
<script src="scripts/combined.concat.min.js"></script>
5+
</body>
6+
</html>

test/testfiles/02.html

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<html>
2+
<head></head>
3+
<body>
4+
<!-- build:js scripts/combined.concat.min.js -->
5+
<script type="text/javascript" src="scripts/this.js"></script>
6+
<script type="text/javascript" src="scripts/that.js"></script>
7+
<!-- endbuild -->
8+
</body>
9+
</html>

test/testfiles/03-expected.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<html>
2+
<head></head>
3+
<body>
4+
<script src="scripts/combined.concat.min.js"></script>
5+
</body>
6+
</html>

0 commit comments

Comments
 (0)