@jsenv/core 39.11.2 → 39.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/css/directory_listing.css +211 -0
- package/dist/html/directory_listing.html +18 -0
- package/dist/js/directory_listing.js +240 -0
- package/dist/jsenv_core.js +1057 -757
- package/dist/other/dir.png +0 -0
- package/dist/other/file.png +0 -0
- package/dist/other/home.svg +6 -0
- package/package.json +6 -6
- package/src/build/build.js +7 -7
- package/src/build/build_specifier_manager.js +0 -1
- package/src/dev/start_dev_server.js +39 -49
- package/src/kitchen/kitchen.js +20 -4
- package/src/kitchen/out_directory_url.js +2 -1
- package/src/kitchen/url_graph/references.js +3 -1
- package/src/kitchen/url_graph/url_graph.js +1 -0
- package/src/kitchen/url_graph/url_info_transformations.js +37 -4
- package/src/plugins/inlining/jsenv_plugin_inlining_into_html.js +10 -8
- package/src/plugins/plugin_controller.js +170 -114
- package/src/plugins/plugins.js +5 -4
- package/src/plugins/protocol_file/client/assets/home.svg +6 -0
- package/src/plugins/protocol_file/client/directory_listing.css +190 -0
- package/src/plugins/protocol_file/client/directory_listing.html +18 -0
- package/src/plugins/protocol_file/client/directory_listing.jsx +250 -0
- package/src/plugins/protocol_file/file_and_server_urls_converter.js +32 -0
- package/src/plugins/protocol_file/jsenv_plugin_directory_listing.js +398 -0
- package/src/plugins/protocol_file/jsenv_plugin_protocol_file.js +40 -333
- package/src/plugins/protocol_http/jsenv_plugin_protocol_http.js +3 -2
- package/src/plugins/reference_analysis/html/jsenv_plugin_html_reference_analysis.js +7 -6
- package/src/plugins/reference_analysis/js/jsenv_plugin_js_reference_analysis.js +1 -3
- package/src/plugins/reference_analysis/jsenv_plugin_reference_analysis.js +2 -18
- package/src/plugins/server_events/jsenv_plugin_server_events.js +100 -0
- package/dist/html/directory.html +0 -165
- package/dist/html/html_404_and_ancestor_dir.html +0 -203
- package/src/plugins/protocol_file/client/assets/directory.css +0 -133
- package/src/plugins/protocol_file/client/directory.html +0 -17
- package/src/plugins/protocol_file/client/html_404_and_ancestor_dir.html +0 -54
- package/src/plugins/server_events/jsenv_plugin_server_events_client_injection.js +0 -37
|
@@ -3,6 +3,7 @@ import { performance } from "node:perf_hooks";
|
|
|
3
3
|
const HOOK_NAMES = [
|
|
4
4
|
"init",
|
|
5
5
|
"serve", // is called only during dev/tests
|
|
6
|
+
"serveWebsocket",
|
|
6
7
|
"resolveReference",
|
|
7
8
|
"redirectReference",
|
|
8
9
|
"transformReferenceSearchParams",
|
|
@@ -15,6 +16,7 @@ const HOOK_NAMES = [
|
|
|
15
16
|
"cooked",
|
|
16
17
|
"augmentResponse", // is called only during dev/tests
|
|
17
18
|
"destroy",
|
|
19
|
+
"effect",
|
|
18
20
|
];
|
|
19
21
|
|
|
20
22
|
export const createPluginController = (
|
|
@@ -28,19 +30,18 @@ export const createPluginController = (
|
|
|
28
30
|
return value;
|
|
29
31
|
};
|
|
30
32
|
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
//
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
const pluginCandidates = [];
|
|
34
|
+
const activeEffectSet = new Set();
|
|
35
|
+
const activePlugins = [];
|
|
36
|
+
// precompute a list of hooks per hookName because:
|
|
37
|
+
// 1. [MAJOR REASON] when debugging, there is less iteration (so much better)
|
|
38
|
+
// 2. [MINOR REASON] it should increase perf as there is less work to do
|
|
39
|
+
const hookSetMap = new Map();
|
|
40
|
+
const addPlugin = (plugin, options) => {
|
|
37
41
|
if (Array.isArray(plugin)) {
|
|
38
|
-
|
|
39
|
-
|
|
42
|
+
for (const value of plugin) {
|
|
43
|
+
addPlugin(value, options);
|
|
40
44
|
}
|
|
41
|
-
plugin.forEach((plugin) => {
|
|
42
|
-
addPlugin(plugin, { position });
|
|
43
|
-
});
|
|
44
45
|
return;
|
|
45
46
|
}
|
|
46
47
|
if (plugin === null || typeof plugin !== "object") {
|
|
@@ -50,65 +51,10 @@ export const createPluginController = (
|
|
|
50
51
|
plugin.name = "anonymous";
|
|
51
52
|
}
|
|
52
53
|
if (!testAppliesDuring(plugin) || !initPlugin(plugin)) {
|
|
53
|
-
|
|
54
|
-
plugin.destroy();
|
|
55
|
-
}
|
|
54
|
+
plugin.destroy?.();
|
|
56
55
|
return;
|
|
57
56
|
}
|
|
58
|
-
|
|
59
|
-
for (const key of Object.keys(plugin)) {
|
|
60
|
-
if (key === "meta") {
|
|
61
|
-
const value = plugin[key];
|
|
62
|
-
if (typeof value !== "object" || value === null) {
|
|
63
|
-
console.warn(`plugin.meta must be an object, got ${value}`);
|
|
64
|
-
continue;
|
|
65
|
-
}
|
|
66
|
-
Object.assign(pluginsMeta, value);
|
|
67
|
-
// any extension/modification on plugin.meta
|
|
68
|
-
// won't be taken into account so we freeze object
|
|
69
|
-
// to throw in case it happen
|
|
70
|
-
Object.freeze(value);
|
|
71
|
-
continue;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
if (
|
|
75
|
-
key === "name" ||
|
|
76
|
-
key === "appliesDuring" ||
|
|
77
|
-
key === "init" ||
|
|
78
|
-
key === "serverEvents" ||
|
|
79
|
-
key === "mustStayFirst"
|
|
80
|
-
) {
|
|
81
|
-
continue;
|
|
82
|
-
}
|
|
83
|
-
const isHook = HOOK_NAMES.includes(key);
|
|
84
|
-
if (!isHook) {
|
|
85
|
-
console.warn(`Unexpected "${key}" property on "${plugin.name}" plugin`);
|
|
86
|
-
continue;
|
|
87
|
-
}
|
|
88
|
-
const hookName = key;
|
|
89
|
-
const hookValue = plugin[hookName];
|
|
90
|
-
if (hookValue) {
|
|
91
|
-
const group = hookGroups[hookName] || (hookGroups[hookName] = []);
|
|
92
|
-
const hook = {
|
|
93
|
-
plugin,
|
|
94
|
-
name: hookName,
|
|
95
|
-
value: hookValue,
|
|
96
|
-
};
|
|
97
|
-
if (position === "start") {
|
|
98
|
-
let i = 0;
|
|
99
|
-
while (i < group.length) {
|
|
100
|
-
const before = group[i];
|
|
101
|
-
if (!before.plugin.mustStayFirst) {
|
|
102
|
-
break;
|
|
103
|
-
}
|
|
104
|
-
i++;
|
|
105
|
-
}
|
|
106
|
-
group.splice(i, 0, hook);
|
|
107
|
-
} else {
|
|
108
|
-
group.push(hook);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
57
|
+
pluginCandidates.push(plugin);
|
|
112
58
|
};
|
|
113
59
|
const testAppliesDuring = (plugin) => {
|
|
114
60
|
const { appliesDuring } = plugin;
|
|
@@ -147,22 +93,131 @@ export const createPluginController = (
|
|
|
147
93
|
);
|
|
148
94
|
};
|
|
149
95
|
const initPlugin = (plugin) => {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
96
|
+
const { init } = plugin;
|
|
97
|
+
if (!init) {
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
const initReturnValue = init(kitchenContext, { plugin });
|
|
101
|
+
if (initReturnValue === false) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
if (typeof initReturnValue === "function" && !plugin.destroy) {
|
|
105
|
+
plugin.destroy = initReturnValue;
|
|
158
106
|
}
|
|
159
107
|
return true;
|
|
160
108
|
};
|
|
161
|
-
const pushPlugin = (
|
|
162
|
-
|
|
109
|
+
const pushPlugin = (...args) => {
|
|
110
|
+
for (const arg of args) {
|
|
111
|
+
addPlugin(arg);
|
|
112
|
+
}
|
|
113
|
+
updateActivePlugins();
|
|
163
114
|
};
|
|
164
|
-
const
|
|
165
|
-
|
|
115
|
+
const updateActivePlugins = () => {
|
|
116
|
+
// construct activePlugins and hooks according
|
|
117
|
+
// to the one present in candidates and their effects
|
|
118
|
+
// 1. active plugins is an empty array
|
|
119
|
+
// 2. all active effects are cleaned-up
|
|
120
|
+
// 3. all effects are re-activated if still relevant
|
|
121
|
+
// 4. hooks are precomputed according to plugin order
|
|
122
|
+
|
|
123
|
+
// 1.
|
|
124
|
+
activePlugins.length = 0;
|
|
125
|
+
// 2.
|
|
126
|
+
for (const { cleanup } of activeEffectSet) {
|
|
127
|
+
cleanup();
|
|
128
|
+
}
|
|
129
|
+
activeEffectSet.clear();
|
|
130
|
+
for (const pluginCandidate of pluginCandidates) {
|
|
131
|
+
const effect = pluginCandidate.effect;
|
|
132
|
+
if (!effect) {
|
|
133
|
+
activePlugins.push(pluginCandidate);
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// 3.
|
|
138
|
+
for (const pluginCandidate of pluginCandidates) {
|
|
139
|
+
const effect = pluginCandidate.effect;
|
|
140
|
+
if (!effect) {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
const returnValue = effect({
|
|
144
|
+
kitchenContext,
|
|
145
|
+
otherPlugins: activePlugins,
|
|
146
|
+
});
|
|
147
|
+
if (!returnValue) {
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
activePlugins.push(pluginCandidate);
|
|
151
|
+
activeEffectSet.add({
|
|
152
|
+
plugin: pluginCandidate,
|
|
153
|
+
cleanup: typeof returnValue === "function" ? returnValue : () => {},
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
// 4.
|
|
157
|
+
activePlugins.sort((a, b) => {
|
|
158
|
+
return pluginCandidates.indexOf(a) - pluginCandidates.indexOf(b);
|
|
159
|
+
});
|
|
160
|
+
hookSetMap.clear();
|
|
161
|
+
for (const activePlugin of activePlugins) {
|
|
162
|
+
for (const key of Object.keys(activePlugin)) {
|
|
163
|
+
if (key === "meta") {
|
|
164
|
+
const value = activePlugin[key];
|
|
165
|
+
if (typeof value !== "object" || value === null) {
|
|
166
|
+
console.warn(`plugin.meta must be an object, got ${value}`);
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
Object.assign(pluginsMeta, value);
|
|
170
|
+
// any extension/modification on plugin.meta
|
|
171
|
+
// won't be taken into account so we freeze object
|
|
172
|
+
// to throw in case it happen
|
|
173
|
+
Object.freeze(value);
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
if (
|
|
177
|
+
key === "name" ||
|
|
178
|
+
key === "appliesDuring" ||
|
|
179
|
+
key === "init" ||
|
|
180
|
+
key === "serverEvents" ||
|
|
181
|
+
key === "mustStayFirst" ||
|
|
182
|
+
key === "effect"
|
|
183
|
+
) {
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
const isHook = HOOK_NAMES.includes(key);
|
|
187
|
+
if (!isHook) {
|
|
188
|
+
console.warn(
|
|
189
|
+
`Unexpected "${key}" property on "${activePlugin.name}" plugin`,
|
|
190
|
+
);
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
const hookName = key;
|
|
194
|
+
const hookValue = activePlugin[hookName];
|
|
195
|
+
if (hookValue) {
|
|
196
|
+
let hookSet = hookSetMap.get(hookName);
|
|
197
|
+
if (!hookSet) {
|
|
198
|
+
hookSet = new Set();
|
|
199
|
+
hookSetMap.set(hookName, hookSet);
|
|
200
|
+
}
|
|
201
|
+
const hook = {
|
|
202
|
+
plugin: activePlugin,
|
|
203
|
+
name: hookName,
|
|
204
|
+
value: hookValue,
|
|
205
|
+
};
|
|
206
|
+
// if (position === "start") {
|
|
207
|
+
// let i = 0;
|
|
208
|
+
// while (i < group.length) {
|
|
209
|
+
// const before = group[i];
|
|
210
|
+
// if (!before.plugin.mustStayFirst) {
|
|
211
|
+
// break;
|
|
212
|
+
// }
|
|
213
|
+
// i++;
|
|
214
|
+
// }
|
|
215
|
+
// group.splice(i, 0, hook);
|
|
216
|
+
// } else {
|
|
217
|
+
hookSet.add(hook);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
166
221
|
};
|
|
167
222
|
|
|
168
223
|
let lastPluginUsed = null;
|
|
@@ -215,75 +270,76 @@ export const createPluginController = (
|
|
|
215
270
|
};
|
|
216
271
|
|
|
217
272
|
const callHooks = (hookName, info, callback) => {
|
|
218
|
-
const
|
|
219
|
-
if (
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
273
|
+
const hookSet = hookSetMap.get(hookName);
|
|
274
|
+
if (!hookSet) {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
const setHookParams = (firstArg = info) => {
|
|
278
|
+
info = firstArg;
|
|
279
|
+
};
|
|
280
|
+
for (const hook of hookSet) {
|
|
281
|
+
const returnValue = callHook(hook, info);
|
|
282
|
+
if (returnValue && callback) {
|
|
283
|
+
callback(returnValue, hook.plugin, setHookParams);
|
|
228
284
|
}
|
|
229
285
|
}
|
|
230
286
|
};
|
|
231
287
|
const callAsyncHooks = async (hookName, info, callback, options) => {
|
|
232
|
-
const
|
|
233
|
-
if (
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
288
|
+
const hookSet = hookSetMap.get(hookName);
|
|
289
|
+
if (!hookSet) {
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
for (const hook of hookSet) {
|
|
293
|
+
const returnValue = await callAsyncHook(hook, info, options);
|
|
294
|
+
if (returnValue && callback) {
|
|
295
|
+
await callback(returnValue, hook.plugin);
|
|
239
296
|
}
|
|
240
297
|
}
|
|
241
298
|
};
|
|
242
299
|
|
|
243
300
|
const callHooksUntil = (hookName, info) => {
|
|
244
|
-
const
|
|
245
|
-
if (
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
301
|
+
const hookSet = hookSetMap.get(hookName);
|
|
302
|
+
if (!hookSet) {
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
305
|
+
for (const hook of hookSet) {
|
|
306
|
+
const returnValue = callHook(hook, info);
|
|
307
|
+
if (returnValue) {
|
|
308
|
+
return returnValue;
|
|
251
309
|
}
|
|
252
310
|
}
|
|
253
311
|
return null;
|
|
254
312
|
};
|
|
255
313
|
const callAsyncHooksUntil = async (hookName, info, options) => {
|
|
256
|
-
const
|
|
257
|
-
if (!
|
|
314
|
+
const hookSet = hookSetMap.get(hookName);
|
|
315
|
+
if (!hookSet) {
|
|
258
316
|
return null;
|
|
259
317
|
}
|
|
260
|
-
if (
|
|
318
|
+
if (hookSet.size === 0) {
|
|
261
319
|
return null;
|
|
262
320
|
}
|
|
321
|
+
const iterator = hookSet.values()[Symbol.iterator]();
|
|
263
322
|
let result;
|
|
264
|
-
let index = 0;
|
|
265
323
|
const visit = async () => {
|
|
266
|
-
|
|
324
|
+
const { done, value: hook } = iterator.next();
|
|
325
|
+
if (done) {
|
|
267
326
|
return;
|
|
268
327
|
}
|
|
269
|
-
const hook = hooks[index];
|
|
270
328
|
const returnValue = await callAsyncHook(hook, info, options);
|
|
271
329
|
if (returnValue) {
|
|
272
330
|
result = returnValue;
|
|
273
331
|
return;
|
|
274
332
|
}
|
|
275
|
-
index++;
|
|
276
333
|
await visit();
|
|
277
334
|
};
|
|
278
|
-
await visit(
|
|
335
|
+
await visit();
|
|
279
336
|
return result;
|
|
280
337
|
};
|
|
281
338
|
|
|
282
339
|
return {
|
|
283
340
|
pluginsMeta,
|
|
284
|
-
|
|
341
|
+
activePlugins,
|
|
285
342
|
pushPlugin,
|
|
286
|
-
unshiftPlugin,
|
|
287
343
|
getHookFunction,
|
|
288
344
|
callHook,
|
|
289
345
|
callAsyncHook,
|
package/src/plugins/plugins.js
CHANGED
|
@@ -65,8 +65,8 @@ export const getCorePlugins = ({
|
|
|
65
65
|
jsenvPluginReferenceAnalysis(referenceAnalysis),
|
|
66
66
|
...(injections ? [jsenvPluginInjections(injections)] : []),
|
|
67
67
|
jsenvPluginTranspilation(transpilation),
|
|
68
|
+
// "jsenvPluginInlining" must be very soon because all other plugins will react differently once they see the file is inlined
|
|
68
69
|
...(inlining ? [jsenvPluginInlining()] : []),
|
|
69
|
-
...(supervisor ? [jsenvPluginSupervisor(supervisor)] : []), // after inline as it needs inline script to be cooked
|
|
70
70
|
|
|
71
71
|
/* When resolving references the following applies by default:
|
|
72
72
|
- http urls are resolved by jsenvPluginHttpUrls
|
|
@@ -80,7 +80,6 @@ export const getCorePlugins = ({
|
|
|
80
80
|
magicDirectoryIndex,
|
|
81
81
|
directoryListingUrlMocks,
|
|
82
82
|
}),
|
|
83
|
-
|
|
84
83
|
{
|
|
85
84
|
name: "jsenv:resolve_root_as_main",
|
|
86
85
|
appliesDuring: "*",
|
|
@@ -99,12 +98,14 @@ export const getCorePlugins = ({
|
|
|
99
98
|
: []),
|
|
100
99
|
jsenvPluginWebResolution(),
|
|
101
100
|
jsenvPluginDirectoryReferenceEffect(directoryReferenceEffect),
|
|
102
|
-
|
|
103
101
|
jsenvPluginVersionSearchParam(),
|
|
102
|
+
|
|
103
|
+
// "jsenvPluginSupervisor" MUST be after "jsenvPluginInlining" as it needs inline script to be cooked
|
|
104
|
+
...(supervisor ? [jsenvPluginSupervisor(supervisor)] : []),
|
|
105
|
+
|
|
104
106
|
jsenvPluginCommonJsGlobals(),
|
|
105
107
|
jsenvPluginImportMetaScenarios(),
|
|
106
108
|
...(scenarioPlaceholders ? [jsenvPluginGlobalScenarios()] : []),
|
|
107
|
-
|
|
108
109
|
jsenvPluginNodeRuntime({ runtimeCompat }),
|
|
109
110
|
|
|
110
111
|
jsenvPluginImportMetaHot(),
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<svg id="root" fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="30.4 23.47 40.05 45.63">
|
|
2
|
+
<path d="M32.5,69.1c-0.6,0-1.1-0.2-1.4-0.5c-0.7-0.6-0.7-1.5-0.7-1.8V41.1h4v24h9v4H32.9C32.8,69.1,32.6,69.1,32.5,69.1z" />
|
|
3
|
+
<path d="M67.8,69.1c-0.2,0-0.3,0-0.5,0c-0.1,0-0.1,0-0.2,0H56.4v-4h10v-23h4v24.7c0,0.6-0.3,1.2-0.7,1.7 C69.1,69,68.3,69.1,67.8,69.1z" />
|
|
4
|
+
<path d="M68.4,44.1c-0.5,0-1-0.2-1.3-0.5L50,28.2L33.7,42.6c-0.8,0.7-2.1,0.7-2.8-0.2c-0.7-0.8-0.7-2.1,0.2-2.8L48.7,24 c0.8-0.7,1.9-0.7,2.7,0l18.4,16.6c0.8,0.7,0.9,2,0.1,2.8C69.5,43.8,68.9,44.1,68.4,44.1z" />
|
|
5
|
+
<path d="M58.4,69.1h-4v-13h-8v13h-4v-15c0-1.1,0.9-2,2-2h12c1.1,0,2,0.9,2,2V69.1z" />
|
|
6
|
+
</svg>
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
html,
|
|
2
|
+
body,
|
|
3
|
+
h1,
|
|
4
|
+
div,
|
|
5
|
+
p,
|
|
6
|
+
ul,
|
|
7
|
+
li,
|
|
8
|
+
a,
|
|
9
|
+
p,
|
|
10
|
+
button {
|
|
11
|
+
margin: 0;
|
|
12
|
+
padding: 0;
|
|
13
|
+
border: 0;
|
|
14
|
+
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
|
15
|
+
-webkit-font-smoothing: antialiased;
|
|
16
|
+
-moz-osx-font-smoothing: grayscale;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/* ERROR MESSAGE */
|
|
20
|
+
.error_message {
|
|
21
|
+
background-color: #fce4e4;
|
|
22
|
+
border: 1px solid #fcc2c3;
|
|
23
|
+
padding: 1.5em 1.2em;
|
|
24
|
+
margin: 1em;
|
|
25
|
+
display: block;
|
|
26
|
+
}
|
|
27
|
+
.error_text {
|
|
28
|
+
color: #853611;
|
|
29
|
+
font-weight: bold;
|
|
30
|
+
text-shadow: 1px 1px rgba(250, 250, 250, 0.3);
|
|
31
|
+
}
|
|
32
|
+
.hint_message {
|
|
33
|
+
background-color: lightblue;
|
|
34
|
+
border: 1px solid #fcc2c3;
|
|
35
|
+
padding: 1.5em 1.2em;
|
|
36
|
+
margin: 1em;
|
|
37
|
+
display: block;
|
|
38
|
+
}
|
|
39
|
+
.hint_text {
|
|
40
|
+
color: black;
|
|
41
|
+
font-weight: bold;
|
|
42
|
+
line-height: 2em;
|
|
43
|
+
text-shadow: 1px 1px rgba(250, 250, 250, 0.3);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* NAV */
|
|
47
|
+
.nav {
|
|
48
|
+
font-size: 16px;
|
|
49
|
+
font-weight: bold;
|
|
50
|
+
margin: 20px 25px 15px 25px;
|
|
51
|
+
display: flex;
|
|
52
|
+
gap: 0.3em;
|
|
53
|
+
}
|
|
54
|
+
.nav_item {
|
|
55
|
+
display: flex;
|
|
56
|
+
align-items: center;
|
|
57
|
+
}
|
|
58
|
+
.nav_item_icon {
|
|
59
|
+
display: flex;
|
|
60
|
+
width: 1em;
|
|
61
|
+
height: 1em;
|
|
62
|
+
margin-right: 0.25em;
|
|
63
|
+
color: #f1f1f1;
|
|
64
|
+
}
|
|
65
|
+
a.nav_item_icon {
|
|
66
|
+
text-decoration: none;
|
|
67
|
+
}
|
|
68
|
+
.nav_item_text {
|
|
69
|
+
position: relative;
|
|
70
|
+
color: inherit;
|
|
71
|
+
}
|
|
72
|
+
a.nav_item_text {
|
|
73
|
+
text-decoration: none;
|
|
74
|
+
}
|
|
75
|
+
.nav_item[data-404] .nav_item_text {
|
|
76
|
+
background-color: yellow;
|
|
77
|
+
color: #853611;
|
|
78
|
+
}
|
|
79
|
+
.file_path_bad {
|
|
80
|
+
text-decoration-line: underline;
|
|
81
|
+
text-decoration-color: red;
|
|
82
|
+
text-decoration-style: wavy;
|
|
83
|
+
}
|
|
84
|
+
.nav_item_badge_404 {
|
|
85
|
+
color: #853611;
|
|
86
|
+
text-align: center;
|
|
87
|
+
background-color: yellow;
|
|
88
|
+
border-radius: 5px;
|
|
89
|
+
padding: 0.2em 0.1em;
|
|
90
|
+
font-size: 0.8em;
|
|
91
|
+
margin-right: 0.3em;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/* CONTENT */
|
|
95
|
+
.directory_empty_message {
|
|
96
|
+
margin: 1em;
|
|
97
|
+
padding: 0.5em;
|
|
98
|
+
color: #bbbbbb;
|
|
99
|
+
}
|
|
100
|
+
.directory_content {
|
|
101
|
+
margin: 10px 15px 10px 15px;
|
|
102
|
+
list-style-type: none;
|
|
103
|
+
border-radius: 3px;
|
|
104
|
+
}
|
|
105
|
+
.directory_content_item {
|
|
106
|
+
display: flex;
|
|
107
|
+
position: relative;
|
|
108
|
+
padding: 10px 15px 10px 15px;
|
|
109
|
+
font-size: 15px;
|
|
110
|
+
min-height: 19px;
|
|
111
|
+
box-sizing: border-box;
|
|
112
|
+
align-items: center;
|
|
113
|
+
}
|
|
114
|
+
.directory_content_item_link {
|
|
115
|
+
display: flex;
|
|
116
|
+
flex: 1;
|
|
117
|
+
gap: 10px;
|
|
118
|
+
align-items: center;
|
|
119
|
+
text-decoration: none;
|
|
120
|
+
}
|
|
121
|
+
.directory_content_item_icon {
|
|
122
|
+
width: 15px;
|
|
123
|
+
aspect-ratio: 1/1;
|
|
124
|
+
display: flex;
|
|
125
|
+
color: #f1f1f1;
|
|
126
|
+
}
|
|
127
|
+
.directory_content_item_icon img {
|
|
128
|
+
width: 100%;
|
|
129
|
+
}
|
|
130
|
+
.directory_content_item_text {
|
|
131
|
+
display: flex;
|
|
132
|
+
}
|
|
133
|
+
.directory_content_item:last-child {
|
|
134
|
+
border-bottom: none;
|
|
135
|
+
padding-bottom: 10px;
|
|
136
|
+
}
|
|
137
|
+
.directory_content_item_arrow {
|
|
138
|
+
display: flex;
|
|
139
|
+
color: #666666;
|
|
140
|
+
height: 10px;
|
|
141
|
+
stroke-width: 15%;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
body[data-theme="dark"] {
|
|
145
|
+
background: #121212;
|
|
146
|
+
}
|
|
147
|
+
body[data-theme="light"] {
|
|
148
|
+
background: #f1f1f1;
|
|
149
|
+
}
|
|
150
|
+
body[data-theme="dark"] .nav_item {
|
|
151
|
+
color: #999999;
|
|
152
|
+
}
|
|
153
|
+
body[data-theme="dark"] .nav_item[data-current] {
|
|
154
|
+
color: #f1f1f1;
|
|
155
|
+
}
|
|
156
|
+
body[data-theme="light"] .nav_item {
|
|
157
|
+
color: #000000;
|
|
158
|
+
}
|
|
159
|
+
body[data-theme="dark"] .directory_separator {
|
|
160
|
+
color: #666666;
|
|
161
|
+
}
|
|
162
|
+
body[data-theme="light"] .directory_separator {
|
|
163
|
+
color: #999999;
|
|
164
|
+
}
|
|
165
|
+
body[data-theme="dark"] .directory_content {
|
|
166
|
+
background: #1e1e1e;
|
|
167
|
+
}
|
|
168
|
+
body[data-theme="light"] .directory_content {
|
|
169
|
+
background: #fefefe;
|
|
170
|
+
}
|
|
171
|
+
body[data-theme="dark"] .directory_content_item {
|
|
172
|
+
border-bottom: 1px solid #121212;
|
|
173
|
+
}
|
|
174
|
+
body[data-theme="light"] .directory_content_item {
|
|
175
|
+
border-bottom: 1px solid #f1f1f1;
|
|
176
|
+
}
|
|
177
|
+
body[data-theme="dark"] .directory_content_item[data-type="dir"]::before {
|
|
178
|
+
border-top: 2px solid #666666;
|
|
179
|
+
border-right: 2px solid #666666;
|
|
180
|
+
}
|
|
181
|
+
body[data-theme="light"] .directory_content_item[data-type="file"]::before {
|
|
182
|
+
border-top: 2px solid #999999;
|
|
183
|
+
border-right: 2px solid #999999;
|
|
184
|
+
}
|
|
185
|
+
body[data-theme="dark"] .directory_content_item a {
|
|
186
|
+
color: #ffffff;
|
|
187
|
+
}
|
|
188
|
+
body[data-theme="light"] .directory_content_item a {
|
|
189
|
+
color: #000000;
|
|
190
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>Directory explorer</title>
|
|
5
|
+
<meta charset="utf-8" />
|
|
6
|
+
<link rel="icon" href="data:," />
|
|
7
|
+
<link rel="stylesheet" href="./directory_listing.css" />
|
|
8
|
+
</head>
|
|
9
|
+
|
|
10
|
+
<body data-theme="dark">
|
|
11
|
+
<div id="root"></div>
|
|
12
|
+
<script>
|
|
13
|
+
// eslint-disable-next-line no-undef
|
|
14
|
+
window.DIRECTORY_LISTING = __DIRECTORY_LISTING__;
|
|
15
|
+
</script>
|
|
16
|
+
<script type="module" src="./directory_listing.jsx"></script>
|
|
17
|
+
</body>
|
|
18
|
+
</html>
|