@jsenv/core 23.6.2 → 23.8.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.
Files changed (135) hide show
  1. package/{license → LICENSE} +21 -21
  2. package/dist/jsenv_browser_system.js +35 -35
  3. package/dist/jsenv_browser_system.js.map +14 -14
  4. package/dist/jsenv_compile_proxy.js +47 -128
  5. package/dist/jsenv_compile_proxy.js.map +25 -62
  6. package/dist/jsenv_exploring_index.js.map +5 -5
  7. package/dist/jsenv_exploring_redirector.js +47 -54
  8. package/dist/jsenv_exploring_redirector.js.map +17 -19
  9. package/dist/jsenv_toolbar.js +47 -130
  10. package/dist/jsenv_toolbar.js.map +13 -48
  11. package/dist/jsenv_toolbar_injector.js.map +5 -5
  12. package/helpers/babel/.eslintrc.cjs +24 -24
  13. package/helpers/babel/AsyncGenerator/AsyncGenerator.js +81 -81
  14. package/helpers/babel/AwaitValue/AwaitValue.js +3 -3
  15. package/helpers/babel/applyDecoratorDescriptor/applyDecoratorDescriptor.js +33 -33
  16. package/helpers/babel/arrayLikeToArray/arrayLikeToArray.js +7 -7
  17. package/helpers/babel/arrayWithHoles/arrayWithHoles.js +4 -4
  18. package/helpers/babel/arrayWithoutHoles/arrayWithoutHoles.js +6 -6
  19. package/helpers/babel/assertThisInitialized/assertThisInitialized.js +7 -7
  20. package/helpers/babel/asyncGeneratorDelegate/asyncGeneratorDelegate.js +40 -40
  21. package/helpers/babel/asyncIterator/asyncIterator.js +12 -12
  22. package/helpers/babel/asyncToGenerator/asyncToGenerator.js +34 -34
  23. package/helpers/babel/awaitAsyncGenerator/awaitAsyncGenerator.js +5 -5
  24. package/helpers/babel/classApplyDescriptorDestructureSet/classApplyDescriptorDestructureSet.js +20 -20
  25. package/helpers/babel/classApplyDescriptorGet/classApplyDescriptorGet.js +6 -6
  26. package/helpers/babel/classApplyDescriptorSet/classApplyDescriptorSet.js +13 -13
  27. package/helpers/babel/classCallCheck/classCallCheck.js +5 -5
  28. package/helpers/babel/classCheckPrivateStaticAccess/classCheckPrivateStaticAccess.js +5 -5
  29. package/helpers/babel/classCheckPrivateStaticFieldDescriptor/classCheckPrivateStaticFieldDescriptor.js +6 -6
  30. package/helpers/babel/classExtractFieldDescriptor/classExtractFieldDescriptor.js +7 -7
  31. package/helpers/babel/classNameTDZError/classNameTDZError.js +4 -4
  32. package/helpers/babel/classPrivateFieldDestructureSet/classPrivateFieldDestructureSet.js +7 -7
  33. package/helpers/babel/classPrivateFieldGet/classPrivateFieldGet.js +7 -7
  34. package/helpers/babel/classPrivateFieldLooseBase/classPrivateFieldLooseBase.js +6 -6
  35. package/helpers/babel/classPrivateFieldLooseKey/classPrivateFieldLooseKey.js +5 -5
  36. package/helpers/babel/classPrivateFieldSet/classPrivateFieldSet.js +8 -8
  37. package/helpers/babel/classPrivateMethodGet/classPrivateMethodGet.js +6 -6
  38. package/helpers/babel/classPrivateMethodSet/classPrivateMethodSet.js +3 -3
  39. package/helpers/babel/classStaticPrivateFieldSpecGet/classStaticPrivateFieldSpecGet.js +9 -9
  40. package/helpers/babel/classStaticPrivateFieldSpecSet/classStaticPrivateFieldSpecSet.js +15 -15
  41. package/helpers/babel/classStaticPrivateMethodGet/classStaticPrivateMethodGet.js +6 -6
  42. package/helpers/babel/classStaticPrivateMethodSet/classStaticPrivateMethodSet.js +3 -3
  43. package/helpers/babel/construct/construct.js +16 -16
  44. package/helpers/babel/createClass/createClass.js +15 -15
  45. package/helpers/babel/createForOfIteratorHelper/createForOfIteratorHelper.js +60 -60
  46. package/helpers/babel/createForOfIteratorHelperLoose/createForOfIteratorHelperLoose.js +23 -23
  47. package/helpers/babel/createRawReactElement/createRawReactElement.js +50 -50
  48. package/helpers/babel/createSuper/createSuper.js +22 -22
  49. package/helpers/babel/decorate/decorate.js +403 -403
  50. package/helpers/babel/defaults/defaults.js +11 -11
  51. package/helpers/babel/defineEnumerableProperties/defineEnumerableProperties.js +23 -23
  52. package/helpers/babel/defineProperty/defineProperty.js +18 -18
  53. package/helpers/babel/extends/extends.js +14 -14
  54. package/helpers/babel/get/get.js +13 -13
  55. package/helpers/babel/getPrototypeOf/getPrototypeOf.js +4 -4
  56. package/helpers/babel/inherits/inherits.js +15 -15
  57. package/helpers/babel/inheritsLoose/inheritsLoose.js +7 -7
  58. package/helpers/babel/initializerDefineProperty/initializerDefineProperty.js +10 -10
  59. package/helpers/babel/initializerWarningHelper/initializerWarningHelper.js +6 -6
  60. package/helpers/babel/instanceof/instanceof.js +6 -6
  61. package/helpers/babel/interopRequireDefault/interopRequireDefault.js +3 -3
  62. package/helpers/babel/interopRequireWildcard/interopRequireWildcard.js +37 -37
  63. package/helpers/babel/isNativeFunction/isNativeFunction.js +4 -4
  64. package/helpers/babel/isNativeReflectConstruct/isNativeReflectConstruct.js +21 -21
  65. package/helpers/babel/iterableToArray/iterableToArray.js +7 -7
  66. package/helpers/babel/iterableToArrayLimit/iterableToArrayLimit.js +36 -36
  67. package/helpers/babel/iterableToArrayLimitLoose/iterableToArrayLimitLoose.js +10 -10
  68. package/helpers/babel/jsx/jsx.js +45 -45
  69. package/helpers/babel/maybeArrayLike/maybeArrayLike.js +10 -10
  70. package/helpers/babel/newArrowCheck/newArrowCheck.js +5 -5
  71. package/helpers/babel/nonIterableRest/nonIterableRest.js +5 -5
  72. package/helpers/babel/nonIterableSpread/nonIterableSpread.js +5 -5
  73. package/helpers/babel/objectDestructuringEmpty/objectDestructuringEmpty.js +3 -3
  74. package/helpers/babel/objectSpread/objectSpread.js +23 -23
  75. package/helpers/babel/objectSpread2/objectSpread2.js +33 -33
  76. package/helpers/babel/objectWithoutProperties/objectWithoutProperties.js +19 -19
  77. package/helpers/babel/objectWithoutPropertiesLoose/objectWithoutPropertiesLoose.js +13 -13
  78. package/helpers/babel/possibleConstructorReturn/possibleConstructorReturn.js +10 -10
  79. package/helpers/babel/readOnlyError/readOnlyError.js +4 -4
  80. package/helpers/babel/readme.md +9 -9
  81. package/helpers/babel/set/set.js +44 -44
  82. package/helpers/babel/setPrototypeOf/setPrototypeOf.js +6 -6
  83. package/helpers/babel/skipFirstGeneratorNext/skipFirstGeneratorNext.js +8 -8
  84. package/helpers/babel/slicedToArray/slicedToArray.js +10 -10
  85. package/helpers/babel/slicedToArrayLoose/slicedToArrayLoose.js +13 -13
  86. package/helpers/babel/superPropBase/superPropBase.js +10 -10
  87. package/helpers/babel/taggedTemplateLiteral/taggedTemplateLiteral.js +10 -10
  88. package/helpers/babel/taggedTemplateLiteralLoose/taggedTemplateLiteralLoose.js +7 -7
  89. package/helpers/babel/tdz/tdz.js +4 -4
  90. package/helpers/babel/temporalRef/temporalRef.js +6 -6
  91. package/helpers/babel/temporalUndefined/temporalUndefined.js +3 -3
  92. package/helpers/babel/toArray/toArray.js +10 -10
  93. package/helpers/babel/toConsumableArray/toConsumableArray.js +10 -10
  94. package/helpers/babel/toPrimitive/toPrimitive.js +10 -10
  95. package/helpers/babel/toPropertyKey/toPropertyKey.js +6 -6
  96. package/helpers/babel/typeof/typeof.js +14 -14
  97. package/helpers/babel/unsupportedIterableToArray/unsupportedIterableToArray.js +12 -12
  98. package/helpers/babel/wrapAsyncGenerator/wrapAsyncGenerator.js +8 -8
  99. package/helpers/babel/wrapNativeSuper/wrapNativeSuper.js +30 -30
  100. package/helpers/babel/wrapRegExp/wrapRegExp.js +63 -63
  101. package/helpers/babel/writeOnlyError/writeOnlyError.js +4 -4
  102. package/helpers/regenerator-runtime/regenerator-runtime.js +748 -748
  103. package/package.json +5 -3
  104. package/src/buildProject.js +300 -300
  105. package/src/execute.js +184 -184
  106. package/src/internal/browser-launcher/jsenv-browser-system.js +199 -199
  107. package/src/internal/compiling/babel_plugin_import_assertions.js +121 -100
  108. package/src/internal/compiling/babel_plugin_import_metadata.js +22 -0
  109. package/src/internal/compiling/babel_plugin_import_visitor.js +84 -0
  110. package/src/internal/compiling/compile-directory/getOrGenerateCompiledFile.js +268 -265
  111. package/src/internal/compiling/compile-directory/updateMeta.js +154 -150
  112. package/src/internal/compiling/compile-directory/validateCache.js +265 -265
  113. package/src/internal/compiling/compileFile.js +224 -194
  114. package/src/internal/compiling/compileHtml.js +550 -494
  115. package/src/internal/compiling/createCompiledFileService.js +291 -290
  116. package/src/internal/compiling/html_source_file_service.js +403 -379
  117. package/src/internal/compiling/js-compilation-service/jsenvTransform.js +270 -269
  118. package/src/internal/compiling/jsenvCompilerForHtml.js +300 -293
  119. package/src/internal/compiling/startCompileServer.js +1048 -1131
  120. package/src/internal/compiling/transformResultToCompilationResult.js +220 -217
  121. package/src/internal/executing/coverage/babel_plugin_instrument.js +90 -90
  122. package/src/internal/executing/coverage/reportToCoverage.js +187 -187
  123. package/src/internal/executing/executePlan.js +183 -183
  124. package/src/internal/executing/launchAndExecute.js +458 -450
  125. package/src/internal/runtime/createBrowserRuntime/scanBrowserRuntimeFeatures.js +246 -250
  126. package/src/internal/runtime/createNodeRuntime/scanNodeRuntimeFeatures.js +112 -115
  127. package/src/internal/runtime/s.js +727 -727
  128. package/src/internal/toolbar/jsenv-logo.svg +144 -144
  129. package/src/internal/toolbar/toolbar.main.css +196 -188
  130. package/src/internal/toolbar/toolbar.main.js +227 -228
  131. package/src/internal/url_conversion.js +317 -317
  132. package/src/startExploring.js +309 -309
  133. package/src/internal/compiling/babel_plugin_transform_import_specifier.js +0 -86
  134. package/src/internal/toolbar/animation/animation.css +0 -5
  135. package/src/internal/toolbar/variant/variant.css +0 -3
