Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(79)

Side by Side Diff: node_modules/vulcanize/lib/vulcan.js

Issue 800513006: Added vulcanize under third_party/npm_modules (Closed) Base URL: https://chromium.googlesource.com/infra/third_party/npm_modules.git@master
Patch Set: Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « node_modules/vulcanize/lib/utils.js ('k') | node_modules/vulcanize/node_modules/.bin/nopt » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 /**
2 * @license
3 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
4 * This code may only be used under the BSD style license found at http://polyme r.github.io/LICENSE.txt
5 * The complete set of authors may be found at http://polymer.github.io/AUTHORS. txt
6 * The complete set of contributors may be found at http://polymer.github.io/CON TRIBUTORS.txt
7 * Code distributed by Google as part of the polymer project is also
8 * subject to an additional IP rights grant found at http://polymer.github.io/PA TENTS.txt
9 */
10
11 // jshint node: true
12
13 var cssom = require('cssom');
14 var fs = require('fs');
15 var path = require('path');
16 var uglify = require('uglify-js');
17 var url = require('url');
18 var whacko = require('whacko');
19
20 var constants = require('./constants.js');
21 var optparser = require('./optparser.js');
22 var pathresolver = require('./pathresolver');
23 var utils = require('./utils');
24 var setTextContent = utils.setTextContent;
25 var getTextContent = utils.getTextContent;
26 var searchAll = utils.searchAll;
27
28 var read = {};
29 var options = {};
30
31 // validate options with boolean return
32 function setOptions(optHash, callback) {
33 optparser.processOptions(optHash, function(err, o) {
34 if (err) {
35 return callback(err);
36 }
37 options = o;
38 callback();
39 });
40 }
41
42 function exclude(regexes, href) {
43 return regexes.some(function(r) {
44 return r.test(href);
45 });
46 }
47
48 function excludeImport(href) {
49 return exclude(options.excludes.imports, href);
50 }
51
52 function excludeScript(href) {
53 return exclude(options.excludes.scripts, href);
54 }
55
56 function excludeStyle(href) {
57 return exclude(options.excludes.styles, href);
58 }
59
60 function readFile(file) {
61 var content = fs.readFileSync(file, 'utf8');
62 return content.replace(/^\uFEFF/, '');
63 }
64
65 // inline relative linked stylesheets into <style> tags
66 function inlineSheets($, inputPath, outputPath) {
67 searchAll($, 'link[rel="stylesheet"]').each(function() {
68 var el = $(this);
69 var href = el.attr('href');
70 if (href && !excludeStyle(href)) {
71
72 var rel = href;
73 var inputPath = path.dirname(options.input);
74 if (constants.ABS_URL.test(rel)) {
75 var abs = path.resolve(inputPath, path.join(options.abspath, rel));
76 rel = path.relative(options.outputDir, abs);
77 }
78
79 var filepath = path.resolve(options.outputDir, rel);
80 // fix up paths in the stylesheet to be relative to the location of the st yle
81 var content = pathresolver.rewriteURL(path.dirname(filepath), outputPath, readFile(filepath));
82 var styleEl = whacko('<style>' + content + '</style>');
83 // clone attributes
84 styleEl.attr(el.attr());
85 // don't set href or rel on the <style>
86 styleEl.attr('href', null);
87 styleEl.attr('rel', null);
88 el.replaceWith(whacko.html(styleEl));
89 }
90 });
91 }
92
93 function inlineScripts($, dir) {
94 searchAll($, constants.JS_SRC).each(function() {
95 var el = $(this);
96 var src = el.attr('src');
97 if (src && !excludeScript(src)) {
98
99 var rel = src;
100 var inputPath = path.dirname(options.input);
101 if (constants.ABS_URL.test(rel)) {
102 var abs = path.resolve(inputPath, path.join(options.abspath, rel));
103 rel = path.relative(options.outputDir, abs);
104 }
105
106 var filepath = path.resolve(dir, rel);
107 var content = readFile(filepath);
108 // NOTE: reusing UglifyJS's inline script printer (not exported from Outpu tStream :/)
109 content = content.replace(/<\x2fscript([>\/\t\n\f\r ])/gi, "<\\/script$1") ;
110 el.replaceWith('<script>' + content + '</script>');
111 }
112 });
113 }
114
115 function concat(filename) {
116 if (!read[filename]) {
117 read[filename] = true;
118 var $ = whacko.load(readFile(filename));
119 var dir = path.dirname(filename);
120 pathresolver.resolvePaths($, dir, options.outputDir, options.abspath);
121 processImports($);
122 inlineSheets($, dir, options.outputDir);
123 return $;
124 } else if (options.verbose) {
125 console.log('Dependency deduplicated');
126 }
127 }
128
129 function processImports($, mainDoc) {
130 var bodyContent = [];
131 searchAll($, constants.IMPORTS).each(function() {
132 var el = $(this);
133 var href = el.attr('href');
134 if (!excludeImport(href)) {
135 var rel = href;
136 var inputPath = path.dirname(options.input);
137 if (constants.ABS_URL.test(rel)) {
138 var abs = path.resolve(inputPath, path.join(options.abspath, rel));
139 rel = path.relative(options.outputDir, abs);
140 }
141 var $$ = concat(path.resolve(options.outputDir, rel));
142 if (!$$) {
143 // remove import link
144 el.remove();
145 return;
146 }
147 // append import document head to main document head
148 el.replaceWith($$('head').html());
149 var bodyHTML = $$('body').html();
150 // keep the ordering of the import body in main document, before main docu ment's body
151 bodyContent.push(bodyHTML);
152 } else if (!options.keepExcludes) {
153 // if the path is excluded for being absolute, then the import link must r emain
154 var absexclude = options.abspath ? constants.REMOTE_ABS_URL : constants.AB S_URL;
155 if (!absexclude.test(href)) {
156 el.remove();
157 }
158 }
159 });
160 // prepend the import document body contents to the main document, in order
161 var content = bodyContent.join('\n');
162 // hide import body content in the main document
163 if (mainDoc && content) {
164 content = '<div hidden>' + content + '</div>';
165 }
166 $('body').prepend(content);
167 }
168
169 function findScriptLocation($) {
170 var pos = $('body').last();
171 if (!pos.length) {
172 pos = $.root();
173 }
174 return pos;
175 }
176
177 function isCommentOrEmptyTextNode(node) {
178 if (node.type === 'comment'){
179 return true;
180 } else if (node.type === 'text') {
181 // return true if the node is only whitespace
182 return !((/\S/).test(node.data));
183 }
184 }
185
186 function compressJS(content, inline) {
187 try {
188 var ast = uglify.parse(content);
189 return ast.print_to_string({inline_script: inline});
190 } catch (e) {
191 // return a useful error
192 var js_err = new Error('Compress JS Error');
193 js_err.detail = e.message + ' at line: ' + e.line + ' col: ' + e.col;
194 js_err.content = content;
195 js_err.toString = function() {
196 return this.message + '\n' + this.detail + '\n' + this.content;
197 };
198 throw js_err;
199 }
200 }
201
202 function compressCSS(content) {
203 var out;
204 try {
205 var ast = cssom.parse(content);
206 out = ast.toString();
207 } catch (e) {
208 if (options.verbose) {
209 console.log('Error parsing CSS:', e.toString());
210 console.log('Falling back to removing newlines only');
211 }
212 out = content;
213 } finally {
214 return out.replace(/[\r\n]/g, '');
215 }
216 }
217
218 function removeCommentsAndWhitespace($) {
219 function walk(node) {
220 var content, c;
221 if (!node) {
222 return;
223 } else if (isCommentOrEmptyTextNode(node)) {
224 $(node).remove();
225 return true;
226 } else if (node.type == 'script') {
227 // only run uglify on inline javascript scripts
228 if (!node.attribs.src && (!node.attribs.type || node.attribs.type == "text /javascript")) {
229 content = getTextContent(node);
230 setTextContent(node, compressJS(content, true));
231 }
232 } else if (node.type == 'style') {
233 content = getTextContent(node);
234 setTextContent(node, compressCSS(content));
235 } else if ((c = node.children)) {
236 for (var i = 0; i < c.length; i++) {
237 // since .remove() will modify this array, decrement `i` on successful c omment removal
238 if (walk(c[i])) {
239 i--;
240 }
241 }
242 }
243 }
244
245 // walk the whole AST from root
246 walk($.root().get(0));
247 }
248
249 function writeFileSync(filename, data, eop) {
250 if (!options.outputHandler) {
251 fs.writeFileSync(filename, data, 'utf8');
252 } else {
253 options.outputHandler(filename, data, eop);
254 }
255 }
256
257 function handleMainDocument() {
258 // reset shared buffers
259 read = {};
260 var content = options.inputSrc ? options.inputSrc.toString() : readFile(option s.input);
261 var $ = whacko.load(content);
262 var dir = path.dirname(options.input);
263 pathresolver.resolvePaths($, dir, options.outputDir, options.abspath);
264 processImports($, true);
265 if (options.inline) {
266 inlineSheets($, dir, options.outputDir);
267 }
268
269 if (options.inline) {
270 inlineScripts($, options.outputDir);
271 }
272
273 searchAll($, constants.JS_INLINE).each(function() {
274 var el = $(this);
275 var content = getTextContent(el);
276 // find ancestor polymer-element node
277 var parentElement = el.closest('polymer-element').get(0);
278 if (parentElement) {
279 var match = constants.POLYMER_INVOCATION.exec(content);
280 var elementName = $(parentElement).attr('name');
281 if (match) {
282 var invocation = utils.processPolymerInvocation(elementName, match);
283 content = content.replace(match[0], invocation);
284 setTextContent(el, content);
285 }
286 }
287 });
288
289 // strip noscript from elements, and instead inject explicit Polymer() invocat ion
290 // script, so registration order is preserved
291 searchAll($, constants.ELEMENTS_NOSCRIPT).each(function() {
292 var el = $(this);
293 var name = el.attr('name');
294 if (options.verbose) {
295 console.log('Injecting explicit Polymer invocation for noscript element "' + name + '"');
296 }
297 el.append('<script>Polymer(\'' + name + '\');</script>');
298 el.attr('noscript', null);
299 });
300
301 // strip scripts into a separate file
302 if (options.csp) {
303 if (options.verbose) {
304 console.log('Separating scripts into separate file');
305 }
306
307 // CSPify main page by removing inline scripts
308 var scripts = [];
309 searchAll($, constants.JS_INLINE).each(function() {
310 var el = $(this);
311 var content = getTextContent(el);
312 scripts.push(content);
313 el.remove();
314 });
315
316 // join scripts with ';' to prevent breakages due to EOF semicolon insertion
317 var scriptName = path.basename(options.output, '.html') + '.js';
318 var scriptContent = scripts.join(';' + constants.EOL);
319 if (options.strip) {
320 scriptContent = compressJS(scriptContent, false);
321 }
322
323 writeFileSync(path.resolve(options.outputDir, scriptName), scriptContent);
324 // insert out-of-lined script into document
325 findScriptLocation($).append('<script charset="utf-8" src="' + scriptName + '"></script>');
326 }
327
328 deduplicateImports($);
329
330 if (options.strip) {
331 removeCommentsAndWhitespace($);
332 }
333
334 writeFileSync(options.output, $.html(), true);
335 }
336
337 function deduplicateImports($) {
338 var imports = {};
339 searchAll($, constants.IMPORTS).each(function() {
340 var el = $(this);
341 var href = el.attr('href');
342 // TODO(dfreedm): allow a user defined base url?
343 var abs = url.resolve('http://', href);
344 if (!imports[abs]) {
345 imports[abs] = true;
346 } else {
347 if(options.verbose) {
348 console.log('Import Dependency deduplicated');
349 }
350 el.remove();
351 }
352 });
353 }
354
355 exports.processDocument = handleMainDocument;
356 exports.setOptions = setOptions;
OLDNEW
« no previous file with comments | « node_modules/vulcanize/lib/utils.js ('k') | node_modules/vulcanize/node_modules/.bin/nopt » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698