@jsenv/core 23.7.1 → 23.8.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.
Files changed (34) hide show
  1. package/dist/jsenv_browser_system.js.map +1 -1
  2. package/dist/jsenv_compile_proxy.js.map +1 -1
  3. package/dist/jsenv_exploring_redirector.js.map +1 -1
  4. package/dist/jsenv_toolbar.js +0 -2
  5. package/dist/jsenv_toolbar.js.map +3 -3
  6. package/package.json +2 -2
  7. package/src/buildProject.js +300 -300
  8. package/src/execute.js +184 -184
  9. package/src/internal/browser-launcher/jsenv-browser-system.js +199 -199
  10. package/src/internal/compiling/babel_plugin_import_assertions.js +121 -100
  11. package/src/internal/compiling/babel_plugin_import_metadata.js +22 -0
  12. package/src/internal/compiling/babel_plugin_import_visitor.js +84 -0
  13. package/src/internal/compiling/compile-directory/getOrGenerateCompiledFile.js +268 -265
  14. package/src/internal/compiling/compile-directory/updateMeta.js +154 -150
  15. package/src/internal/compiling/compile-directory/validateCache.js +265 -265
  16. package/src/internal/compiling/compileFile.js +215 -194
  17. package/src/internal/compiling/compileHtml.js +550 -494
  18. package/src/internal/compiling/createCompiledFileService.js +291 -290
  19. package/src/internal/compiling/html_source_file_service.js +403 -379
  20. package/src/internal/compiling/js-compilation-service/jsenvTransform.js +270 -269
  21. package/src/internal/compiling/jsenvCompilerForHtml.js +300 -293
  22. package/src/internal/compiling/startCompileServer.js +1048 -1052
  23. package/src/internal/compiling/transformResultToCompilationResult.js +220 -217
  24. package/src/internal/executing/executePlan.js +183 -183
  25. package/src/internal/executing/launchAndExecute.js +458 -458
  26. package/src/internal/runtime/createBrowserRuntime/scanBrowserRuntimeFeatures.js +246 -246
  27. package/src/internal/runtime/createNodeRuntime/scanNodeRuntimeFeatures.js +112 -112
  28. package/src/internal/toolbar/toolbar.main.css +196 -188
  29. package/src/internal/toolbar/toolbar.main.js +227 -228
  30. package/src/internal/url_conversion.js +317 -317
  31. package/src/startExploring.js +309 -309
  32. package/src/internal/compiling/babel_plugin_transform_import_specifier.js +0 -86
  33. package/src/internal/toolbar/animation/animation.css +0 -5
  34. package/src/internal/toolbar/variant/variant.css +0 -3
