@jsenv/core 23.5.1 → 23.6.2

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/core",
3
- "version": "23.5.1",
3
+ "version": "23.6.2",
4
4
  "description": "Tool to develop, test and build js projects",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -67,7 +67,7 @@
67
67
  "@jsenv/importmap": "1.1.0",
68
68
  "@jsenv/log": "1.4.0",
69
69
  "@jsenv/logger": "4.0.1",
70
- "@jsenv/server": "10.0.2",
70
+ "@jsenv/server": "10.0.4",
71
71
  "@jsenv/uneval": "1.6.0",
72
72
  "@jsenv/workers": "1.2.0",
73
73
  "@rollup/plugin-commonjs": "21.0.0",
@@ -14,14 +14,11 @@ export const getOrGenerateCompiledFile = async ({
14
14
  projectDirectoryUrl,
15
15
  originalFileUrl,
16
16
  compiledFileUrl = originalFileUrl,
17
- useFilesystemAsCache,
17
+ compileCacheStrategy,
18
18
  compileCacheSourcesValidation,
19
19
  compileCacheAssetsValidation,
20
20
  fileContentFallback,
21
- clientNeedsEtagHeader,
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
- clientNeedsEtagHeader,
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
- clientNeedsEtagHeader,
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
- return {
118
- isValid: false,
119
- code: "META_FILE_NOT_FOUND",
120
- meta: {
121
- isValid: false,
122
- code: "META_FILE_NOT_FOUND",
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
- clientNeedsEtagHeader,
131
- ifEtagMatch,
132
- clientNeedsLastModifiedHeader,
133
- ifModifiedSinceDate,
120
+ compileCacheStrategy,
134
121
  compileCacheSourcesValidation,
135
122
  compileCacheAssetsValidation,
123
+ request,
136
124
  })
137
125
  },
138
126
  )
