-
Notifications
You must be signed in to change notification settings - Fork 807
Expand file tree
/
Copy pathScriptCache.js
More file actions
139 lines (115 loc) · 4.89 KB
/
ScriptCache.js
File metadata and controls
139 lines (115 loc) · 4.89 KB
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
let counter = 0;
let scriptMap = typeof window !== 'undefined' && window._scriptMap || new Map();
const window = require('./windowOrGlobal');
export const ScriptCache = (function(global) {
global._scriptMap = global._scriptMap || scriptMap;
return function ScriptCache(scripts) {
const Cache = {};
Cache._onLoad = function(key) {
return (cb) => {
let registered = true;
function unregister() {
registered = false;
}
let stored = scriptMap.get(key);
if (stored) {
stored.promise.then(() => {
if (registered) {
stored.error ? cb(stored.error) : cb(null, stored)
}
return stored;
}).catch(error => cb(error));
} else {
// TODO:
}
return unregister;
}
};
Cache._scriptTag = (key, src) => {
if (!scriptMap.has(key)) {
// Server side rendering environments don't always have access to the `document` global.
// In these cases, we're not going to be able to return a script tag, so just return null.
if (typeof document === 'undefined') return null;
let tag = document.createElement('script');
let promise = new Promise((resolve, reject) => {
let body = document.getElementsByTagName('body')[0];
tag.type = 'text/javascript';
tag.async = false; // Load in order
const cbName = `loaderCB${counter++}${Date.now()}`;
let cb;
let handleResult = (state) => {
return (evt) => {
let stored = scriptMap.get(key);
if (state === 'loaded') {
stored.resolved = true;
resolve(src);
// stored.handlers.forEach(h => h.call(null, stored))
// stored.handlers = []
} else if (state === 'error') {
stored.errored = true;
// stored.handlers.forEach(h => h.call(null, stored))
// stored.handlers = [];
reject(evt)
}
stored.loaded = true;
cleanup();
}
};
const cleanup = () => {
if (global[cbName] && typeof global[cbName] === 'function') {
global[cbName] = null;
delete global[cbName]
}
};
tag.onload = handleResult('loaded');
tag.onerror = handleResult('error');
tag.onreadystatechange = () => {
handleResult(tag.readyState)
};
// Pick off callback, if there is one
if (src.match(/callback=CALLBACK_NAME/)) {
src = src.replace(/(callback=)[^\&]+/, `$1${cbName}`);
cb = window[cbName] = tag.onload;
} else {
tag.addEventListener('load', tag.onload)
}
tag.addEventListener('error', tag.onerror);
tag.src = src;
body.appendChild(tag);
return tag;
});
let initialState = {
loaded: false,
error: false,
promise,
tag
};
scriptMap.set(key, initialState);
}
return scriptMap.get(key).tag;
};
// let scriptTags = document.querySelectorAll('script')
//
// NodeList.prototype.filter = Array.prototype.filter;
// NodeList.prototype.map = Array.prototype.map;
// const initialScripts = scriptTags
// .filter(s => !!s.src)
// .map(s => s.src.split('?')[0])
// .reduce((memo, script) => {
// memo[script] = script;
// return memo;
// }, {});
Object.keys(scripts).forEach(function(key) {
const script = scripts[key];
const tag = window._scriptMap.has(key) ?
window._scriptMap.get(key).tag :
Cache._scriptTag(key, script);
Cache[key] = {
tag: tag,
onLoad: Cache._onLoad(key),
}
});
return Cache;
}
})(window);
export default ScriptCache;