This repository has been archived by the owner on Sep 7, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 22
/
Copy pathindex.js
109 lines (98 loc) · 3.71 KB
/
index.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
/**
* @license
* Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
* This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
* The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
* The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
* Code distributed by Google as part of the polymer project is also
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
// jshint node: true
'use strict';
var crypto = require("crypto");
var dom5 = require('dom5');
var pred = dom5.predicates;
var inlineScriptFinder = pred.AND(
pred.hasTagName('script'),
pred.OR(
pred.NOT(
pred.hasAttr('type')
),
pred.hasAttrValue('type', 'text/ecmascript-6'),
pred.hasAttrValue('type', 'application/javascript'),
pred.hasAttrValue('type', 'text/javascript')
),
pred.NOT(
pred.hasAttr('src')
)
);
var noSemiColonInsertion = /\/\/|;\s*$|\*\/\s*$/;
module.exports = function crisp(options) {
var source = options.source || '';
var jsFileName = options.jsFileName || '';
var scriptInHead = options.scriptInHead !== false;
var onlySplit = options.onlySplit || false;
var alwaysWriteScript = options.alwaysWriteScript || false;
var cspHashableScriptLoader = options.cspHashableScriptLoader || false;
var doc = dom5.parse(source);
var body = dom5.query(doc, pred.hasTagName('body'));
var head = dom5.query(doc, pred.hasTagName('head'));
var scripts = dom5.queryAll(doc, inlineScriptFinder);
var contents = [];
scripts.forEach(function(sn) {
var nidx = sn.parentNode.childNodes.indexOf(sn) + 1;
var next = sn.parentNode.childNodes[nidx];
dom5.remove(sn);
// remove newline after script to get rid of nasty whitespace
if (next && dom5.isTextNode(next) && !/\S/.test(dom5.getTextContent(next))) {
dom5.remove(next);
}
var content = dom5.getTextContent(sn).trim();
var lines = content.split('\n');
var lastline = lines[lines.length - 1];
if (!noSemiColonInsertion.test(lastline)) {
content += ';';
}
contents.push(content);
});
if (!onlySplit) {
if (contents.length > 0 || alwaysWriteScript) {
var newScript = dom5.constructors.element('script');
var comment;
if (cspHashableScriptLoader) {
// hashable script loader to support hash-based CSP with strict-dynamic.
var loader = '// CSP \'strict-dynamic\' compatible script loader. ';
loader += 'Add \'strict-dynamic\' and a hash of this script to your CSP.\n';
loader += 'var s = document.createElement("script");\n';
loader += 's.src = "' + jsFileName + '";\n';
loader += '(document.body||document.head).appendChild(s);\n';
dom5.setTextContent(newScript, loader);
// Calculate hash of loader script for CSP.
var scriptHash = crypto.createHash('sha256');
scriptHash.update(loader, 'utf-8');
var digest = scriptHash.digest('base64');
var commentText = ' CSP hash: \'sha256-' + digest + '\' ';
comment = dom5.constructors.comment(commentText);
} else {
dom5.setAttribute(newScript, 'src', jsFileName);
}
if (scriptInHead) {
dom5.setAttribute(newScript, 'defer', '');
head.childNodes.unshift(newScript);
newScript.parentNode = head;
} else {
dom5.append(body, newScript);
}
if (cspHashableScriptLoader) {
dom5.insertBefore(newScript.parentNode, newScript, comment);
}
}
}
var html = dom5.serialize(doc);
// newline + semicolon should be enough to capture all cases of concat
var js = contents.join('\n');
return {
html: html,
js: js
};
};