This repository has been archived by the owner on Apr 20, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 338
/
fileprocessor.js
237 lines (206 loc) · 6.64 KB
/
fileprocessor.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
'use strict';
var debug = require('debug')('fileprocessor');
var File = require('./file');
var _ = require('lodash');
var chalk = require('chalk');
var _defaultPatterns = {
html: [
[
/<script.+src=['"]([^"']+)["']/gm,
'Update the HTML to reference our concat/min/revved script files'
],
[
/<link[^\>]+href=['"]([^"']+)["']/gm,
'Update the HTML with the new css filenames'
],
[
/<img[^\>]*[^\>\S]+src=['"]([^'"\)#]+)(#.+)?["']/gm,
'Update the HTML with the new img filenames'
],
[
/<video[^\>]+src=['"]([^"']+)["']/gm,
'Update the HTML with the new video filenames'
],
[
/<video[^\>]+poster=['"]([^"']+)["']/gm,
'Update the HTML with the new poster filenames'
],
[
/<source[^\>]+src=['"]([^"']+)["']/gm,
'Update the HTML with the new source filenames'
],
[
/data-main\s*=['"]([^"']+)['"]/gm,
'Update the HTML with data-main tags',
function (m) {
return m.match(/\.js$/) ? m : m + '.js';
},
function (m) {
return m.replace('.js', '');
}
],
[
/data-(?!main).[^=]+=['"]([^'"]+)['"]/gm,
'Update the HTML with data-* tags'
],
[
/url\(\s*['"]?([^"'\)]+)["']?\s*\)/gm,
'Update the HTML with background imgs, case there is some inline style'
],
[
/<a[^\>]+href=['"]([^"']+)["']/gm,
'Update the HTML with anchors images'
],
[
/<input[^\>]+src=['"]([^"']+)["']/gm,
'Update the HTML with reference in input'
],
[
/<meta[^\>]+content=['"]([^"']+)["']/gm,
'Update the HTML with the new img filenames in meta tags'
],
[
/<object[^\>]+data=['"]([^"']+)["']/gm,
'Update the HTML with the new object filenames'
],
[
/<image[^\>]*[^\>\S]+xlink:href=['"]([^"'#]+)(#.+)?["']/gm,
'Update the HTML with the new image filenames for svg xlink:href links'
],
[
/<image[^\>]*[^\>\S]+src=['"]([^'"\)#]+)(#.+)?["']/gm,
'Update the HTML with the new image filenames for src links'
],
[
/<(?:img|source)[^\>]*[^\>\S]+srcset=['"]([^"'\s]+)(?:\s\d[mx])["']/gm,
'Update the HTML with the new image filenames for srcset links'
],
[
/<(?:use|image)[^\>]*[^\>\S]+xlink:href=['"]([^'"\)#]+)(#.+)?["']/gm,
'Update the HTML within the <use> tag when referencing an external url with svg sprites as in svg4everybody'
]
],
css: [
[
/(?:src=|url\(\s*)['"]?([^'"\)(\?|#)]+)['"]?\s*\)?/gm,
'Update the CSS to reference our revved images'
]
],
json: [
[
/:\s*['"]([^"']+)["']/gm,
'Update the json value to reference our revved url'
]
]
};
//
// Default block replacement functions, for css and js types
//
var defaultBlockReplacements = {
css: function (block) {
var media = block.media ? ' media="' + block.media + '"' : '';
return '<link rel="stylesheet" href="' + block.dest + '"' + media + '>';
},
js: function (block) {
var defer = block.defer ? 'defer ' : '';
var async = block.async ? 'async ' : '';
return '<script ' + defer + async + 'src="' + block.dest + '"><\/script>';
}
};
var FileProcessor = module.exports = function (type, patterns, finder, logcb, blockReplacements) {
if (!type) {
throw new Error('No type given');
}
if (!_.isArray(patterns)) {
throw new Error('Patterns must be an array');
}
this.patterns = _defaultPatterns[type] || [];
if (patterns.length) {
this.patterns = this.patterns.concat(patterns);
}
this.log = logcb || function () {};
if (!finder) {
throw new Error('Missing parameter: finder');
}
this.finder = finder;
this.blockReplacements = _.assign({}, defaultBlockReplacements);
if (blockReplacements && _.keys(blockReplacements).length > 0) {
_.assign(this.blockReplacements, blockReplacements);
}
};
//
// Replace blocks by their target
//
FileProcessor.prototype.replaceBlocks = function replaceBlocks(file) {
var result = file.content;
var linefeed = /\r\n/g.test(result) ? '\r\n' : '\n';
file.blocks.forEach(function (block) {
var blockLine = block.raw.join(linefeed);
result = result.replace(blockLine, this.replaceWith(block));
}, this);
return result;
};
FileProcessor.prototype.replaceWith = function replaceWith(block) {
var result;
var conditionalStart = block.conditionalStart ? block.conditionalStart + '\n' + block.indent : '';
var conditionalEnd = block.conditionalEnd ? '\n' + block.indent + block.conditionalEnd : '';
if (typeof block.src === 'undefined' || block.src === null || block.src.length === 0) {
// there are no css or js files in the block, remove it completely
result = '';
} else if (_.contains(_.keys(this.blockReplacements), block.type)) {
result = block.indent + conditionalStart + this.blockReplacements[block.type](block) + conditionalEnd;
} else {
result = '';
}
return result;
};
//
// Replace reference to scripts, css, images, ... in +lines+ with their revved version
// If +lines+ is not furnished, use instead the cached version (i.e. stored at constructor time)
//
FileProcessor.prototype.replaceWithRevved = function replaceWithRevved(lines, assetSearchPath) {
// Replace script sources
var self = this;
var content = lines;
var regexps = this.patterns;
var identity = function (m) {
return m;
};
// Replace reference to script with the actual name of the revved script
regexps.forEach(function (rxl) {
var filterIn = rxl[2] || identity;
var filterOut = rxl[3] || identity;
self.log(rxl[1]);
content = content.replace(rxl[0], function (match, src) {
// Consider reference from site root
var srcFile = filterIn(src);
debug('Let\'s replace ' + src);
debug('Looking for revved version of ' + srcFile + ' in ', assetSearchPath);
var file = self.finder.find(srcFile, assetSearchPath);
debug('Found file \'%s\'', file);
if (src === '/') {
return match;
}
var res = match.replace(src, filterOut(file));
if (srcFile !== file) {
self.log(chalk.cyan(src) + ' changed to ' + chalk.cyan(file));
}
return res;
});
});
return content;
};
FileProcessor.prototype.process = function (filename, assetSearchPath) {
debug('processing file %s', filename, assetSearchPath);
if (_.isString(filename)) {
this.file = new File(filename);
} else {
// filename is an object and should conform to lib/file.js API
this.file = filename;
}
if (assetSearchPath && assetSearchPath.length !== 0) {
this.file.searchPath = assetSearchPath;
}
var content = this.replaceWithRevved(this.replaceBlocks(this.file), this.file.searchPath);
return content;
};