1 |
|
2 | module.exports = function (s, data) {
|
3 | var minimatch = require('minimatch');
|
4 | var Promise = require('bluebird');
|
5 | var jsdom = require('jsdom');
|
6 | var streamRead = require('./stream');
|
7 | var concator = require('./concator');
|
8 |
|
9 | var Jsdom = jsdom.JSDOM;
|
10 | var VirtualConsole = jsdom.VirtualConsole;
|
11 |
|
12 | var route = this.route;
|
13 | var logger = this.log || console;
|
14 | var root = this.config.root;
|
15 | var config = this.config.filter_optimize;
|
16 | var css = config.css;
|
17 | var js = config.js;
|
18 | var removeComments = config.remove_comments;
|
19 |
|
20 | var list = route.list();
|
21 |
|
22 |
|
23 | var htmls = list.filter(function (path) {
|
24 | return minimatch(path, '**/*.html', { nocase: true });
|
25 | });
|
26 |
|
27 |
|
28 | var inExcludes = function (path, excludes) {
|
29 | for (var i = 0; i < excludes.length; i++) {
|
30 | if (minimatch(path, excludes[i], { nocase: true })) {
|
31 | return true;
|
32 | }
|
33 | }
|
34 | return false;
|
35 | };
|
36 |
|
37 | css.excludes = css.excludes || [];
|
38 | css.inlines = css.inlines || ['css/main.css'];
|
39 | js.excludes = js.excludes || [];
|
40 |
|
41 |
|
42 | var inlineCssText = '';
|
43 | var first = Promise.resolve();
|
44 |
|
45 | if (css.enable) {
|
46 | var inlines = list.filter(function (path) {
|
47 | if (inExcludes(path, css.excludes)) {
|
48 | return false;
|
49 | }
|
50 | return css.inlines.indexOf(path) >= 0;
|
51 | });
|
52 |
|
53 | if (inlines.length > 0) {
|
54 | first = concator.bundleFiles(inlines, list, route, '', removeComments)
|
55 | .then(function (content) {
|
56 | inlineCssText = content;
|
57 | }).catch(function (err) {
|
58 | logger.log('Errors when get the inline css: ', err);
|
59 | });
|
60 | }
|
61 | }
|
62 |
|
63 |
|
64 | var bundleJsList = [];
|
65 | var bundleCssList = [];
|
66 |
|
67 |
|
68 | var vc = new VirtualConsole();
|
69 |
|
70 | return first.then(function () {
|
71 | return Promise.map(htmls, function (path) {
|
72 | var stream = route.get(path);
|
73 | return streamRead(stream)
|
74 | .then(function (str) {
|
75 |
|
76 | var dom = new Jsdom(str, { virtualConsole: vc });
|
77 | var doc = dom.window && dom.window.document;
|
78 | if (doc == null) {
|
79 | return;
|
80 | }
|
81 |
|
82 | var links = Array.from(doc.querySelectorAll('link'));
|
83 | if (links.length <= 0) {
|
84 |
|
85 | return;
|
86 | }
|
87 |
|
88 |
|
89 |
|
90 | var regQuery = /\?v=[\d.]*$/;
|
91 |
|
92 | var cssCode = '';
|
93 | var cssList = [];
|
94 | var hasInlines = false;
|
95 | var hasDelivery = false;
|
96 | if (config.remove_query_string) {
|
97 | links.filter(function (el) { return regQuery.test(el.href); })
|
98 | .forEach(function (el) {
|
99 | el.href = el.href.replace(regQuery, '');
|
100 | });
|
101 | }
|
102 | if (css.enable) {
|
103 | links
|
104 | .filter(function (el) { return el.rel === 'stylesheet'; })
|
105 | .forEach(function (el) {
|
106 | var href = el.href;
|
107 | if (inExcludes(href, css.excludes)) {
|
108 | return;
|
109 | }
|
110 | var isCssBundle = false;
|
111 | if (css.inlines.filter(function (p) {
|
112 | return href.indexOf(p) >= 0;
|
113 | }).length > 0) {
|
114 | hasInlines = true;
|
115 | } else {
|
116 | if (href[0] === '/' && href[1] !== '/') {
|
117 | if (bundleCssList.indexOf(href) < 0) {
|
118 | bundleCssList.push(href);
|
119 | isCssBundle = true;
|
120 | }
|
121 | }
|
122 | if (!isCssBundle && bundleCssList.indexOf(href) < 0) {
|
123 | cssCode += 'loadCss(\'' + href + '\');';
|
124 | cssList.push(href);
|
125 | }
|
126 | hasDelivery = true;
|
127 | }
|
128 | el.remove();
|
129 | });
|
130 | }
|
131 |
|
132 | if (js.bundle || config.remove_query_string) {
|
133 | var scripts = Array.from(doc.querySelectorAll('script'));
|
134 | var scriptText = null;
|
135 | scripts.forEach(function (el) {
|
136 | var src = el.src;
|
137 | if (config.remove_query_string && regQuery.test(src)) {
|
138 | el.src = src.replace(regQuery, '');
|
139 | }
|
140 | if (js.bundle) {
|
141 | var isJsBundle = false;
|
142 |
|
143 |
|
144 | if (el.id !== 'hexo.configurations' && scriptText != null
|
145 |
|
146 | && !src
|
147 |
|
148 | && el.textContent && el.textContent.length > 0) {
|
149 |
|
150 | scriptText = concator.combine(scriptText, el.textContent,
|
151 | ';', removeComments);
|
152 | el.remove();
|
153 | } else if (src && src[0] === '/' && src[1] !== '/') {
|
154 | if (bundleJsList.indexOf(src) < 0) {
|
155 | bundleJsList.push(src);
|
156 | isJsBundle = true;
|
157 | }
|
158 | }
|
159 | if (isJsBundle || bundleJsList.indexOf(src) >= 0) {
|
160 | if (scriptText == null) {
|
161 | scriptText = '';
|
162 | }
|
163 | el.remove();
|
164 | }
|
165 | }
|
166 | });
|
167 |
|
168 | if (bundleJsList.length > 0) {
|
169 | var bundleJs = doc.createElement('script');
|
170 | bundleJs.type = 'text/javascript';
|
171 | bundleJs.src = root + 'bundle.js';
|
172 | doc.body.appendChild(bundleJs);
|
173 | }
|
174 |
|
175 | if (scriptText != null && scriptText.length > 0) {
|
176 | var textScript = doc.createElement('script');
|
177 | textScript.type = 'text/javascript';
|
178 | textScript.textContent = scriptText;
|
179 | doc.body.appendChild(textScript);
|
180 | }
|
181 | }
|
182 |
|
183 | var changed = bundleJsList.length > 0;
|
184 |
|
185 | if (hasDelivery) {
|
186 | var cssElement = doc.createElement('script');
|
187 | if (bundleCssList.length > 0) {
|
188 | cssCode = 'loadCss(\'' + root + 'style.css\');' + cssCode;
|
189 | }
|
190 |
|
191 | cssCode = "function loadCss(l){var d=document,h=d.head,s=d.createElement('link');s.rel='stylesheet';s.href=l;!function e(f){if (d.body)return f();setTimeout(function(){e(f)})}(function(){h.appendChild(s);});}"
|
192 | + cssCode;
|
193 | cssElement.textContent = cssCode;
|
194 | doc.head.appendChild(cssElement);
|
195 |
|
196 | if (cssList != null && cssList.length > 0) {
|
197 | var ns = doc.createElement('noscript');
|
198 | var c;
|
199 | if (bundleCssList.length > 0) {
|
200 | c = doc.createElement('link');
|
201 | c.rel = 'stylesheet';
|
202 | c.href = root + 'style.css';
|
203 | ns.appendChild(c);
|
204 | }
|
205 | for (var i = 0; i < cssList.length; i++) {
|
206 | c = doc.createElement('link');
|
207 | c.rel = 'stylesheet';
|
208 | c.href = cssList[i];
|
209 | ns.appendChild(c);
|
210 | }
|
211 | doc.head.appendChild(ns);
|
212 | }
|
213 | changed = true;
|
214 | }
|
215 |
|
216 | var noscripts = doc.getElementsByTagName('noscript');
|
217 | var noscript;
|
218 | if (noscripts.length > 0 && noscripts[0].parentNode === doc.head) {
|
219 | noscript = noscripts[0];
|
220 | }
|
221 |
|
222 |
|
223 | if (hasInlines && inlineCssText.length > 0) {
|
224 | var main = doc.createElement('style');
|
225 | main.textContent = inlineCssText;
|
226 | if (noscript != null) {
|
227 |
|
228 | doc.head.insertBefore(main, noscript);
|
229 | } else {
|
230 | doc.head.appendChild(main);
|
231 | }
|
232 | changed = true;
|
233 | }
|
234 |
|
235 | if (changed) {
|
236 |
|
237 | str = dom.serialize();
|
238 | route.set(path, str);
|
239 | }
|
240 | });
|
241 | });
|
242 |
|
243 |
|
244 | |
245 |
|
246 |
|
247 | }).then(function () {
|
248 | var p;
|
249 | if (bundleCssList.length > 0) {
|
250 | p = concator
|
251 | .bundleFiles(bundleCssList, list, route, '', removeComments)
|
252 | .then(function (content) {
|
253 | return new Promise(function (resolve) {
|
254 | route.set(root + 'style.css', content);
|
255 | resolve();
|
256 | });
|
257 | });
|
258 | } else {
|
259 | p = Promise.resolve();
|
260 | }
|
261 |
|
262 | if (bundleJsList.length > 0) {
|
263 | p = p.then(function () {
|
264 | return concator
|
265 | .bundleFiles(bundleJsList, list, route, ';', removeComments)
|
266 | .then(function (content) {
|
267 | return new Promise(function (resolve) {
|
268 | route.set(root + 'bundle.js', content);
|
269 | resolve();
|
270 | });
|
271 | });
|
272 | });
|
273 | }
|
274 |
|
275 | return p;
|
276 | });
|
277 | };
|