@jsenv/core 39.14.3 → 40.0.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.
Files changed (34) hide show
  1. package/dist/js/directory_listing.js +16 -9
  2. package/dist/js/server_events_client.js +2 -2
  3. package/dist/jsenv_core.js +7220 -11089
  4. package/package.json +22 -19
  5. package/src/build/build.js +122 -93
  6. package/src/build/build_specifier_manager.js +103 -94
  7. package/src/build/build_urls_generator.js +1 -1
  8. package/src/build/{version_mappings_injection.js → mappings_injection.js} +62 -21
  9. package/src/build/start_build_server.js +46 -36
  10. package/src/dev/start_dev_server.js +246 -248
  11. package/src/helpers/watch_source_files.js +50 -36
  12. package/src/kitchen/fetched_content_compliance.js +4 -2
  13. package/src/kitchen/kitchen.js +31 -24
  14. package/src/kitchen/url_graph/references.js +10 -2
  15. package/src/kitchen/url_graph/url_graph.js +3 -0
  16. package/src/kitchen/url_graph/url_graph_visitor.js +3 -0
  17. package/src/plugins/autoreload/jsenv_plugin_autoreload_server.js +29 -16
  18. package/src/plugins/html_syntax_error_fallback/jsenv_plugin_html_syntax_error_fallback.js +1 -1
  19. package/src/plugins/plugin_controller.js +194 -200
  20. package/src/plugins/plugins.js +5 -0
  21. package/src/plugins/protocol_file/client/directory_listing.jsx +5 -0
  22. package/src/plugins/protocol_file/jsenv_plugin_directory_listing.js +92 -67
  23. package/src/plugins/protocol_file/jsenv_plugin_fs_redirection.js +17 -7
  24. package/src/plugins/protocol_file/jsenv_plugin_protocol_file.js +6 -0
  25. package/src/plugins/protocol_http/jsenv_plugin_protocol_http.js +33 -3
  26. package/src/plugins/reference_analysis/html/jsenv_plugin_html_reference_analysis.js +15 -22
  27. package/src/plugins/reference_analysis/js/jsenv_plugin_js_reference_analysis.js +53 -2
  28. package/src/plugins/resolution_node_esm/jsenv_plugin_node_esm_resolution.js +37 -30
  29. package/src/plugins/resolution_node_esm/node_esm_resolver.js +4 -8
  30. package/src/plugins/resolution_web/jsenv_plugin_web_resolution.js +8 -6
  31. package/src/plugins/server_events/client/server_events_client.js +2 -2
  32. package/src/plugins/server_events/jsenv_plugin_server_events.js +18 -16
  33. package/dist/js/ws.js +0 -6863
  34. package/src/helpers/lookup_package_directory.js +0 -9
@@ -1,46 +1,13 @@
1
1
  import { performance } from "node:perf_hooks";
2
+ import { jsenvPluginHtmlSyntaxErrorFallback } from "./html_syntax_error_fallback/jsenv_plugin_html_syntax_error_fallback.js";
2
3
 
