-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathModuleLoader.js
More file actions
142 lines (122 loc) · 4.05 KB
/
Copy pathModuleLoader.js
File metadata and controls
142 lines (122 loc) · 4.05 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
140
141
142
/**
* Dynamic asset loader that fetches & imports HTML, CSS, & JS.
*
* @author Vicente G. (@SharkPool-SP)
*
* @version 2026.1.0.0
*/
class ModuleLoader {
/** URLs of CSS and JavaScript dependencies that have already been loaded */
static LOADED_DEPENDENCIES = new Set();
/** Cached HTML content keyed by its URL */
static CONTENT_CACHE = new Map();
/**
* Imports a dependency and applies it to the DOM.
* Supports HTML, CSS, and JavaScript.
*
* @param {String} url URL of the dependency to load
* @param {Element} [opt_container] Optional container to put the content of an HTML dependancy,
* defaults to the document body
*/
static async importDependency(url, opt_container) {
if (ModuleLoader.LOADED_DEPENDENCIES.has(url)) return;
const container =
opt_container && opt_container instanceof Element
? opt_container
: document.body;
const pathname = new URL(url, window.location.href).pathname;
const type = pathname
.substring(pathname.lastIndexOf(".") + 1)
.toLowerCase();
switch (type) {
case "html": {
let htmlText;
if (ModuleLoader.CONTENT_CACHE.has(url)) {
htmlText = ModuleLoader.CONTENT_CACHE.get(url);
} else {
const response = await fetch(url);
if (!response.ok) {
console.error(response);
throw new Error(`Couldn't fetch dependency: ${url}`);
}
htmlText = await response.text();
ModuleLoader.CONTENT_CACHE.set(url, htmlText);
}
container.insertAdjacentHTML("beforeend", htmlText);
return;
}
case "css": {
const element = document.createElement("link");
element.rel = "stylesheet";
element.href = url;
await new Promise((resolve, reject) => {
element.onload = resolve;
element.onerror = (error) => {
console.error(error);
reject(new Error(`Failed to load dependency: ${url}`));
};
document.head.appendChild(element);
});
ModuleLoader.LOADED_DEPENDENCIES.add(url);
return;
}
case "js": {
const element = document.createElement("script");
element.type = "module";
element.src = url;
await new Promise((resolve, reject) => {
element.onload = resolve;
element.onerror = (error) => {
console.error(error);
reject(new Error(`Failed to load dependency: ${url}`));
};
document.body.appendChild(element);
});
ModuleLoader.LOADED_DEPENDENCIES.add(url);
return;
}
default:
throw new Error(`Unsupported dependency type: ${type}`);
}
}
/**
* Imports a list of dependencies.
*
* @param {Array<String>} urls Array of URL dependencies to load
* @returns {Promise<void>}
*/
static async importDependencies(urls) {
return Promise.all(urls.map((url) => ModuleLoader.importDependency(url)));
}
/**
* Dynamically imports an ES module.
*
* @param {String} url URL of the module to load
* @returns {Promise<Module>} Module namespace object
*/
static importModule(url) {
return import(url);
}
/**
* Fetches and evaluates a JavaScript source,
* returning the result of the specified expression.
*
* Unlike importModule(), this does not execute the source as an
* ES module (i.e., does not support import/export syntax).
*
* @param {String} url URL of the JavaScript source to evaluate
* @param {String} returnExpression Expression whose result should be returned
* @returns {Promise<*>} Result of the evaluated expression
*/
static async evalAndReturn(url, returnExpression) {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Couldn't fetch module: ${url}`);
}
const moduleCode = await response.text();
const code = `${moduleCode}\nreturn ${returnExpression};`;
const executor = new Function(code);
return executor();
}
}
export { ModuleLoader };