@@ -1,265 +1,265 @@
1
- import { resolveUrl, bufferToEtag } from "@jsenv/filesystem"
2
-
3
- import { fileURLToPath } from "node:url"
4
- import { readFileSync, statSync } from "node:fs"
5
-
6
- export const validateCache = async ({
7
- compiledFileUrl,
8
- compileCacheStrategy,
9
- compileCacheSourcesValidation = true,
10
- // When "compileCacheAssetsValidation" is enabled, code ensures that
11
- // - asset file exists
12
- // - asset file content matches an etag generated when the file was compiled
13
- // In practice a compiled file asset is a sourcemap file or a coverage.json file.
14
- // It's unlikely that something or someone would update an asset file
15
- // and even when it happens it would be a bit strict to invalidate the cache
16
- // so by default "compileCacheAssetsValidation" is disabled
17
- // to avoid checking things for nothing
18
- compileCacheAssetsValidation = false,
19
- request,
20
- }) => {
21
- const validity = { isValid: true }
22
-
23
- const metaJsonFileUrl = `${compiledFileUrl}__asset__meta.json`
24
- const metaValidity = await validateMetaFile(metaJsonFileUrl)
25
- mergeValidity(validity, "meta", metaValidity)
26
- if (!validity.isValid) {
27
- return validity
28
- }
29
-
30
- const compiledFileValidation = await validateCompiledFile({
31
- compiledFileUrl,
32
- request,
33
- compileCacheStrategy,
34
- })
35
- mergeValidity(validity, "compiledFile", compiledFileValidation)
36
- if (!validity.isValid) {
37
- return validity
38
- }
39
-
40
- const meta = metaValidity.data
41
- const [sourcesValidity, assetsValidity] = await Promise.all([
42
- compileCacheSourcesValidation
43
- ? validateSources({
44
- meta,
45
- metaJsonFileUrl,
46
- })
47
- : { isValid: true, code: "SOURCES_VALIDATION_DISABLED" },
48
- compileCacheAssetsValidation
49
- ? validateAssets({
50
- meta,
51
- metaJsonFileUrl,
52
- })
53
- : { isValid: true, code: "ASSETS_VALIDATION_DISABLED" },
54
- ])
55
- mergeValidity(validity, "sources", sourcesValidity)
56
- if (!validity.valid) {
57
- return validity
58
- }
59
- mergeValidity(validity, "assets", assetsValidity)
60
-
61
- return validity
62
- }
63
-
64
- const mergeValidity = (parentValidity, childValidityName, childValidity) => {
65
- parentValidity.isValid = childValidity.isValid
66
- if (childValidity.code) parentValidity.code = childValidity.code
67
- parentValidity[childValidityName] = childValidity
68
- }
69
-
70
- const validateMetaFile = async (metaJsonFileUrl) => {
71
- const validity = { isValid: true, data: {} }
72
-
73
- let metaJsonBuffer
74
- try {
75
- metaJsonBuffer = readFileSync(fileURLToPath(metaJsonFileUrl))
76
- } catch (error) {
77
- if (error && error.code === "ENOENT") {
78
- validity.isValid = false
79
- validity.code = "META_FILE_NOT_FOUND"
80
- return validity
81
- }
82
- throw error
83
- }
84
-
85
- const metaJsonString = String(metaJsonBuffer)
86
- let meta
87
- try {
88
- meta = JSON.parse(metaJsonString)
89
- } catch (error) {
90
- if (error && error.name === "SyntaxError") {
91
- validity.isValid = false
92
- validity.code = "META_FILE_SYNTAX_ERROR"
93
- return validity
94
- }
95
- throw error
96
- }
97
-
98
- validity.data = meta
99
- if (meta.sources.length === 0) {
100
- validity.isValid = false
101
- validity.code = "SOURCES_EMPTY"
102
- return validity
103
- }
104
-
105
- return validity
106
- }
107
-
108
- const validateCompiledFile = async ({
109
- compiledFileUrl,
110
- compileCacheStrategy,
111
- request,
112
- }) => {
113
- const validity = { isValid: true, data: {} }
114
-
115
- const clientCacheDisabled = request.headers["cache-control"] === "no-cache"
116
-
117
- try {
118
- const compiledSourceBuffer = readFileSync(fileURLToPath(compiledFileUrl))
119
- validity.data.compiledSourceBuffer = compiledSourceBuffer
120
-
121
- if (!clientCacheDisabled && compileCacheStrategy === "etag") {
122
- const compiledEtag = bufferToEtag(compiledSourceBuffer)
123
- validity.data.compiledEtag = compiledEtag
124
- const ifNoneMatch = request.headers["if-none-match"]
125
- if (ifNoneMatch && ifNoneMatch !== compiledEtag) {
126
- validity.isValid = false
127
- validity.code = "COMPILED_FILE_ETAG_MISMATCH"
128
- return validity
129
- }
130
- }
131
-
132
- if (!clientCacheDisabled && compileCacheStrategy === "mtime") {
133
- const stats = statSync(fileURLToPath(compiledFileUrl))
134
- const compiledMtime = Math.floor(stats.mtimeMs)
135
- validity.data.compiledMtime = compiledMtime
136
-
137
- const ifModifiedSince = request.headers["if-modified-since"]
138
- let ifModifiedSinceDate
139
- try {
140
- ifModifiedSinceDate = new Date(ifModifiedSince)
141
- } catch (e) {
142
- ifModifiedSinceDate = null
143
- // ideally we should rather respond with
144
- // 400 "if-modified-since header is not a valid date"
145
- }
146
-
147
- if (
148
- ifModifiedSinceDate &&
149
- ifModifiedSinceDate < dateToSecondsPrecision(compiledMtime)
150
- ) {
151
- validity.isValid = false
152
- validity.code = "COMPILED_FILE_MTIME_OUTDATED"
153
- return validity
154
- }
155
- }
156
-
157
- return validity
158
- } catch (error) {
159
- if (error && error.code === "ENOENT") {
160
- validity.isValid = false
161
- validity.code = "COMPILED_FILE_NOT_FOUND"
162
- return validity
163
- }
164
- throw error
165
- }
166
- }
167
-
168
- const validateSources = async ({ meta, metaJsonFileUrl }) => {
169
- const sourcesValidity = { isValid: true }
170
- await Promise.all(
171
- meta.sources.map(async (source, index) => {
172
- const sourceValidity = await validateSource({
173
- metaJsonFileUrl,
174
- source,
175
- eTag: meta.sourcesEtag[index],
176
- })
177
- mergeValidity(sourcesValidity, source, sourceValidity)
178
- }),
179
- )
180
- return sourcesValidity
181
- }
182
-
183
- const validateSource = async ({ metaJsonFileUrl, source, eTag }) => {
184
- const validity = { isValid: true, data: {} }
185
- const sourceFileUrl = resolveUrl(source, metaJsonFileUrl)
186
-
187
- try {
188
- const sourceBuffer = readFileSync(fileURLToPath(sourceFileUrl))
189
- const sourceETag = bufferToEtag(sourceBuffer)
190
- validity.data.sourceBuffer = sourceBuffer
191
- validity.data.sourceETag = sourceETag
192
-
193
- if (sourceETag !== eTag) {
194
- validity.isValid = false
195
- validity.code = "SOURCE_ETAG_MISMATCH"
196
- return validity
197
- }
198
-
199
- return validity
200
- } catch (e) {
201
- if (e && e.code === "ENOENT") {
202
- // missing source invalidates the cache because
203
- // we cannot check its validity
204
- // HOWEVER inside writeMeta we will check if a source can be found
205
- // when it cannot we will not put it as a dependency
206
- // to invalidate the cache.
207
- // It is important because some files are constructed on other files
208
- // which are not truly on the filesystem
209
- // (IN theory the above happens only for convertCommonJsWithRollup because jsenv
210
- // always have a concrete file especially to avoid that kind of thing)
211
- validity.isValid = false
212
- validity.code = "SOURCE_NOT_FOUND"
213
- return validity
214
- }
215
- throw e
216
- }
217
- }
218
-
219
- const validateAssets = async ({ metaJsonFileUrl, meta }) => {
220
- const assetsValidity = { isValid: true }
221
- await Promise.all(
222
- meta.assets.map(async (asset, index) => {
223
- const assetValidity = await validateAsset({
224
- asset,
225
- metaJsonFileUrl,
226
- eTag: meta.assetsEtag[index],
227
- })
228
- mergeValidity(assetsValidity, asset, assetValidity)
229
- }),
230
- )
231
- return assetsValidity
232
- }
233
-
234
- const validateAsset = async ({ asset, metaJsonFileUrl, eTag }) => {
235
- const validity = { isValid: true, data: {} }
236
- const assetFileUrl = resolveUrl(asset, metaJsonFileUrl)
237
-
238
- try {
239
- const assetBuffer = readFileSync(fileURLToPath(assetFileUrl))
240
- const assetContentETag = bufferToEtag(assetBuffer)
241
- validity.data.buffer = assetBuffer
242
- validity.data.etag = assetContentETag
243
-
244
- if (eTag !== assetContentETag) {
245
- validity.isValid = false
246
- validity.code = "ASSET_ETAG_MISMATCH"
247
- return validity
248
- }
249
-
250
- return validity
251
- } catch (error) {
252
- if (error && error.code === "ENOENT") {
253
- validity.isValid = false
254
- validity.code = "ASSET_FILE_NOT_FOUND"
255
- return validity
256
- }
257
- throw error
258
- }
259
- }
260
-
261
- const dateToSecondsPrecision = (date) => {
262
- const dateWithSecondsPrecision = new Date(date)
263
- dateWithSecondsPrecision.setMilliseconds(0)
264
- return dateWithSecondsPrecision
265
- }
1
+ import { resolveUrl, bufferToEtag } from "@jsenv/filesystem"
2
+
3
+ import { fileURLToPath } from "node:url"
4
+ import { readFileSync, statSync } from "node:fs"
5
+
6
+ export const validateCache = async ({
7
+ compiledFileUrl,
8
+ compileCacheStrategy,
9
+ compileCacheSourcesValidation = true,
10
+ // When "compileCacheAssetsValidation" is enabled, code ensures that
11
+ // - asset file exists
12
+ // - asset file content matches an etag generated when the file was compiled
13
+ // In practice a compiled file asset is a sourcemap file or a coverage.json file.
14
+ // It's unlikely that something or someone would update an asset file
15
+ // and even when it happens it would be a bit strict to invalidate the cache
16
+ // so by default "compileCacheAssetsValidation" is disabled
17
+ // to avoid checking things for nothing
18
+ compileCacheAssetsValidation = false,
19
+ request,
20
+ }) => {
21
+ const validity = { isValid: true }
22
+
23
+ const metaJsonFileUrl = `${compiledFileUrl}__asset__meta.json`
24
+ const metaValidity = await validateMetaFile(metaJsonFileUrl)
25
+ mergeValidity(validity, "meta", metaValidity)
26
+ if (!validity.isValid) {
27
+ return validity
28
+ }
29
+
30
+ const compiledFileValidation = await validateCompiledFile({
31
+ compiledFileUrl,
32
+ request,
33
+ compileCacheStrategy,
34
+ })
35
+ mergeValidity(validity, "compiledFile", compiledFileValidation)
36
+ if (!validity.isValid) {
37
+ return validity
38
+ }
39
+
40
+ const meta = metaValidity.data
41
+ const [sourcesValidity, assetsValidity] = await Promise.all([
42
+ compileCacheSourcesValidation
43
+ ? validateSources({
44
+ meta,
45
+ metaJsonFileUrl,
46
+ })
47
+ : { isValid: true, code: "SOURCES_VALIDATION_DISABLED" },
48
+ compileCacheAssetsValidation
49
+ ? validateAssets({
50
+ meta,
51
+ metaJsonFileUrl,
52
+ })
53
+ : { isValid: true, code: "ASSETS_VALIDATION_DISABLED" },
54
+ ])
55
+ mergeValidity(validity, "sources", sourcesValidity)
56
+ if (!validity.valid) {
57
+ return validity
58
+ }
59
+ mergeValidity(validity, "assets", assetsValidity)
60
+
61
+ return validity
62
+ }
63
+
64
+ const mergeValidity = (parentValidity, childValidityName, childValidity) => {
65
+ parentValidity.isValid = childValidity.isValid
66
+ if (childValidity.code) parentValidity.code = childValidity.code
67
+ parentValidity[childValidityName] = childValidity
68
+ }
69
+
70
+ const validateMetaFile = async (metaJsonFileUrl) => {
71
+ const validity = { isValid: true, data: {} }
72
+
73
+ let metaJsonBuffer
74
+ try {
75
+ metaJsonBuffer = readFileSync(fileURLToPath(metaJsonFileUrl))
76
+ } catch (error) {
77
+ if (error && error.code === "ENOENT") {
78
+ validity.isValid = false
79
+ validity.code = "META_FILE_NOT_FOUND"
80
+ return validity
81
+ }
82
+ throw error
83
+ }
84
+
85
+ const metaJsonString = String(metaJsonBuffer)
86
+ let meta
87
+ try {
88
+ meta = JSON.parse(metaJsonString)
89
+ } catch (error) {
90
+ if (error && error.name === "SyntaxError") {
91
+ validity.isValid = false
92
+ validity.code = "META_FILE_SYNTAX_ERROR"
93
+ return validity
94
+ }
95
+ throw error
96
+ }
97
+
98
+ validity.data = meta
99
+ if (meta.sources.length === 0) {
100
+ validity.isValid = false
101
+ validity.code = "SOURCES_EMPTY"
102
+ return validity
103
+ }
104
+
105
+ return validity
106
+ }
107
+
108
+ const validateCompiledFile = async ({
109
+ compiledFileUrl,
110
+ compileCacheStrategy,
111
+ request,
112
+ }) => {
113
+ const validity = { isValid: true, data: {} }
114
+
115
+ const clientCacheDisabled = request.headers["cache-control"] === "no-cache"
116
+
117
+ try {
118
+ const compiledSourceBuffer = readFileSync(fileURLToPath(compiledFileUrl))
119
+ validity.data.compiledSourceBuffer = compiledSourceBuffer
120
+
121
+ if (!clientCacheDisabled && compileCacheStrategy === "etag") {
122
+ const compiledEtag = bufferToEtag(compiledSourceBuffer)
123
+ validity.data.compiledEtag = compiledEtag
124
+ const ifNoneMatch = request.headers["if-none-match"]
125
+ if (ifNoneMatch && ifNoneMatch !== compiledEtag) {
126
+ validity.isValid = false
127
+ validity.code = "COMPILED_FILE_ETAG_MISMATCH"
128
+ return validity
129
+ }
130
+ }
131
+
132
+ if (!clientCacheDisabled && compileCacheStrategy === "mtime") {
133
+ const stats = statSync(fileURLToPath(compiledFileUrl))
134
+ const compiledMtime = Math.floor(stats.mtimeMs)
135
+ validity.data.compiledMtime = compiledMtime
136
+
137
+ const ifModifiedSince = request.headers["if-modified-since"]
138
+ let ifModifiedSinceDate
139
+ try {
140
+ ifModifiedSinceDate = new Date(ifModifiedSince)
141
+ } catch (e) {
142
+ ifModifiedSinceDate = null
143
+ // ideally we should rather respond with
144
+ // 400 "if-modified-since header is not a valid date"
145
+ }
146
+
147
+ if (
148
+ ifModifiedSinceDate &&
149
+ ifModifiedSinceDate < dateToSecondsPrecision(compiledMtime)
150
+ ) {
151
+ validity.isValid = false
152
+ validity.code = "COMPILED_FILE_MTIME_OUTDATED"
153
+ return validity
154
+ }
155
+ }
156
+
157
+ return validity
158
+ } catch (error) {
159
+ if (error && error.code === "ENOENT") {
160
+ validity.isValid = false
161
+ validity.code = "COMPILED_FILE_NOT_FOUND"
162
+ return validity
163
+ }
164
+ throw error
165
+ }
166
+ }
167
+
168
+ const validateSources = async ({ meta, metaJsonFileUrl }) => {
169
+ const sourcesValidity = { isValid: true }
170
+ await Promise.all(
171
+ meta.sources.map(async (source, index) => {
172
+ const sourceValidity = await validateSource({
173
+ metaJsonFileUrl,
174
+ source,
175
+ eTag: meta.sourcesEtag[index],
176
+ })
177
+ mergeValidity(sourcesValidity, source, sourceValidity)
178
+ }),
179
+ )
180
+ return sourcesValidity
181
+ }
182
+
183
+ const validateSource = async ({ metaJsonFileUrl, source, eTag }) => {
184
+ const validity = { isValid: true, data: {} }
185
+ const sourceFileUrl = resolveUrl(source, metaJsonFileUrl)
186
+
187
+ try {
188
+ const sourceBuffer = readFileSync(fileURLToPath(sourceFileUrl))
189
+ const sourceETag = bufferToEtag(sourceBuffer)
190
+ validity.data.sourceBuffer = sourceBuffer
191
+ validity.data.sourceETag = sourceETag
192
+
193
+ if (sourceETag !== eTag) {
194
+ validity.isValid = false
195
+ validity.code = "SOURCE_ETAG_MISMATCH"
196
+ return validity
197
+ }
198
+
199
+ return validity
200
+ } catch (e) {
201
+ if (e && e.code === "ENOENT") {
202
+ // missing source invalidates the cache because
203
+ // we cannot check its validity
204
+ // HOWEVER inside writeMeta we will check if a source can be found
205
+ // when it cannot we will not put it as a dependency
206
+ // to invalidate the cache.
207
+ // It is important because some files are constructed on other files
208
+ // which are not truly on the filesystem
209
+ // (IN theory the above happens only for convertCommonJsWithRollup because jsenv
210
+ // always have a concrete file especially to avoid that kind of thing)
211
+ validity.isValid = false
212
+ validity.code = "SOURCE_NOT_FOUND"
213
+ return validity
214
+ }
215
+ throw e
216
+ }
217
+ }
218
+
219
+ const validateAssets = async ({ metaJsonFileUrl, meta }) => {
220
+ const assetsValidity = { isValid: true }
221
+ await Promise.all(
222
+ meta.assets.map(async (asset, index) => {
223
+ const assetValidity = await validateAsset({
224
+ asset,
225
+ metaJsonFileUrl,
226
+ eTag: meta.assetsEtag[index],
227
+ })
228
+ mergeValidity(assetsValidity, asset, assetValidity)
229
+ }),
230
+ )
231
+ return assetsValidity
232
+ }
233
+
234
+ const validateAsset = async ({ asset, metaJsonFileUrl, eTag }) => {
235
+ const validity = { isValid: true, data: {} }
236
+ const assetFileUrl = resolveUrl(asset, metaJsonFileUrl)
237
+
238
+ try {
239
+ const assetBuffer = readFileSync(fileURLToPath(assetFileUrl))
240
+ const assetContentETag = bufferToEtag(assetBuffer)
241
+ validity.data.buffer = assetBuffer
242
+ validity.data.etag = assetContentETag
243
+
244
+ if (eTag !== assetContentETag) {
245
+ validity.isValid = false
246
+ validity.code = "ASSET_ETAG_MISMATCH"
247
+ return validity
248
+ }
249
+
250
+ return validity
251
+ } catch (error) {
252
+ if (error && error.code === "ENOENT") {
253
+ validity.isValid = false
254
+ validity.code = "ASSET_FILE_NOT_FOUND"
255
+ return validity
256
+ }
257
+ throw error
258
+ }
259
+ }
260
+
261
+ const dateToSecondsPrecision = (date) => {
262
+ const dateWithSecondsPrecision = new Date(date)
263
+ dateWithSecondsPrecision.setMilliseconds(0)
264
+ return dateWithSecondsPrecision
265
+ }