3
- const HOOK_NAMES = [
4
- "init",
5
- "serve", // is called only during dev/tests
6
- "serveWebsocket",
7
- "resolveReference",
8
- "redirectReference",
9
- "transformReferenceSearchParams",
10
- "formatReference",
11
- "fetchUrlContent",
12
- "transformUrlContent",
13
- "finalizeUrlContent",
14
- "bundle", // is called only during build
15
- "optimizeUrlContent", // is called only during build
16
- "cooked",
17
- "augmentResponse", // is called only during dev/tests
18
- "destroy",
19
- "effect",
20
- ];
21
-
22
- export const createPluginController = (
23
- kitchenContext,
24
- initialPuginsMeta = {},
25
- ) => {
26
- const pluginsMeta = initialPuginsMeta;
27
-
28
- kitchenContext.getPluginMeta = (id) => {
29
- const value = pluginsMeta[id];
30
- return value;
31
- };
32
-
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) => {
4
+ export const createPluginStore = (plugins) => {
5
+ const allDevServerRoutes = [];
6
+ const pluginArray = [];
7
+ const addPlugin = (plugin) => {
41
8
  if (Array.isArray(plugin)) {
42
- for (const value of plugin) {
43
- addPlugin(value, options);
9
+ for (const subplugin of plugin) {
10
+ addPlugin(subplugin);
44
11
  }
45
12
  return;
46
13
  }
@@ -50,175 +17,140 @@ export const createPluginController = (
50
17
  if (!plugin.name) {
51
18
  plugin.name = "anonymous";
52
19
  }
53
- if (!testAppliesDuring(plugin) || !initPlugin(plugin)) {
54
- plugin.destroy?.();
55
- return;
56
- }
57
- pluginCandidates.push(plugin);
58
- };
59
- const testAppliesDuring = (plugin) => {
60
- const { appliesDuring } = plugin;
61
- if (appliesDuring === undefined) {
62
- // console.debug(`"appliesDuring" is undefined on ${pluginEntry.name}`)
63
- return true;
64
- }
65
- if (appliesDuring === "*") {
66
- return true;
67
- }
68
- if (typeof appliesDuring === "string") {
69
- if (appliesDuring !== "dev" && appliesDuring !== "build") {
70
- throw new TypeError(
71
- `"appliesDuring" must be "dev" or "build", got ${appliesDuring}`,
72
- );
20
+ if (plugin.devServerRoutes) {
21
+ const devServerRoutes = plugin.devServerRoutes;
22
+ for (const devServerRoute of devServerRoutes) {
23
+ allDevServerRoutes.push(devServerRoute);
73
24
  }
74
- if (kitchenContext[appliesDuring]) {
75
- return true;
76
- }
77
- return false;
78
25
  }
79
- if (typeof appliesDuring === "object") {
80
- for (const key of Object.keys(appliesDuring)) {
81
- if (!appliesDuring[key] && kitchenContext[key]) {
82
- return false;
83
- }
84
- if (appliesDuring[key] && kitchenContext[key]) {
85
- return true;
86
- }
87
- }
88
- // throw new Error(`"appliesDuring" is empty`)
89
- return false;
90
- }
91
- throw new TypeError(
92
- `"appliesDuring" must be an object or a string, got ${appliesDuring}`,
93
- );
26
+ pluginArray.push(plugin);
94
27
  };
95
- const initPlugin = (plugin) => {
96
- const { init } = plugin;
97
- if (!init) {
98
- return true;
99
- }
100
- const initReturnValue = init(kitchenContext, { plugin });
101
- if (initReturnValue === false) {
102
- return false;
28
+ addPlugin(jsenvPluginHtmlSyntaxErrorFallback());
29
+ for (const plugin of plugins) {
30
+ addPlugin(plugin);
31
+ }
32
+
33
+ return {
34
+ pluginArray,
35
+
36
+ allDevServerRoutes,
37
+ };
38
+ };
39
+
40
+ export const createPluginController = (
41
+ pluginStore,
42
+ kitchen,
43
+ { initialPuginsMeta = {} } = {},
44
+ ) => {
45
+ const pluginsMeta = initialPuginsMeta;
46
+ kitchen.context.getPluginMeta = (id) => {
47
+ const value = pluginsMeta[id];
48
+ return value;
49
+ };
50
+
51
+ // precompute a list of hooks per hookName because:
52
+ // 1. [MAJOR REASON] when debugging, there is less iteration (so much better)
53
+ // 2. [MINOR REASON] it should increase perf as there is less work to do
54
+ const hookSetMap = new Map();
55
+ const pluginCandidates = pluginStore.pluginArray;
56
+ const activePluginArray = [];
57
+ const pluginWithEffectCandidateForActivationArray = [];
58
+ for (const pluginCandidate of pluginCandidates) {
59
+ if (!testAppliesDuring(pluginCandidate, kitchen)) {
60
+ pluginCandidate.destroy?.();
61
+ continue;
103
62
  }
104
- if (typeof initReturnValue === "function" && !plugin.destroy) {
105
- plugin.destroy = initReturnValue;
63
+ const initPluginResult = initPlugin(pluginCandidate, kitchen);
64
+ if (!initPluginResult) {
65
+ pluginCandidate.destroy?.();
66
+ continue;
106
67
  }
107
- return true;
108
- };
109
- const pushPlugin = (...args) => {
110
- for (const arg of args) {
111
- addPlugin(arg);
68
+ if (pluginCandidate.effect) {
69
+ pluginWithEffectCandidateForActivationArray.push(pluginCandidate);
70
+ } else {
71
+ activePluginArray.push(pluginCandidate);
112
72
  }
113
- updateActivePlugins();
114
- };
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
73
+ }
122
74
 
123
- // 1.
124
- activePlugins.length = 0;
125
- // 2.
126
- for (const { cleanup } of activeEffectSet) {
127
- cleanup();
75
+ const activeEffectSet = new Set();
76
+ for (const pluginWithEffectCandidateForActivation of pluginWithEffectCandidateForActivationArray) {
77
+ const returnValue = pluginWithEffectCandidateForActivation.effect({
78
+ kitchenContext: kitchen.context,
79
+ otherPlugins: activePluginArray,
80
+ });
81
+ if (!returnValue) {
82
+ continue;
128
83
  }
129
- activeEffectSet.clear();
130
- for (const pluginCandidate of pluginCandidates) {
131
- const effect = pluginCandidate.effect;
132
- if (!effect) {
133
- activePlugins.push(pluginCandidate);
84
+ activePluginArray.push(pluginWithEffectCandidateForActivation);
85
+ activeEffectSet.add({
86
+ plugin: pluginWithEffectCandidateForActivation,
87
+ cleanup: typeof returnValue === "function" ? returnValue : () => {},
88
+ });
89
+ }
90
+ activePluginArray.sort((a, b) => {
91
+ return pluginCandidates.indexOf(a) - pluginCandidates.indexOf(b);
92
+ });
93
+ for (const activePlugin of activePluginArray) {
94
+ for (const key of Object.keys(activePlugin)) {
95
+ if (key === "meta") {
96
+ const value = activePlugin[key];
97
+ if (typeof value !== "object" || value === null) {
98
+ console.warn(`plugin.meta must be an object, got ${value}`);
99
+ continue;
100
+ }
101
+ Object.assign(pluginsMeta, value);
102
+ // any extension/modification on plugin.meta
103
+ // won't be taken into account so we freeze object
104
+ // to throw in case it happen
105
+ Object.freeze(value);
134
106
  continue;
135
107
  }
136
- }
137
- // 3.
138
- for (const pluginCandidate of pluginCandidates) {
139
- const effect = pluginCandidate.effect;
140
- if (!effect) {
108
+ if (
109
+ key === "name" ||
110
+ key === "appliesDuring" ||
111
+ key === "init" ||
112
+ key === "serverEvents" ||
113
+ key === "mustStayFirst" ||
114
+ key === "devServerRoutes" ||
115
+ key === "effect"
116
+ ) {
141
117
  continue;
142
118
  }
143
- const returnValue = effect({
144
- kitchenContext,
145
- otherPlugins: activePlugins,
146
- });
147
- if (!returnValue) {
119
+ const isHook = HOOK_NAMES.includes(key);
120
+ if (!isHook) {
121
+ console.warn(
122
+ `Unexpected "${key}" property on "${activePlugin.name}" plugin`,
123
+ );
148
124
  continue;
149
125
  }
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);
126
+ const hookName = key;
127
+ const hookValue = activePlugin[hookName];
128
+ if (hookValue) {
129
+ let hookSet = hookSetMap.get(hookName);
130
+ if (!hookSet) {
131
+ hookSet = new Set();
132
+ hookSetMap.set(hookName, hookSet);
218
133
  }
134
+ const hook = {
135
+ plugin: activePlugin,
136
+ name: hookName,
137
+ value: hookValue,
138
+ };
139
+ // if (position === "start") {
140
+ // let i = 0;
141
+ // while (i < group.length) {
142
+ // const before = group[i];
143
+ // if (!before.plugin.mustStayFirst) {
144
+ // break;
145
+ // }
146
+ // i++;
147
+ // }
148
+ // group.splice(i, 0, hook);
149
+ // } else {
150
+ hookSet.add(hook);
219
151
  }
220
152
  }
221
- };
153
+ }
222
154
 
223
155
  let lastPluginUsed = null;
224
156
  let currentPlugin = null;
@@ -268,7 +200,6 @@ export const createPluginController = (
268
200
  currentHookName = null;
269
201
  return valueReturned;
270
202
  };
271
-
272
203
  const callHooks = (hookName, info, callback) => {
273
204
  const hookSet = hookSetMap.get(hookName);
274
205
  if (!hookSet) {
@@ -296,7 +227,6 @@ export const createPluginController = (
296
227
  }
297
228
  }
298
229
  };
299
-
300
230
  const callHooksUntil = (hookName, info) => {
301
231
  const hookSet = hookSetMap.get(hookName);
302
232
  if (!hookSet) {
@@ -337,13 +267,10 @@ export const createPluginController = (
337
267
  };
338
268
 
339
269
  return {
340
- pluginsMeta,
341
- activePlugins,
342
- pushPlugin,
343
- getHookFunction,
270
+ activePlugins: activePluginArray,
271
+
344
272
  callHook,
345
273
  callAsyncHook,
346
-
347
274
  callHooks,
348
275
  callHooksUntil,
349
276
  callAsyncHooks,
@@ -355,6 +282,74 @@ export const createPluginController = (
355
282
  };
356
283
  };
357
284
 
285
+ const HOOK_NAMES = [
286
+ "init",
287
+ "devServerRoutes", // is called only during dev/tests
288
+ "resolveReference",
289
+ "redirectReference",
290
+ "transformReferenceSearchParams",
291
+ "formatReference",
292
+ "fetchUrlContent",
293
+ "transformUrlContent",
294
+ "finalizeUrlContent",
295
+ "bundle", // is called only during build
296
+ "optimizeUrlContent", // is called only during build
297
+ "cooked",
298
+ "augmentResponse", // is called only during dev/tests
299
+ "destroy",
300
+ "effect",
301
+ ];
302
+
303
+ const testAppliesDuring = (plugin, kitchen) => {
304
+ const { appliesDuring } = plugin;
305
+ if (appliesDuring === undefined) {
306
+ // console.debug(`"appliesDuring" is undefined on ${pluginEntry.name}`)
307
+ return true;
308
+ }
309
+ if (appliesDuring === "*") {
310
+ return true;
311
+ }
312
+ if (typeof appliesDuring === "string") {
313
+ if (appliesDuring !== "dev" && appliesDuring !== "build") {
314
+ throw new TypeError(
315
+ `"appliesDuring" must be "dev" or "build", got ${appliesDuring}`,
316
+ );
317
+ }
318
+ if (kitchen.context[appliesDuring]) {
319
+ return true;
320
+ }
321
+ return false;
322
+ }
323
+ if (typeof appliesDuring === "object") {
324
+ for (const key of Object.keys(appliesDuring)) {
325
+ if (!appliesDuring[key] && kitchen.context[key]) {
326
+ return false;
327
+ }
328
+ if (appliesDuring[key] && kitchen.context[key]) {
329
+ return true;
330
+ }
331
+ }
332
+ // throw new Error(`"appliesDuring" is empty`)
333
+ return false;
334
+ }
335
+ throw new TypeError(
336
+ `"appliesDuring" must be an object or a string, got ${appliesDuring}`,
337
+ );
338
+ };
339
+ const initPlugin = (plugin, kitchen) => {
340
+ const { init } = plugin;
341
+ if (!init) {
342
+ return true;
343
+ }
344
+ const initReturnValue = init(kitchen.context, { plugin });
345
+ if (initReturnValue === false) {
346
+ return false;
347
+ }
348
+ if (typeof initReturnValue === "function" && !plugin.destroy) {
349
+ plugin.destroy = initReturnValue;
350
+ }
351
+ return true;
352
+ };
358
353
  const getHookFunction = (
359
354
  hook,
360
355
  // can be undefined, reference, or urlInfo
@@ -391,7 +386,6 @@ const assertAndNormalizeReturnValue = (hook, returnValue, info) => {
391
386
  }
392
387
  return returnValue;
393
388
  };
394
-
395
389
  const returnValueAssertions = [
396
390
  {
397
391
  name: "url_assertion",
@@ -26,7 +26,9 @@ import { jsenvPluginCleanHTML } from "./clean_html/jsenv_plugin_clean_html.js";
26
26
 
27
27
  export const getCorePlugins = ({
28
28
  rootDirectoryUrl,
29
+ mainFilePath,
29
30
  runtimeCompat,
31
+ sourceFilesConfig,
30
32
 
31
33
  referenceAnalysis = {},
32
34
  nodeEsmResolution = {},
@@ -82,6 +84,9 @@ export const getCorePlugins = ({
82
84
  magicExtensions,
83
85
  magicDirectoryIndex,
84
86
  directoryListing,
87
+ rootDirectoryUrl,
88
+ mainFilePath,
89
+ sourceFilesConfig,
85
90
  }),
86
91
  {
87
92
  name: "jsenv:resolve_root_as_main",
@@ -68,6 +68,11 @@ const ErrorMessage = () => {
68
68
  </code>
69
69
  .
70
70
  </span>
71
+ <br />
72
+ <span className="error_text" style="font-size: 70%;">
73
+ See also available routes in the{" "}
74
+ <a href="/.internal/route_inspector">route inspector</a>.
75
+ </span>
71
76
  </p>
72
77
  );
73
78
  };