-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathprocess-tpl.js
162 lines (133 loc) · 5.37 KB
/
process-tpl.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
// from https://github.com/kuitos/import-html-entry/blob/master/src/process-tpl.js
function getInlineCode(match) {
const start = match.indexOf('>') + 1;
const end = match.lastIndexOf('<');
return match.substring(start, end);
}
const ALL_SCRIPT_REGEX = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi;
const SCRIPT_TAG_REGEX = /<(script)\s+((?!type=('|')text\/ng-template\3).)*?>.*?<\/\1>/is;
const SCRIPT_SRC_REGEX = /.*\ssrc=('|")?([^>'"\s]+)/;
const SCRIPT_ENTRY_REGEX = /.*\sentry\s*.*/;
const LINK_TAG_REGEX = /<(link)\s+.*?>/gi;
const LINK_IGNORE_REGEX = /.*ignore\s*.*/;
const STYLE_TAG_REGEX = /<style[^>]*>[\s\S]*?<\/style>/gi;
const STYLE_TYPE_REGEX = /\s+rel=('|")?stylesheet\1.*/;
const STYLE_HREF_REGEX = /.*\shref=('|")?([^>'"\s]+)/;
const STYLE_IGNORE_REGEX = /<style(\s+|\s+.+\s+)ignore(\s*|\s+.*)>/i;
const HTML_COMMENT_REGEX = /<!--([\s\S]*?)-->/g;
const SCRIPT_IGNORE_REGEX = /<script(\s+|\s+.+\s+)ignore(\s*|\s+.*)>/i;
function hasProtocol(url) {
return url.startsWith('//') || url.startsWith('http://') || url.startsWith('https://');
}
function getBaseDomain(url) {
return url.endsWith('/') ? url.substr(0, url.length - 1) : url;
}
const genLinkReplaceSymbol = linkHref => `<!-- link ${linkHref} replaced by import-html-entry -->`;
const genScriptReplaceSymbol = scriptSrc => `<!-- script ${scriptSrc} replaced by import-html-entry -->`;
const inlineScriptReplaceSymbol = `<!-- inline scripts replaced by import-html-entry -->`;
const genIgnoreAssetReplaceSymbol = url => `<!-- ignore asset ${url || 'file'} replaced by import-html-entry -->`;
/**
* parse the script link from the template
* 1. collect stylesheets
* 2. use global eval to evaluate the inline scripts
* see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function#Difference_between_Function_constructor_and_function_declaration
* see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval#Do_not_ever_use_eval!
* @param tpl
* @param domain
* @stripStyles whether to strip the css links
* @returns {{template: void | string | *, scripts: *[], entry: *}}
*/
function processTpl(tpl, domain) {
let scripts = [];
const styles = [];
let entry = null;
const template = tpl
/*
remove html comment first
*/
.replace(HTML_COMMENT_REGEX, '')
.replace(LINK_TAG_REGEX, match => {
/*
change the css link
*/
const styleType = !!match.match(STYLE_TYPE_REGEX);
if (styleType) {
const styleHref = match.match(STYLE_HREF_REGEX);
const styleIgnore = match.match(LINK_IGNORE_REGEX);
if (styleHref) {
const href = styleHref && styleHref[2];
let newHref = href;
if (href && !hasProtocol(href)) {
// 处理一下使用相对路径的场景
newHref = getBaseDomain(domain) + (href.startsWith('/') ? href : `/${href}`);
}
if (styleIgnore) {
return genIgnoreAssetReplaceSymbol(newHref);
}
styles.push(newHref);
return genLinkReplaceSymbol(newHref);
}
}
return match;
})
.replace(STYLE_TAG_REGEX, match => {
if (STYLE_IGNORE_REGEX.test(match)) {
return genIgnoreAssetReplaceSymbol('style file');
}
return match;
})
.replace(ALL_SCRIPT_REGEX, match => {
const scriptIgnore = match.match(SCRIPT_IGNORE_REGEX);
// in order to keep the exec order of all javascripts
// if it is a external script
if (SCRIPT_TAG_REGEX.test(match) && match.match(SCRIPT_SRC_REGEX)) {
/*
collect scripts and replace the ref
*/
const matchedScriptEntry = match.match(SCRIPT_ENTRY_REGEX);
const matchedScriptSrcMatch = match.match(SCRIPT_SRC_REGEX);
let matchedScriptSrc = matchedScriptSrcMatch && matchedScriptSrcMatch[2];
if (entry && matchedScriptEntry) {
throw new SyntaxError('You should not set multiply entry script!');
} else {
// append the domain while the script not have an protocol prefix
if (matchedScriptSrc && !hasProtocol(matchedScriptSrc)) {
matchedScriptSrc = getBaseDomain(domain) + (matchedScriptSrc.startsWith('/') ? matchedScriptSrc : `/${matchedScriptSrc}`);
}
entry = entry || matchedScriptEntry && matchedScriptSrc;
}
if (scriptIgnore) {
return genIgnoreAssetReplaceSymbol(matchedScriptSrc || 'js file');
}
if (matchedScriptSrc) {
scripts.push(matchedScriptSrc);
return genScriptReplaceSymbol(matchedScriptSrc);
}
return match;
} else {
if (scriptIgnore) {
return genIgnoreAssetReplaceSymbol('js file');
}
// if it is an inline script
const code = getInlineCode(match);
// remove script blocks when all of these lines are comments.
const isPureCommentBlock = code.split(/[\r\n]+/).every(line => !line.trim() || line.trim().startsWith('//'));
if (!isPureCommentBlock) {
scripts.push(match);
}
return inlineScriptReplaceSymbol;
}
});
scripts = scripts.filter(function (script) {
// filter empty script
return !!script;
});
return {
template,
scripts,
styles,
// set the last script as entry if have not set
entry: entry || scripts[scripts.length - 1],
};
}
module.exports = processTpl;