@jsenv/core 28.5.1 → 29.0.1
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/html/explorer.html +2 -2
- package/dist/js/autoreload.js +1 -7
- package/dist/js/supervisor.js +10 -6
- package/dist/main.js +335 -344
- package/package.json +2 -2
- package/src/build/build.js +7 -17
- package/src/dev/start_dev_server.js +7 -17
- package/src/execute/runtimes/browsers/from_playwright.js +1 -5
- package/src/omega/kitchen.js +1 -50
- package/src/omega/omega_server.js +6 -4
- package/src/omega/server/file_service.js +10 -8
- package/src/plugins/autoreload/client/autoreload.js +0 -8
- package/src/plugins/autoreload/jsenv_plugin_autoreload_server.js +0 -1
- package/src/plugins/cache_control/jsenv_plugin_cache_control.js +1 -11
- package/src/plugins/explorer/client/explorer.html +2 -2
- package/src/plugins/explorer/jsenv_plugin_explorer.js +73 -53
- package/src/plugins/import_meta_hot/html_hot_dependencies.js +2 -7
- package/src/plugins/plugins.js +34 -17
- package/src/plugins/supervisor/client/supervisor.js +8 -9
- package/src/plugins/toolbar/client/util/fetch_using_xhr.js +0 -3
- package/src/plugins/url_analysis/jsenv_plugin_url_analysis.js +2 -2
- package/src/plugins/url_resolution/jsenv_plugin_url_resolution.js +104 -23
- package/src/plugins/url_resolution/node_esm_resolver.js +110 -0
- package/src/plugins/url_version/jsenv_plugin_url_version.js +10 -5
- package/src/plugins/leading_slash/jsenv_plugin_leading_slash.js +0 -13
- package/src/plugins/node_esm_resolution/jsenv_plugin_node_esm_resolution.js +0 -148
|
@@ -1,34 +1,115 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/*
|
|
2
|
+
* This plugin is responsible to resolve urls except for a few cases:
|
|
3
|
+
* - A custom plugin implements a resolveUrl hook returning something
|
|
4
|
+
* - The reference.type is "filesystem" -> it is handled by jsenv_plugin_file_urls.js
|
|
5
|
+
*
|
|
6
|
+
* By default node esm resolution applies inside js modules
|
|
7
|
+
* and the rest uses the web standard url resolution (new URL):
|
|
8
|
+
* - "http_request"
|
|
9
|
+
* - "entry_point"
|
|
10
|
+
* - "js_import_export"
|
|
11
|
+
* - "link_href"
|
|
12
|
+
* - "script_src"
|
|
13
|
+
* - "a_href"
|
|
14
|
+
* - "iframe_src
|
|
15
|
+
* - "img_src"
|
|
16
|
+
* - "img_srcset"
|
|
17
|
+
* - "source_src"
|
|
18
|
+
* - "source_srcset"
|
|
19
|
+
* - "image_href"
|
|
20
|
+
* - "use_href"
|
|
21
|
+
* - "css_@import"
|
|
22
|
+
* - "css_url"
|
|
23
|
+
* - "sourcemap_comment"
|
|
24
|
+
* - "js_url_specifier"
|
|
25
|
+
* - "js_inline_content"
|
|
26
|
+
* - "webmanifest_icon_src"
|
|
27
|
+
* - "package_json"
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
import { createNodeEsmResolver } from "./node_esm_resolver.js"
|
|
31
|
+
|
|
32
|
+
export const jsenvPluginUrlResolution = ({
|
|
33
|
+
runtimeCompat,
|
|
34
|
+
clientMainFileUrl,
|
|
35
|
+
urlResolution,
|
|
36
|
+
}) => {
|
|
37
|
+
const resolveUrlUsingWebResolution = (reference) => {
|
|
3
38
|
return new URL(
|
|
4
39
|
reference.specifier,
|
|
5
40
|
reference.baseUrl || reference.parentUrl,
|
|
6
41
|
).href
|
|
7
42
|
}
|
|
43
|
+
|
|
44
|
+
const resolvers = {}
|
|
45
|
+
Object.keys(urlResolution).forEach((referenceType) => {
|
|
46
|
+
const resolver = urlResolution[referenceType]
|
|
47
|
+
if (typeof resolver !== "object") {
|
|
48
|
+
throw new Error(
|
|
49
|
+
`Unexpected urlResolution configuration:
|
|
50
|
+
"${referenceType}" resolution value must be an object, got ${resolver}`,
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
let { web, node_esm, ...rest } = resolver
|
|
54
|
+
const unexpectedKey = Object.keys(rest)[0]
|
|
55
|
+
if (unexpectedKey) {
|
|
56
|
+
throw new Error(
|
|
57
|
+
`Unexpected urlResolution configuration:
|
|
58
|
+
"${referenceType}" resolution key must be "web" or "node_esm", found "${
|
|
59
|
+
Object.keys(rest)[0]
|
|
60
|
+
}"`,
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
if (node_esm === undefined) {
|
|
64
|
+
node_esm = referenceType === "js_import_export"
|
|
65
|
+
}
|
|
66
|
+
if (web === undefined) {
|
|
67
|
+
web = true
|
|
68
|
+
}
|
|
69
|
+
if (node_esm) {
|
|
70
|
+
if (node_esm === true) node_esm = {}
|
|
71
|
+
const { packageConditions } = node_esm
|
|
72
|
+
resolvers[referenceType] = createNodeEsmResolver({
|
|
73
|
+
runtimeCompat,
|
|
74
|
+
packageConditions,
|
|
75
|
+
})
|
|
76
|
+
} else if (web) {
|
|
77
|
+
resolvers[referenceType] = resolveUrlUsingWebResolution
|
|
78
|
+
}
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
if (!resolvers["js_import_export"]) {
|
|
82
|
+
resolvers.js_import_export = createNodeEsmResolver({ runtimeCompat })
|
|
83
|
+
}
|
|
84
|
+
if (!resolvers["*"]) {
|
|
85
|
+
resolvers["*"] = resolveUrlUsingWebResolution
|
|
86
|
+
}
|
|
87
|
+
|
|
8
88
|
return {
|
|
9
89
|
name: "jsenv:url_resolution",
|
|
10
90
|
appliesDuring: "*",
|
|
11
|
-
resolveUrl: {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
91
|
+
resolveUrl: (reference, context) => {
|
|
92
|
+
if (reference.specifier === "/") {
|
|
93
|
+
return String(clientMainFileUrl)
|
|
94
|
+
}
|
|
95
|
+
if (reference.specifier[0] === "/") {
|
|
96
|
+
return new URL(reference.specifier.slice(1), context.rootDirectoryUrl)
|
|
97
|
+
.href
|
|
98
|
+
}
|
|
99
|
+
const resolver = resolvers[reference.type] || resolvers["*"]
|
|
100
|
+
return resolver(reference, context)
|
|
101
|
+
},
|
|
102
|
+
// when specifier is prefixed by "file:///@ignore/"
|
|
103
|
+
// we return an empty js module (used by node esm)
|
|
104
|
+
fetchUrlContent: (urlInfo) => {
|
|
105
|
+
if (urlInfo.url.startsWith("file:///@ignore/")) {
|
|
106
|
+
return {
|
|
107
|
+
content: "export default {}",
|
|
108
|
+
contentType: "text/javascript",
|
|
109
|
+
type: "js_module",
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return null
|
|
32
113
|
},
|
|
33
114
|
}
|
|
34
115
|
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* - should I restore eventual search params lost during node esm resolution
|
|
3
|
+
* - what about symlinks?
|
|
4
|
+
* It feels like I should apply symlink (when we don't want to preserve them)
|
|
5
|
+
* once a file:/// url is found, regardless
|
|
6
|
+
* if that comes from node resolution or anything else (not even magic resolution)
|
|
7
|
+
* it should likely be an other plugin happening after the others
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { readFileSync } from "node:fs"
|
|
11
|
+
import { bufferToEtag } from "@jsenv/filesystem"
|
|
12
|
+
import {
|
|
13
|
+
applyNodeEsmResolution,
|
|
14
|
+
readCustomConditionsFromProcessArgs,
|
|
15
|
+
defaultLookupPackageScope,
|
|
16
|
+
defaultReadPackageJson,
|
|
17
|
+
} from "@jsenv/node-esm-resolution"
|
|
18
|
+
|
|
19
|
+
export const createNodeEsmResolver = ({ runtimeCompat, packageConditions }) => {
|
|
20
|
+
const nodeRuntimeEnabled = Object.keys(runtimeCompat).includes("node")
|
|
21
|
+
// https://nodejs.org/api/esm.html#resolver-algorithm-specification
|
|
22
|
+
packageConditions = packageConditions || [
|
|
23
|
+
...readCustomConditionsFromProcessArgs(),
|
|
24
|
+
nodeRuntimeEnabled ? "node" : "browser",
|
|
25
|
+
"import",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
return (reference, context) => {
|
|
29
|
+
const { parentUrl, specifier } = reference
|
|
30
|
+
const { url, type, packageUrl } = applyNodeEsmResolution({
|
|
31
|
+
conditions: packageConditions,
|
|
32
|
+
parentUrl,
|
|
33
|
+
specifier,
|
|
34
|
+
})
|
|
35
|
+
if (context.scenarios.dev) {
|
|
36
|
+
const dependsOnPackageJson =
|
|
37
|
+
type !== "relative_specifier" &&
|
|
38
|
+
type !== "absolute_specifier" &&
|
|
39
|
+
type !== "node_builtin_specifier"
|
|
40
|
+
if (dependsOnPackageJson) {
|
|
41
|
+
// this reference depends on package.json and node_modules
|
|
42
|
+
// to be resolved. Each file using this specifier
|
|
43
|
+
// must be invalidated when corresponding package.json changes
|
|
44
|
+
addRelationshipWithPackageJson({
|
|
45
|
+
reference,
|
|
46
|
+
context,
|
|
47
|
+
packageJsonUrl: `${packageUrl}package.json`,
|
|
48
|
+
field: type.startsWith("field:")
|
|
49
|
+
? `#${type.slice("field:".length)}`
|
|
50
|
+
: "",
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (context.scenarios.dev) {
|
|
55
|
+
// without this check a file inside a project without package.json
|
|
56
|
+
// could be considered as a node module if there is a ancestor package.json
|
|
57
|
+
// but we want to version only node modules
|
|
58
|
+
if (url.includes("/node_modules/")) {
|
|
59
|
+
const packageDirectoryUrl = defaultLookupPackageScope(url)
|
|
60
|
+
if (
|
|
61
|
+
packageDirectoryUrl &&
|
|
62
|
+
packageDirectoryUrl !== context.rootDirectoryUrl
|
|
63
|
+
) {
|
|
64
|
+
const packageVersion =
|
|
65
|
+
defaultReadPackageJson(packageDirectoryUrl).version
|
|
66
|
+
// package version can be null, see https://github.com/babel/babel/blob/2ce56e832c2dd7a7ed92c89028ba929f874c2f5c/packages/babel-runtime/helpers/esm/package.json#L2
|
|
67
|
+
if (packageVersion) {
|
|
68
|
+
addRelationshipWithPackageJson({
|
|
69
|
+
reference,
|
|
70
|
+
context,
|
|
71
|
+
packageJsonUrl: `${packageDirectoryUrl}package.json`,
|
|
72
|
+
field: "version",
|
|
73
|
+
hasVersioningEffect: true,
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
reference.version = packageVersion
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return url
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const addRelationshipWithPackageJson = ({
|
|
85
|
+
context,
|
|
86
|
+
packageJsonUrl,
|
|
87
|
+
field,
|
|
88
|
+
hasVersioningEffect = false,
|
|
89
|
+
}) => {
|
|
90
|
+
const referenceFound = context.referenceUtils.find(
|
|
91
|
+
(ref) => ref.type === "package_json" && ref.subtype === field,
|
|
92
|
+
)
|
|
93
|
+
if (referenceFound) {
|
|
94
|
+
return
|
|
95
|
+
}
|
|
96
|
+
const [, packageJsonUrlInfo] = context.referenceUtils.inject({
|
|
97
|
+
type: "package_json",
|
|
98
|
+
subtype: field,
|
|
99
|
+
specifier: packageJsonUrl,
|
|
100
|
+
isImplicit: true,
|
|
101
|
+
hasVersioningEffect,
|
|
102
|
+
})
|
|
103
|
+
if (packageJsonUrlInfo.type === undefined) {
|
|
104
|
+
const packageJsonContentAsBuffer = readFileSync(new URL(packageJsonUrl))
|
|
105
|
+
packageJsonUrlInfo.type = "json"
|
|
106
|
+
packageJsonUrlInfo.content = String(packageJsonContentAsBuffer)
|
|
107
|
+
packageJsonUrlInfo.originalContentEtag = packageJsonUrlInfo.contentEtag =
|
|
108
|
+
bufferToEtag(packageJsonContentAsBuffer)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -9,19 +9,24 @@ export const jsenvPluginUrlVersion = () => {
|
|
|
9
9
|
// this goal is achieved when we reach this part of the code
|
|
10
10
|
// We get rid of this params so that urlGraph and other parts of the code
|
|
11
11
|
// recognize the url (it is not considered as a different url)
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
const version = reference.searchParams.get("v")
|
|
13
|
+
if (version) {
|
|
14
|
+
const urlObject = new URL(reference.url)
|
|
15
|
+
urlObject.searchParams.delete("v")
|
|
16
|
+
reference.version = version
|
|
17
|
+
return urlObject.href
|
|
18
|
+
}
|
|
19
|
+
return null
|
|
15
20
|
},
|
|
16
21
|
transformUrlSearchParams: (reference) => {
|
|
17
|
-
if (!reference.
|
|
22
|
+
if (!reference.version) {
|
|
18
23
|
return null
|
|
19
24
|
}
|
|
20
25
|
if (reference.searchParams.has("v")) {
|
|
21
26
|
return null
|
|
22
27
|
}
|
|
23
28
|
return {
|
|
24
|
-
v: reference.
|
|
29
|
+
v: reference.version,
|
|
25
30
|
}
|
|
26
31
|
},
|
|
27
32
|
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export const jsenvPluginLeadingSlash = () => {
|
|
2
|
-
return {
|
|
3
|
-
name: "jsenv:leading_slash",
|
|
4
|
-
appliesDuring: "*",
|
|
5
|
-
resolveUrl: (reference, context) => {
|
|
6
|
-
if (reference.specifier[0] !== "/") {
|
|
7
|
-
return null
|
|
8
|
-
}
|
|
9
|
-
return new URL(reference.specifier.slice(1), context.rootDirectoryUrl)
|
|
10
|
-
.href
|
|
11
|
-
},
|
|
12
|
-
}
|
|
13
|
-
}
|
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* - should I restore eventual search params lost during node esm resolution
|
|
3
|
-
* - what about symlinks?
|
|
4
|
-
* It feels like I should apply symlink (when we don't want to preserve them)
|
|
5
|
-
* once a file:/// url is found, regardless
|
|
6
|
-
* if that comes from node resolution or anything else (not even magic resolution)
|
|
7
|
-
* it should likely be an other plugin happening after the others
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { readFileSync } from "node:fs"
|
|
11
|
-
import { bufferToEtag } from "@jsenv/filesystem"
|
|
12
|
-
import {
|
|
13
|
-
applyNodeEsmResolution,
|
|
14
|
-
defaultLookupPackageScope,
|
|
15
|
-
defaultReadPackageJson,
|
|
16
|
-
readCustomConditionsFromProcessArgs,
|
|
17
|
-
} from "@jsenv/node-esm-resolution"
|
|
18
|
-
|
|
19
|
-
export const jsenvPluginNodeEsmResolution = ({ packageConditions }) => {
|
|
20
|
-
const addRelationshipWithPackageJson = ({
|
|
21
|
-
context,
|
|
22
|
-
packageJsonUrl,
|
|
23
|
-
field,
|
|
24
|
-
hasVersioningEffect = false,
|
|
25
|
-
}) => {
|
|
26
|
-
const referenceFound = context.referenceUtils.find(
|
|
27
|
-
(ref) => ref.type === "package_json" && ref.subtype === field,
|
|
28
|
-
)
|
|
29
|
-
if (referenceFound) {
|
|
30
|
-
return
|
|
31
|
-
}
|
|
32
|
-
const [, packageJsonUrlInfo] = context.referenceUtils.inject({
|
|
33
|
-
type: "package_json",
|
|
34
|
-
subtype: field,
|
|
35
|
-
specifier: packageJsonUrl,
|
|
36
|
-
isImplicit: true,
|
|
37
|
-
hasVersioningEffect,
|
|
38
|
-
})
|
|
39
|
-
if (packageJsonUrlInfo.type === undefined) {
|
|
40
|
-
const packageJsonContentAsBuffer = readFileSync(new URL(packageJsonUrl))
|
|
41
|
-
packageJsonUrlInfo.type = "json"
|
|
42
|
-
packageJsonUrlInfo.content = String(packageJsonContentAsBuffer)
|
|
43
|
-
packageJsonUrlInfo.originalContentEtag = packageJsonUrlInfo.contentEtag =
|
|
44
|
-
bufferToEtag(packageJsonContentAsBuffer)
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return {
|
|
49
|
-
name: "jsenv:node_esm_resolution",
|
|
50
|
-
appliesDuring: "*",
|
|
51
|
-
init: (context) => {
|
|
52
|
-
const nodeRuntimeEnabled = Object.keys(context.runtimeCompat).includes(
|
|
53
|
-
"node",
|
|
54
|
-
)
|
|
55
|
-
// https://nodejs.org/api/esm.html#resolver-algorithm-specification
|
|
56
|
-
packageConditions = packageConditions || [
|
|
57
|
-
...readCustomConditionsFromProcessArgs(),
|
|
58
|
-
nodeRuntimeEnabled ? "node" : "browser",
|
|
59
|
-
"import",
|
|
60
|
-
]
|
|
61
|
-
},
|
|
62
|
-
resolveUrl: {
|
|
63
|
-
js_import_export: (reference, context) => {
|
|
64
|
-
const { parentUrl, specifier } = reference
|
|
65
|
-
const { url, type, packageUrl } = applyNodeEsmResolution({
|
|
66
|
-
conditions: packageConditions,
|
|
67
|
-
parentUrl,
|
|
68
|
-
specifier,
|
|
69
|
-
})
|
|
70
|
-
if (context.scenarios.dev) {
|
|
71
|
-
const dependsOnPackageJson =
|
|
72
|
-
type !== "relative_specifier" &&
|
|
73
|
-
type !== "absolute_specifier" &&
|
|
74
|
-
type !== "node_builtin_specifier"
|
|
75
|
-
if (dependsOnPackageJson) {
|
|
76
|
-
// this reference depends on package.json and node_modules
|
|
77
|
-
// to be resolved. Each file using this specifier
|
|
78
|
-
// must be invalidated when corresponding package.json changes
|
|
79
|
-
addRelationshipWithPackageJson({
|
|
80
|
-
reference,
|
|
81
|
-
context,
|
|
82
|
-
packageJsonUrl: `${packageUrl}package.json`,
|
|
83
|
-
field: type.startsWith("field:")
|
|
84
|
-
? `#${type.slice("field:".length)}`
|
|
85
|
-
: "",
|
|
86
|
-
})
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
return url
|
|
90
|
-
},
|
|
91
|
-
},
|
|
92
|
-
transformUrlSearchParams: (reference, context) => {
|
|
93
|
-
if (reference.type === "package_json") {
|
|
94
|
-
return null
|
|
95
|
-
}
|
|
96
|
-
if (context.scenarios.build) {
|
|
97
|
-
return null
|
|
98
|
-
}
|
|
99
|
-
if (!reference.url.startsWith("file:")) {
|
|
100
|
-
return null
|
|
101
|
-
}
|
|
102
|
-
// without this check a file inside a project without package.json
|
|
103
|
-
// could be considered as a node module if there is a ancestor package.json
|
|
104
|
-
// but we want to version only node modules
|
|
105
|
-
if (!reference.url.includes("/node_modules/")) {
|
|
106
|
-
return null
|
|
107
|
-
}
|
|
108
|
-
if (reference.searchParams.has("v")) {
|
|
109
|
-
return null
|
|
110
|
-
}
|
|
111
|
-
const packageDirectoryUrl = defaultLookupPackageScope(reference.url)
|
|
112
|
-
if (!packageDirectoryUrl) {
|
|
113
|
-
return null
|
|
114
|
-
}
|
|
115
|
-
if (packageDirectoryUrl === context.rootDirectoryUrl) {
|
|
116
|
-
return null
|
|
117
|
-
}
|
|
118
|
-
// there is a dependency between this file and the package.json version field
|
|
119
|
-
const packageVersion = defaultReadPackageJson(packageDirectoryUrl).version
|
|
120
|
-
if (!packageVersion) {
|
|
121
|
-
// example where it happens: https://github.com/babel/babel/blob/2ce56e832c2dd7a7ed92c89028ba929f874c2f5c/packages/babel-runtime/helpers/esm/package.json#L2
|
|
122
|
-
return null
|
|
123
|
-
}
|
|
124
|
-
if (reference.type === "js_import_export") {
|
|
125
|
-
addRelationshipWithPackageJson({
|
|
126
|
-
reference,
|
|
127
|
-
context,
|
|
128
|
-
packageJsonUrl: `${packageDirectoryUrl}package.json`,
|
|
129
|
-
field: "version",
|
|
130
|
-
hasVersioningEffect: true,
|
|
131
|
-
})
|
|
132
|
-
}
|
|
133
|
-
return {
|
|
134
|
-
v: packageVersion,
|
|
135
|
-
}
|
|
136
|
-
},
|
|
137
|
-
fetchUrlContent: (urlInfo) => {
|
|
138
|
-
if (urlInfo.url.startsWith("file:///@ignore/")) {
|
|
139
|
-
return {
|
|
140
|
-
content: "export default {}",
|
|
141
|
-
contentType: "text/javascript",
|
|
142
|
-
type: "js_module",
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
return null
|
|
146
|
-
},
|
|
147
|
-
}
|
|
148
|
-
}
|