@jsenv/core 27.0.0-alpha.12 → 27.0.0-alpha.13
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/main.js +4 -0
- package/package.json +13 -7
- package/readme.md +4 -12
- package/src/build/build.js +438 -387
- package/src/build/build_urls_generator.js +23 -20
- package/src/build/graph_utils.js +31 -0
- package/src/build/{inject_version_mappings.js → inject_global_version_mappings.js} +31 -14
- package/src/build/inject_service_worker_urls.js +66 -12
- package/src/build/resync_ressource_hints.js +83 -0
- package/src/dev/plugins/autoreload/babel_plugin_metadata_import_meta_hot.js +1 -1
- package/src/dev/plugins/autoreload/client/import_meta_hot.js +3 -1
- package/src/dev/plugins/autoreload/html_hot_dependencies.js +2 -2
- package/src/dev/plugins/autoreload/jsenv_plugin_autoreload.js +61 -51
- package/src/dev/plugins/autoreload/sse_service.js +23 -3
- 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 +10 -5
- package/src/execute/execute.js +10 -4
- package/src/execute/run.js +17 -54
- package/src/execute/runtimes/browsers/from_playwright.js +167 -146
- package/src/execute/runtimes/node/node_process.js +281 -37
- 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} +30 -7
- package/src/omega/{runtime_support/runtime_support.js → compat/runtime_compat.js} +14 -16
- package/src/omega/errors.js +51 -58
- package/src/omega/fetched_content_compliance.js +24 -0
- package/src/omega/kitchen.js +396 -280
- package/src/omega/server/file_service.js +9 -11
- package/src/omega/url_graph/url_graph_load.js +13 -7
- package/src/omega/url_graph/url_graph_report.js +7 -5
- package/src/omega/url_graph.js +22 -10
- package/src/omega/web_workers.js +42 -0
- package/src/plugins/bundling/css/bundle_css.js +17 -0
- package/src/plugins/bundling/js_classic_workers/bundle_js_classic_workers.js +13 -0
- package/src/{build/plugins/bundle_js_module/jsenv_plugin_bundle_js_module.js → plugins/bundling/js_module/bundle_js_module.js} +100 -75
- package/src/plugins/bundling/jsenv_plugin_bundling.js +51 -0
- package/src/{omega/core_plugins → plugins}/commonjs_globals/jsenv_plugin_commonjs_globals.js +48 -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 +7 -4
- 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/{omega/core_plugins → plugins}/html_supervisor/client/html_supervisor_installer.js +3 -2
- package/src/{omega/core_plugins → plugins}/html_supervisor/client/html_supervisor_setup.js +0 -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 +38 -46
- package/src/plugins/http_urls/jsenv_plugin_http_urls.js +12 -0
- package/src/{omega/core_plugins → plugins}/import_meta_scenarios/jsenv_plugin_import_meta_scenarios.js +26 -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 +37 -31
- package/src/{omega/core_plugins → plugins}/inject_globals/jsenv_plugin_inject_globals.js +4 -6
- 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} +61 -40
- 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 +263 -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 +77 -0
- package/src/plugins/minification/json/minify_json.js +8 -0
- package/src/{omega/core_plugins → plugins}/node_esm_resolution/jsenv_plugin_node_esm_resolution.js +15 -15
- package/src/{omega → plugins}/plugin_controller.js +18 -10
- package/src/plugins/plugins.js +50 -0
- package/src/plugins/transpilation/as_js_classic/client/s.js +808 -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 +178 -0
- package/src/plugins/transpilation/as_js_classic/jsenv_plugin_script_type_module_as_classic.js +156 -0
- package/src/plugins/transpilation/as_js_classic/jsenv_plugin_top_level_await.js +37 -0
- package/src/plugins/transpilation/as_js_classic/jsenv_plugin_workers_type_module_as_classic.js +133 -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 +3 -21
- 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 +29 -27
- package/src/{omega/core_plugins → plugins/transpilation}/babel/new_stylesheet/babel_plugin_new_stylesheet_as_jsenv_import.js +0 -0
- 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/{omega/core_plugins → plugins/transpilation}/import_assertions/helpers/babel_plugin_metadata_import_assertions.js +0 -0
- package/src/plugins/transpilation/import_assertions/jsenv_plugin_import_assertions.js +243 -0
- package/src/plugins/transpilation/jsenv_plugin_transpilation.js +40 -0
- package/src/plugins/url_references/css/css_urls.js +49 -0
- package/src/plugins/url_references/html/html_urls.js +273 -0
- package/src/plugins/url_references/js/js_urls.js +170 -0
- package/src/plugins/url_references/jsenv_plugin_url_references.js +18 -0
- package/src/plugins/url_references/webmanifest/webmanifest_urls.js +17 -0
- package/src/{omega/core_plugins → plugins}/url_resolution/jsenv_plugin_url_resolution.js +12 -5
- package/src/{omega/core_plugins → plugins}/url_version/jsenv_plugin_url_version.js +8 -8
- package/src/preview/preview.js +3 -0
- package/src/test/execute_plan.js +18 -11
- package/src/test/execute_test_plan.js +5 -6
- package/src/test/logs_file_execution.js +8 -7
- package/src/build/plugins/minify_html/jsenv_plugin_minify_html.js +0 -30
- 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/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 -207
- package/src/omega/core_plugins/leading_slash/jsenv_plugin_leading_slash.js +0 -12
- package/src/omega/core_plugins.js +0 -42
- 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
|
@@ -4,23 +4,21 @@ import { memoizeByUrl } from "@jsenv/utils/memoize/memoize_by_url.js"
|
|
|
4
4
|
|
|
5
5
|
export const createBuilUrlsGenerator = ({ buildDirectoryUrl }) => {
|
|
6
6
|
const cache = {}
|
|
7
|
-
const generate = memoizeByUrl((url, urlInfo, parentUrlInfo) => {
|
|
8
|
-
const directoryPath = determineDirectoryPath(
|
|
7
|
+
const generate = memoizeByUrl((url, { urlInfo, parentUrlInfo }) => {
|
|
8
|
+
const directoryPath = determineDirectoryPath({
|
|
9
|
+
urlInfo,
|
|
10
|
+
parentUrlInfo,
|
|
11
|
+
})
|
|
9
12
|
let names = cache[directoryPath]
|
|
10
13
|
if (!names) {
|
|
11
14
|
names = []
|
|
12
15
|
cache[directoryPath] = names
|
|
13
16
|
}
|
|
14
|
-
|
|
15
|
-
let
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
searchParams.has("json_module") ||
|
|
20
|
-
searchParams.has("text_module")
|
|
21
|
-
) {
|
|
22
|
-
name = `${name}.js`
|
|
23
|
-
}
|
|
17
|
+
const urlObject = new URL(url)
|
|
18
|
+
let { search, hash } = urlObject
|
|
19
|
+
let name = urlInfo
|
|
20
|
+
? urlInfo.filename || urlToFilename(url)
|
|
21
|
+
: urlToFilename(url)
|
|
24
22
|
const [basename, extension] = splitFileExtension(name)
|
|
25
23
|
let nameCandidate = name
|
|
26
24
|
let integer = 1
|
|
@@ -33,7 +31,7 @@ export const createBuilUrlsGenerator = ({ buildDirectoryUrl }) => {
|
|
|
33
31
|
integer++
|
|
34
32
|
nameCandidate = `${basename}${integer}${extension}`
|
|
35
33
|
}
|
|
36
|
-
return `${buildDirectoryUrl}${directoryPath}${nameCandidate}`
|
|
34
|
+
return `${buildDirectoryUrl}${directoryPath}${nameCandidate}${search}${hash}`
|
|
37
35
|
})
|
|
38
36
|
|
|
39
37
|
return {
|
|
@@ -49,12 +47,17 @@ const splitFileExtension = (filename) => {
|
|
|
49
47
|
return [filename.slice(0, dotLastIndex), filename.slice(dotLastIndex)]
|
|
50
48
|
}
|
|
51
49
|
|
|
52
|
-
const determineDirectoryPath = (urlInfo, parentUrlInfo) => {
|
|
50
|
+
const determineDirectoryPath = ({ urlInfo, parentUrlInfo }) => {
|
|
53
51
|
if (urlInfo.isInline) {
|
|
54
|
-
const parentDirectoryPath = determineDirectoryPath(
|
|
52
|
+
const parentDirectoryPath = determineDirectoryPath({
|
|
53
|
+
urlInfo: parentUrlInfo,
|
|
54
|
+
})
|
|
55
55
|
return parentDirectoryPath
|
|
56
56
|
}
|
|
57
|
-
if (urlInfo.data.isEntryPoint) {
|
|
57
|
+
if (urlInfo.data.isEntryPoint || urlInfo.data.isWebWorkerEntryPoint) {
|
|
58
|
+
return ""
|
|
59
|
+
}
|
|
60
|
+
if (urlInfo.type === "importmap") {
|
|
58
61
|
return ""
|
|
59
62
|
}
|
|
60
63
|
if (urlInfo.type === "html") {
|
|
@@ -64,10 +67,10 @@ const determineDirectoryPath = (urlInfo, parentUrlInfo) => {
|
|
|
64
67
|
return "css/"
|
|
65
68
|
}
|
|
66
69
|
if (urlInfo.type === "js_module" || urlInfo.type === "js_classic") {
|
|
67
|
-
if (urlInfo.subtype === "service_worker") {
|
|
68
|
-
return ""
|
|
69
|
-
}
|
|
70
70
|
return "js/"
|
|
71
71
|
}
|
|
72
|
-
|
|
72
|
+
if (urlInfo.type === "json") {
|
|
73
|
+
return "json/"
|
|
74
|
+
}
|
|
75
|
+
return "other/"
|
|
73
76
|
}
|
|
@@ -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,11 +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
|
-
|
|
11
|
+
import { GRAPH } from "./graph_utils.js"
|
|
12
|
+
|
|
13
|
+
export const injectGlobalVersionMapping = async ({
|
|
14
|
+
finalGraphKitchen,
|
|
15
|
+
finalGraph,
|
|
14
16
|
versionMappings,
|
|
15
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 }) => {
|
|
16
32
|
const injector = injectors[urlInfo.type]
|
|
17
33
|
if (injector) {
|
|
18
34
|
const { content, sourcemap } = injector(urlInfo, { versionMappings })
|
|
@@ -23,6 +39,12 @@ export const injectVersionMappings = async ({
|
|
|
23
39
|
}
|
|
24
40
|
}
|
|
25
41
|
|
|
42
|
+
const jsInjector = (urlInfo, { versionMappings }) => {
|
|
43
|
+
const magicSource = createMagicSource(urlInfo.content)
|
|
44
|
+
magicSource.prepend(generateClientCodeForVersionMappings(versionMappings))
|
|
45
|
+
return magicSource.toContentAndSourcemap()
|
|
46
|
+
}
|
|
47
|
+
|
|
26
48
|
const injectors = {
|
|
27
49
|
html: (urlInfo, { versionMappings }) => {
|
|
28
50
|
// ideally we would inject an importmap but browser support is too low
|
|
@@ -40,24 +62,19 @@ const injectors = {
|
|
|
40
62
|
}),
|
|
41
63
|
)
|
|
42
64
|
return {
|
|
43
|
-
content: stringifyHtmlAst(htmlAst,
|
|
44
|
-
removeOriginalPositionAttributes: true,
|
|
45
|
-
}),
|
|
65
|
+
content: stringifyHtmlAst(htmlAst),
|
|
46
66
|
}
|
|
47
67
|
},
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
magicSource.prepend(generateClientCodeForVersionMappings(versionMappings))
|
|
51
|
-
return magicSource.toContentAndSourcemap()
|
|
52
|
-
},
|
|
68
|
+
js_classic: jsInjector,
|
|
69
|
+
js_module: jsInjector,
|
|
53
70
|
}
|
|
54
71
|
|
|
55
72
|
const generateClientCodeForVersionMappings = (versionMappings) => {
|
|
56
73
|
return `
|
|
57
|
-
var __versionMappings__ = ${JSON.stringify(versionMappings, null, " ")}
|
|
58
|
-
var __envGlobal__ = typeof self === 'undefined' ? global : self
|
|
74
|
+
var __versionMappings__ = ${JSON.stringify(versionMappings, null, " ")};
|
|
75
|
+
var __envGlobal__ = typeof self === 'undefined' ? global : self;
|
|
59
76
|
__envGlobal__.__v__ = function (specifier) {
|
|
60
77
|
return __versionMappings__[specifier] || specifier
|
|
61
|
-
}
|
|
78
|
+
};
|
|
62
79
|
`
|
|
63
80
|
}
|
|
@@ -1,21 +1,75 @@
|
|
|
1
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"
|
|
2
5
|
|
|
3
6
|
export const injectServiceWorkerUrls = async ({
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
+
finalGraph,
|
|
8
|
+
finalGraphKitchen,
|
|
9
|
+
lineBreakNormalization,
|
|
7
10
|
}) => {
|
|
8
|
-
const
|
|
9
|
-
|
|
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
|
|
11
22
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
+
}
|
|
18
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
|
+
)
|
|
19
73
|
}
|
|
20
74
|
|
|
21
75
|
const generateClientCode = (serviceWorkerUrls) => {
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import {
|
|
2
|
+
parseHtmlString,
|
|
3
|
+
visitHtmlAst,
|
|
4
|
+
stringifyHtmlAst,
|
|
5
|
+
getHtmlNodeAttributeByName,
|
|
6
|
+
assignHtmlNodeAttributes,
|
|
7
|
+
removeHtmlNode,
|
|
8
|
+
} from "@jsenv/utils/html_ast/html_ast.js"
|
|
9
|
+
|
|
10
|
+
import { GRAPH } from "./graph_utils.js"
|
|
11
|
+
|
|
12
|
+
// update ressource hint that where targeting a file that has changed during build
|
|
13
|
+
// (happens for import assertions and file modified by "?as_js_classic")
|
|
14
|
+
export const resyncRessourceHints = async ({
|
|
15
|
+
finalGraphKitchen,
|
|
16
|
+
finalGraph,
|
|
17
|
+
buildUrls,
|
|
18
|
+
buildUrlRedirections,
|
|
19
|
+
}) => {
|
|
20
|
+
const ressourceHintActions = []
|
|
21
|
+
GRAPH.forEach(finalGraph, (urlInfo) => {
|
|
22
|
+
if (urlInfo.type !== "html") {
|
|
23
|
+
return
|
|
24
|
+
}
|
|
25
|
+
ressourceHintActions.push(async () => {
|
|
26
|
+
const htmlAst = parseHtmlString(urlInfo.content, {
|
|
27
|
+
storeOriginalPositions: false,
|
|
28
|
+
})
|
|
29
|
+
const visitLinkWithHref = (linkNode, hrefAttribute) => {
|
|
30
|
+
const href = hrefAttribute.value
|
|
31
|
+
if (!href || href.startsWith("data:")) {
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
const buildUrl = buildUrls[href]
|
|
35
|
+
const urlInfo = finalGraph.getUrlInfo(buildUrl)
|
|
36
|
+
if (!urlInfo) {
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
if (urlInfo.dependents.size === 0) {
|
|
40
|
+
removeHtmlNode(linkNode)
|
|
41
|
+
return
|
|
42
|
+
}
|
|
43
|
+
const buildUrlRedirected = buildUrlRedirections[buildUrl]
|
|
44
|
+
if (buildUrlRedirected) {
|
|
45
|
+
const urlInfoRedirected = finalGraph.getUrlInfo(buildUrlRedirected)
|
|
46
|
+
hrefAttribute.value = urlInfoRedirected.data.buildUrlSpecifier
|
|
47
|
+
|
|
48
|
+
if (
|
|
49
|
+
urlInfo.type === "js_module" &&
|
|
50
|
+
urlInfoRedirected.type === "js_classic"
|
|
51
|
+
) {
|
|
52
|
+
const relAttribute = getHtmlNodeAttributeByName(linkNode, "rel")
|
|
53
|
+
if (relAttribute && relAttribute.value === "modulepreload") {
|
|
54
|
+
assignHtmlNodeAttributes(linkNode, {
|
|
55
|
+
rel: "preload",
|
|
56
|
+
as: "script",
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
visitHtmlAst(htmlAst, (node) => {
|
|
63
|
+
if (node.nodeName !== "link") {
|
|
64
|
+
return
|
|
65
|
+
}
|
|
66
|
+
const hrefAttribute = getHtmlNodeAttributeByName(node, "href")
|
|
67
|
+
if (!hrefAttribute) {
|
|
68
|
+
return
|
|
69
|
+
}
|
|
70
|
+
visitLinkWithHref(node, hrefAttribute)
|
|
71
|
+
})
|
|
72
|
+
await finalGraphKitchen.urlInfoTransformer.applyFinalTransformations(
|
|
73
|
+
urlInfo,
|
|
74
|
+
{
|
|
75
|
+
content: stringifyHtmlAst(htmlAst),
|
|
76
|
+
},
|
|
77
|
+
)
|
|
78
|
+
})
|
|
79
|
+
})
|
|
80
|
+
await Promise.all(
|
|
81
|
+
ressourceHintActions.map((ressourceHintAction) => ressourceHintAction()),
|
|
82
|
+
)
|
|
83
|
+
}
|
|
@@ -40,7 +40,9 @@ export const createImportMetaHot = (importMetaUrl) => {
|
|
|
40
40
|
})
|
|
41
41
|
return
|
|
42
42
|
}
|
|
43
|
-
throw new Error(
|
|
43
|
+
throw new Error(
|
|
44
|
+
`invalid call to import.meta.hot.accept(), received ${firstArg}`,
|
|
45
|
+
)
|
|
44
46
|
},
|
|
45
47
|
dispose: (callback) => {
|
|
46
48
|
addUrlMeta(url, {
|
|
@@ -53,7 +53,7 @@ export const collectHotDataFromHtmlAst = (htmlAst) => {
|
|
|
53
53
|
})
|
|
54
54
|
visitUrlSpecifierAttribute({
|
|
55
55
|
node,
|
|
56
|
-
attributeName: "
|
|
56
|
+
attributeName: "generated-from-href",
|
|
57
57
|
hotAccepted,
|
|
58
58
|
})
|
|
59
59
|
}
|
|
@@ -65,7 +65,7 @@ export const collectHotDataFromHtmlAst = (htmlAst) => {
|
|
|
65
65
|
})
|
|
66
66
|
visitUrlSpecifierAttribute({
|
|
67
67
|
node,
|
|
68
|
-
attributeName: "
|
|
68
|
+
attributeName: "generated-from-src",
|
|
69
69
|
hotAccepted,
|
|
70
70
|
})
|
|
71
71
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { urlToRelativeUrl } from "@jsenv/filesystem"
|
|
2
2
|
import { createCallbackList } from "@jsenv/abort"
|
|
3
3
|
|
|
4
|
-
import { injectQueryParams } from "@jsenv/utils/urls/url_utils.js"
|
|
5
4
|
import { createMagicSource } from "@jsenv/utils/sourcemap/magic_source.js"
|
|
6
5
|
import {
|
|
7
6
|
parseHtmlString,
|
|
@@ -19,37 +18,31 @@ export const jsenvPluginAutoreload = ({
|
|
|
19
18
|
rootDirectoryUrl,
|
|
20
19
|
urlGraph,
|
|
21
20
|
autoreloadPatterns,
|
|
21
|
+
cooldownBetweenFileEvents,
|
|
22
22
|
}) => {
|
|
23
23
|
return [
|
|
24
|
+
jsenvPluginHmr(),
|
|
24
25
|
jsenvPluginHot(),
|
|
25
26
|
jsenvPluginHotSSE({
|
|
26
27
|
rootDirectoryUrl,
|
|
27
28
|
urlGraph,
|
|
28
29
|
autoreloadPatterns,
|
|
30
|
+
cooldownBetweenFileEvents,
|
|
29
31
|
}),
|
|
30
32
|
]
|
|
31
33
|
}
|
|
32
34
|
|
|
33
|
-
const
|
|
34
|
-
const eventSourceClientFileUrl = new URL(
|
|
35
|
-
"./client/event_source_client.js",
|
|
36
|
-
import.meta.url,
|
|
37
|
-
).href
|
|
38
|
-
const importMetaHotClientFileUrl = new URL(
|
|
39
|
-
"./client/import_meta_hot.js",
|
|
40
|
-
import.meta.url,
|
|
41
|
-
).href
|
|
42
|
-
|
|
35
|
+
const jsenvPluginHmr = () => {
|
|
43
36
|
return {
|
|
44
|
-
name: "jsenv:
|
|
37
|
+
name: "jsenv:hmr",
|
|
45
38
|
appliesDuring: { dev: true },
|
|
46
|
-
|
|
47
|
-
const urlObject = new URL(url)
|
|
39
|
+
normalizeUrl: (reference) => {
|
|
40
|
+
const urlObject = new URL(reference.url)
|
|
48
41
|
if (!urlObject.searchParams.has("hmr")) {
|
|
49
|
-
data.hmr = false
|
|
42
|
+
reference.data.hmr = false
|
|
50
43
|
return null
|
|
51
44
|
}
|
|
52
|
-
data.hmr = true
|
|
45
|
+
reference.data.hmr = true
|
|
53
46
|
// "hmr" search param goal is to mark url as enabling hmr:
|
|
54
47
|
// this goal is achieved when we reach this part of the code
|
|
55
48
|
// We get rid of this params so that urlGraph and other parts of the code
|
|
@@ -58,36 +51,54 @@ const jsenvPluginHot = () => {
|
|
|
58
51
|
urlObject.searchParams.delete("v")
|
|
59
52
|
return urlObject.href
|
|
60
53
|
},
|
|
61
|
-
|
|
62
|
-
const parentUrlInfo = urlGraph.getUrlInfo(parentUrl)
|
|
54
|
+
transformUrlSearchParams: (reference, context) => {
|
|
55
|
+
const parentUrlInfo = context.urlGraph.getUrlInfo(reference.parentUrl)
|
|
63
56
|
if (!parentUrlInfo || !parentUrlInfo.data.hmr) {
|
|
64
57
|
return null
|
|
65
58
|
}
|
|
66
|
-
|
|
59
|
+
const urlInfo = context.urlGraph.getUrlInfo(reference.url)
|
|
60
|
+
if (!urlInfo.data.hmrTimestamp) {
|
|
67
61
|
return null
|
|
68
62
|
}
|
|
69
|
-
return
|
|
63
|
+
return {
|
|
70
64
|
hmr: "",
|
|
71
|
-
v: data.hmrTimestamp,
|
|
72
|
-
}
|
|
65
|
+
v: urlInfo.data.hmrTimestamp,
|
|
66
|
+
}
|
|
73
67
|
},
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const jsenvPluginHot = () => {
|
|
72
|
+
const eventSourceClientFileUrl = new URL(
|
|
73
|
+
"./client/event_source_client.js",
|
|
74
|
+
import.meta.url,
|
|
75
|
+
).href
|
|
76
|
+
const importMetaHotClientFileUrl = new URL(
|
|
77
|
+
"./client/import_meta_hot.js",
|
|
78
|
+
import.meta.url,
|
|
79
|
+
).href
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
name: "jsenv:hot",
|
|
83
|
+
appliesDuring: { dev: true },
|
|
84
|
+
transformUrlContent: {
|
|
85
|
+
html: (htmlUrlInfo, context) => {
|
|
86
|
+
const htmlAst = parseHtmlString(htmlUrlInfo.content)
|
|
77
87
|
const { hotReferences } = collectHotDataFromHtmlAst(htmlAst)
|
|
78
|
-
data.hotDecline = false
|
|
79
|
-
data.hotAcceptSelf = false
|
|
80
|
-
data.hotAcceptDependencies = hotReferences.map(
|
|
88
|
+
htmlUrlInfo.data.hotDecline = false
|
|
89
|
+
htmlUrlInfo.data.hotAcceptSelf = false
|
|
90
|
+
htmlUrlInfo.data.hotAcceptDependencies = hotReferences.map(
|
|
81
91
|
({ type, specifier }) => {
|
|
82
|
-
const [reference] = referenceUtils.
|
|
92
|
+
const [reference] = context.referenceUtils.found({
|
|
83
93
|
type,
|
|
84
94
|
specifier,
|
|
85
95
|
})
|
|
86
96
|
return reference.url
|
|
87
97
|
},
|
|
88
98
|
)
|
|
89
|
-
const [eventSourceClientReference] = referenceUtils.inject({
|
|
99
|
+
const [eventSourceClientReference] = context.referenceUtils.inject({
|
|
90
100
|
type: "script_src",
|
|
101
|
+
expectedType: "js_module",
|
|
91
102
|
specifier: eventSourceClientFileUrl,
|
|
92
103
|
})
|
|
93
104
|
injectScriptAsEarlyAsPossible(
|
|
@@ -104,20 +115,15 @@ const jsenvPluginHot = () => {
|
|
|
104
115
|
content: htmlModified,
|
|
105
116
|
}
|
|
106
117
|
},
|
|
107
|
-
css: (
|
|
108
|
-
data.hotDecline = false
|
|
109
|
-
data.hotAcceptSelf = false
|
|
110
|
-
data.hotAcceptDependencies = []
|
|
118
|
+
css: (cssUrlInfo) => {
|
|
119
|
+
cssUrlInfo.data.hotDecline = false
|
|
120
|
+
cssUrlInfo.data.hotAcceptSelf = false
|
|
121
|
+
cssUrlInfo.data.hotAcceptDependencies = []
|
|
111
122
|
},
|
|
112
|
-
js_module: async (
|
|
113
|
-
{ url, generatedUrl, data, content },
|
|
114
|
-
{ referenceUtils },
|
|
115
|
-
) => {
|
|
123
|
+
js_module: async (urlInfo, context) => {
|
|
116
124
|
const { metadata } = await applyBabelPlugins({
|
|
117
125
|
babelPlugins: [babelPluginMetadataImportMetaHot],
|
|
118
|
-
|
|
119
|
-
generatedUrl,
|
|
120
|
-
content,
|
|
126
|
+
urlInfo,
|
|
121
127
|
})
|
|
122
128
|
const {
|
|
123
129
|
importMetaHotDetected,
|
|
@@ -125,9 +131,9 @@ const jsenvPluginHot = () => {
|
|
|
125
131
|
hotAcceptSelf,
|
|
126
132
|
hotAcceptDependencies,
|
|
127
133
|
} = metadata
|
|
128
|
-
data.hotDecline = hotDecline
|
|
129
|
-
data.hotAcceptSelf = hotAcceptSelf
|
|
130
|
-
data.hotAcceptDependencies = hotAcceptDependencies
|
|
134
|
+
urlInfo.data.hotDecline = hotDecline
|
|
135
|
+
urlInfo.data.hotAcceptSelf = hotAcceptSelf
|
|
136
|
+
urlInfo.data.hotAcceptDependencies = hotAcceptDependencies
|
|
131
137
|
if (!importMetaHotDetected) {
|
|
132
138
|
return null
|
|
133
139
|
}
|
|
@@ -135,12 +141,14 @@ const jsenvPluginHot = () => {
|
|
|
135
141
|
// better sourcemap than doing the equivalent with babel
|
|
136
142
|
// I suspect it's because I was doing injectAstAfterImport(programPath, ast.program.body[0])
|
|
137
143
|
// which is likely not well supported by babel
|
|
138
|
-
const [importMetaHotClientFileReference] =
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
+
const [importMetaHotClientFileReference] =
|
|
145
|
+
context.referenceUtils.inject({
|
|
146
|
+
parentUrl: urlInfo.url,
|
|
147
|
+
type: "js_import_export",
|
|
148
|
+
expectedType: "js_module",
|
|
149
|
+
specifier: importMetaHotClientFileUrl,
|
|
150
|
+
})
|
|
151
|
+
const magicSource = createMagicSource(urlInfo.content)
|
|
144
152
|
magicSource.prepend(
|
|
145
153
|
`import { createImportMetaHot } from ${importMetaHotClientFileReference.generatedSpecifier}
|
|
146
154
|
import.meta.hot = createImportMetaHot(import.meta.url)
|
|
@@ -156,6 +164,7 @@ const jsenvPluginHotSSE = ({
|
|
|
156
164
|
rootDirectoryUrl,
|
|
157
165
|
urlGraph,
|
|
158
166
|
autoreloadPatterns,
|
|
167
|
+
cooldownBetweenFileEvents,
|
|
159
168
|
}) => {
|
|
160
169
|
const hotUpdateCallbackList = createCallbackList()
|
|
161
170
|
const notifyDeclined = ({ cause, reason, declinedBy }) => {
|
|
@@ -274,6 +283,8 @@ const jsenvPluginHotSSE = ({
|
|
|
274
283
|
const sseService = createSSEService({
|
|
275
284
|
rootDirectoryUrl,
|
|
276
285
|
autoreloadPatterns,
|
|
286
|
+
hotUpdateCallbackList,
|
|
287
|
+
cooldownBetweenFileEvents,
|
|
277
288
|
onFileChange: ({ relativeUrl, event }) => {
|
|
278
289
|
const url = new URL(relativeUrl, rootDirectoryUrl).href
|
|
279
290
|
const urlInfo = urlGraph.urlInfos[url]
|
|
@@ -297,7 +308,6 @@ const jsenvPluginHotSSE = ({
|
|
|
297
308
|
})
|
|
298
309
|
}
|
|
299
310
|
},
|
|
300
|
-
hotUpdateCallbackList,
|
|
301
311
|
})
|
|
302
312
|
urlGraph.prunedCallbackList.add(({ prunedUrlInfos, firstUrlInfo }) => {
|
|
303
313
|
prunedUrlInfos.forEach((prunedUrlInfo) => {
|
|
@@ -10,6 +10,7 @@ export const createSSEService = ({
|
|
|
10
10
|
autoreloadPatterns,
|
|
11
11
|
onFileChange,
|
|
12
12
|
hotUpdateCallbackList,
|
|
13
|
+
cooldownBetweenFileEvents = 0,
|
|
13
14
|
}) => {
|
|
14
15
|
const destroyCallbackList = createCallbackListNotifiedOnce()
|
|
15
16
|
const projectFileModified = createCallbackList()
|
|
@@ -111,9 +112,11 @@ export const createSSEService = ({
|
|
|
111
112
|
}
|
|
112
113
|
},
|
|
113
114
|
)
|
|
114
|
-
const stopWatching = watchProjectFiles(
|
|
115
|
-
|
|
116
|
-
|
|
115
|
+
const stopWatching = watchProjectFiles(
|
|
116
|
+
cooldownBetweenFileEvents
|
|
117
|
+
? guardTooFastSecondCall(onFileChange, cooldownBetweenFileEvents)
|
|
118
|
+
: onFileChange,
|
|
119
|
+
)
|
|
117
120
|
return () => {
|
|
118
121
|
removeHotUpdateCallback()
|
|
119
122
|
stopWatching()
|
|
@@ -147,3 +150,20 @@ export const createSSEService = ({
|
|
|
147
150
|
},
|
|
148
151
|
}
|
|
149
152
|
}
|
|
153
|
+
|
|
154
|
+
const guardTooFastSecondCall = (callback, cooldownBetweenFileEvents = 40) => {
|
|
155
|
+
const previousCallMsMap = new Map()
|
|
156
|
+
return ({ relativeUrl, event }) => {
|
|
157
|
+
const previousCallMs = previousCallMsMap.get(relativeUrl)
|
|
158
|
+
const nowMs = Date.now()
|
|
159
|
+
if (previousCallMs) {
|
|
160
|
+
const msEllapsed = nowMs - previousCallMs
|
|
161
|
+
if (msEllapsed < cooldownBetweenFileEvents) {
|
|
162
|
+
previousCallMsMap.delete(relativeUrl)
|
|
163
|
+
return
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
previousCallMsMap.set(relativeUrl, nowMs)
|
|
167
|
+
callback({ relativeUrl, event })
|
|
168
|
+
}
|
|
169
|
+
}
|
|
@@ -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
|
}),
|