@jsenv/core 27.0.0-alpha.6 → 27.0.0-alpha.60
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/event_source_client.js +545 -0
- package/dist/event_source_client.js.map +187 -0
- package/dist/html_supervisor_installer.js +1236 -0
- package/dist/html_supervisor_installer.js.map +337 -0
- package/dist/html_supervisor_setup.js +95 -0
- package/dist/html_supervisor_setup.js.map +57 -0
- package/dist/import_meta_hot.js +86 -0
- package/dist/import_meta_hot.js.map +42 -0
- package/main.js +8 -1
- package/package.json +30 -28
- package/readme.md +6 -14
- package/src/build/build.js +943 -555
- package/src/build/build_urls_generator.js +48 -23
- package/src/build/graph_utils.js +31 -0
- package/src/build/{inject_version_mappings.js → inject_global_version_mappings.js} +33 -15
- package/src/build/inject_service_worker_urls.js +79 -0
- package/src/build/resync_ressource_hints.js +68 -0
- package/src/build/start_build_server.js +192 -0
- package/src/dev/plugins/explorer/jsenv_plugin_explorer.js +2 -2
- package/src/dev/plugins/toolbar/jsenv_plugin_toolbar.js +3 -1
- package/src/dev/start_dev_server.js +136 -30
- package/src/execute/execute.js +31 -6
- package/src/execute/run.js +19 -56
- package/src/execute/runtimes/browsers/from_playwright.js +207 -147
- package/src/execute/runtimes/node/controllable_file.mjs +26 -10
- package/src/execute/runtimes/node/node_execution_performance.js +67 -0
- package/src/execute/runtimes/node/node_process.js +280 -39
- package/src/jsenv_root_directory_url.js +1 -0
- package/src/omega/{runtime_support/default_runtime_support.js → compat/default_runtime_compat.js} +3 -5
- package/src/omega/{runtime_support/features_compatibility.js → compat/features_compats.js} +66 -4
- package/src/omega/compat/runtime_compat.js +50 -0
- package/src/omega/errors.js +51 -58
- package/src/omega/fetched_content_compliance.js +24 -0
- package/src/omega/file_url_converter.js +8 -50
- package/src/omega/kitchen.js +482 -304
- package/src/omega/omega_server.js +2 -3
- package/src/omega/server/file_service.js +53 -25
- package/src/omega/server/user_agent.js +4 -2
- package/src/omega/url_graph/url_graph_load.js +22 -7
- package/src/omega/url_graph/url_graph_report.js +98 -48
- package/src/omega/url_graph/url_info_transformations.js +26 -9
- package/src/omega/url_graph.js +80 -16
- package/src/omega/web_workers.js +42 -0
- package/src/{dev/plugins/autoreload → plugins/autoreload/dev_sse}/client/autoreload_preference.js +0 -0
- package/src/{dev/plugins/autoreload → plugins/autoreload/dev_sse}/client/event_source_client.js +2 -2
- package/src/{dev/plugins/autoreload → plugins/autoreload/dev_sse}/client/reload.js +0 -0
- package/src/{dev/plugins/autoreload → plugins/autoreload/dev_sse}/client/url_helpers.js +0 -0
- package/src/plugins/autoreload/dev_sse/jsenv_plugin_dev_sse_client.js +46 -0
- package/src/plugins/autoreload/dev_sse/jsenv_plugin_dev_sse_server.js +204 -0
- package/src/plugins/autoreload/jsenv_plugin_autoreload.js +27 -0
- package/src/plugins/autoreload/jsenv_plugin_hmr.js +35 -0
- package/src/plugins/bundling/css/bundle_css.js +140 -0
- package/src/plugins/bundling/js_classic_workers/bundle_js_classic_workers.js +13 -0
- package/src/plugins/bundling/js_module/bundle_js_module.js +309 -0
- package/src/plugins/bundling/jsenv_plugin_bundling.js +54 -0
- package/src/plugins/cache_control/jsenv_plugin_cache_control.js +34 -0
- package/src/{omega/core_plugins → plugins}/commonjs_globals/jsenv_plugin_commonjs_globals.js +54 -41
- package/src/plugins/file_urls/jsenv_plugin_file_urls.js +66 -0
- package/src/{omega/core_plugins → plugins}/filesystem_magic/jsenv_plugin_filesystem_magic.js +8 -5
- package/src/{omega/core_plugins → plugins}/html_supervisor/client/error_in_document.js +0 -0
- package/src/{omega/core_plugins → plugins}/html_supervisor/client/error_in_notification.js +0 -0
- package/src/plugins/html_supervisor/client/html_supervisor_installer.js +242 -0
- package/src/plugins/html_supervisor/client/html_supervisor_setup.js +79 -0
- package/src/{omega/core_plugins → plugins}/html_supervisor/client/perf_browser.js +0 -0
- package/src/{omega/core_plugins → plugins}/html_supervisor/client/uneval_exception.js +0 -0
- package/src/{omega/core_plugins → plugins}/html_supervisor/jsenv_plugin_html_supervisor.js +83 -61
- package/src/plugins/http_urls/jsenv_plugin_http_urls.js +12 -0
- package/src/{dev/plugins/autoreload → plugins/import_meta_hot}/babel_plugin_metadata_import_meta_hot.js +4 -5
- package/src/{dev/plugins/autoreload → plugins/import_meta_hot}/client/import_meta_hot.js +3 -1
- package/src/{dev/plugins/autoreload → plugins/import_meta_hot}/html_hot_dependencies.js +2 -2
- package/src/plugins/import_meta_hot/jsenv_plugin_import_meta_hot.js +105 -0
- package/src/{omega/core_plugins → plugins}/import_meta_scenarios/jsenv_plugin_import_meta_scenarios.js +33 -8
- package/src/plugins/import_meta_url/client/import_meta_url_browser.js +52 -0
- package/src/plugins/import_meta_url/client/import_meta_url_commonjs.mjs +9 -0
- package/src/{omega/core_plugins → plugins}/importmap/jsenv_plugin_importmap.js +39 -33
- package/src/plugins/inject_globals/jsenv_plugin_inject_globals.js +67 -0
- package/src/{omega/core_plugins → plugins}/inline/client/inline_content.js +0 -0
- package/src/{omega/core_plugins → plugins}/inline/jsenv_plugin_data_urls.js +18 -14
- package/src/{omega/core_plugins/inline/jsenv_plugin_js_and_css_inside_html.js → plugins/inline/jsenv_plugin_html_inline_content.js} +65 -44
- package/src/plugins/inline/jsenv_plugin_inline.js +36 -0
- package/src/{omega/core_plugins → plugins}/inline/jsenv_plugin_inline_query_param.js +6 -6
- package/src/plugins/inline/jsenv_plugin_js_inline_content.js +297 -0
- package/src/plugins/leading_slash/jsenv_plugin_leading_slash.js +13 -0
- package/src/plugins/minification/css/minify_css.js +9 -0
- package/src/plugins/minification/html/minify_html.js +15 -0
- package/src/{build/plugins/minify_js/jsenv_plugin_minify_js.js → plugins/minification/js/minify_js.js} +6 -22
- package/src/plugins/minification/jsenv_plugin_minification.js +78 -0
- package/src/plugins/minification/json/minify_json.js +8 -0
- package/src/plugins/node_esm_resolution/jsenv_plugin_node_esm_resolution.js +146 -0
- package/src/{omega → plugins}/plugin_controller.js +42 -11
- package/src/plugins/plugins.js +92 -0
- package/src/plugins/transpilation/as_js_classic/client/s.js +874 -0
- package/src/plugins/transpilation/as_js_classic/client/s.js.md +1 -0
- package/src/plugins/transpilation/as_js_classic/helpers/babel_plugin_transform_import_meta_url.js +47 -0
- package/src/plugins/transpilation/as_js_classic/helpers/systemjs_old.js +43 -0
- package/src/plugins/transpilation/as_js_classic/jsenv_plugin_as_js_classic.js +199 -0
- package/src/plugins/transpilation/as_js_classic/jsenv_plugin_script_type_module_as_classic.js +270 -0
- package/src/plugins/transpilation/as_js_classic/jsenv_plugin_workers_type_module_as_classic.js +55 -0
- package/src/{omega/core_plugins → plugins/transpilation}/babel/global_this/babel_plugin_global_this_as_jsenv_import.js +0 -0
- package/src/{omega/core_plugins → plugins/transpilation}/babel/global_this/client/global_this.js +0 -0
- package/src/{omega/core_plugins → plugins/transpilation}/babel/helpers/babel_plugin_babel_helpers_as_jsenv_imports.js +0 -0
- package/src/{omega/core_plugins → plugins/transpilation}/babel/helpers/babel_plugin_structure.js +12 -19
- package/src/{omega/core_plugins → plugins/transpilation}/babel/helpers/babel_plugins_compatibility.js +0 -0
- package/src/{omega/core_plugins → plugins/transpilation}/babel/jsenv_plugin_babel.js +45 -27
- package/src/{omega/core_plugins → plugins/transpilation}/babel/new_stylesheet/babel_plugin_new_stylesheet_as_jsenv_import.js +30 -6
- package/src/{omega/core_plugins → plugins/transpilation}/babel/new_stylesheet/client/.eslintrc.cjs +0 -0
- package/src/{omega/core_plugins → plugins/transpilation}/babel/new_stylesheet/client/new_stylesheet.js +0 -0
- package/src/{omega/core_plugins → plugins/transpilation}/babel/regenerator_runtime/babel_plugin_regenerator_runtime_as_jsenv_import.js +0 -0
- package/src/{omega/core_plugins → plugins/transpilation}/babel/regenerator_runtime/client/regenerator_runtime.js +0 -0
- package/src/plugins/transpilation/css_parcel/jsenv_plugin_css_parcel.js +18 -0
- package/src/plugins/transpilation/fetch_original_url_info.js +30 -0
- package/src/plugins/transpilation/import_assertions/jsenv_plugin_import_assertions.js +181 -0
- package/src/plugins/transpilation/jsenv_plugin_top_level_await.js +80 -0
- package/src/plugins/transpilation/jsenv_plugin_transpilation.js +44 -0
- package/src/plugins/url_analysis/css/css_urls.js +49 -0
- package/src/plugins/url_analysis/html/html_urls.js +269 -0
- package/src/plugins/url_analysis/js/js_urls.js +67 -0
- package/src/plugins/url_analysis/jsenv_plugin_url_analysis.js +18 -0
- package/src/plugins/url_analysis/webmanifest/webmanifest_urls.js +17 -0
- package/src/{omega/core_plugins → plugins}/url_resolution/jsenv_plugin_url_resolution.js +12 -5
- package/src/plugins/url_version/jsenv_plugin_url_version.js +28 -0
- package/src/test/execute_plan.js +30 -18
- package/src/test/execute_test_plan.js +23 -8
- package/src/test/logs_file_execution.js +9 -8
- package/src/build/plugins/bundle_js_module/jsenv_plugin_bundle_js_module.js +0 -225
- package/src/build/plugins/minify_html/jsenv_plugin_minify_html.js +0 -30
- package/src/dev/plugins/autoreload/client/event_source_connection.js +0 -195
- package/src/dev/plugins/autoreload/jsenv_plugin_autoreload.js +0 -374
- package/src/dev/plugins/autoreload/sse_service.js +0 -149
- package/src/execute/runtimes/node/controlled_process.js +0 -316
- package/src/omega/core_plugins/file_urls/jsenv_plugin_file_urls.js +0 -67
- package/src/omega/core_plugins/html_supervisor/client/html_supervisor_installer.js +0 -168
- package/src/omega/core_plugins/html_supervisor/client/html_supervisor_setup.js +0 -77
- package/src/omega/core_plugins/import_assertions/helpers/babel_plugin_metadata_import_assertions.js +0 -98
- package/src/omega/core_plugins/import_assertions/helpers/json_module.js +0 -12
- package/src/omega/core_plugins/import_assertions/helpers/text_module.js +0 -6
- package/src/omega/core_plugins/import_assertions/jsenv_plugin_import_assertions.js +0 -211
- package/src/omega/core_plugins/inline/jsenv_plugin_inline.js +0 -13
- package/src/omega/core_plugins/inline/jsenv_plugin_new_inline_content.js +0 -210
- package/src/omega/core_plugins/leading_slash/jsenv_plugin_leading_slash.js +0 -12
- package/src/omega/core_plugins/node_esm_resolution/jsenv_plugin_node_esm_resolution.js +0 -77
- package/src/omega/core_plugins/url_version/jsenv_plugin_url_version.js +0 -50
- package/src/omega/core_plugins.js +0 -39
- package/src/omega/runtime_support/runtime_support.js +0 -20
- package/src/omega/url_graph/url_graph_sort.js +0 -29
- package/src/omega/url_mentions/css_url_mentions.js +0 -63
- package/src/omega/url_mentions/html_url_mentions.js +0 -185
- package/src/omega/url_mentions/js_module_url_mentions.js +0 -91
- package/src/omega/url_mentions/parse_url_mentions.js +0 -37
- package/src/omega/url_mentions/worker_classic_url_mentions.js +0 -37
|
@@ -8,14 +8,13 @@ import {
|
|
|
8
8
|
} from "@jsenv/server"
|
|
9
9
|
import { convertFileSystemErrorToResponseProperties } from "@jsenv/server/src/internal/convertFileSystemErrorToResponseProperties.js"
|
|
10
10
|
import { createCallbackListNotifiedOnce } from "@jsenv/abort"
|
|
11
|
-
import { loggerToLogLevel } from "@jsenv/logger"
|
|
12
11
|
|
|
13
12
|
import { createFileService } from "./server/file_service.js"
|
|
14
13
|
|
|
15
14
|
export const startOmegaServer = async ({
|
|
16
15
|
signal,
|
|
17
16
|
handleSIGINT,
|
|
18
|
-
|
|
17
|
+
logLevel,
|
|
19
18
|
protocol = "http",
|
|
20
19
|
http2 = protocol === "https",
|
|
21
20
|
privateKey,
|
|
@@ -48,7 +47,7 @@ export const startOmegaServer = async ({
|
|
|
48
47
|
stopOnSIGINT: handleSIGINT,
|
|
49
48
|
stopOnInternalError: false,
|
|
50
49
|
keepProcessAlive,
|
|
51
|
-
logLevel
|
|
50
|
+
logLevel,
|
|
52
51
|
startLog: false,
|
|
53
52
|
|
|
54
53
|
protocol,
|
|
@@ -20,6 +20,11 @@ export const createFileService = ({
|
|
|
20
20
|
urlGraph,
|
|
21
21
|
scenario,
|
|
22
22
|
}
|
|
23
|
+
const augmentResponseContext = {
|
|
24
|
+
rootDirectoryUrl,
|
|
25
|
+
urlGraph,
|
|
26
|
+
scenario,
|
|
27
|
+
}
|
|
23
28
|
|
|
24
29
|
const getResponse = async (request) => {
|
|
25
30
|
// serve file inside ".jsenv" directory
|
|
@@ -39,43 +44,75 @@ export const createFileService = ({
|
|
|
39
44
|
if (responseFromPlugin) {
|
|
40
45
|
return responseFromPlugin
|
|
41
46
|
}
|
|
42
|
-
const
|
|
43
|
-
request.headers["user-agent"],
|
|
44
|
-
)
|
|
45
|
-
const runtimeSupport = {
|
|
46
|
-
[runtimeName]: runtimeVersion,
|
|
47
|
-
}
|
|
48
|
-
const reference = kitchen.createReference({
|
|
47
|
+
const [reference, urlInfo] = kitchen.prepareEntryPoint({
|
|
49
48
|
parentUrl: inferParentFromRequest(request, rootDirectoryUrl),
|
|
50
49
|
type: "entry_point",
|
|
51
50
|
specifier: request.ressource,
|
|
52
51
|
})
|
|
53
|
-
const
|
|
52
|
+
const ifNoneMatch = request.headers["if-none-match"]
|
|
53
|
+
if (ifNoneMatch && urlInfo.contentEtag === ifNoneMatch) {
|
|
54
|
+
return {
|
|
55
|
+
status: 304,
|
|
56
|
+
headers: {
|
|
57
|
+
"cache-control": `private,max-age=0,must-revalidate`,
|
|
58
|
+
},
|
|
59
|
+
}
|
|
60
|
+
}
|
|
54
61
|
const referenceFromGraph = urlGraph.inferReference(
|
|
55
62
|
reference.url,
|
|
56
63
|
reference.parentUrl,
|
|
57
64
|
)
|
|
58
65
|
try {
|
|
66
|
+
// urlInfo objects are reused, they must be "reset" before cooking them again
|
|
67
|
+
if (
|
|
68
|
+
urlInfo.contentEtag &&
|
|
69
|
+
!urlInfo.isInline &&
|
|
70
|
+
urlInfo.type !== "sourcemap"
|
|
71
|
+
) {
|
|
72
|
+
urlInfo.sourcemap = null
|
|
73
|
+
urlInfo.sourcemapReference = null
|
|
74
|
+
urlInfo.content = null
|
|
75
|
+
urlInfo.originalContent = null
|
|
76
|
+
urlInfo.type = null
|
|
77
|
+
urlInfo.subtype = null
|
|
78
|
+
urlInfo.timing = {}
|
|
79
|
+
}
|
|
80
|
+
const { runtimeName, runtimeVersion } = parseUserAgentHeader(
|
|
81
|
+
request.headers["user-agent"],
|
|
82
|
+
)
|
|
59
83
|
await kitchen.cook({
|
|
60
84
|
reference: referenceFromGraph || reference,
|
|
61
|
-
urlInfo
|
|
85
|
+
urlInfo,
|
|
62
86
|
outDirectoryUrl: `${rootDirectoryUrl}.jsenv/${scenario}/${runtimeName}@${runtimeVersion}/`,
|
|
63
|
-
|
|
87
|
+
clientRuntimeCompat: {
|
|
88
|
+
[runtimeName]: runtimeVersion,
|
|
89
|
+
},
|
|
64
90
|
})
|
|
65
|
-
|
|
91
|
+
let { response, contentType, content, contentEtag } = urlInfo
|
|
66
92
|
if (response) {
|
|
67
93
|
return response
|
|
68
94
|
}
|
|
69
|
-
|
|
95
|
+
response = {
|
|
70
96
|
url: reference.url,
|
|
71
97
|
status: 200,
|
|
72
98
|
headers: {
|
|
73
99
|
"content-type": contentType,
|
|
74
100
|
"content-length": Buffer.byteLength(content),
|
|
75
101
|
"cache-control": `private,max-age=0,must-revalidate`,
|
|
102
|
+
"eTag": contentEtag,
|
|
76
103
|
},
|
|
77
104
|
body: content,
|
|
105
|
+
timing: urlInfo.timing,
|
|
78
106
|
}
|
|
107
|
+
kitchen.pluginController.callHooks(
|
|
108
|
+
"augmentResponse",
|
|
109
|
+
{ reference, urlInfo },
|
|
110
|
+
augmentResponseContext,
|
|
111
|
+
(returnValue) => {
|
|
112
|
+
response = composeTwoResponses(response, returnValue)
|
|
113
|
+
},
|
|
114
|
+
)
|
|
115
|
+
return response
|
|
79
116
|
} catch (e) {
|
|
80
117
|
const code = e.code
|
|
81
118
|
if (code === "PARSE_ERROR") {
|
|
@@ -86,11 +123,11 @@ export const createFileService = ({
|
|
|
86
123
|
statusText: e.reason,
|
|
87
124
|
statusMessage: e.message,
|
|
88
125
|
headers: {
|
|
89
|
-
"content-type":
|
|
90
|
-
"content-length": Buffer.byteLength(
|
|
126
|
+
"content-type": urlInfo.contentType,
|
|
127
|
+
"content-length": Buffer.byteLength(urlInfo.content),
|
|
91
128
|
"cache-control": "no-store",
|
|
92
129
|
},
|
|
93
|
-
body:
|
|
130
|
+
body: urlInfo.content,
|
|
94
131
|
}
|
|
95
132
|
}
|
|
96
133
|
if (code === "EISDIR") {
|
|
@@ -127,16 +164,7 @@ export const createFileService = ({
|
|
|
127
164
|
}
|
|
128
165
|
return async (request) => {
|
|
129
166
|
let response = await getResponse(request)
|
|
130
|
-
|
|
131
|
-
kitchen.pluginController.callHooks(
|
|
132
|
-
"augmentResponse",
|
|
133
|
-
response,
|
|
134
|
-
{},
|
|
135
|
-
(returnValue) => {
|
|
136
|
-
response = composeTwoResponses(response, returnValue)
|
|
137
|
-
},
|
|
138
|
-
)
|
|
139
|
-
}
|
|
167
|
+
|
|
140
168
|
return response
|
|
141
169
|
}
|
|
142
170
|
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { createRequire } from "node:module"
|
|
2
2
|
|
|
3
|
+
import { memoizeByFirstArgument } from "@jsenv/utils/memoize/memoize_by_first_argument.js"
|
|
4
|
+
|
|
3
5
|
const require = createRequire(import.meta.url)
|
|
4
6
|
|
|
5
|
-
export const parseUserAgentHeader = (userAgent) => {
|
|
7
|
+
export const parseUserAgentHeader = memoizeByFirstArgument((userAgent) => {
|
|
6
8
|
if (userAgent.includes("node-fetch/")) {
|
|
7
9
|
// it's not really node and conceptually we can't assume the node version
|
|
8
10
|
// but good enough for now
|
|
@@ -19,4 +21,4 @@ export const parseUserAgentHeader = (userAgent) => {
|
|
|
19
21
|
runtimeVersion:
|
|
20
22
|
family === "Other" ? "unknown" : `${major}.${minor}${patch}`,
|
|
21
23
|
}
|
|
22
|
-
}
|
|
24
|
+
})
|
|
@@ -1,27 +1,29 @@
|
|
|
1
1
|
import { ensureEmptyDirectory } from "@jsenv/filesystem"
|
|
2
2
|
|
|
3
3
|
export const loadUrlGraph = async ({
|
|
4
|
+
operation,
|
|
4
5
|
urlGraph,
|
|
5
6
|
kitchen,
|
|
6
7
|
startLoading,
|
|
7
8
|
outDirectoryUrl,
|
|
8
|
-
|
|
9
|
+
clientRuntimeCompat,
|
|
9
10
|
}) => {
|
|
10
11
|
if (outDirectoryUrl) {
|
|
11
12
|
await ensureEmptyDirectory(outDirectoryUrl)
|
|
12
13
|
}
|
|
13
14
|
const promises = []
|
|
15
|
+
const promiseMap = new Map()
|
|
14
16
|
const cook = ({ urlInfo, ...rest }) => {
|
|
15
|
-
const promiseFromData = urlInfo
|
|
17
|
+
const promiseFromData = promiseMap.get(urlInfo)
|
|
16
18
|
if (promiseFromData) return promiseFromData
|
|
17
19
|
const promise = _cook({
|
|
18
20
|
urlInfo,
|
|
19
21
|
outDirectoryUrl,
|
|
20
|
-
|
|
22
|
+
clientRuntimeCompat,
|
|
21
23
|
...rest,
|
|
22
24
|
})
|
|
23
25
|
promises.push(promise)
|
|
24
|
-
urlInfo
|
|
26
|
+
promiseMap.set(urlInfo, promise)
|
|
25
27
|
return promise
|
|
26
28
|
}
|
|
27
29
|
const _cook = async ({ urlInfo, ...rest }) => {
|
|
@@ -32,21 +34,32 @@ export const loadUrlGraph = async ({
|
|
|
32
34
|
})
|
|
33
35
|
const { references } = urlInfo
|
|
34
36
|
references.forEach((reference) => {
|
|
37
|
+
// we don't cook ressource hints
|
|
38
|
+
// because they might refer to ressource that will be modified during build
|
|
39
|
+
// It also means something else have to reference that url in order to cook it
|
|
40
|
+
// so that the preload is deleted by "resync_ressource_hints.js" otherwise
|
|
41
|
+
if (reference.isRessourceHint) {
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
// we use reference.generatedUrl to mimic what a browser would do:
|
|
45
|
+
// do a fetch to the specifier as found in the file
|
|
46
|
+
const referencedUrlInfo = urlGraph.reuseOrCreateUrlInfo(
|
|
47
|
+
reference.generatedUrl,
|
|
48
|
+
)
|
|
35
49
|
cook({
|
|
36
50
|
reference,
|
|
37
|
-
urlInfo:
|
|
51
|
+
urlInfo: referencedUrlInfo,
|
|
38
52
|
})
|
|
39
53
|
})
|
|
40
54
|
}
|
|
41
55
|
startLoading(
|
|
42
56
|
({ trace, parentUrl = kitchen.rootDirectoryUrl, type, specifier }) => {
|
|
43
|
-
const entryReference = kitchen.
|
|
57
|
+
const [entryReference, entryUrlInfo] = kitchen.prepareEntryPoint({
|
|
44
58
|
trace,
|
|
45
59
|
parentUrl,
|
|
46
60
|
type,
|
|
47
61
|
specifier,
|
|
48
62
|
})
|
|
49
|
-
const entryUrlInfo = kitchen.resolveReference(entryReference)
|
|
50
63
|
entryUrlInfo.data.isEntryPoint = true
|
|
51
64
|
cook({
|
|
52
65
|
reference: entryReference,
|
|
@@ -57,6 +70,7 @@ export const loadUrlGraph = async ({
|
|
|
57
70
|
)
|
|
58
71
|
|
|
59
72
|
const waitAll = async () => {
|
|
73
|
+
operation.throwIfAborted()
|
|
60
74
|
if (promises.length === 0) {
|
|
61
75
|
return
|
|
62
76
|
}
|
|
@@ -66,4 +80,5 @@ export const loadUrlGraph = async ({
|
|
|
66
80
|
await waitAll()
|
|
67
81
|
}
|
|
68
82
|
await waitAll()
|
|
83
|
+
promiseMap.clear()
|
|
69
84
|
}
|
|
@@ -1,37 +1,36 @@
|
|
|
1
1
|
import { ANSI } from "@jsenv/log"
|
|
2
2
|
|
|
3
3
|
import { byteAsFileSize } from "@jsenv/utils/logs/size_log.js"
|
|
4
|
+
import { distributeNumbers } from "@jsenv/utils/logs/number_distribution.js"
|
|
4
5
|
|
|
5
6
|
export const createUrlGraphSummary = (
|
|
6
7
|
urlGraph,
|
|
7
8
|
{ title = "graph summary" } = {},
|
|
8
9
|
) => {
|
|
9
10
|
const graphReport = createUrlGraphReport(urlGraph)
|
|
10
|
-
const totalLabel = `Total`
|
|
11
11
|
return `--- ${title} ---
|
|
12
12
|
${createRepartitionMessage(graphReport)}
|
|
13
|
-
${ANSI.color(totalLabel, ANSI.GREY)} ${
|
|
14
|
-
graphReport.total.count
|
|
15
|
-
} (${byteAsFileSize(graphReport.total.size)})
|
|
16
13
|
--------------------`
|
|
17
14
|
}
|
|
18
15
|
|
|
19
16
|
const createUrlGraphReport = (urlGraph) => {
|
|
20
17
|
const { urlInfos } = urlGraph
|
|
21
18
|
const countGroups = {
|
|
19
|
+
sourcemaps: 0,
|
|
22
20
|
html: 0,
|
|
23
21
|
css: 0,
|
|
24
22
|
js: 0,
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
json: 0,
|
|
24
|
+
other: 0,
|
|
27
25
|
total: 0,
|
|
28
26
|
}
|
|
29
27
|
const sizeGroups = {
|
|
28
|
+
sourcemaps: 0,
|
|
30
29
|
html: 0,
|
|
31
30
|
css: 0,
|
|
32
31
|
js: 0,
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
json: 0,
|
|
33
|
+
other: 0,
|
|
35
34
|
total: 0,
|
|
36
35
|
}
|
|
37
36
|
Object.keys(urlInfos).forEach((url) => {
|
|
@@ -39,8 +38,10 @@ const createUrlGraphReport = (urlGraph) => {
|
|
|
39
38
|
return
|
|
40
39
|
}
|
|
41
40
|
const urlInfo = urlInfos[url]
|
|
42
|
-
// ignore
|
|
43
|
-
|
|
41
|
+
// ignore:
|
|
42
|
+
// - inline files: they are already taken into account in the file where they appear
|
|
43
|
+
// - external files: we don't know their content
|
|
44
|
+
if (urlInfo.isInline || urlInfo.external) {
|
|
44
45
|
return
|
|
45
46
|
}
|
|
46
47
|
// file loaded via import assertion are already inside the graph
|
|
@@ -49,9 +50,9 @@ const createUrlGraphReport = (urlGraph) => {
|
|
|
49
50
|
// and only the js module remain (likely bundled)
|
|
50
51
|
const urlObject = new URL(urlInfo.url)
|
|
51
52
|
if (
|
|
52
|
-
urlObject.searchParams.has("
|
|
53
|
-
urlObject.searchParams.has("
|
|
54
|
-
urlObject.searchParams.has("
|
|
53
|
+
urlObject.searchParams.has("as_json_module") ||
|
|
54
|
+
urlObject.searchParams.has("as_css_module") ||
|
|
55
|
+
urlObject.searchParams.has("as_text_module")
|
|
55
56
|
) {
|
|
56
57
|
return
|
|
57
58
|
}
|
|
@@ -79,17 +80,67 @@ const createUrlGraphReport = (urlGraph) => {
|
|
|
79
80
|
sizeGroups.js += urlContentSize
|
|
80
81
|
return
|
|
81
82
|
}
|
|
82
|
-
|
|
83
|
-
|
|
83
|
+
if (category === "json") {
|
|
84
|
+
countGroups.json++
|
|
85
|
+
sizeGroups.json += urlContentSize
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
countGroups.other++
|
|
89
|
+
sizeGroups.other += urlContentSize
|
|
84
90
|
return
|
|
85
91
|
})
|
|
92
|
+
|
|
93
|
+
const sizesToDistribute = {}
|
|
94
|
+
Object.keys(sizeGroups).forEach((groupName) => {
|
|
95
|
+
if (groupName !== "sourcemaps" && groupName !== "total") {
|
|
96
|
+
sizesToDistribute[groupName] = sizeGroups[groupName]
|
|
97
|
+
}
|
|
98
|
+
})
|
|
99
|
+
const ratios = distributeNumbers(sizesToDistribute)
|
|
100
|
+
const percentageGroups = {}
|
|
101
|
+
Object.keys(ratios).forEach((groupName) => {
|
|
102
|
+
percentageGroups[groupName] = parseFloat(ratios[groupName]) * 100
|
|
103
|
+
})
|
|
104
|
+
|
|
86
105
|
return {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
106
|
+
// sourcemaps are special, there size are ignored
|
|
107
|
+
// so there is no "percentage" associated
|
|
108
|
+
sourcemaps: {
|
|
109
|
+
count: countGroups.sourcemaps,
|
|
110
|
+
size: sizeGroups.sourcemaps,
|
|
111
|
+
percentage: undefined,
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
html: {
|
|
115
|
+
count: countGroups.html,
|
|
116
|
+
size: sizeGroups.html,
|
|
117
|
+
percentage: percentageGroups.html,
|
|
118
|
+
},
|
|
119
|
+
css: {
|
|
120
|
+
count: countGroups.css,
|
|
121
|
+
size: sizeGroups.css,
|
|
122
|
+
percentage: percentageGroups.css,
|
|
123
|
+
},
|
|
124
|
+
js: {
|
|
125
|
+
count: countGroups.js,
|
|
126
|
+
size: sizeGroups.js,
|
|
127
|
+
percentage: percentageGroups.js,
|
|
128
|
+
},
|
|
129
|
+
json: {
|
|
130
|
+
count: countGroups.json,
|
|
131
|
+
size: sizeGroups.json,
|
|
132
|
+
percentage: percentageGroups.json,
|
|
133
|
+
},
|
|
134
|
+
other: {
|
|
135
|
+
count: countGroups.other,
|
|
136
|
+
size: sizeGroups.other,
|
|
137
|
+
percentage: percentageGroups.other,
|
|
138
|
+
},
|
|
139
|
+
total: {
|
|
140
|
+
count: countGroups.total,
|
|
141
|
+
size: sizeGroups.total,
|
|
142
|
+
percentage: 100,
|
|
143
|
+
},
|
|
93
144
|
}
|
|
94
145
|
}
|
|
95
146
|
|
|
@@ -106,32 +157,22 @@ const determineCategory = (urlInfo) => {
|
|
|
106
157
|
if (urlInfo.type === "js_module" || urlInfo.type === "js_classic") {
|
|
107
158
|
return "js"
|
|
108
159
|
}
|
|
109
|
-
|
|
160
|
+
if (urlInfo.type === "json") {
|
|
161
|
+
return "json"
|
|
162
|
+
}
|
|
163
|
+
return "other"
|
|
110
164
|
}
|
|
111
165
|
|
|
112
|
-
const createRepartitionMessage = ({ html, css, js,
|
|
113
|
-
const
|
|
114
|
-
if (html.count) {
|
|
115
|
-
parts.push(
|
|
116
|
-
`${ANSI.color(`html:`, ANSI.GREY)} ${html.count} (${byteAsFileSize(
|
|
117
|
-
html.size,
|
|
118
|
-
)})`,
|
|
119
|
-
)
|
|
120
|
-
}
|
|
121
|
-
if (css.count) {
|
|
166
|
+
const createRepartitionMessage = ({ html, css, js, json, other, total }) => {
|
|
167
|
+
const addPart = (name, { count, size, percentage }) => {
|
|
122
168
|
parts.push(
|
|
123
|
-
`${ANSI.color(
|
|
124
|
-
|
|
125
|
-
)})`,
|
|
126
|
-
)
|
|
127
|
-
}
|
|
128
|
-
if (js.count) {
|
|
129
|
-
parts.push(
|
|
130
|
-
`${ANSI.color(`js:`, ANSI.GREY)} ${js.count} (${byteAsFileSize(
|
|
131
|
-
js.size,
|
|
132
|
-
)})`,
|
|
169
|
+
`${ANSI.color(`${name}:`, ANSI.GREY)} ${count} (${byteAsFileSize(
|
|
170
|
+
size,
|
|
171
|
+
)} / ${percentage} %)`,
|
|
133
172
|
)
|
|
134
173
|
}
|
|
174
|
+
|
|
175
|
+
const parts = []
|
|
135
176
|
// if (sourcemaps.count) {
|
|
136
177
|
// parts.push(
|
|
137
178
|
// `${ANSI.color(`sourcemaps:`, ANSI.GREY)} ${
|
|
@@ -139,13 +180,22 @@ const createRepartitionMessage = ({ html, css, js, assets }) => {
|
|
|
139
180
|
// } (${byteAsFileSize(sourcemaps.size)})`,
|
|
140
181
|
// )
|
|
141
182
|
// }
|
|
142
|
-
if (
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
183
|
+
if (html.count) {
|
|
184
|
+
addPart("html ", html)
|
|
185
|
+
}
|
|
186
|
+
if (css.count) {
|
|
187
|
+
addPart("css ", css)
|
|
188
|
+
}
|
|
189
|
+
if (js.count) {
|
|
190
|
+
addPart("js ", js)
|
|
191
|
+
}
|
|
192
|
+
if (json.count) {
|
|
193
|
+
addPart("json ", json)
|
|
194
|
+
}
|
|
195
|
+
if (other.count) {
|
|
196
|
+
addPart("other", other)
|
|
148
197
|
}
|
|
198
|
+
addPart("total", total)
|
|
149
199
|
return `- ${parts.join(`
|
|
150
200
|
- `)}`
|
|
151
201
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { bufferToEtag, urlToRelativeUrl } from "@jsenv/filesystem"
|
|
2
|
+
|
|
1
3
|
import { composeTwoSourcemaps } from "@jsenv/utils/sourcemap/sourcemap_composition_v3.js"
|
|
2
4
|
import {
|
|
3
5
|
SOURCEMAP,
|
|
@@ -8,7 +10,8 @@ import {
|
|
|
8
10
|
export const createUrlInfoTransformer = ({
|
|
9
11
|
logger,
|
|
10
12
|
sourcemaps,
|
|
11
|
-
|
|
13
|
+
sourcemapsSourcesContent,
|
|
14
|
+
sourcemapsRelativeSources,
|
|
12
15
|
urlGraph,
|
|
13
16
|
injectSourcemapPlaceholder,
|
|
14
17
|
foundSourcemap,
|
|
@@ -23,7 +26,12 @@ export const createUrlInfoTransformer = ({
|
|
|
23
26
|
// for inline content (<script> insdide html)
|
|
24
27
|
// chrome won't be able to fetch the file as it does not exists
|
|
25
28
|
// so sourcemap must contain sources
|
|
26
|
-
|
|
29
|
+
sourcemapsSourcesContent ||
|
|
30
|
+
urlInfo.isInline ||
|
|
31
|
+
(sourcemap.sources &&
|
|
32
|
+
sourcemap.sources.some(
|
|
33
|
+
(source) => !source || !source.startsWith("file:"),
|
|
34
|
+
))
|
|
27
35
|
if (sourcemap.sources && sourcemap.sources.length > 1) {
|
|
28
36
|
sourcemap.sources = sourcemap.sources.map(
|
|
29
37
|
(source) => new URL(source, urlInfo.data.rawUrl || urlInfo.url).href,
|
|
@@ -78,9 +86,9 @@ export const createUrlInfoTransformer = ({
|
|
|
78
86
|
const [sourcemapReference, sourcemapUrlInfo] = foundSourcemap({
|
|
79
87
|
urlInfo,
|
|
80
88
|
type,
|
|
81
|
-
line,
|
|
82
|
-
column,
|
|
83
89
|
specifier,
|
|
90
|
+
specifierLine: line,
|
|
91
|
+
specifierColumn: column,
|
|
84
92
|
})
|
|
85
93
|
try {
|
|
86
94
|
await context.cook({
|
|
@@ -137,20 +145,29 @@ export const createUrlInfoTransformer = ({
|
|
|
137
145
|
const sourcemapReference = urlInfo.sourcemapReference
|
|
138
146
|
const sourcemapUrlInfo = urlGraph.getUrlInfo(sourcemapReference.url)
|
|
139
147
|
sourcemapUrlInfo.contentType = "application/json"
|
|
140
|
-
|
|
148
|
+
const sourcemap = urlInfo.sourcemap
|
|
149
|
+
if (sourcemapsRelativeSources) {
|
|
150
|
+
sourcemap.sources = sourcemap.sources.map((source) => {
|
|
151
|
+
const sourceRelative = urlToRelativeUrl(source, urlInfo.url)
|
|
152
|
+
return sourceRelative
|
|
153
|
+
})
|
|
154
|
+
}
|
|
155
|
+
sourcemapUrlInfo.content = JSON.stringify(sourcemap, null, " ")
|
|
141
156
|
if (sourcemaps === "inline") {
|
|
142
|
-
sourcemapReference.generatedSpecifier = sourcemapToBase64Url(
|
|
143
|
-
urlInfo.sourcemap,
|
|
144
|
-
)
|
|
157
|
+
sourcemapReference.generatedSpecifier = sourcemapToBase64Url(sourcemap)
|
|
145
158
|
}
|
|
146
159
|
if (sourcemaps === "file" || sourcemaps === "inline") {
|
|
147
160
|
urlInfo.content = SOURCEMAP.writeComment({
|
|
148
161
|
contentType: urlInfo.contentType,
|
|
149
162
|
content: urlInfo.content,
|
|
150
|
-
specifier:
|
|
163
|
+
specifier:
|
|
164
|
+
sourcemaps === "file" && sourcemapsRelativeSources
|
|
165
|
+
? urlToRelativeUrl(sourcemapReference.url, urlInfo.url)
|
|
166
|
+
: sourcemapReference.generatedSpecifier,
|
|
151
167
|
})
|
|
152
168
|
}
|
|
153
169
|
}
|
|
170
|
+
urlInfo.contentEtag = bufferToEtag(Buffer.from(urlInfo.content))
|
|
154
171
|
}
|
|
155
172
|
|
|
156
173
|
return {
|
package/src/omega/url_graph.js
CHANGED
|
@@ -1,10 +1,21 @@
|
|
|
1
|
-
import { createCallbackList } from "@jsenv/abort"
|
|
2
1
|
import { urlToRelativeUrl } from "@jsenv/filesystem"
|
|
3
2
|
|
|
4
|
-
export const createUrlGraph = (
|
|
3
|
+
export const createUrlGraph = ({
|
|
4
|
+
clientFileChangeCallbackList,
|
|
5
|
+
clientFilesPruneCallbackList,
|
|
6
|
+
} = {}) => {
|
|
5
7
|
const urlInfos = {}
|
|
6
8
|
const getUrlInfo = (url) => urlInfos[url]
|
|
7
|
-
const deleteUrlInfo = (url) =>
|
|
9
|
+
const deleteUrlInfo = (url) => {
|
|
10
|
+
const urlInfo = urlInfos[url]
|
|
11
|
+
if (urlInfo) {
|
|
12
|
+
delete urlInfos[url]
|
|
13
|
+
if (urlInfo.sourcemapReference) {
|
|
14
|
+
deleteUrlInfo(urlInfo.sourcemapReference.url)
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
8
19
|
const reuseOrCreateUrlInfo = (url) => {
|
|
9
20
|
const existingUrlInfo = urlInfos[url]
|
|
10
21
|
if (existingUrlInfo) return existingUrlInfo
|
|
@@ -40,13 +51,22 @@ export const createUrlGraph = () => {
|
|
|
40
51
|
return visitDependents(urlInfo)
|
|
41
52
|
}
|
|
42
53
|
|
|
43
|
-
const prunedCallbackList = createCallbackList()
|
|
44
54
|
const updateReferences = (urlInfo, references) => {
|
|
45
55
|
const dependencyUrls = []
|
|
46
56
|
references.forEach((reference) => {
|
|
47
|
-
if (
|
|
48
|
-
|
|
57
|
+
if (reference.isRessourceHint) {
|
|
58
|
+
// ressource hint are a special kind of reference.
|
|
59
|
+
// They are a sort of weak reference to an url.
|
|
60
|
+
// We ignore them so that url referenced only by ressource hints
|
|
61
|
+
// have url.dependents.size === 0 and can be considered as not used
|
|
62
|
+
// It means html won't consider url referenced solely
|
|
63
|
+
// by <link> as dependency and it's fine
|
|
64
|
+
return
|
|
65
|
+
}
|
|
66
|
+
if (dependencyUrls.includes(reference.url)) {
|
|
67
|
+
return
|
|
49
68
|
}
|
|
69
|
+
dependencyUrls.push(reference.url)
|
|
50
70
|
})
|
|
51
71
|
pruneDependencies(
|
|
52
72
|
urlInfo,
|
|
@@ -82,7 +102,47 @@ export const createUrlGraph = () => {
|
|
|
82
102
|
if (prunedUrlInfos.length === 0) {
|
|
83
103
|
return
|
|
84
104
|
}
|
|
85
|
-
|
|
105
|
+
prunedUrlInfos.forEach((prunedUrlInfo) => {
|
|
106
|
+
prunedUrlInfo.modifiedTimestamp = Date.now()
|
|
107
|
+
// should we delete?
|
|
108
|
+
// delete urlInfos[prunedUrlInfo.url]
|
|
109
|
+
})
|
|
110
|
+
if (clientFilesPruneCallbackList) {
|
|
111
|
+
clientFilesPruneCallbackList.forEach((callback) => {
|
|
112
|
+
callback({
|
|
113
|
+
firstUrlInfo,
|
|
114
|
+
prunedUrlInfos,
|
|
115
|
+
})
|
|
116
|
+
})
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (clientFileChangeCallbackList) {
|
|
121
|
+
const updateModifiedTimestamp = (urlInfo, modifiedTimestamp) => {
|
|
122
|
+
const seen = []
|
|
123
|
+
const iterate = (urlInfo) => {
|
|
124
|
+
if (seen.includes(urlInfo.url)) {
|
|
125
|
+
return
|
|
126
|
+
}
|
|
127
|
+
seen.push(urlInfo.url)
|
|
128
|
+
urlInfo.modifiedTimestamp = modifiedTimestamp
|
|
129
|
+
urlInfo.dependents.forEach((dependentUrl) => {
|
|
130
|
+
const dependentUrlInfo = urlInfos[dependentUrl]
|
|
131
|
+
const { hotAcceptDependencies = [] } = dependentUrlInfo.data
|
|
132
|
+
if (!hotAcceptDependencies.includes(urlInfo.url)) {
|
|
133
|
+
iterate(dependentUrlInfo)
|
|
134
|
+
}
|
|
135
|
+
})
|
|
136
|
+
}
|
|
137
|
+
iterate(urlInfo)
|
|
138
|
+
}
|
|
139
|
+
clientFileChangeCallbackList.push(({ url }) => {
|
|
140
|
+
const urlInfo = urlInfos[url]
|
|
141
|
+
if (urlInfo) {
|
|
142
|
+
updateModifiedTimestamp(urlInfo, Date.now())
|
|
143
|
+
urlInfo.contentEtag = null
|
|
144
|
+
}
|
|
145
|
+
})
|
|
86
146
|
}
|
|
87
147
|
|
|
88
148
|
return {
|
|
@@ -92,8 +152,6 @@ export const createUrlGraph = () => {
|
|
|
92
152
|
deleteUrlInfo,
|
|
93
153
|
inferReference,
|
|
94
154
|
findDependent,
|
|
95
|
-
|
|
96
|
-
prunedCallbackList,
|
|
97
155
|
updateReferences,
|
|
98
156
|
|
|
99
157
|
toJSON: (rootDirectoryUrl) => {
|
|
@@ -114,19 +172,25 @@ export const createUrlGraph = () => {
|
|
|
114
172
|
|
|
115
173
|
const createUrlInfo = (url) => {
|
|
116
174
|
return {
|
|
175
|
+
modifiedTimestamp: 0,
|
|
117
176
|
data: {}, // plugins can put whatever they want here
|
|
177
|
+
references: [],
|
|
178
|
+
dependencies: new Set(),
|
|
179
|
+
dependents: new Set(),
|
|
180
|
+
type: undefined, // "html", "css", "js_classic", "js_module", "importmap", "json", "webmanifest", ...
|
|
181
|
+
subtype: undefined, // "worker", "service_worker", "shared_worker" for js, otherwise undefined
|
|
182
|
+
contentType: "", // "text/html", "text/css", "text/javascript", "application/json", ...
|
|
118
183
|
url,
|
|
184
|
+
filename: "",
|
|
119
185
|
generatedUrl: null,
|
|
120
186
|
isInline: false,
|
|
121
187
|
inlineUrlSite: null,
|
|
122
|
-
|
|
123
|
-
originalContent:
|
|
124
|
-
content:
|
|
188
|
+
external: false,
|
|
189
|
+
originalContent: undefined,
|
|
190
|
+
content: undefined,
|
|
191
|
+
contentEtag: null,
|
|
125
192
|
sourcemap: null,
|
|
126
193
|
sourcemapReference: null,
|
|
127
|
-
|
|
128
|
-
references: [],
|
|
129
|
-
dependencies: new Set(),
|
|
130
|
-
dependents: new Set(),
|
|
194
|
+
timing: {},
|
|
131
195
|
}
|
|
132
196
|
}
|