@@ -214,6 +202,7 @@ const callCompile = async ({
214
202
  sourcesContent = [],
215
203
  assets = [],
216
204
  assetsContent = [],
205
+ responseHeaders,
217
206
  } = compileReturnValue
218
207
  if (typeof contentType !== "string") {
219
208
  throw new TypeError(
@@ -233,6 +222,7 @@ const callCompile = async ({
233
222
  sourcesContent,
234
223
  assets,
235
224
  assetsContent,
225
+ responseHeaders,
236
226
  }
237
227
  }
238
228
 
@@ -5,10 +5,7 @@ import { readFileSync, statSync } from "node:fs"
5
5
 
6
6
  export const validateCache = async ({
7
7
  compiledFileUrl,
8
- clientNeedsEtagHeader,
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
- clientNeedsEtagHeader,
35
- ifEtagMatch,
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
- clientNeedsEtagHeader,
115
- ifEtagMatch,
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 (clientNeedsEtagHeader) {
121
+ if (!clientCacheDisabled && compileCacheStrategy === "etag") {
126
122
  const compiledEtag = bufferToEtag(compiledSourceBuffer)
127
123
  validity.data.compiledEtag = compiledEtag
128
- if (ifEtagMatch && ifEtagMatch !== compiledEtag) {
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 (clientNeedsLastModifiedHeader) {
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 mtime , got ${compileCacheStrategy}`,
32
+ `compileCacheStrategy must be "etag", "mtime" or "none", got ${compileCacheStrategy}`,
35
33
  )
36
34
  }
37
35
 
38
- const { headers = {} } = request
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
- clientNeedsEtagHeader,
73
- clientNeedsLastModifiedHeader,
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 (clientNeedsEtagHeader && !compileResult.compiledEtag) {
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 (clientNeedsLastModifiedHeader && !compileResult.compiledMtime) {
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,23 @@ export const compileFile = async ({
107
77
  compileResult.compiledMtime = Date.now()
108
78
  }
109
79
 
110
- if (
111
- compileResultStatus === "created" ||
112
- compileResultStatus === "updated"
113
- ) {
114
- if (writeOnFilesystem) {
115
- updateMeta({
116
- logger,
117
- meta,
118
- compileResult,
119
- compileResultStatus,
120
- compiledFileUrl,
121
- // originalFileUrl,
122
- })
123
- }
80
+ const {
81
+ contentType,
82
+ compiledEtag,
83
+ compiledMtime,
84
+ compiledSource,
85
+ responseHeaders = {},
86
+ } = compileResult
87
+
88
+ if (compileResultStatus !== "cached" && compileCacheStrategy !== "none") {
89
+ updateMeta({
90
+ logger,
91
+ meta,
92
+ compileResult,
93
+ compileResultStatus,
94
+ compiledFileUrl,
95
+ // originalFileUrl,
96
+ })
124
97
  }
125
98
 
126
99
  compileResult.sources.forEach((source) => {
@@ -131,9 +104,6 @@ export const compileFile = async ({
131
104
  )
132
105
  })
133
106
 
134
- const { contentType, compiledEtag, compiledMtime, compiledSource } =
135
- compileResult
136
-
137
107
  // when a compiled version of the source file was just created or updated
138
108
  // we don't want to rely on filesystem because we might want to delay
139
109
  // when the file is written for perf reasons
@@ -144,6 +114,7 @@ export const compileFile = async ({
144
114
  headers: {
145
115
  "content-length": Buffer.byteLength(compiledSource),
146
116
  "content-type": contentType,
117
+ ...responseHeaders,
147
118
  },
148
119
  body: compiledSource,
149
120
  timing,
@@ -152,8 +123,11 @@ export const compileFile = async ({
152
123
  return response
153
124
  }
154
125
 
155
- if (clientNeedsEtagHeader) {
156
- if (ifEtagMatch && compileResultStatus === "cached") {
126
+ if (!clientCacheDisabled && compileCacheStrategy === "etag") {
127
+ if (
128
+ request.headers["if-none-match"] &&
129
+ compileResultStatus === "cached"
130
+ ) {
157
131
  return {
158
132
  status: 304,
159
133
  timing,
@@ -165,8 +139,11 @@ export const compileFile = async ({
165
139
  })
166
140
  }
167
141
 
168
- if (clientNeedsLastModifiedHeader) {
169
- if (ifModifiedSinceDate && compileResultStatus === "cached") {
142
+ if (!clientCacheDisabled && compileCacheStrategy === "mtime") {
143
+ if (
144
+ request.headers["if-modified-since"] &&
145
+ compileResultStatus === "cached"
146
+ ) {
170
147
  return {
171
148
  status: 304,
172
149
  timing,
@@ -86,10 +86,7 @@ export const getHtmlNodeAttributeByName = (htmlNode, attributeName) => {
86
86
  }
87
87
 
88
88
  export const removeHtmlNodeAttribute = (htmlNode, attributeToRemove) => {
89
- let attrIndex
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,14 +152,11 @@ 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,
161
- compile: ({ signal, code, map }) => {
158
+ compile: ({ code, map }) => {
162
159
  return compiler({
163
- signal,
164
160
  logger,
165
161
 
166
162
  code,
@@ -171,6 +167,7 @@ export const createCompiledFileService = ({
171
167
  compileServerOrigin: request.origin,
172
168
  outDirectoryRelativeUrl,
173
169
  compileId,
170
+ request,
174
171
 
175
172
  runtimeSupport,
176
173
  moduleOutFormat,
@@ -226,7 +223,10 @@ const getCompiler = ({ originalFileUrl, compileMeta }) => {
226
223
  code: customResult.compiledSource,
227
224
  map: customResult.sourcemap,
228
225
  })
229
- return jsenvResult
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
  ? {
@@ -1,4 +1,5 @@
1
1
  import { Abort, raceProcessTeardownEvents } from "@jsenv/abort"
2
+ import { createDetailedMessage } from "@jsenv/logger"
2
3
 
3
4
  import { mergeRuntimeSupport } from "@jsenv/core/src/internal/generateGroupMap/runtime_support.js"
4
5
  import { startCompileServer } from "../compiling/startCompileServer.js"
@@ -73,6 +74,12 @@ export const executePlan = async (
73
74
  })
74
75
  })
75
76
 
77
+ logger.debug(
78
+ createDetailedMessage(`Prepare executing plan`, {
79
+ runtimeSupport: JSON.stringify(runtimeSupport, null, " "),
80
+ }),
81
+ )
82
+
76
83
  const multipleExecutionsOperation = Abort.startOperation()
77
84
  multipleExecutionsOperation.addAbortSignal(signal)
78
85
  if (handleSIGINT) {
@@ -82,6 +89,7 @@ export const executePlan = async (
82
89
  SIGINT: true,
83
90
  },
84
91
  () => {
92
+ logger.debug(`SIGINT abort`)
85
93
  abort()
86
94
  },
87
95
  )
@@ -119,6 +127,7 @@ export const executePlan = async (
119
127
  await compileServer.stop()
120
128
  })
121
129
 
130
+ logger.debug(`Generate executions`)
122
131
  const executionSteps = await generateExecutionSteps(
123
132
  {
124
133
  ...plan,
@@ -129,6 +138,7 @@ export const executePlan = async (
129
138
  projectDirectoryUrl,
130
139
  },
131
140
  )
141
+ logger.debug(`${executionSteps.length} executions planned`)
132
142
 
133
143
  const result = await executeConcurrently(executionSteps, {
134
144
  multipleExecutionsOperation,