@jsenv/core 23.5.2 → 23.6.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/package.json +1 -1
- package/src/internal/compiling/compile-directory/getOrGenerateCompiledFile.js +23 -30
- package/src/internal/compiling/compile-directory/validateCache.js +23 -15
- package/src/internal/compiling/compileFile.js +40 -57
- package/src/internal/compiling/compileHtml.js +1 -4
- package/src/internal/compiling/createCompiledFileService.js +4 -4
- package/src/internal/compiling/startCompileServer.js +3 -3
package/package.json
CHANGED
|
@@ -14,14 +14,11 @@ export const getOrGenerateCompiledFile = async ({
|
|
|
14
14
|
projectDirectoryUrl,
|
|
15
15
|
originalFileUrl,
|
|
16
16
|
compiledFileUrl = originalFileUrl,
|
|
17
|
-
|
|
17
|
+
compileCacheStrategy,
|
|
18
18
|
compileCacheSourcesValidation,
|
|
19
19
|
compileCacheAssetsValidation,
|
|
20
20
|
fileContentFallback,
|
|
21
|
-
|
|
22
|
-
ifEtagMatch,
|
|
23
|
-
clientNeedsLastModifiedHeader,
|
|
24
|
-
ifModifiedSinceDate,
|
|
21
|
+
request,
|
|
25
22
|
compile,
|
|
26
23
|
}) => {
|
|
27
24
|
if (typeof projectDirectoryUrl !== "string") {
|
|
@@ -69,13 +66,10 @@ export const getOrGenerateCompiledFile = async ({
|
|
|
69
66
|
compiledFileUrl,
|
|
70
67
|
compile,
|
|
71
68
|
fileContentFallback,
|
|
72
|
-
|
|
73
|
-
ifEtagMatch,
|
|
74
|
-
clientNeedsLastModifiedHeader,
|
|
75
|
-
ifModifiedSinceDate,
|
|
76
|
-
useFilesystemAsCache,
|
|
69
|
+
compileCacheStrategy,
|
|
77
70
|
compileCacheSourcesValidation,
|
|
78
71
|
compileCacheAssetsValidation,
|
|
72
|
+
request,
|
|
79
73
|
logger,
|
|
80
74
|
})
|
|
81
75
|
|
|
@@ -101,38 +95,32 @@ const computeCompileReport = async ({
|
|
|
101
95
|
compiledFileUrl,
|
|
102
96
|
compile,
|
|
103
97
|
fileContentFallback,
|
|
104
|
-
|
|
105
|
-
ifEtagMatch,
|
|
106
|
-
clientNeedsLastModifiedHeader,
|
|
107
|
-
ifModifiedSinceDate,
|
|
108
|
-
useFilesystemAsCache,
|
|
98
|
+
compileCacheStrategy,
|
|
109
99
|
compileCacheSourcesValidation,
|
|
110
100
|
compileCacheAssetsValidation,
|
|
101
|
+
request,
|
|
111
102
|
logger,
|
|
112
103
|
}) => {
|
|
113
104
|
const [readCacheTiming, cacheValidity] = await timeFunction(
|
|
114
105
|
"read cache",
|
|
115
106
|
() => {
|
|
116
|
-
if (!useFilesystemAsCache) {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
}
|
|
107
|
+
// if (!useFilesystemAsCache) {
|
|
108
|
+
// return {
|
|
109
|
+
// isValid: false,
|
|
110
|
+
// code: "META_FILE_NOT_FOUND",
|
|
111
|
+
// meta: {
|
|
112
|
+
// isValid: false,
|
|
113
|
+
// code: "META_FILE_NOT_FOUND",
|
|
114
|
+
// },
|
|
115
|
+
// }
|
|
116
|
+
// }
|
|
126
117
|
return validateCache({
|
|
127
118
|
logger,
|
|
128
|
-
useFilesystemAsCache,
|
|
129
119
|
compiledFileUrl,
|
|
130
|
-
|
|
131
|
-
ifEtagMatch,
|
|
132
|
-
clientNeedsLastModifiedHeader,
|
|
133
|
-
ifModifiedSinceDate,
|
|
120
|
+
compileCacheStrategy,
|
|
134
121
|
compileCacheSourcesValidation,
|
|
135
122
|
compileCacheAssetsValidation,
|
|
123
|
+
request,
|
|
136
124
|
})
|
|
137
125
|
},
|
|
138
126
|
)
|
|
@@ -149,6 +137,7 @@ const computeCompileReport = async ({
|
|
|
149
137
|
logger,
|
|
150
138
|
originalFileUrl,
|
|
151
139
|
fileContentFallback,
|
|
140
|
+
request,
|
|
152
141
|
compile,
|
|
153
142
|
}),
|
|
154
143
|
)
|
|
@@ -189,6 +178,7 @@ const callCompile = async ({
|
|
|
189
178
|
logger,
|
|
190
179
|
originalFileUrl,
|
|
191
180
|
fileContentFallback,
|
|
181
|
+
request,
|
|
192
182
|
compile,
|
|
193
183
|
}) => {
|
|
194
184
|
logger.debug(`compile ${originalFileUrl}`)
|
|
@@ -201,6 +191,7 @@ const callCompile = async ({
|
|
|
201
191
|
const compileReturnValue = await compile({
|
|
202
192
|
code: codeBeforeCompile,
|
|
203
193
|
map: undefined,
|
|
194
|
+
request,
|
|
204
195
|
})
|
|
205
196
|
if (typeof compileReturnValue !== "object" || compileReturnValue === null) {
|
|
206
197
|
throw new TypeError(
|
|
@@ -214,6 +205,7 @@ const callCompile = async ({
|
|
|
214
205
|
sourcesContent = [],
|
|
215
206
|
assets = [],
|
|
216
207
|
assetsContent = [],
|
|
208
|
+
responseHeaders,
|
|
217
209
|
} = compileReturnValue
|
|
218
210
|
if (typeof contentType !== "string") {
|
|
219
211
|
throw new TypeError(
|
|
@@ -233,6 +225,7 @@ const callCompile = async ({
|
|
|
233
225
|
sourcesContent,
|
|
234
226
|
assets,
|
|
235
227
|
assetsContent,
|
|
228
|
+
responseHeaders,
|
|
236
229
|
}
|
|
237
230
|
}
|
|
238
231
|
|
|
@@ -5,10 +5,7 @@ import { readFileSync, statSync } from "node:fs"
|
|
|
5
5
|
|
|
6
6
|
export const validateCache = async ({
|
|
7
7
|
compiledFileUrl,
|
|
8
|
-
|
|
9
|
-
ifEtagMatch,
|
|
10
|
-
clientNeedsLastModifiedHeader,
|
|
11
|
-
ifModifiedSinceDate,
|
|
8
|
+
compileCacheStrategy,
|
|
12
9
|
compileCacheSourcesValidation = true,
|
|
13
10
|
// When "compileCacheAssetsValidation" is enabled, code ensures that
|
|
14
11
|
// - asset file exists
|
|
@@ -19,6 +16,7 @@ export const validateCache = async ({
|
|
|
19
16
|
// so by default "compileCacheAssetsValidation" is disabled
|
|
20
17
|
// to avoid checking things for nothing
|
|
21
18
|
compileCacheAssetsValidation = false,
|
|
19
|
+
request,
|
|
22
20
|
}) => {
|
|
23
21
|
const validity = { isValid: true }
|
|
24
22
|
|
|
@@ -31,10 +29,8 @@ export const validateCache = async ({
|
|
|
31
29
|
|
|
32
30
|
const compiledFileValidation = await validateCompiledFile({
|
|
33
31
|
compiledFileUrl,
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
clientNeedsLastModifiedHeader,
|
|
37
|
-
ifModifiedSinceDate,
|
|
32
|
+
request,
|
|
33
|
+
compileCacheStrategy,
|
|
38
34
|
})
|
|
39
35
|
mergeValidity(validity, "compiledFile", compiledFileValidation)
|
|
40
36
|
if (!validity.isValid) {
|
|
@@ -111,31 +107,43 @@ const validateMetaFile = async (metaJsonFileUrl) => {
|
|
|
111
107
|
|
|
112
108
|
const validateCompiledFile = async ({
|
|
113
109
|
compiledFileUrl,
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
clientNeedsLastModifiedHeader,
|
|
117
|
-
ifModifiedSinceDate,
|
|
110
|
+
compileCacheStrategy,
|
|
111
|
+
request,
|
|
118
112
|
}) => {
|
|
119
113
|
const validity = { isValid: true, data: {} }
|
|
120
114
|
|
|
115
|
+
const clientCacheDisabled = request.headers["cache-control"] === "no-cache"
|
|
116
|
+
|
|
121
117
|
try {
|
|
122
118
|
const compiledSourceBuffer = readFileSync(fileURLToPath(compiledFileUrl))
|
|
123
119
|
validity.data.compiledSourceBuffer = compiledSourceBuffer
|
|
124
120
|
|
|
125
|
-
if (
|
|
121
|
+
if (!clientCacheDisabled && compileCacheStrategy === "etag") {
|
|
126
122
|
const compiledEtag = bufferToEtag(compiledSourceBuffer)
|
|
127
123
|
validity.data.compiledEtag = compiledEtag
|
|
128
|
-
|
|
124
|
+
const ifNoneMatch = request.headers["if-none-match"]
|
|
125
|
+
if (ifNoneMatch && ifNoneMatch !== compiledEtag) {
|
|
129
126
|
validity.isValid = false
|
|
130
127
|
validity.code = "COMPILED_FILE_ETAG_MISMATCH"
|
|
131
128
|
return validity
|
|
132
129
|
}
|
|
133
130
|
}
|
|
134
131
|
|
|
135
|
-
if (
|
|
132
|
+
if (!clientCacheDisabled && compileCacheStrategy === "mtime") {
|
|
136
133
|
const stats = statSync(fileURLToPath(compiledFileUrl))
|
|
137
134
|
const compiledMtime = Math.floor(stats.mtimeMs)
|
|
138
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
|
+
|
|
139
147
|
if (
|
|
140
148
|
ifModifiedSinceDate &&
|
|
141
149
|
ifModifiedSinceDate < dateToSecondsPrecision(compiledMtime)
|
|
@@ -19,47 +19,21 @@ export const compileFile = async ({
|
|
|
19
19
|
projectFileRequestedCallback = () => {},
|
|
20
20
|
request,
|
|
21
21
|
compile,
|
|
22
|
-
writeOnFilesystem,
|
|
23
|
-
useFilesystemAsCache,
|
|
24
22
|
compileCacheStrategy = "etag",
|
|
25
23
|
compileCacheSourcesValidation,
|
|
26
24
|
compileCacheAssetsValidation,
|
|
27
25
|
}) => {
|
|
28
26
|
if (
|
|
29
|
-
writeOnFilesystem &&
|
|
30
27
|
compileCacheStrategy !== "etag" &&
|
|
31
|
-
compileCacheStrategy !== "mtime"
|
|
28
|
+
compileCacheStrategy !== "mtime" &&
|
|
29
|
+
compileCacheStrategy !== "none"
|
|
32
30
|
) {
|
|
33
31
|
throw new Error(
|
|
34
|
-
`compileCacheStrategy must be etag or
|
|
32
|
+
`compileCacheStrategy must be "etag", "mtime" or "none", got ${compileCacheStrategy}`,
|
|
35
33
|
)
|
|
36
34
|
}
|
|
37
35
|
|
|
38
|
-
const
|
|
39
|
-
const clientCacheDisabled = headers["cache-control"] === "no-cache"
|
|
40
|
-
const cacheWithETag = writeOnFilesystem && compileCacheStrategy === "etag"
|
|
41
|
-
|
|
42
|
-
let ifEtagMatch
|
|
43
|
-
if (cacheWithETag && "if-none-match" in headers) {
|
|
44
|
-
ifEtagMatch = headers["if-none-match"]
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const cacheWithMtime = writeOnFilesystem && compileCacheStrategy === "mtime"
|
|
48
|
-
let ifModifiedSinceDate
|
|
49
|
-
if (cacheWithMtime && "if-modified-since" in headers) {
|
|
50
|
-
const ifModifiedSince = headers["if-modified-since"]
|
|
51
|
-
try {
|
|
52
|
-
ifModifiedSinceDate = new Date(ifModifiedSince)
|
|
53
|
-
} catch (e) {
|
|
54
|
-
return {
|
|
55
|
-
status: 400,
|
|
56
|
-
statusText: "if-modified-since header is not a valid date",
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const clientNeedsEtagHeader = cacheWithETag && !clientCacheDisabled
|
|
62
|
-
const clientNeedsLastModifiedHeader = cacheWithMtime && !clientCacheDisabled
|
|
36
|
+
const clientCacheDisabled = request.headers["cache-control"] === "no-cache"
|
|
63
37
|
|
|
64
38
|
try {
|
|
65
39
|
const { meta, compileResult, compileResultStatus, timing } =
|
|
@@ -69,25 +43,21 @@ export const compileFile = async ({
|
|
|
69
43
|
originalFileUrl,
|
|
70
44
|
compiledFileUrl,
|
|
71
45
|
fileContentFallback,
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
ifEtagMatch,
|
|
75
|
-
ifModifiedSinceDate,
|
|
76
|
-
useFilesystemAsCache,
|
|
46
|
+
request,
|
|
47
|
+
compileCacheStrategy,
|
|
77
48
|
compileCacheSourcesValidation,
|
|
78
49
|
compileCacheAssetsValidation,
|
|
79
50
|
compile,
|
|
80
51
|
})
|
|
81
52
|
|
|
82
|
-
if (
|
|
53
|
+
if (compileCacheStrategy === "etag" && !compileResult.compiledEtag) {
|
|
83
54
|
// happens when file was just compiled so etag was not computed
|
|
84
|
-
|
|
85
55
|
compileResult.compiledEtag = bufferToEtag(
|
|
86
56
|
Buffer.from(compileResult.compiledSource),
|
|
87
57
|
)
|
|
88
58
|
}
|
|
89
59
|
|
|
90
|
-
if (
|
|
60
|
+
if (compileCacheStrategy === "mtime" && !compileResult.compiledMtime) {
|
|
91
61
|
// happens when file was just compiled so it's not yet written on filesystem
|
|
92
62
|
// Here we know the compiled file will be written on the filesystem
|
|
93
63
|
// We could wait for the file to be written before responding to the client
|
|
@@ -107,20 +77,29 @@ export const compileFile = async ({
|
|
|
107
77
|
compileResult.compiledMtime = Date.now()
|
|
108
78
|
}
|
|
109
79
|
|
|
80
|
+
const {
|
|
81
|
+
contentType,
|
|
82
|
+
compiledEtag,
|
|
83
|
+
compiledMtime,
|
|
84
|
+
compiledSource,
|
|
85
|
+
responseHeaders = {},
|
|
86
|
+
} = compileResult
|
|
87
|
+
|
|
110
88
|
if (
|
|
111
|
-
compileResultStatus
|
|
112
|
-
|
|
89
|
+
compileResultStatus !== "cached" &&
|
|
90
|
+
compileCacheStrategy !== "none" &&
|
|
91
|
+
// a cutom compiler explicitely disables cache by returning "cache-control": "no-store"
|
|
92
|
+
// this file must not be cached on the filesystem and always re-generated
|
|
93
|
+
responseHeaders["cache-control"] !== "no-store"
|
|
113
94
|
) {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
})
|
|
123
|
-
}
|
|
95
|
+
updateMeta({
|
|
96
|
+
logger,
|
|
97
|
+
meta,
|
|
98
|
+
compileResult,
|
|
99
|
+
compileResultStatus,
|
|
100
|
+
compiledFileUrl,
|
|
101
|
+
// originalFileUrl,
|
|
102
|
+
})
|
|
124
103
|
}
|
|
125
104
|
|
|
126
105
|
compileResult.sources.forEach((source) => {
|
|
@@ -131,9 +110,6 @@ export const compileFile = async ({
|
|
|
131
110
|
)
|
|
132
111
|
})
|
|
133
112
|
|
|
134
|
-
const { contentType, compiledEtag, compiledMtime, compiledSource } =
|
|
135
|
-
compileResult
|
|
136
|
-
|
|
137
113
|
// when a compiled version of the source file was just created or updated
|
|
138
114
|
// we don't want to rely on filesystem because we might want to delay
|
|
139
115
|
// when the file is written for perf reasons
|
|
@@ -144,6 +120,7 @@ export const compileFile = async ({
|
|
|
144
120
|
headers: {
|
|
145
121
|
"content-length": Buffer.byteLength(compiledSource),
|
|
146
122
|
"content-type": contentType,
|
|
123
|
+
...responseHeaders,
|
|
147
124
|
},
|
|
148
125
|
body: compiledSource,
|
|
149
126
|
timing,
|
|
@@ -152,8 +129,11 @@ export const compileFile = async ({
|
|
|
152
129
|
return response
|
|
153
130
|
}
|
|
154
131
|
|
|
155
|
-
if (
|
|
156
|
-
if (
|
|
132
|
+
if (!clientCacheDisabled && compileCacheStrategy === "etag") {
|
|
133
|
+
if (
|
|
134
|
+
request.headers["if-none-match"] &&
|
|
135
|
+
compileResultStatus === "cached"
|
|
136
|
+
) {
|
|
157
137
|
return {
|
|
158
138
|
status: 304,
|
|
159
139
|
timing,
|
|
@@ -165,8 +145,11 @@ export const compileFile = async ({
|
|
|
165
145
|
})
|
|
166
146
|
}
|
|
167
147
|
|
|
168
|
-
if (
|
|
169
|
-
if (
|
|
148
|
+
if (!clientCacheDisabled && compileCacheStrategy === "mtime") {
|
|
149
|
+
if (
|
|
150
|
+
request.headers["if-modified-since"] &&
|
|
151
|
+
compileResultStatus === "cached"
|
|
152
|
+
) {
|
|
170
153
|
return {
|
|
171
154
|
status: 304,
|
|
172
155
|
timing,
|
|
@@ -86,10 +86,7 @@ export const getHtmlNodeAttributeByName = (htmlNode, attributeName) => {
|
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
export const removeHtmlNodeAttribute = (htmlNode, attributeToRemove) => {
|
|
89
|
-
|
|
90
|
-
if (typeof attributeToRemove === "object") {
|
|
91
|
-
attrIndex = htmlNode.attrs.indexOf(attributeToRemove)
|
|
92
|
-
}
|
|
89
|
+
const attrIndex = htmlNode.attrs.indexOf(attributeToRemove)
|
|
93
90
|
if (attrIndex === -1) {
|
|
94
91
|
return false
|
|
95
92
|
}
|
|
@@ -48,7 +48,6 @@ export const createCompiledFileService = ({
|
|
|
48
48
|
jsenvToolbarInjection,
|
|
49
49
|
|
|
50
50
|
projectFileRequestedCallback,
|
|
51
|
-
useFilesystemAsCache,
|
|
52
51
|
compileCacheStrategy,
|
|
53
52
|
sourcemapMethod,
|
|
54
53
|
sourcemapExcludeSources,
|
|
@@ -153,8 +152,6 @@ export const createCompiledFileService = ({
|
|
|
153
152
|
originalFileUrl,
|
|
154
153
|
compiledFileUrl,
|
|
155
154
|
|
|
156
|
-
writeOnFilesystem: true, // we always need them
|
|
157
|
-
useFilesystemAsCache,
|
|
158
155
|
compileCacheStrategy,
|
|
159
156
|
projectFileRequestedCallback,
|
|
160
157
|
request,
|
|
@@ -226,7 +223,10 @@ const getCompiler = ({ originalFileUrl, compileMeta }) => {
|
|
|
226
223
|
code: customResult.compiledSource,
|
|
227
224
|
map: customResult.sourcemap,
|
|
228
225
|
})
|
|
229
|
-
return
|
|
226
|
+
return {
|
|
227
|
+
...customResult,
|
|
228
|
+
...jsenvResult,
|
|
229
|
+
}
|
|
230
230
|
}
|
|
231
231
|
}
|
|
232
232
|
|
|
@@ -316,11 +316,11 @@ export const startCompileServer = async ({
|
|
|
316
316
|
jsenvToolbarInjection,
|
|
317
317
|
|
|
318
318
|
projectFileRequestedCallback,
|
|
319
|
-
useFilesystemAsCache: compileServerCanReadFromFilesystem,
|
|
320
|
-
writeOnFilesystem: compileServerCanWriteOnFilesystem,
|
|
321
319
|
sourcemapMethod,
|
|
322
320
|
sourcemapExcludeSources,
|
|
323
|
-
compileCacheStrategy
|
|
321
|
+
compileCacheStrategy: compileServerCanReadFromFilesystem
|
|
322
|
+
? compileCacheStrategy
|
|
323
|
+
: "none",
|
|
324
324
|
}),
|
|
325
325
|
...(transformHtmlSourceFiles
|
|
326
326
|
? {
|