@jsenv/core 25.1.1 → 25.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser_runtime/browser_runtime_91c5a3b8.js.map +2 -2
- package/dist/build_manifest.js +4 -4
- package/dist/compile_proxy/asset-manifest.json +2 -2
- package/dist/compile_proxy/{compile_proxy_e3b0c442_809f35f7.js.map → compile_proxy.html__inline__20_809f35f7.js.map} +0 -0
- package/dist/compile_proxy/{compile_proxy_7ad5faa6.html → compile_proxy_8dfaee51.html} +3 -4
- package/dist/redirector/asset-manifest.json +2 -2
- package/dist/redirector/{redirector_e3b0c442_e391410e.js.map → redirector.html__inline__15_e391410e.js.map} +0 -0
- package/dist/redirector/{redirector_eb92e8a7.html → redirector_3e9a97b9.html} +3 -4
- package/dist/toolbar/asset-manifest.json +1 -1
- package/dist/toolbar/{toolbar_f7b8a263.html → toolbar_361afb84.html} +2 -3
- package/dist/toolbar_injector/asset-manifest.json +2 -2
- package/dist/toolbar_injector/{toolbar_injector_49e4756e.js → toolbar_injector_fac1e995.js} +2 -2
- package/dist/toolbar_injector/{toolbar_injector_49e4756e.js.map → toolbar_injector_fac1e995.js.map} +2 -2
- package/package.json +7 -7
- package/readme.md +43 -49
- package/src/buildProject.js +21 -13
- package/src/commonJsToJavaScriptModule.js +8 -7
- package/src/execute.js +2 -0
- package/src/executeTestPlan.js +4 -1
- package/src/internal/building/buildUsingRollup.js +4 -2
- package/src/internal/building/build_stats.js +3 -0
- package/src/internal/building/build_url_generator.js +153 -0
- package/src/internal/building/css/parseCssRessource.js +32 -26
- package/src/internal/building/html/parseHtmlRessource.js +92 -68
- package/src/internal/building/js/parseJsRessource.js +4 -7
- package/src/internal/building/parseRessource.js +3 -0
- package/src/internal/building/ressource_builder.js +64 -62
- package/src/internal/building/ressource_builder_util.js +17 -5
- package/src/internal/building/rollup_plugin_jsenv.js +259 -189
- package/src/internal/building/url_fetcher.js +16 -7
- package/src/internal/building/url_loader.js +1 -5
- package/src/internal/building/url_versioning.js +0 -173
- package/src/internal/compiling/babel_plugin_import_metadata.js +7 -11
- package/src/internal/compiling/babel_plugin_proxy_external_imports.js +31 -0
- package/src/internal/compiling/compile-directory/compile-asset.js +8 -4
- package/src/internal/compiling/compile-directory/getOrGenerateCompiledFile.js +43 -8
- package/src/internal/compiling/compile-directory/updateMeta.js +2 -8
- package/src/internal/compiling/compile-directory/validateCache.js +1 -2
- package/src/internal/compiling/compileFile.js +22 -10
- package/src/internal/compiling/createCompiledFileService.js +22 -24
- package/src/internal/compiling/html_source_file_service.js +9 -9
- package/src/internal/compiling/js-compilation-service/jsenvTransform.js +14 -4
- package/src/internal/compiling/js-compilation-service/transformJs.js +9 -5
- package/src/internal/compiling/jsenvCompilerForHtml.js +221 -182
- package/src/internal/compiling/jsenvCompilerForJavaScript.js +15 -11
- package/src/internal/compiling/startCompileServer.js +79 -19
- package/src/internal/compiling/transformResultToCompilationResult.js +47 -25
- package/src/internal/executing/executePlan.js +2 -0
- package/src/internal/fetchUrl.js +3 -2
- package/src/internal/integrity/integrity_algorithms.js +26 -0
- package/src/internal/integrity/integrity_parsing.js +50 -0
- package/src/internal/integrity/integrity_update.js +23 -0
- package/src/internal/integrity/integrity_validation.js +49 -0
- package/src/internal/jsenv_remote_directory.js +156 -0
- package/src/internal/origin_directory_converter.js +62 -0
- package/src/internal/response_validation.js +11 -24
- package/src/internal/sourceMappingURLUtils.js +10 -0
- package/src/internal/url_conversion.js +1 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import {
|
|
2
|
+
urlIsInsideOf,
|
|
3
|
+
urlToRelativeUrl,
|
|
4
|
+
urlToRessource,
|
|
5
|
+
normalizeStructuredMetaMap,
|
|
6
|
+
urlToMeta,
|
|
7
|
+
writeFile,
|
|
8
|
+
} from "@jsenv/filesystem"
|
|
9
|
+
|
|
10
|
+
import { fetchUrl } from "@jsenv/core/src/internal/fetchUrl.js"
|
|
11
|
+
import { validateResponseIntegrity } from "@jsenv/core/src/internal/integrity/integrity_validation.js"
|
|
12
|
+
|
|
13
|
+
import { originDirectoryConverter } from "./origin_directory_converter.js"
|
|
14
|
+
|
|
15
|
+
export const createJsenvRemoteDirectory = ({
|
|
16
|
+
projectDirectoryUrl,
|
|
17
|
+
jsenvDirectoryRelativeUrl,
|
|
18
|
+
preservedUrls,
|
|
19
|
+
}) => {
|
|
20
|
+
const jsenvRemoteDirectoryUrl = `${projectDirectoryUrl}${jsenvDirectoryRelativeUrl}.remote/`
|
|
21
|
+
const structuredMetaMap = normalizeStructuredMetaMap(
|
|
22
|
+
{ preserved: preservedUrls },
|
|
23
|
+
projectDirectoryUrl,
|
|
24
|
+
)
|
|
25
|
+
const isPreservedUrl = (url) => {
|
|
26
|
+
const meta = urlToMeta({ url, structuredMetaMap })
|
|
27
|
+
return Boolean(meta.preserved)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const jsenvRemoteDirectory = {
|
|
31
|
+
isPreservedUrl,
|
|
32
|
+
|
|
33
|
+
isRemoteUrl: (url) => {
|
|
34
|
+
return url.startsWith("http://") || url.startsWith("https://")
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
isFileUrlForRemoteUrl: (url) => {
|
|
38
|
+
return urlIsInsideOf(url, jsenvRemoteDirectoryUrl)
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
fileUrlFromRemoteUrl: (remoteUrl) => {
|
|
42
|
+
const origin = originFromUrlOrUrlPattern(remoteUrl)
|
|
43
|
+
const ressource = urlToRessource(remoteUrl)
|
|
44
|
+
const [pathname, search = ""] = ressource.split("?")
|
|
45
|
+
const directoryName = originDirectoryConverter.toDirectoryName(origin)
|
|
46
|
+
const fileRelativeUrl = `${directoryName}${
|
|
47
|
+
pathname === "" ? "/" : pathname
|
|
48
|
+
}`
|
|
49
|
+
const fileUrl = `${jsenvRemoteDirectoryUrl}${fileRelativeUrl}${search}`
|
|
50
|
+
return fileUrl
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
remoteUrlFromFileUrl: (fileUrl) => {
|
|
54
|
+
const fileRelativeUrl = urlToRelativeUrl(fileUrl, jsenvRemoteDirectoryUrl)
|
|
55
|
+
const firstSlashIndex = fileRelativeUrl.indexOf("/")
|
|
56
|
+
const directoryName = fileRelativeUrl.slice(0, firstSlashIndex)
|
|
57
|
+
const origin = originDirectoryConverter.fromDirectoryName(directoryName)
|
|
58
|
+
const ressource = fileRelativeUrl.slice(firstSlashIndex)
|
|
59
|
+
const remoteUrlObject = new URL(ressource, origin)
|
|
60
|
+
remoteUrlObject.searchParams.delete("integrity")
|
|
61
|
+
const remoteUrl = remoteUrlObject.href
|
|
62
|
+
return remoteUrl
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
loadFileUrlFromRemote: async (url, request) => {
|
|
66
|
+
const urlObject = new URL(url)
|
|
67
|
+
const integrity = urlObject.searchParams.get("integrity")
|
|
68
|
+
if (integrity) {
|
|
69
|
+
urlObject.searchParams.delete("integrity")
|
|
70
|
+
}
|
|
71
|
+
const remoteUrl = jsenvRemoteDirectory.remoteUrlFromFileUrl(
|
|
72
|
+
urlObject.href,
|
|
73
|
+
)
|
|
74
|
+
const requestHeadersToForward = { ...request.headers }
|
|
75
|
+
delete requestHeadersToForward.host
|
|
76
|
+
const response = await fetchUrl(remoteUrl, {
|
|
77
|
+
mode: "cors",
|
|
78
|
+
headers: requestHeadersToForward,
|
|
79
|
+
})
|
|
80
|
+
if (response.status !== 200) {
|
|
81
|
+
throw createRemoteFetchError({
|
|
82
|
+
code: "UNEXPECTED_STATUS",
|
|
83
|
+
message: `unexpected status for ressource "${remoteUrl}", received ${response.status}`,
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
const responseBodyAsBuffer = Buffer.from(await response.arrayBuffer())
|
|
87
|
+
if (integrity) {
|
|
88
|
+
try {
|
|
89
|
+
validateResponseIntegrity(
|
|
90
|
+
{
|
|
91
|
+
url: response.url,
|
|
92
|
+
type: response.type,
|
|
93
|
+
dataRepresentation: responseBodyAsBuffer,
|
|
94
|
+
},
|
|
95
|
+
integrity,
|
|
96
|
+
)
|
|
97
|
+
} catch (e) {
|
|
98
|
+
throw createRemoteFetchError({
|
|
99
|
+
code: e.code,
|
|
100
|
+
message: e.message,
|
|
101
|
+
})
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
await writeFile(url, responseBodyAsBuffer)
|
|
105
|
+
return responseBodyAsBuffer
|
|
106
|
+
},
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return jsenvRemoteDirectory
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const createRemoteFetchError = ({ message, code }) => {
|
|
113
|
+
const error = new Error(message)
|
|
114
|
+
error.code = "UNEXPECTED_REMOTE_URL_RESPONSE"
|
|
115
|
+
error.asResponse = () => {
|
|
116
|
+
const data = {
|
|
117
|
+
code,
|
|
118
|
+
message,
|
|
119
|
+
}
|
|
120
|
+
const json = JSON.stringify(data)
|
|
121
|
+
return {
|
|
122
|
+
status: 502,
|
|
123
|
+
statusText: "Bad Gateway",
|
|
124
|
+
headers: {
|
|
125
|
+
"cache-control": "no-store",
|
|
126
|
+
"content-length": Buffer.byteLength(json),
|
|
127
|
+
"content-type": "application/json",
|
|
128
|
+
},
|
|
129
|
+
body: json,
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return error
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const originFromUrlOrUrlPattern = (url) => {
|
|
136
|
+
if (url.startsWith("http://")) {
|
|
137
|
+
const slashAfterProtocol = url.indexOf("/", "http://".length + 1)
|
|
138
|
+
if (slashAfterProtocol === -1) {
|
|
139
|
+
return url
|
|
140
|
+
}
|
|
141
|
+
const origin = url.slice(0, slashAfterProtocol)
|
|
142
|
+
return origin
|
|
143
|
+
}
|
|
144
|
+
if (url.startsWith("https://")) {
|
|
145
|
+
const slashAfterProtocol = url.indexOf("/", "https://".length + 1)
|
|
146
|
+
if (slashAfterProtocol === -1) {
|
|
147
|
+
return url
|
|
148
|
+
}
|
|
149
|
+
const origin = url.slice(0, slashAfterProtocol)
|
|
150
|
+
return origin
|
|
151
|
+
}
|
|
152
|
+
if (url.startsWith("file://")) {
|
|
153
|
+
return "file://"
|
|
154
|
+
}
|
|
155
|
+
return new URL(url).origin
|
|
156
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// https://stackoverflow.com/a/1184263
|
|
2
|
+
// using the list of bad characters from:
|
|
3
|
+
// https://github.com/parshap/node-sanitize-filename/blob/master/index.js
|
|
4
|
+
// so that the directory name can be decoded
|
|
5
|
+
// so that we can find back the http origin
|
|
6
|
+
// we should also ignore the query string params part
|
|
7
|
+
// it does not matter, it can be removed from the url and added back
|
|
8
|
+
// when performing the http request
|
|
9
|
+
|
|
10
|
+
const escapeChar = "$"
|
|
11
|
+
const reservedChars = ["/", "?", "<", ">", "\\", ":", "*", "|", '"']
|
|
12
|
+
|
|
13
|
+
export const originDirectoryConverter = {
|
|
14
|
+
toDirectoryName: (origin) => {
|
|
15
|
+
let directoryName = ""
|
|
16
|
+
let i = 0
|
|
17
|
+
while (i < origin.length) {
|
|
18
|
+
const char = origin[i]
|
|
19
|
+
i++
|
|
20
|
+
if (reservedChars.includes(char)) {
|
|
21
|
+
const charAsHex = charToHexString(char)
|
|
22
|
+
directoryName += `${escapeChar}${charAsHex}`
|
|
23
|
+
} else {
|
|
24
|
+
directoryName += char
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return directoryName
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
fromDirectoryName: (filename) => {
|
|
31
|
+
let origin = ""
|
|
32
|
+
let i = 0
|
|
33
|
+
while (i < filename.length) {
|
|
34
|
+
const char = filename[i]
|
|
35
|
+
i++
|
|
36
|
+
if (char === escapeChar) {
|
|
37
|
+
const encodedChar = `${filename[i]}${filename[i + 1]}`
|
|
38
|
+
i += 2
|
|
39
|
+
const decodedChar = charFromHexString(encodedChar)
|
|
40
|
+
origin += decodedChar
|
|
41
|
+
} else {
|
|
42
|
+
origin += char
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return origin
|
|
46
|
+
},
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const charToHexString = (char) => {
|
|
50
|
+
const charCode = char.charCodeAt(0)
|
|
51
|
+
const hexString = charCode.toString(16)
|
|
52
|
+
if (charCode < 16) {
|
|
53
|
+
return `0${hexString}`
|
|
54
|
+
}
|
|
55
|
+
return hexString
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const charFromHexString = (hexString) => {
|
|
59
|
+
const charCode = parseInt(hexString, 16)
|
|
60
|
+
const char = String.fromCharCode(charCode)
|
|
61
|
+
return char
|
|
62
|
+
}
|
|
@@ -10,7 +10,6 @@ export const validateResponse = async (
|
|
|
10
10
|
} = {},
|
|
11
11
|
) => {
|
|
12
12
|
const validity = { isValid: true }
|
|
13
|
-
|
|
14
13
|
if (statusValidation) {
|
|
15
14
|
const statusValidity = await checkStatus(response, {
|
|
16
15
|
originalUrl,
|
|
@@ -21,7 +20,6 @@ export const validateResponse = async (
|
|
|
21
20
|
return validity
|
|
22
21
|
}
|
|
23
22
|
}
|
|
24
|
-
|
|
25
23
|
if (contentTypeExpected) {
|
|
26
24
|
const contentTypeValidity = await checkContentType(response, {
|
|
27
25
|
originalUrl,
|
|
@@ -33,7 +31,6 @@ export const validateResponse = async (
|
|
|
33
31
|
return validity
|
|
34
32
|
}
|
|
35
33
|
}
|
|
36
|
-
|
|
37
34
|
return validity
|
|
38
35
|
}
|
|
39
36
|
|
|
@@ -47,36 +44,28 @@ const mergeValidity = (parentValidity, childValidityName, childValidity) => {
|
|
|
47
44
|
const checkStatus = async (response, { originalUrl, urlTrace }) => {
|
|
48
45
|
const url = originalUrl || response.url
|
|
49
46
|
const { status } = response
|
|
50
|
-
|
|
51
|
-
if (status === 500) {
|
|
52
|
-
if (response.headers["content-type"] === "application/json") {
|
|
53
|
-
return {
|
|
54
|
-
isValid: false,
|
|
55
|
-
message: `error 500 on url`,
|
|
56
|
-
details: {
|
|
57
|
-
"response status": status,
|
|
58
|
-
"response json": JSON.stringify(await response.json(), null, " "),
|
|
59
|
-
url,
|
|
60
|
-
...formatUrlTrace(urlTrace),
|
|
61
|
-
},
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
47
|
if (status < 200 || status > 299) {
|
|
67
|
-
const responseText = await response.text()
|
|
68
48
|
return {
|
|
69
49
|
isValid: false,
|
|
70
50
|
message: `invalid response status on url`,
|
|
71
51
|
details: {
|
|
72
52
|
"response status": status,
|
|
73
|
-
...(responseText ? { "response text": responseText } : {}),
|
|
74
53
|
url,
|
|
75
54
|
...formatUrlTrace(urlTrace),
|
|
55
|
+
...(response.headers["content-type"] === "application/json"
|
|
56
|
+
? {
|
|
57
|
+
"response text": JSON.stringify(
|
|
58
|
+
await response.json(),
|
|
59
|
+
null,
|
|
60
|
+
" ",
|
|
61
|
+
),
|
|
62
|
+
}
|
|
63
|
+
: {
|
|
64
|
+
"response text": await response.text(),
|
|
65
|
+
}),
|
|
76
66
|
},
|
|
77
67
|
}
|
|
78
68
|
}
|
|
79
|
-
|
|
80
69
|
return { isValid: true }
|
|
81
70
|
}
|
|
82
71
|
|
|
@@ -86,11 +75,9 @@ const checkContentType = async (
|
|
|
86
75
|
) => {
|
|
87
76
|
const url = originalUrl || response.url
|
|
88
77
|
const responseContentType = response.headers["content-type"] || ""
|
|
89
|
-
|
|
90
78
|
const isOk = Array.isArray(contentTypeExpected)
|
|
91
79
|
? contentTypeExpected.includes(responseContentType)
|
|
92
80
|
: responseContentType === contentTypeExpected
|
|
93
|
-
|
|
94
81
|
if (!isOk) {
|
|
95
82
|
return {
|
|
96
83
|
isValid: false,
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
export const generateSourcemapUrl = (url) => {
|
|
2
|
+
// we want to remove eventual search params from url
|
|
3
|
+
const urlString = String(url)
|
|
4
|
+
const urlObject = new URL(url)
|
|
5
|
+
const origin = urlString.startsWith("file://") ? "file://" : urlObject.origin
|
|
6
|
+
const pathname = urlObject.pathname
|
|
7
|
+
const sourcemapUrl = `${origin}${pathname}.map`
|
|
8
|
+
return sourcemapUrl
|
|
9
|
+
}
|
|
10
|
+
|
|
1
11
|
export const getJavaScriptSourceMappingUrl = (javaScriptSource) => {
|
|
2
12
|
let sourceMappingUrl
|
|
3
13
|
replaceSourceMappingUrl(
|