@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
|
@@ -5,6 +5,7 @@ import { fetchUrl as jsenvFetchUrl } from "@jsenv/core/src/internal/fetchUrl.js"
|
|
|
5
5
|
import { validateResponse } from "@jsenv/core/src/internal/response_validation.js"
|
|
6
6
|
|
|
7
7
|
export const createUrlFetcher = ({
|
|
8
|
+
jsenvRemoteDirectory,
|
|
8
9
|
asOriginalUrl,
|
|
9
10
|
asProjectUrl,
|
|
10
11
|
applyUrlMappings,
|
|
@@ -13,18 +14,28 @@ export const createUrlFetcher = ({
|
|
|
13
14
|
}) => {
|
|
14
15
|
const urlRedirectionMap = {}
|
|
15
16
|
|
|
16
|
-
const fetchUrl = async (
|
|
17
|
+
const fetchUrl = async (
|
|
18
|
+
url,
|
|
19
|
+
{ signal, urlTrace, contentTypeExpected, crossorigin },
|
|
20
|
+
) => {
|
|
17
21
|
const urlToFetch = applyUrlMappings(url)
|
|
18
|
-
|
|
19
22
|
const response = await jsenvFetchUrl(urlToFetch, {
|
|
20
23
|
signal,
|
|
21
24
|
ignoreHttpsError: true,
|
|
25
|
+
credentials:
|
|
26
|
+
crossorigin === "anonymous" || crossorigin === ""
|
|
27
|
+
? "same-origin"
|
|
28
|
+
: crossorigin === "use-credentials"
|
|
29
|
+
? "include"
|
|
30
|
+
: undefined,
|
|
22
31
|
})
|
|
23
32
|
const responseUrl = response.url
|
|
24
|
-
|
|
33
|
+
const originalUrl =
|
|
34
|
+
asOriginalUrl(responseUrl) || asProjectUrl(responseUrl) || responseUrl
|
|
25
35
|
const responseValidity = await validateResponse(response, {
|
|
26
|
-
originalUrl:
|
|
27
|
-
|
|
36
|
+
originalUrl: jsenvRemoteDirectory.isFileUrlForRemoteUrl(originalUrl)
|
|
37
|
+
? jsenvRemoteDirectory.remoteUrlFromFileUrl(originalUrl)
|
|
38
|
+
: originalUrl,
|
|
28
39
|
urlTrace,
|
|
29
40
|
contentTypeExpected,
|
|
30
41
|
})
|
|
@@ -56,11 +67,9 @@ export const createUrlFetcher = ({
|
|
|
56
67
|
beforeThrowingResponseValidationError(responseValidationError)
|
|
57
68
|
throw responseValidationError
|
|
58
69
|
}
|
|
59
|
-
|
|
60
70
|
if (url !== responseUrl) {
|
|
61
71
|
urlRedirectionMap[url] = responseUrl
|
|
62
72
|
}
|
|
63
|
-
|
|
64
73
|
return response
|
|
65
74
|
}
|
|
66
75
|
|
|
@@ -8,7 +8,6 @@ export const createUrlLoader = ({
|
|
|
8
8
|
allowJson,
|
|
9
9
|
urlImporterMap,
|
|
10
10
|
|
|
11
|
-
asServerUrl,
|
|
12
11
|
asProjectUrl,
|
|
13
12
|
asOriginalUrl,
|
|
14
13
|
|
|
@@ -16,15 +15,12 @@ export const createUrlLoader = ({
|
|
|
16
15
|
}) => {
|
|
17
16
|
const urlResponseBodyMap = {}
|
|
18
17
|
|
|
19
|
-
const loadUrl = async (
|
|
20
|
-
let url = asServerUrl(rollupUrl)
|
|
21
|
-
|
|
18
|
+
const loadUrl = async (url, { signal, logger }) => {
|
|
22
19
|
const customLoader = urlCustomLoaders[url]
|
|
23
20
|
if (customLoader) {
|
|
24
21
|
const result = await customLoader()
|
|
25
22
|
return result
|
|
26
23
|
}
|
|
27
|
-
|
|
28
24
|
const response = await urlFetcher.fetchUrl(url, {
|
|
29
25
|
signal,
|
|
30
26
|
contentTypeExpected: [
|
|
@@ -1,177 +1,4 @@
|
|
|
1
1
|
import { createHash } from "crypto"
|
|
2
|
-
import {
|
|
3
|
-
urlToParentUrl,
|
|
4
|
-
urlToBasename,
|
|
5
|
-
urlToExtension,
|
|
6
|
-
} from "@jsenv/filesystem"
|
|
7
|
-
|
|
8
|
-
export const createUrlVersioner = ({
|
|
9
|
-
entryPointUrls,
|
|
10
|
-
asOriginalUrl,
|
|
11
|
-
lineBreakNormalization,
|
|
12
|
-
}) => {
|
|
13
|
-
const availableNameGenerator = createAvailableNameGenerator()
|
|
14
|
-
|
|
15
|
-
const computeBuildRelativeUrl = (ressource, precomputation) => {
|
|
16
|
-
const pattern = getFilenamePattern({
|
|
17
|
-
ressource,
|
|
18
|
-
entryPointUrls,
|
|
19
|
-
asOriginalUrl,
|
|
20
|
-
precomputeBuildRelativeUrl,
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
const buildRelativeUrl = generateBuildRelativeUrl(
|
|
24
|
-
ressource.url,
|
|
25
|
-
ressource.bufferAfterBuild,
|
|
26
|
-
{
|
|
27
|
-
pattern,
|
|
28
|
-
getName: precomputation
|
|
29
|
-
? () => urlToBasename(ressource.url)
|
|
30
|
-
: () =>
|
|
31
|
-
availableNameGenerator.getAvailableNameForPattern(
|
|
32
|
-
urlToBasename(ressource.url),
|
|
33
|
-
pattern,
|
|
34
|
-
),
|
|
35
|
-
contentType: ressource.contentType,
|
|
36
|
-
lineBreakNormalization,
|
|
37
|
-
},
|
|
38
|
-
)
|
|
39
|
-
return buildRelativeUrl
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const precomputeBuildRelativeUrl = (ressource, bufferAfterBuild = "") => {
|
|
43
|
-
if (ressource.buildRelativeUrl) {
|
|
44
|
-
return ressource.buildRelativeUrl
|
|
45
|
-
}
|
|
46
|
-
if (ressource.precomputedBuildRelativeUrl) {
|
|
47
|
-
return ressource.precomputedBuildRelativeUrl
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
ressource.bufferAfterBuild = bufferAfterBuild
|
|
51
|
-
const precomputedBuildRelativeUrl = computeBuildRelativeUrl(ressource, true)
|
|
52
|
-
ressource.bufferAfterBuild = undefined
|
|
53
|
-
ressource.precomputedBuildRelativeUrl = precomputedBuildRelativeUrl
|
|
54
|
-
return precomputedBuildRelativeUrl
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return {
|
|
58
|
-
computeBuildRelativeUrl,
|
|
59
|
-
precomputeBuildRelativeUrl,
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const createAvailableNameGenerator = () => {
|
|
64
|
-
const cache = {}
|
|
65
|
-
const getAvailableNameForPattern = (name, pattern) => {
|
|
66
|
-
let names = cache[pattern]
|
|
67
|
-
if (!names) {
|
|
68
|
-
names = []
|
|
69
|
-
cache[pattern] = names
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
let nameCandidate = name
|
|
73
|
-
let integer = 1
|
|
74
|
-
// eslint-disable-next-line no-constant-condition
|
|
75
|
-
while (true) {
|
|
76
|
-
if (!names.includes(nameCandidate)) {
|
|
77
|
-
names.push(nameCandidate)
|
|
78
|
-
return nameCandidate
|
|
79
|
-
}
|
|
80
|
-
integer++
|
|
81
|
-
nameCandidate = `${name}${integer}`
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return {
|
|
86
|
-
getAvailableNameForPattern,
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const getFilenamePattern = ({
|
|
91
|
-
ressource,
|
|
92
|
-
entryPointUrls,
|
|
93
|
-
asOriginalUrl,
|
|
94
|
-
precomputeBuildRelativeUrl,
|
|
95
|
-
}) => {
|
|
96
|
-
if (ressource.isEntryPoint) {
|
|
97
|
-
const originalUrl = asOriginalUrl(ressource.url)
|
|
98
|
-
const entryPointBuildRelativeUrl = entryPointUrls[originalUrl]
|
|
99
|
-
return entryPointBuildRelativeUrl
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// inline ressource inherits location
|
|
103
|
-
if (ressource.isInline) {
|
|
104
|
-
// inherit parent directory location because it's an inline file
|
|
105
|
-
const importerBuildRelativeUrl = precomputeBuildRelativeUrl(
|
|
106
|
-
ressource.importer,
|
|
107
|
-
)
|
|
108
|
-
const name = urlToBasename(`file://${importerBuildRelativeUrl}`)
|
|
109
|
-
return `${name}_[hash][extname]`
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// importmap.
|
|
113
|
-
// we want to force the fileName for the importmap
|
|
114
|
-
// so that we don't have to rewrite its content
|
|
115
|
-
// the goal is to put the importmap at the same relative path
|
|
116
|
-
// than in the project
|
|
117
|
-
if (ressource.contentType === "application/importmap+json") {
|
|
118
|
-
const importmapRessourceUrl = ressource.url
|
|
119
|
-
const name = urlToBasename(importmapRessourceUrl)
|
|
120
|
-
return `${name}_[hash][extname]`
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// service worker:
|
|
124
|
-
// - MUST be at the root (same level than the HTML file)
|
|
125
|
-
// otherwise it might be registered for the scope "/assets/" instead of "/"
|
|
126
|
-
// - MUST not be versioned
|
|
127
|
-
if (ressource.isServiceWorker) {
|
|
128
|
-
return "[name][extname]"
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
if (ressource.isWorker) {
|
|
132
|
-
return "[name]_[hash][extname]"
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
if (ressource.isJsModule) {
|
|
136
|
-
return "[name]_[hash].js"
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
return ressource.urlVersioningDisabled
|
|
140
|
-
? "assets/[name][extname]"
|
|
141
|
-
: "assets/[name]_[hash][extname]"
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const generateBuildRelativeUrl = (
|
|
145
|
-
fileUrl,
|
|
146
|
-
fileContent,
|
|
147
|
-
{
|
|
148
|
-
getName,
|
|
149
|
-
pattern,
|
|
150
|
-
contentType = "application/octet-stream",
|
|
151
|
-
lineBreakNormalization = false,
|
|
152
|
-
} = {},
|
|
153
|
-
) => {
|
|
154
|
-
pattern = typeof pattern === "function" ? pattern() : pattern
|
|
155
|
-
if (pattern.startsWith("./")) pattern = pattern.slice(2)
|
|
156
|
-
const buildRelativeUrl = renderFileNamePattern(pattern, {
|
|
157
|
-
dirname: () => urlToParentUrl(fileUrl),
|
|
158
|
-
name: getName,
|
|
159
|
-
hash: () =>
|
|
160
|
-
generateContentHash(fileContent, {
|
|
161
|
-
contentType,
|
|
162
|
-
lineBreakNormalization,
|
|
163
|
-
}),
|
|
164
|
-
extname: () => urlToExtension(fileUrl),
|
|
165
|
-
})
|
|
166
|
-
return buildRelativeUrl
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
const renderFileNamePattern = (pattern, replacements) => {
|
|
170
|
-
return pattern.replace(/\[(\w+)\]/g, (_match, type) => {
|
|
171
|
-
const replacement = replacements[type]()
|
|
172
|
-
return replacement
|
|
173
|
-
})
|
|
174
|
-
}
|
|
175
2
|
|
|
176
3
|
// https://github.com/rollup/rollup/blob/19e50af3099c2f627451a45a84e2fa90d20246d5/src/utils/FileEmitter.ts#L47
|
|
177
4
|
export const generateContentHash = (
|
|
@@ -2,21 +2,17 @@ import { babelPluginImportVisitor } from "./babel_plugin_import_visitor.js"
|
|
|
2
2
|
|
|
3
3
|
export const babelPluginImportMetadata = (babel) => {
|
|
4
4
|
return {
|
|
5
|
-
...babelPluginImportVisitor(
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
// Here there is no strong need to throw because keeping the source code intact
|
|
10
|
-
// will throw an error when browser will execute the code
|
|
11
|
-
({ state, specifierPath }) => {
|
|
5
|
+
...babelPluginImportVisitor(babel, ({ state, specifierPath }) => {
|
|
6
|
+
const specifierNode = specifierPath.node
|
|
7
|
+
if (specifierNode.type === "StringLiteral") {
|
|
8
|
+
const specifier = specifierNode.value
|
|
12
9
|
const { metadata } = state.file
|
|
13
|
-
|
|
14
10
|
metadata.dependencies = [
|
|
15
11
|
...(metadata.dependencies ? metadata.dependencies : []),
|
|
16
|
-
|
|
12
|
+
specifier,
|
|
17
13
|
]
|
|
18
|
-
}
|
|
19
|
-
),
|
|
14
|
+
}
|
|
15
|
+
}),
|
|
20
16
|
name: "import-metadata",
|
|
21
17
|
}
|
|
22
18
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { urlToRelativeUrl, fileSystemPathToUrl } from "@jsenv/filesystem"
|
|
2
|
+
|
|
3
|
+
import { babelPluginImportVisitor } from "./babel_plugin_import_visitor.js"
|
|
4
|
+
|
|
5
|
+
export const babelPluginProxyExternalImports = (
|
|
6
|
+
babel,
|
|
7
|
+
{ jsenvRemoteDirectory },
|
|
8
|
+
) => {
|
|
9
|
+
return {
|
|
10
|
+
...babelPluginImportVisitor(babel, ({ specifierPath, state }) => {
|
|
11
|
+
const specifierNode = specifierPath.node
|
|
12
|
+
if (specifierNode.type === "StringLiteral") {
|
|
13
|
+
const specifier = specifierNode.value
|
|
14
|
+
if (
|
|
15
|
+
jsenvRemoteDirectory.isRemoteUrl(specifier) &&
|
|
16
|
+
!jsenvRemoteDirectory.isPreservedUrl(specifier)
|
|
17
|
+
) {
|
|
18
|
+
const fileUrl = jsenvRemoteDirectory.fileUrlFromRemoteUrl(specifier)
|
|
19
|
+
const importerFileUrl = fileSystemPathToUrl(state.filename)
|
|
20
|
+
const urlRelativeToProject = urlToRelativeUrl(
|
|
21
|
+
fileUrl,
|
|
22
|
+
importerFileUrl,
|
|
23
|
+
)
|
|
24
|
+
const specifierProxy = `./${urlRelativeToProject}`
|
|
25
|
+
specifierPath.replaceWith(babel.types.stringLiteral(specifierProxy))
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}),
|
|
29
|
+
name: "proxy-external-imports",
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { urlToFilename } from "@jsenv/filesystem"
|
|
1
|
+
import { urlToFilename, urlToOrigin, urlToPathname } from "@jsenv/filesystem"
|
|
2
2
|
|
|
3
3
|
export const urlIsCompilationAsset = (url) => {
|
|
4
4
|
const filename = urlToFilename(url)
|
|
@@ -13,8 +13,12 @@ export const urlIsCompilationAsset = (url) => {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export const getMetaJsonFileUrl = (compileFileUrl) =>
|
|
16
|
-
|
|
16
|
+
generateCompilationAssetUrl(compileFileUrl, "meta.json")
|
|
17
17
|
|
|
18
|
-
export const
|
|
19
|
-
|
|
18
|
+
export const generateCompilationAssetUrl = (compiledFileUrl, assetName) => {
|
|
19
|
+
// we want to remove eventual search params from url
|
|
20
|
+
const origin = urlToOrigin(compiledFileUrl)
|
|
21
|
+
const pathname = urlToPathname(compiledFileUrl)
|
|
22
|
+
const compilationAssetUrl = `${origin}${pathname}__asset__${assetName}`
|
|
23
|
+
return compilationAssetUrl
|
|
20
24
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { timeStart, timeFunction } from "@jsenv/server"
|
|
1
2
|
import { urlToFileSystemPath, readFile } from "@jsenv/filesystem"
|
|
2
3
|
import { createDetailedMessage } from "@jsenv/logger"
|
|
3
|
-
import { timeStart, timeFunction } from "@jsenv/server"
|
|
4
4
|
|
|
5
5
|
import { validateCache } from "./validateCache.js"
|
|
6
6
|
import { getMetaJsonFileUrl } from "./compile-asset.js"
|
|
@@ -14,6 +14,8 @@ export const getOrGenerateCompiledFile = async ({
|
|
|
14
14
|
projectDirectoryUrl,
|
|
15
15
|
originalFileUrl,
|
|
16
16
|
compiledFileUrl = originalFileUrl,
|
|
17
|
+
jsenvRemoteDirectory,
|
|
18
|
+
|
|
17
19
|
compileCacheStrategy,
|
|
18
20
|
compileCacheSourcesValidation,
|
|
19
21
|
compileCacheAssetsValidation,
|
|
@@ -61,8 +63,11 @@ export const getOrGenerateCompiledFile = async ({
|
|
|
61
63
|
const lockTiming = lockTimeEnd()
|
|
62
64
|
const { meta, compileResult, compileResultStatus, timing } =
|
|
63
65
|
await computeCompileReport({
|
|
66
|
+
projectDirectoryUrl,
|
|
64
67
|
originalFileUrl,
|
|
65
68
|
compiledFileUrl,
|
|
69
|
+
jsenvRemoteDirectory,
|
|
70
|
+
|
|
66
71
|
compile,
|
|
67
72
|
compileCacheStrategy,
|
|
68
73
|
compileCacheSourcesValidation,
|
|
@@ -89,14 +94,17 @@ export const getOrGenerateCompiledFile = async ({
|
|
|
89
94
|
}
|
|
90
95
|
|
|
91
96
|
const computeCompileReport = async ({
|
|
97
|
+
// projectDirectoryUrl,
|
|
98
|
+
logger,
|
|
92
99
|
originalFileUrl,
|
|
93
100
|
compiledFileUrl,
|
|
101
|
+
jsenvRemoteDirectory,
|
|
102
|
+
|
|
94
103
|
compile,
|
|
95
104
|
compileCacheStrategy,
|
|
96
105
|
compileCacheSourcesValidation,
|
|
97
106
|
compileCacheAssetsValidation,
|
|
98
107
|
request,
|
|
99
|
-
logger,
|
|
100
108
|
}) => {
|
|
101
109
|
const [readCacheTiming, cacheValidity] = await timeFunction(
|
|
102
110
|
"read cache",
|
|
@@ -128,10 +136,41 @@ const computeCompileReport = async ({
|
|
|
128
136
|
}
|
|
129
137
|
|
|
130
138
|
const metaIsValid = cacheValidity.meta ? cacheValidity.meta.isValid : false
|
|
139
|
+
|
|
140
|
+
const fetchOriginalFile = async () => {
|
|
141
|
+
// The original file might be behind an http url.
|
|
142
|
+
// In that case jsenv try first to read file from filesystem
|
|
143
|
+
// in ".jsenv/.http/" directory. If not found, the url
|
|
144
|
+
// is fetched and file is written in that ".jsenv/.http/" directory.
|
|
145
|
+
// After that the only way to re-fetch this ressource is
|
|
146
|
+
// to delete the content of ".jsenv/.http/"
|
|
147
|
+
try {
|
|
148
|
+
const code = await readFile(originalFileUrl)
|
|
149
|
+
return { code }
|
|
150
|
+
} catch (e) {
|
|
151
|
+
// when file is not found and the file is referenced with an http url
|
|
152
|
+
if (
|
|
153
|
+
e &&
|
|
154
|
+
e.code === "ENOENT" &&
|
|
155
|
+
jsenvRemoteDirectory.isFileUrlForRemoteUrl(originalFileUrl)
|
|
156
|
+
) {
|
|
157
|
+
const responseBodyAsBuffer =
|
|
158
|
+
await jsenvRemoteDirectory.loadFileUrlFromRemote(
|
|
159
|
+
originalFileUrl,
|
|
160
|
+
request,
|
|
161
|
+
)
|
|
162
|
+
const code = String(responseBodyAsBuffer)
|
|
163
|
+
return { code }
|
|
164
|
+
}
|
|
165
|
+
throw e
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
const { code } = await fetchOriginalFile()
|
|
131
169
|
const [compileTiming, compileResult] = await timeFunction("compile", () =>
|
|
132
170
|
callCompile({
|
|
133
171
|
logger,
|
|
134
172
|
originalFileUrl,
|
|
173
|
+
code,
|
|
135
174
|
compile,
|
|
136
175
|
}),
|
|
137
176
|
)
|
|
@@ -169,12 +208,9 @@ const computeCompileReport = async ({
|
|
|
169
208
|
}
|
|
170
209
|
}
|
|
171
210
|
|
|
172
|
-
const callCompile = async ({ logger, originalFileUrl, compile }) => {
|
|
211
|
+
const callCompile = async ({ logger, code, originalFileUrl, compile }) => {
|
|
173
212
|
logger.debug(`compile ${originalFileUrl}`)
|
|
174
|
-
|
|
175
|
-
const compileReturnValue = await compile({
|
|
176
|
-
code: await readFile(originalFileUrl),
|
|
177
|
-
})
|
|
213
|
+
const compileReturnValue = await compile({ code })
|
|
178
214
|
if (typeof compileReturnValue !== "object" || compileReturnValue === null) {
|
|
179
215
|
throw new TypeError(
|
|
180
216
|
`compile must return an object, got ${compileReturnValue}`,
|
|
@@ -200,7 +236,6 @@ const callCompile = async ({ logger, originalFileUrl, compile }) => {
|
|
|
200
236
|
`compile must return a compiledSource string, got ${compiledSource}`,
|
|
201
237
|
)
|
|
202
238
|
}
|
|
203
|
-
|
|
204
239
|
return {
|
|
205
240
|
contentType,
|
|
206
241
|
compiledSource,
|
|
@@ -30,14 +30,8 @@ export const updateMeta = async ({
|
|
|
30
30
|
const promises = []
|
|
31
31
|
if (isNew || isUpdated) {
|
|
32
32
|
// ensure source that does not leads to concrete files are not capable to invalidate the cache
|
|
33
|
-
const sourcesToRemove =
|
|
34
|
-
|
|
35
|
-
const sourceFileExists = testFilePresence(sourceFileUrl)
|
|
36
|
-
if (sourceFileExists) {
|
|
37
|
-
return
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
sourcesToRemove.push(sourceFileUrl)
|
|
33
|
+
const sourcesToRemove = sources.filter((sourceFileUrl) => {
|
|
34
|
+
return !testFilePresence(sourceFileUrl)
|
|
41
35
|
})
|
|
42
36
|
const sourceNotFoundCount = sourcesToRemove.length
|
|
43
37
|
if (sourceNotFoundCount > 0) {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { resolveUrl, bufferToEtag } from "@jsenv/filesystem"
|
|
2
|
-
|
|
3
1
|
import { fileURLToPath } from "node:url"
|
|
4
2
|
import { readFileSync, statSync } from "node:fs"
|
|
3
|
+
import { resolveUrl, bufferToEtag } from "@jsenv/filesystem"
|
|
5
4
|
|
|
6
5
|
export const validateCache = async ({
|
|
7
6
|
compiledFileUrl,
|
|
@@ -14,10 +14,13 @@ export const compileFile = async ({
|
|
|
14
14
|
logger,
|
|
15
15
|
|
|
16
16
|
projectDirectoryUrl,
|
|
17
|
+
jsenvRemoteDirectory,
|
|
17
18
|
originalFileUrl,
|
|
18
19
|
compiledFileUrl,
|
|
20
|
+
|
|
19
21
|
projectFileRequestedCallback = () => {},
|
|
20
22
|
request,
|
|
23
|
+
responseHeaders = {},
|
|
21
24
|
pushResponse,
|
|
22
25
|
importmapInfos,
|
|
23
26
|
compile,
|
|
@@ -35,6 +38,11 @@ export const compileFile = async ({
|
|
|
35
38
|
)
|
|
36
39
|
}
|
|
37
40
|
|
|
41
|
+
projectFileRequestedCallback(
|
|
42
|
+
urlToRelativeUrl(originalFileUrl, projectDirectoryUrl),
|
|
43
|
+
request,
|
|
44
|
+
)
|
|
45
|
+
|
|
38
46
|
const clientCacheDisabled = request.headers["cache-control"] === "no-cache"
|
|
39
47
|
|
|
40
48
|
try {
|
|
@@ -44,6 +52,8 @@ export const compileFile = async ({
|
|
|
44
52
|
projectDirectoryUrl,
|
|
45
53
|
originalFileUrl,
|
|
46
54
|
compiledFileUrl,
|
|
55
|
+
jsenvRemoteDirectory,
|
|
56
|
+
|
|
47
57
|
request,
|
|
48
58
|
compileCacheStrategy,
|
|
49
59
|
compileCacheSourcesValidation,
|
|
@@ -78,13 +88,15 @@ export const compileFile = async ({
|
|
|
78
88
|
compileResult.compiledMtime = Date.now()
|
|
79
89
|
}
|
|
80
90
|
|
|
81
|
-
const {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
91
|
+
const { contentType, compiledEtag, compiledMtime, compiledSource } =
|
|
92
|
+
compileResult
|
|
93
|
+
|
|
94
|
+
if (compileResult.responseHeaders) {
|
|
95
|
+
responseHeaders = {
|
|
96
|
+
...responseHeaders,
|
|
97
|
+
...compileResult.responseHeaders,
|
|
98
|
+
}
|
|
99
|
+
}
|
|
88
100
|
|
|
89
101
|
if (compileResultStatus !== "cached" && compileCacheStrategy !== "none") {
|
|
90
102
|
// we MUST await updateMeta otherwise we might get 404
|
|
@@ -207,7 +219,6 @@ export const compileFile = async ({
|
|
|
207
219
|
}
|
|
208
220
|
// on the correspondig file
|
|
209
221
|
const json = JSON.stringify(data)
|
|
210
|
-
|
|
211
222
|
return {
|
|
212
223
|
status: 500,
|
|
213
224
|
statusText: "parse error",
|
|
@@ -219,13 +230,14 @@ export const compileFile = async ({
|
|
|
219
230
|
body: json,
|
|
220
231
|
}
|
|
221
232
|
}
|
|
222
|
-
|
|
233
|
+
if (error && error.asResponse) {
|
|
234
|
+
return error.asResponse()
|
|
235
|
+
}
|
|
223
236
|
if (error && error.statusText === "Unexpected directory operation") {
|
|
224
237
|
return {
|
|
225
238
|
status: 403,
|
|
226
239
|
}
|
|
227
240
|
}
|
|
228
|
-
|
|
229
241
|
return convertFileSystemErrorToResponseProperties(error)
|
|
230
242
|
}
|
|
231
243
|
}
|
|
@@ -38,7 +38,9 @@ export const createCompiledFileService = ({
|
|
|
38
38
|
logger,
|
|
39
39
|
|
|
40
40
|
projectDirectoryUrl,
|
|
41
|
+
jsenvDirectoryRelativeUrl,
|
|
41
42
|
outDirectoryRelativeUrl,
|
|
43
|
+
jsenvRemoteDirectory,
|
|
42
44
|
|
|
43
45
|
runtimeSupport,
|
|
44
46
|
babelPluginMap,
|
|
@@ -89,12 +91,9 @@ export const createCompiledFileService = ({
|
|
|
89
91
|
|
|
90
92
|
const importmapInfos = {}
|
|
91
93
|
|
|
92
|
-
return (request, { pushResponse, redirectRequest }) => {
|
|
94
|
+
return async (request, { pushResponse, redirectRequest }) => {
|
|
93
95
|
const { origin, ressource } = request
|
|
94
|
-
|
|
95
|
-
// Without this a pattern like "**/*.js" would not match "file.js?t=1"
|
|
96
|
-
// This would result in file not being compiled when they should
|
|
97
|
-
const requestUrl = `${origin}${ressourceToPathname(ressource)}`
|
|
96
|
+
const requestUrl = `${origin}${ressource}`
|
|
98
97
|
|
|
99
98
|
const requestCompileInfo = serverUrlToCompileInfo(requestUrl, {
|
|
100
99
|
outDirectoryRelativeUrl,
|
|
@@ -140,8 +139,6 @@ export const createCompiledFileService = ({
|
|
|
140
139
|
}
|
|
141
140
|
|
|
142
141
|
const originalFileRelativeUrl = afterCompileId
|
|
143
|
-
projectFileRequestedCallback(originalFileRelativeUrl, request)
|
|
144
|
-
|
|
145
142
|
const originalFileUrl = `${projectDirectoryUrl}${originalFileRelativeUrl}`
|
|
146
143
|
const compileDirectoryRelativeUrl = `${outDirectoryRelativeUrl}${compileId}/`
|
|
147
144
|
const compileDirectoryUrl = resolveDirectoryUrl(
|
|
@@ -152,23 +149,22 @@ export const createCompiledFileService = ({
|
|
|
152
149
|
originalFileRelativeUrl,
|
|
153
150
|
compileDirectoryUrl,
|
|
154
151
|
)
|
|
155
|
-
|
|
156
152
|
const compiler = getCompiler({ originalFileUrl, compileMeta })
|
|
157
153
|
// no compiler -> serve original file
|
|
158
|
-
// we
|
|
159
|
-
// and url resolution
|
|
154
|
+
// we redirect "internally" (we dont send 304 to the browser)
|
|
155
|
+
// to keep ressource tracking and url resolution simple
|
|
160
156
|
if (!compiler) {
|
|
161
157
|
return redirectRequest({
|
|
162
158
|
pathname: `/${originalFileRelativeUrl}`,
|
|
163
159
|
})
|
|
164
160
|
}
|
|
165
|
-
|
|
166
161
|
// compile this if needed
|
|
167
162
|
const compileResponsePromise = compileFile({
|
|
168
163
|
compileServerOperation,
|
|
169
164
|
logger,
|
|
170
165
|
|
|
171
166
|
projectDirectoryUrl,
|
|
167
|
+
jsenvRemoteDirectory,
|
|
172
168
|
originalFileUrl,
|
|
173
169
|
compiledFileUrl,
|
|
174
170
|
|
|
@@ -181,14 +177,16 @@ export const createCompiledFileService = ({
|
|
|
181
177
|
return compiler({
|
|
182
178
|
logger,
|
|
183
179
|
|
|
184
|
-
code,
|
|
185
|
-
url: originalFileUrl,
|
|
186
|
-
compiledUrl: compiledFileUrl,
|
|
187
180
|
projectDirectoryUrl,
|
|
181
|
+
jsenvRemoteDirectory,
|
|
188
182
|
compileServerOrigin: request.origin,
|
|
183
|
+
jsenvDirectoryRelativeUrl,
|
|
189
184
|
outDirectoryRelativeUrl,
|
|
190
|
-
|
|
185
|
+
url: originalFileUrl,
|
|
186
|
+
compiledUrl: compiledFileUrl,
|
|
191
187
|
request,
|
|
188
|
+
|
|
189
|
+
compileId,
|
|
192
190
|
babelPluginMap: shakeBabelPluginMap({
|
|
193
191
|
babelPluginMap,
|
|
194
192
|
missingFeatureNames: groupMap[compileId].missingFeatureNames,
|
|
@@ -204,6 +202,7 @@ export const createCompiledFileService = ({
|
|
|
204
202
|
topLevelAwait,
|
|
205
203
|
prependSystemJs,
|
|
206
204
|
|
|
205
|
+
code,
|
|
207
206
|
sourcemapMethod,
|
|
208
207
|
sourcemapExcludeSources,
|
|
209
208
|
jsenvEventSourceClientInjection,
|
|
@@ -238,6 +237,14 @@ const canAvoidSystemJs = ({
|
|
|
238
237
|
}
|
|
239
238
|
|
|
240
239
|
const getCompiler = ({ originalFileUrl, compileMeta }) => {
|
|
240
|
+
// we remove eventual query param from the url
|
|
241
|
+
// Without this a pattern like "**/*.js" would not match "file.js?t=1"
|
|
242
|
+
// This would result in file not being compiled when they should
|
|
243
|
+
// Ideally we would do a first pass with the query param and a second without
|
|
244
|
+
const urlObject = new URL(originalFileUrl)
|
|
245
|
+
urlObject.search = ""
|
|
246
|
+
originalFileUrl = urlObject.href
|
|
247
|
+
|
|
241
248
|
const { jsenvCompiler, customCompiler } = urlToMeta({
|
|
242
249
|
url: originalFileUrl,
|
|
243
250
|
structuredMetaMap: compileMeta,
|
|
@@ -307,12 +314,3 @@ const contentTypeExtensions = {
|
|
|
307
314
|
"application/importmap+json": ".importmap",
|
|
308
315
|
// "text/css": ".css",
|
|
309
316
|
}
|
|
310
|
-
|
|
311
|
-
const ressourceToPathname = (ressource) => {
|
|
312
|
-
const searchSeparatorIndex = ressource.indexOf("?")
|
|
313
|
-
const pathname =
|
|
314
|
-
searchSeparatorIndex === -1
|
|
315
|
-
? ressource
|
|
316
|
-
: ressource.slice(0, searchSeparatorIndex)
|
|
317
|
-
return pathname
|
|
318
|
-
}
|