@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
|
@@ -1,28 +1,36 @@
|
|
|
1
1
|
import { urlToFilename } from "@jsenv/filesystem"
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { memoizeByFirstArgument } from "@jsenv/utils/memoize/memoize_by_first_argument.js"
|
|
4
4
|
|
|
5
5
|
export const createBuilUrlsGenerator = ({ buildDirectoryUrl }) => {
|
|
6
6
|
const cache = {}
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
|
|
8
|
+
const getUrlName = (url, urlInfo) => {
|
|
9
|
+
if (!urlInfo) {
|
|
10
|
+
return urlToFilename(url)
|
|
11
|
+
}
|
|
12
|
+
if (urlInfo.filename) {
|
|
13
|
+
return urlInfo.filename
|
|
14
|
+
}
|
|
15
|
+
return urlToFilename(url)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const generate = memoizeByFirstArgument((url, { urlInfo, parentUrlInfo }) => {
|
|
19
|
+
const directoryPath = determineDirectoryPath({
|
|
20
|
+
urlInfo,
|
|
21
|
+
parentUrlInfo,
|
|
22
|
+
})
|
|
9
23
|
let names = cache[directoryPath]
|
|
10
24
|
if (!names) {
|
|
11
25
|
names = []
|
|
12
26
|
cache[directoryPath] = names
|
|
13
27
|
}
|
|
14
|
-
|
|
15
|
-
let
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
searchParams.has("text_module")
|
|
21
|
-
) {
|
|
22
|
-
name = `${name}.js`
|
|
23
|
-
}
|
|
24
|
-
const [basename, extension] = splitFileExtension(name)
|
|
25
|
-
let nameCandidate = name
|
|
28
|
+
const urlObject = new URL(url)
|
|
29
|
+
let { search, hash } = urlObject
|
|
30
|
+
let name = getUrlName(url, urlInfo)
|
|
31
|
+
let [basename, extension] = splitFileExtension(name)
|
|
32
|
+
extension = extensionMappings[extension] || extension
|
|
33
|
+
let nameCandidate = `${basename}${extension}` // reconstruct name in case extension was normalized
|
|
26
34
|
let integer = 1
|
|
27
35
|
// eslint-disable-next-line no-constant-condition
|
|
28
36
|
while (true) {
|
|
@@ -33,7 +41,7 @@ export const createBuilUrlsGenerator = ({ buildDirectoryUrl }) => {
|
|
|
33
41
|
integer++
|
|
34
42
|
nameCandidate = `${basename}${integer}${extension}`
|
|
35
43
|
}
|
|
36
|
-
return `${buildDirectoryUrl}${directoryPath}${nameCandidate}`
|
|
44
|
+
return `${buildDirectoryUrl}${directoryPath}${nameCandidate}${search}${hash}`
|
|
37
45
|
})
|
|
38
46
|
|
|
39
47
|
return {
|
|
@@ -41,6 +49,18 @@ export const createBuilUrlsGenerator = ({ buildDirectoryUrl }) => {
|
|
|
41
49
|
}
|
|
42
50
|
}
|
|
43
51
|
|
|
52
|
+
// It's best to generate files with an extension representing what is inside the file
|
|
53
|
+
// and after build js files contains solely js (js or typescript is gone).
|
|
54
|
+
// This way a static file server is already configured to server the correct content-type
|
|
55
|
+
// (otherwise one would have to configure that ".jsx" is "text/javascript")
|
|
56
|
+
// To keep in mind: if you have "user.jsx" and "user.js" AND both file are not bundled
|
|
57
|
+
// you end up with "dist/js/user.js" and "dist/js/user2.js"
|
|
58
|
+
const extensionMappings = {
|
|
59
|
+
".jsx": ".js",
|
|
60
|
+
".ts": ".js",
|
|
61
|
+
".tsx": ".js",
|
|
62
|
+
}
|
|
63
|
+
|
|
44
64
|
const splitFileExtension = (filename) => {
|
|
45
65
|
const dotLastIndex = filename.lastIndexOf(".")
|
|
46
66
|
if (dotLastIndex === -1) {
|
|
@@ -49,12 +69,17 @@ const splitFileExtension = (filename) => {
|
|
|
49
69
|
return [filename.slice(0, dotLastIndex), filename.slice(dotLastIndex)]
|
|
50
70
|
}
|
|
51
71
|
|
|
52
|
-
const determineDirectoryPath = (urlInfo, parentUrlInfo) => {
|
|
72
|
+
const determineDirectoryPath = ({ urlInfo, parentUrlInfo }) => {
|
|
53
73
|
if (urlInfo.isInline) {
|
|
54
|
-
const parentDirectoryPath = determineDirectoryPath(
|
|
74
|
+
const parentDirectoryPath = determineDirectoryPath({
|
|
75
|
+
urlInfo: parentUrlInfo,
|
|
76
|
+
})
|
|
55
77
|
return parentDirectoryPath
|
|
56
78
|
}
|
|
57
|
-
if (urlInfo.data.isEntryPoint) {
|
|
79
|
+
if (urlInfo.data.isEntryPoint || urlInfo.data.isWebWorkerEntryPoint) {
|
|
80
|
+
return ""
|
|
81
|
+
}
|
|
82
|
+
if (urlInfo.type === "importmap") {
|
|
58
83
|
return ""
|
|
59
84
|
}
|
|
60
85
|
if (urlInfo.type === "html") {
|
|
@@ -64,10 +89,10 @@ const determineDirectoryPath = (urlInfo, parentUrlInfo) => {
|
|
|
64
89
|
return "css/"
|
|
65
90
|
}
|
|
66
91
|
if (urlInfo.type === "js_module" || urlInfo.type === "js_classic") {
|
|
67
|
-
if (urlInfo.subtype === "service_worker") {
|
|
68
|
-
return ""
|
|
69
|
-
}
|
|
70
92
|
return "js/"
|
|
71
93
|
}
|
|
72
|
-
|
|
94
|
+
if (urlInfo.type === "json") {
|
|
95
|
+
return "json/"
|
|
96
|
+
}
|
|
97
|
+
return "other/"
|
|
73
98
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export const GRAPH = {
|
|
2
|
+
map: (graph, callback) => {
|
|
3
|
+
return Object.keys(graph.urlInfos).map((url) => {
|
|
4
|
+
return callback(graph.urlInfos[url])
|
|
5
|
+
})
|
|
6
|
+
},
|
|
7
|
+
|
|
8
|
+
forEach: (graph, callback) => {
|
|
9
|
+
Object.keys(graph.urlInfos).forEach((url) => {
|
|
10
|
+
callback(graph.urlInfos[url], url)
|
|
11
|
+
})
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
filter: (graph, callback) => {
|
|
15
|
+
const urlInfos = []
|
|
16
|
+
Object.keys(graph.urlInfos).forEach((url) => {
|
|
17
|
+
const urlInfo = graph.urlInfos[url]
|
|
18
|
+
if (callback(urlInfo)) {
|
|
19
|
+
urlInfos.push(urlInfo)
|
|
20
|
+
}
|
|
21
|
+
})
|
|
22
|
+
return urlInfos
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
find: (graph, callback) => {
|
|
26
|
+
const urlFound = Object.keys(graph.urlInfos).find((url) => {
|
|
27
|
+
return callback(graph.urlInfos[url])
|
|
28
|
+
})
|
|
29
|
+
return graph.urlInfos[urlFound]
|
|
30
|
+
},
|
|
31
|
+
}
|
|
@@ -8,10 +8,27 @@ import {
|
|
|
8
8
|
} from "@jsenv/utils/html_ast/html_ast.js"
|
|
9
9
|
import { createMagicSource } from "@jsenv/utils/sourcemap/magic_source.js"
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
import { GRAPH } from "./graph_utils.js"
|
|
12
|
+
|
|
13
|
+
export const injectGlobalVersionMapping = async ({
|
|
14
|
+
finalGraphKitchen,
|
|
15
|
+
finalGraph,
|
|
16
|
+
versionMappings,
|
|
17
|
+
}) => {
|
|
18
|
+
await Promise.all(
|
|
19
|
+
GRAPH.map(finalGraph, async (urlInfo) => {
|
|
20
|
+
if (urlInfo.data.isEntryPoint || urlInfo.data.isWebWorkerEntryPoint) {
|
|
21
|
+
await injectVersionMappings({
|
|
22
|
+
urlInfo,
|
|
23
|
+
kitchen: finalGraphKitchen,
|
|
24
|
+
versionMappings,
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
}),
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const injectVersionMappings = async ({ urlInfo, kitchen, versionMappings }) => {
|
|
15
32
|
const injector = injectors[urlInfo.type]
|
|
16
33
|
if (injector) {
|
|
17
34
|
const { content, sourcemap } = injector(urlInfo, { versionMappings })
|
|
@@ -22,6 +39,12 @@ export const injectVersionMappings = async (
|
|
|
22
39
|
}
|
|
23
40
|
}
|
|
24
41
|
|
|
42
|
+
const jsInjector = (urlInfo, { versionMappings }) => {
|
|
43
|
+
const magicSource = createMagicSource(urlInfo.content)
|
|
44
|
+
magicSource.prepend(generateClientCodeForVersionMappings(versionMappings))
|
|
45
|
+
return magicSource.toContentAndSourcemap()
|
|
46
|
+
}
|
|
47
|
+
|
|
25
48
|
const injectors = {
|
|
26
49
|
html: (urlInfo, { versionMappings }) => {
|
|
27
50
|
// ideally we would inject an importmap but browser support is too low
|
|
@@ -39,24 +62,19 @@ const injectors = {
|
|
|
39
62
|
}),
|
|
40
63
|
)
|
|
41
64
|
return {
|
|
42
|
-
content: stringifyHtmlAst(htmlAst,
|
|
43
|
-
removeOriginalPositionAttributes: true,
|
|
44
|
-
}),
|
|
65
|
+
content: stringifyHtmlAst(htmlAst),
|
|
45
66
|
}
|
|
46
67
|
},
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
magicSource.prepend(generateClientCodeForVersionMappings(versionMappings))
|
|
50
|
-
return magicSource.toContentAndSourcemap()
|
|
51
|
-
},
|
|
68
|
+
js_classic: jsInjector,
|
|
69
|
+
js_module: jsInjector,
|
|
52
70
|
}
|
|
53
71
|
|
|
54
72
|
const generateClientCodeForVersionMappings = (versionMappings) => {
|
|
55
73
|
return `
|
|
56
|
-
var __versionMappings__ = ${JSON.stringify(versionMappings, null, " ")}
|
|
57
|
-
var __envGlobal__ = typeof self === 'undefined' ? global : self
|
|
74
|
+
var __versionMappings__ = ${JSON.stringify(versionMappings, null, " ")};
|
|
75
|
+
var __envGlobal__ = typeof self === 'undefined' ? global : self;
|
|
58
76
|
__envGlobal__.__v__ = function (specifier) {
|
|
59
77
|
return __versionMappings__[specifier] || specifier
|
|
60
|
-
}
|
|
78
|
+
};
|
|
61
79
|
`
|
|
62
80
|
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { createMagicSource } from "@jsenv/utils/sourcemap/magic_source.js"
|
|
2
|
+
import { createVersionGenerator } from "@jsenv/utils/versioning/version_generator.js"
|
|
3
|
+
|
|
4
|
+
import { GRAPH } from "./graph_utils.js"
|
|
5
|
+
|
|
6
|
+
export const injectServiceWorkerUrls = async ({
|
|
7
|
+
finalGraph,
|
|
8
|
+
finalGraphKitchen,
|
|
9
|
+
lineBreakNormalization,
|
|
10
|
+
}) => {
|
|
11
|
+
const serviceWorkerEntryUrlInfos = GRAPH.filter(
|
|
12
|
+
finalGraph,
|
|
13
|
+
(finalUrlInfo) => {
|
|
14
|
+
return (
|
|
15
|
+
finalUrlInfo.subtype === "service_worker" &&
|
|
16
|
+
finalUrlInfo.data.isWebWorkerEntryPoint
|
|
17
|
+
)
|
|
18
|
+
},
|
|
19
|
+
)
|
|
20
|
+
if (serviceWorkerEntryUrlInfos.length === 0) {
|
|
21
|
+
return
|
|
22
|
+
}
|
|
23
|
+
const serviceWorkerUrls = {}
|
|
24
|
+
GRAPH.forEach(finalGraph, (urlInfo) => {
|
|
25
|
+
if (urlInfo.isInline || urlInfo.external) {
|
|
26
|
+
return
|
|
27
|
+
}
|
|
28
|
+
if (!urlInfo.url.startsWith("file:")) {
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
if (urlInfo.data.buildUrlIsVersioned) {
|
|
32
|
+
serviceWorkerUrls[urlInfo.data.buildUrlSpecifier] = {
|
|
33
|
+
versioned: true,
|
|
34
|
+
}
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
if (!urlInfo.data.version) {
|
|
38
|
+
// when url is not versioned we compute a "version" for that url anyway
|
|
39
|
+
// so that service worker source still changes and navigator
|
|
40
|
+
// detect there is a change
|
|
41
|
+
const versionGenerator = createVersionGenerator()
|
|
42
|
+
versionGenerator.augmentWithContent({
|
|
43
|
+
content: urlInfo.content,
|
|
44
|
+
contentType: urlInfo.contentType,
|
|
45
|
+
lineBreakNormalization,
|
|
46
|
+
})
|
|
47
|
+
const version = versionGenerator.generate()
|
|
48
|
+
urlInfo.data.version = version
|
|
49
|
+
}
|
|
50
|
+
serviceWorkerUrls[urlInfo.data.buildUrlSpecifier] = {
|
|
51
|
+
versioned: false,
|
|
52
|
+
version: urlInfo.data.version,
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
await Promise.all(
|
|
56
|
+
serviceWorkerEntryUrlInfos.map(async (serviceWorkerEntryUrlInfo) => {
|
|
57
|
+
const magicSource = createMagicSource(serviceWorkerEntryUrlInfo.content)
|
|
58
|
+
const urlsWithoutSelf = {
|
|
59
|
+
...serviceWorkerUrls,
|
|
60
|
+
}
|
|
61
|
+
delete urlsWithoutSelf[serviceWorkerEntryUrlInfo.data.buildUrlSpecifier]
|
|
62
|
+
magicSource.prepend(generateClientCode(urlsWithoutSelf))
|
|
63
|
+
const { content, sourcemap } = magicSource.toContentAndSourcemap()
|
|
64
|
+
await finalGraphKitchen.urlInfoTransformer.applyFinalTransformations(
|
|
65
|
+
serviceWorkerEntryUrlInfo,
|
|
66
|
+
{
|
|
67
|
+
content,
|
|
68
|
+
sourcemap,
|
|
69
|
+
},
|
|
70
|
+
)
|
|
71
|
+
}),
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const generateClientCode = (serviceWorkerUrls) => {
|
|
76
|
+
return `
|
|
77
|
+
self.serviceWorkerUrls = ${JSON.stringify(serviceWorkerUrls, null, " ")};
|
|
78
|
+
`
|
|
79
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Update <link rel="preload"> and friends after build (once we know everything)
|
|
3
|
+
*
|
|
4
|
+
* - Used to remove ressource hint targeting an url that is no longer used:
|
|
5
|
+
* - Happens because of import assertions transpilation (file is inlined into JS)
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
parseHtmlString,
|
|
10
|
+
visitHtmlAst,
|
|
11
|
+
stringifyHtmlAst,
|
|
12
|
+
getHtmlNodeAttributeByName,
|
|
13
|
+
removeHtmlNode,
|
|
14
|
+
} from "@jsenv/utils/html_ast/html_ast.js"
|
|
15
|
+
|
|
16
|
+
import { GRAPH } from "./graph_utils.js"
|
|
17
|
+
|
|
18
|
+
export const resyncRessourceHints = async ({
|
|
19
|
+
finalGraphKitchen,
|
|
20
|
+
finalGraph,
|
|
21
|
+
buildUrls,
|
|
22
|
+
}) => {
|
|
23
|
+
const ressourceHintActions = []
|
|
24
|
+
GRAPH.forEach(finalGraph, (urlInfo) => {
|
|
25
|
+
if (urlInfo.type !== "html") {
|
|
26
|
+
return
|
|
27
|
+
}
|
|
28
|
+
ressourceHintActions.push(async () => {
|
|
29
|
+
const htmlAst = parseHtmlString(urlInfo.content, {
|
|
30
|
+
storeOriginalPositions: false,
|
|
31
|
+
})
|
|
32
|
+
const visitLinkWithHref = (linkNode, hrefAttribute) => {
|
|
33
|
+
const href = hrefAttribute.value
|
|
34
|
+
if (!href || href.startsWith("data:")) {
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
const buildUrl = buildUrls[href]
|
|
38
|
+
const urlInfo = finalGraph.getUrlInfo(buildUrl)
|
|
39
|
+
if (!urlInfo) {
|
|
40
|
+
return
|
|
41
|
+
}
|
|
42
|
+
if (urlInfo.dependents.size === 0) {
|
|
43
|
+
removeHtmlNode(linkNode)
|
|
44
|
+
return
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
visitHtmlAst(htmlAst, (node) => {
|
|
48
|
+
if (node.nodeName !== "link") {
|
|
49
|
+
return
|
|
50
|
+
}
|
|
51
|
+
const hrefAttribute = getHtmlNodeAttributeByName(node, "href")
|
|
52
|
+
if (!hrefAttribute) {
|
|
53
|
+
return
|
|
54
|
+
}
|
|
55
|
+
visitLinkWithHref(node, hrefAttribute)
|
|
56
|
+
})
|
|
57
|
+
await finalGraphKitchen.urlInfoTransformer.applyFinalTransformations(
|
|
58
|
+
urlInfo,
|
|
59
|
+
{
|
|
60
|
+
content: stringifyHtmlAst(htmlAst),
|
|
61
|
+
},
|
|
62
|
+
)
|
|
63
|
+
})
|
|
64
|
+
})
|
|
65
|
+
await Promise.all(
|
|
66
|
+
ressourceHintActions.map((ressourceHintAction) => ressourceHintAction()),
|
|
67
|
+
)
|
|
68
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* startBuildServer is mean to interact with the build files;
|
|
3
|
+
* files that will be deployed to production server(s).
|
|
4
|
+
* We want to be as close as possible from the production in order to:
|
|
5
|
+
* - run lighthouse
|
|
6
|
+
* - run an automated test tool such as cypress, playwright
|
|
7
|
+
* - see exactly how build file behaves (debug, measure perf, etc)
|
|
8
|
+
* For these reasons "startBuildServer" must be as close as possible from a static file server.
|
|
9
|
+
* It is not meant to provide a nice developper experience: this is the role "startDevServer".
|
|
10
|
+
*
|
|
11
|
+
* Conclusion:
|
|
12
|
+
* "startBuildServer" must be as close as possible from a static file server because
|
|
13
|
+
* we want to be in the user shoes and we should not alter build files.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
jsenvAccessControlAllowedHeaders,
|
|
18
|
+
startServer,
|
|
19
|
+
pluginServerTiming,
|
|
20
|
+
pluginRequestWaitingCheck,
|
|
21
|
+
pluginCORS,
|
|
22
|
+
fetchFileSystem,
|
|
23
|
+
} from "@jsenv/server"
|
|
24
|
+
import {
|
|
25
|
+
assertAndNormalizeDirectoryUrl,
|
|
26
|
+
registerDirectoryLifecycle,
|
|
27
|
+
} from "@jsenv/filesystem"
|
|
28
|
+
import { createLogger } from "@jsenv/logger"
|
|
29
|
+
|
|
30
|
+
import { initReloadableProcess } from "@jsenv/utils/process_reload/process_reload.js"
|
|
31
|
+
import { createTaskLog } from "@jsenv/utils/logs/task_log.js"
|
|
32
|
+
|
|
33
|
+
export const startBuildServer = async ({
|
|
34
|
+
signal = new AbortController().signal,
|
|
35
|
+
handleSIGINT = true,
|
|
36
|
+
logLevel,
|
|
37
|
+
serverLogLevel = "warn",
|
|
38
|
+
protocol = "http",
|
|
39
|
+
http2,
|
|
40
|
+
certificate,
|
|
41
|
+
privateKey,
|
|
42
|
+
listenAnyIp,
|
|
43
|
+
ip,
|
|
44
|
+
port = 9779,
|
|
45
|
+
|
|
46
|
+
rootDirectoryUrl,
|
|
47
|
+
buildDirectoryUrl,
|
|
48
|
+
buildServerFiles = {
|
|
49
|
+
"./package.json": true,
|
|
50
|
+
"./jsenv.config.mjs": true,
|
|
51
|
+
},
|
|
52
|
+
buildServerMainFile,
|
|
53
|
+
buildServerAutoreload = false,
|
|
54
|
+
cooldownBetweenFileEvents,
|
|
55
|
+
mainBuildFileUrl = "/index.html",
|
|
56
|
+
}) => {
|
|
57
|
+
const logger = createLogger({ logLevel })
|
|
58
|
+
rootDirectoryUrl = assertAndNormalizeDirectoryUrl(rootDirectoryUrl)
|
|
59
|
+
buildDirectoryUrl = assertAndNormalizeDirectoryUrl(buildDirectoryUrl)
|
|
60
|
+
|
|
61
|
+
const reloadableProcess = await initReloadableProcess({
|
|
62
|
+
signal,
|
|
63
|
+
handleSIGINT,
|
|
64
|
+
...(buildServerAutoreload
|
|
65
|
+
? {
|
|
66
|
+
enabled: true,
|
|
67
|
+
logLevel: "info",
|
|
68
|
+
fileToRestart: buildServerMainFile,
|
|
69
|
+
}
|
|
70
|
+
: {
|
|
71
|
+
enabled: false,
|
|
72
|
+
}),
|
|
73
|
+
})
|
|
74
|
+
if (reloadableProcess.isPrimary) {
|
|
75
|
+
const buildServerFileChangeCallback = ({ relativeUrl, event }) => {
|
|
76
|
+
const url = new URL(relativeUrl, rootDirectoryUrl).href
|
|
77
|
+
if (buildServerAutoreload) {
|
|
78
|
+
logger.info(`file ${event} ${url} -> restarting server...`)
|
|
79
|
+
reloadableProcess.reload()
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const stopWatchingBuildServerFiles = registerDirectoryLifecycle(
|
|
83
|
+
rootDirectoryUrl,
|
|
84
|
+
{
|
|
85
|
+
watchPatterns: {
|
|
86
|
+
[buildServerMainFile]: true,
|
|
87
|
+
...buildServerFiles,
|
|
88
|
+
},
|
|
89
|
+
cooldownBetweenFileEvents,
|
|
90
|
+
keepProcessAlive: false,
|
|
91
|
+
recursive: true,
|
|
92
|
+
added: ({ relativeUrl }) => {
|
|
93
|
+
buildServerFileChangeCallback({ relativeUrl, event: "added" })
|
|
94
|
+
},
|
|
95
|
+
updated: ({ relativeUrl }) => {
|
|
96
|
+
buildServerFileChangeCallback({ relativeUrl, event: "modified" })
|
|
97
|
+
},
|
|
98
|
+
removed: ({ relativeUrl }) => {
|
|
99
|
+
buildServerFileChangeCallback({ relativeUrl, event: "removed" })
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
)
|
|
103
|
+
signal.addEventListener("abort", () => {
|
|
104
|
+
stopWatchingBuildServerFiles()
|
|
105
|
+
})
|
|
106
|
+
return {
|
|
107
|
+
origin: `${protocol}://127.0.0.1:${port}`,
|
|
108
|
+
stop: () => {
|
|
109
|
+
stopWatchingBuildServerFiles()
|
|
110
|
+
|
|
111
|
+
reloadableProcess.stop()
|
|
112
|
+
},
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
signal = reloadableProcess.signal
|
|
116
|
+
|
|
117
|
+
const startServerTask = createTaskLog(logger, "start build server")
|
|
118
|
+
const server = await startServer({
|
|
119
|
+
signal,
|
|
120
|
+
stopOnExit: false,
|
|
121
|
+
stopOnSIGINT: false,
|
|
122
|
+
stopOnInternalError: false,
|
|
123
|
+
keepProcessAlive: true,
|
|
124
|
+
logLevel: serverLogLevel,
|
|
125
|
+
startLog: false,
|
|
126
|
+
|
|
127
|
+
protocol,
|
|
128
|
+
http2,
|
|
129
|
+
certificate,
|
|
130
|
+
privateKey,
|
|
131
|
+
listenAnyIp,
|
|
132
|
+
ip,
|
|
133
|
+
port,
|
|
134
|
+
plugins: {
|
|
135
|
+
...pluginCORS({
|
|
136
|
+
accessControlAllowRequestOrigin: true,
|
|
137
|
+
accessControlAllowRequestMethod: true,
|
|
138
|
+
accessControlAllowRequestHeaders: true,
|
|
139
|
+
accessControlAllowedRequestHeaders: [
|
|
140
|
+
...jsenvAccessControlAllowedHeaders,
|
|
141
|
+
"x-jsenv-execution-id",
|
|
142
|
+
],
|
|
143
|
+
accessControlAllowCredentials: true,
|
|
144
|
+
}),
|
|
145
|
+
...pluginServerTiming(),
|
|
146
|
+
...pluginRequestWaitingCheck({
|
|
147
|
+
requestWaitingMs: 60 * 1000,
|
|
148
|
+
}),
|
|
149
|
+
},
|
|
150
|
+
sendErrorDetails: true,
|
|
151
|
+
requestToResponse: (request) => {
|
|
152
|
+
const urlIsVersioned = new URL(
|
|
153
|
+
request.ressource,
|
|
154
|
+
request.origin,
|
|
155
|
+
).searchParams.has("v")
|
|
156
|
+
if (mainBuildFileUrl && request.ressource === "/") {
|
|
157
|
+
request = {
|
|
158
|
+
...request,
|
|
159
|
+
ressource: mainBuildFileUrl,
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return fetchFileSystem(
|
|
163
|
+
new URL(request.ressource.slice(1), buildDirectoryUrl),
|
|
164
|
+
{
|
|
165
|
+
headers: request.headers,
|
|
166
|
+
cacheControl: urlIsVersioned
|
|
167
|
+
? `private,max-age=${SECONDS_IN_30_DAYS},immutable`
|
|
168
|
+
: "private,max-age=0,must-revalidate",
|
|
169
|
+
etagEnabled: true,
|
|
170
|
+
compressionEnabled: !request.pathname.endsWith(".mp4"),
|
|
171
|
+
rootDirectoryUrl: buildDirectoryUrl,
|
|
172
|
+
canReadDirectory: true,
|
|
173
|
+
},
|
|
174
|
+
)
|
|
175
|
+
},
|
|
176
|
+
})
|
|
177
|
+
startServerTask.done()
|
|
178
|
+
logger.info(``)
|
|
179
|
+
Object.keys(server.origins).forEach((key) => {
|
|
180
|
+
logger.info(`- ${server.origins[key]}`)
|
|
181
|
+
})
|
|
182
|
+
logger.info(``)
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
origin: server.origin,
|
|
186
|
+
stop: () => {
|
|
187
|
+
server.stop()
|
|
188
|
+
},
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const SECONDS_IN_30_DAYS = 60 * 60 * 24 * 30
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs"
|
|
2
|
-
import { urlToContentType } from "@jsenv/server"
|
|
3
2
|
import { collectFiles, normalizeStructuredMetaMap } from "@jsenv/filesystem"
|
|
4
3
|
|
|
5
4
|
import { DataUrl } from "@jsenv/utils/urls/data_url.js"
|
|
5
|
+
import { CONTENT_TYPE } from "@jsenv/utils/content_type/content_type.js"
|
|
6
6
|
|
|
7
7
|
export const jsenvPluginExplorer = ({ groups }) => {
|
|
8
8
|
const htmlClientFileUrl = new URL("./client/explorer.html", import.meta.url)
|
|
@@ -43,7 +43,7 @@ export const jsenvPluginExplorer = ({ groups }) => {
|
|
|
43
43
|
html = html.replace(
|
|
44
44
|
"FAVICON_HREF",
|
|
45
45
|
DataUrl.stringify({
|
|
46
|
-
contentType:
|
|
46
|
+
contentType: CONTENT_TYPE.fromUrlExtension(faviconClientFileUrl),
|
|
47
47
|
base64Flag: true,
|
|
48
48
|
data: readFileSync(new URL(faviconClientFileUrl)).toString("base64"),
|
|
49
49
|
}),
|
|
@@ -20,7 +20,7 @@ export const jsenvPluginToolbar = ({ logs = false } = {}) => {
|
|
|
20
20
|
appliesDuring: {
|
|
21
21
|
dev: true,
|
|
22
22
|
},
|
|
23
|
-
|
|
23
|
+
transformUrlContent: {
|
|
24
24
|
html: ({ url, content }, { referenceUtils }) => {
|
|
25
25
|
if (url === toolbarHtmlClientFileUrl) {
|
|
26
26
|
return null
|
|
@@ -28,10 +28,12 @@ export const jsenvPluginToolbar = ({ logs = false } = {}) => {
|
|
|
28
28
|
const htmlAst = parseHtmlString(content)
|
|
29
29
|
const [toolbarInjectorReference] = referenceUtils.inject({
|
|
30
30
|
type: "js_import_export",
|
|
31
|
+
expectedType: "js_module",
|
|
31
32
|
specifier: toolbarInjectorClientFileUrl,
|
|
32
33
|
})
|
|
33
34
|
const [toolbarClientFileReference] = referenceUtils.inject({
|
|
34
35
|
type: "iframe_src",
|
|
36
|
+
expectedType: "html",
|
|
35
37
|
specifier: toolbarHtmlClientFileUrl,
|
|
36
38
|
})
|
|
37
39
|
injectScriptAsEarlyAsPossible(
|