@@ -1,194 +1,215 @@
1
- import {
2
- urlToRelativeUrl,
3
- fileSystemPathToUrl,
4
- resolveUrl,
5
- bufferToEtag,
6
- } from "@jsenv/filesystem"
7
- import { convertFileSystemErrorToResponseProperties } from "@jsenv/server/src/internal/convertFileSystemErrorToResponseProperties.js"
8
-
9
- import { getOrGenerateCompiledFile } from "./compile-directory/getOrGenerateCompiledFile.js"
10
- import { updateMeta } from "./compile-directory/updateMeta.js"
11
-
12
- export const compileFile = async ({
13
- logger,
14
-
15
- projectDirectoryUrl,
16
- originalFileUrl,
17
- compiledFileUrl,
18
- fileContentFallback,
19
- projectFileRequestedCallback = () => {},
20
- request,
21
- compile,
22
- compileCacheStrategy = "etag",
23
- compileCacheSourcesValidation,
24
- compileCacheAssetsValidation,
25
- }) => {
26
- if (
27
- compileCacheStrategy !== "etag" &&
28
- compileCacheStrategy !== "mtime" &&
29
- compileCacheStrategy !== "none"
30
- ) {
31
- throw new Error(
32
- `compileCacheStrategy must be "etag", "mtime" or "none", got ${compileCacheStrategy}`,
33
- )
34
- }
35
-
36
- const clientCacheDisabled = request.headers["cache-control"] === "no-cache"
37
-
38
- try {
39
- const { meta, compileResult, compileResultStatus, timing } =
40
- await getOrGenerateCompiledFile({
41
- logger,
42
- projectDirectoryUrl,
43
- originalFileUrl,
44
- compiledFileUrl,
45
- fileContentFallback,
46
- request,
47
- compileCacheStrategy,
48
- compileCacheSourcesValidation,
49
- compileCacheAssetsValidation,
50
- compile,
51
- })
52
-
53
- if (compileCacheStrategy === "etag" && !compileResult.compiledEtag) {
54
- // happens when file was just compiled so etag was not computed
55
- compileResult.compiledEtag = bufferToEtag(
56
- Buffer.from(compileResult.compiledSource),
57
- )
58
- }
59
-
60
- if (compileCacheStrategy === "mtime" && !compileResult.compiledMtime) {
61
- // happens when file was just compiled so it's not yet written on filesystem
62
- // Here we know the compiled file will be written on the filesystem
63
- // We could wait for the file to be written before responding to the client
64
- // but it could delay a bit the response.
65
- // Inside "updateMeta" the file might be written synchronously
66
- // or batched to be written later for perf reasons.
67
- // Fron this side of the code we would like to be agnostic about this to allow
68
- // eventual perf improvments in that field.
69
- // For this reason the "mtime" we send to the client is decided here
70
- // by "compileResult.compiledMtime = Date.now()"
71
- // "updateMeta" will respect this and when it will write the compiled file it will
72
- // use "utimes" to ensure the file mtime is the one we sent to the client
73
- // This is important so that a request sending an mtime
74
- // can be compared with the compiled file mtime on the filesystem
75
- // In the end etag is preffered over mtime by default so this will rarely
76
- // be useful
77
- compileResult.compiledMtime = Date.now()
78
- }
79
-
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
- })
97
- }
98
-
99
- compileResult.sources.forEach((source) => {
100
- const sourceFileUrl = resolveUrl(source, compiledFileUrl)
101
- projectFileRequestedCallback(
102
- urlToRelativeUrl(sourceFileUrl, projectDirectoryUrl),
103
- request,
104
- )
105
- })
106
-
107
- // when a compiled version of the source file was just created or updated
108
- // we don't want to rely on filesystem because we might want to delay
109
- // when the file is written for perf reasons
110
- // Moreover we already got the data in RAM
111
- const respondUsingRAM = (finalizeResponse = () => {}) => {
112
- const response = {
113
- status: 200,
114
- headers: {
115
- "content-length": Buffer.byteLength(compiledSource),
116
- "content-type": contentType,
117
- ...responseHeaders,
118
- },
119
- body: compiledSource,
120
- timing,
121
- }
122
- finalizeResponse(response)
123
- return response
124
- }
125
-
126
- if (!clientCacheDisabled && compileCacheStrategy === "etag") {
127
- if (
128
- request.headers["if-none-match"] &&
129
- compileResultStatus === "cached"
130
- ) {
131
- return {
132
- status: 304,
133
- timing,
134
- }
135
- }
136
- return respondUsingRAM((response) => {
137
- // eslint-disable-next-line dot-notation
138
- response.headers["etag"] = compiledEtag
139
- })
140
- }
141
-
142
- if (!clientCacheDisabled && compileCacheStrategy === "mtime") {
143
- if (
144
- request.headers["if-modified-since"] &&
145
- compileResultStatus === "cached"
146
- ) {
147
- return {
148
- status: 304,
149
- timing,
150
- }
151
- }
152
- return respondUsingRAM((response) => {
153
- response.headers["last-modified"] = new Date(
154
- compiledMtime,
155
- ).toUTCString()
156
- })
157
- }
158
-
159
- return respondUsingRAM()
160
- } catch (error) {
161
- if (error && error.code === "PARSE_ERROR") {
162
- const { data } = error
163
- const { filename } = data
164
- if (filename) {
165
- const relativeUrl = urlToRelativeUrl(
166
- fileSystemPathToUrl(filename),
167
- projectDirectoryUrl,
168
- )
169
- projectFileRequestedCallback(relativeUrl, request)
170
- }
171
- // on the correspondig file
172
- const json = JSON.stringify(data)
173
-
174
- return {
175
- status: 500,
176
- statusText: "parse error",
177
- headers: {
178
- "cache-control": "no-store",
179
- "content-length": Buffer.byteLength(json),
180
- "content-type": "application/json",
181
- },
182
- body: json,
183
- }
184
- }
185
-
186
- if (error && error.statusText === "Unexpected directory operation") {
187
- return {
188
- status: 403,
189
- }
190
- }
191
-
192
- return convertFileSystemErrorToResponseProperties(error)
193
- }
194
- }
1
+ import {
2
+ urlToRelativeUrl,
3
+ fileSystemPathToUrl,
4
+ resolveUrl,
5
+ bufferToEtag,
6
+ urlIsInsideOf,
7
+ } from "@jsenv/filesystem"
8
+ import { convertFileSystemErrorToResponseProperties } from "@jsenv/server/src/internal/convertFileSystemErrorToResponseProperties.js"
9
+
10
+ import { getOrGenerateCompiledFile } from "./compile-directory/getOrGenerateCompiledFile.js"
11
+ import { updateMeta } from "./compile-directory/updateMeta.js"
12
+
13
+ export const compileFile = async ({
14
+ logger,
15
+
16
+ projectDirectoryUrl,
17
+ originalFileUrl,
18
+ compiledFileUrl,
19
+ fileContentFallback,
20
+ projectFileRequestedCallback = () => {},
21
+ request,
22
+ pushResponse,
23
+ compile,
24
+ compileCacheStrategy,
25
+ compileCacheSourcesValidation,
26
+ compileCacheAssetsValidation,
27
+ }) => {
28
+ if (
29
+ compileCacheStrategy !== "etag" &&
30
+ compileCacheStrategy !== "mtime" &&
31
+ compileCacheStrategy !== "none"
32
+ ) {
33
+ throw new Error(
34
+ `compileCacheStrategy must be "etag", "mtime" or "none", got ${compileCacheStrategy}`,
35
+ )
36
+ }
37
+
38
+ const clientCacheDisabled = request.headers["cache-control"] === "no-cache"
39
+
40
+ try {
41
+ const { meta, compileResult, compileResultStatus, timing } =
42
+ await getOrGenerateCompiledFile({
43
+ logger,
44
+ projectDirectoryUrl,
45
+ originalFileUrl,
46
+ compiledFileUrl,
47
+ fileContentFallback,
48
+ request,
49
+ compileCacheStrategy,
50
+ compileCacheSourcesValidation,
51
+ compileCacheAssetsValidation,
52
+ compile,
53
+ })
54
+
55
+ if (compileCacheStrategy === "etag" && !compileResult.compiledEtag) {
56
+ // happens when file was just compiled so etag was not computed
57
+ compileResult.compiledEtag = bufferToEtag(
58
+ Buffer.from(compileResult.compiledSource),
59
+ )
60
+ }
61
+
62
+ if (compileCacheStrategy === "mtime" && !compileResult.compiledMtime) {
63
+ // happens when file was just compiled so it's not yet written on filesystem
64
+ // Here we know the compiled file will be written on the filesystem
65
+ // We could wait for the file to be written before responding to the client
66
+ // but it could delay a bit the response.
67
+ // Inside "updateMeta" the file might be written synchronously
68
+ // or batched to be written later for perf reasons.
69
+ // From this side of the code we would like to be agnostic about this to allow
70
+ // eventual perf improvments in that field.
71
+ // For this reason the "mtime" we send to the client is decided here
72
+ // by "compileResult.compiledMtime = Date.now()"
73
+ // "updateMeta" will respect this and when it will write the compiled file it will
74
+ // use "utimes" to ensure the file mtime is the one we sent to the client
75
+ // This is important so that a request sending an mtime
76
+ // can be compared with the compiled file mtime on the filesystem
77
+ // In the end etag is preffered over mtime by default so this will rarely
78
+ // be useful
79
+ compileResult.compiledMtime = Date.now()
80
+ }
81
+
82
+ const {
83
+ contentType,
84
+ compiledEtag,
85
+ compiledMtime,
86
+ compiledSource,
87
+ responseHeaders = {},
88
+ } = compileResult
89
+
90
+ if (compileResultStatus !== "cached" && compileCacheStrategy !== "none") {
91
+ updateMeta({
92
+ logger,
93
+ meta,
94
+ compileResult,
95
+ compileResultStatus,
96
+ compiledFileUrl,
97
+ // originalFileUrl,
98
+ })
99
+ }
100
+
101
+ compileResult.sources.forEach((source) => {
102
+ const sourceFileUrl = resolveUrl(source, compiledFileUrl)
103
+ projectFileRequestedCallback(
104
+ urlToRelativeUrl(sourceFileUrl, projectDirectoryUrl),
105
+ request,
106
+ )
107
+ })
108
+
109
+ if (request.http2) {
110
+ compileResult.dependencies.forEach((dependency) => {
111
+ const requestUrl = resolveUrl(request.ressource, request.origin)
112
+ const dependencyUrl = resolveUrl(dependency, requestUrl)
113
+ if (!urlIsInsideOf(dependencyUrl, request.origin)) {
114
+ // ignore external urls
115
+ return
116
+ }
117
+ if (dependencyUrl.startsWith("data:")) {
118
+ return
119
+ }
120
+ const dependencyRelativeUrl = urlToRelativeUrl(
121
+ dependencyUrl,
122
+ request.origin,
123
+ )
124
+ pushResponse({ path: `/${dependencyRelativeUrl}` })
125
+ })
126
+ }
127
+
128
+ // when a compiled version of the source file was just created or updated
129
+ // we don't want to rely on filesystem because we might want to delay
130
+ // when the file is written for perf reasons
131
+ // Moreover we already got the data in RAM
132
+ const respondUsingRAM = (finalizeResponse = () => {}) => {
133
+ const response = {
134
+ status: 200,
135
+ headers: {
136
+ "content-length": Buffer.byteLength(compiledSource),
137
+ "content-type": contentType,
138
+ ...responseHeaders,
139
+ },
140
+ body: compiledSource,
141
+ timing,
142
+ }
143
+ finalizeResponse(response)
144
+ return response
145
+ }
146
+
147
+ if (!clientCacheDisabled && compileCacheStrategy === "etag") {
148
+ if (
149
+ request.headers["if-none-match"] &&
150
+ compileResultStatus === "cached"
151
+ ) {
152
+ return {
153
+ status: 304,
154
+ timing,
155
+ }
156
+ }
157
+ return respondUsingRAM((response) => {
158
+ // eslint-disable-next-line dot-notation
159
+ response.headers["etag"] = compiledEtag
160
+ })
161
+ }
162
+
163
+ if (!clientCacheDisabled && compileCacheStrategy === "mtime") {
164
+ if (
165
+ request.headers["if-modified-since"] &&
166
+ compileResultStatus === "cached"
167
+ ) {
168
+ return {
169
+ status: 304,
170
+ timing,
171
+ }
172
+ }
173
+ return respondUsingRAM((response) => {
174
+ response.headers["last-modified"] = new Date(
175
+ compiledMtime,
176
+ ).toUTCString()
177
+ })
178
+ }
179
+
180
+ return respondUsingRAM()
181
+ } catch (error) {
182
+ if (error && error.code === "PARSE_ERROR") {
183
+ const { data } = error
184
+ const { filename } = data
185
+ if (filename) {
186
+ const relativeUrl = urlToRelativeUrl(
187
+ fileSystemPathToUrl(filename),
188
+ projectDirectoryUrl,
189
+ )
190
+ projectFileRequestedCallback(relativeUrl, request)
191
+ }
192
+ // on the correspondig file
193
+ const json = JSON.stringify(data)
194
+
195
+ return {
196
+ status: 500,
197
+ statusText: "parse error",
198
+ headers: {
199
+ "cache-control": "no-store",
200
+ "content-length": Buffer.byteLength(json),
201
+ "content-type": "application/json",
202
+ },
203
+ body: json,
204
+ }
205
+ }
206
+
207
+ if (error && error.statusText === "Unexpected directory operation") {
208
+ return {
209
+ status: 403,
210
+ }
211
+ }
212
+
213
+ return convertFileSystemErrorToResponseProperties(error)
214
+ }
215
+ }