@jsenv/core 30.3.8 → 30.4.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/js/s.js +0 -1
- package/dist/js/s.js.map +3 -3
- package/dist/js/server_events_client.js +0 -1
- package/dist/main.js +153 -72
- package/package.json +6 -6
- package/src/build/build.js +20 -15
- package/src/build/jsenv_plugin_line_break_normalization.js +16 -0
- package/src/build/readme.md +3 -0
- package/src/build/version_generator.js +2 -13
- package/src/dev/file_service.js +10 -1
- package/src/dev/readme.md +13 -0
- package/src/dev/start_dev_server.js +2 -0
- package/src/execute/execute.js +14 -2
- package/src/execute/run.js +12 -0
- package/src/execute/runtimes/browsers/chromium.js +3 -1
- package/src/execute/runtimes/browsers/firefox.js +3 -1
- package/src/execute/runtimes/browsers/from_playwright.js +8 -5
- package/src/execute/runtimes/browsers/webkit.js +3 -1
- package/src/execute/runtimes/readme.md +13 -0
- package/src/kitchen/kitchen.js +8 -3
- package/src/kitchen/readme.md +8 -0
- package/src/kitchen/url_graph.js +23 -5
- package/src/plugins/cache_control/jsenv_plugin_cache_control.js +6 -2
- package/src/plugins/plugins.js +6 -1
- package/src/plugins/readme.md +18 -0
- package/src/plugins/server_events/client/server_events_client.js +0 -2
- package/src/plugins/transpilation/as_js_classic/client/s.js +0 -1
- package/src/test/execute_plan.js +1 -0
- package/src/test/readme.md +3 -0
package/src/dev/file_service.js
CHANGED
|
@@ -35,6 +35,7 @@ export const createFileService = ({
|
|
|
35
35
|
clientMainFileUrl,
|
|
36
36
|
cooldownBetweenFileEvents,
|
|
37
37
|
explorer,
|
|
38
|
+
cacheControl,
|
|
38
39
|
ribbon,
|
|
39
40
|
sourcemaps,
|
|
40
41
|
sourcemapsSourcesProtocol,
|
|
@@ -145,6 +146,7 @@ export const createFileService = ({
|
|
|
145
146
|
clientFileChangeCallbackList,
|
|
146
147
|
clientFilesPruneCallbackList,
|
|
147
148
|
explorer,
|
|
149
|
+
cacheControl,
|
|
148
150
|
ribbon,
|
|
149
151
|
}),
|
|
150
152
|
],
|
|
@@ -173,18 +175,25 @@ export const createFileService = ({
|
|
|
173
175
|
// when file is modified
|
|
174
176
|
return false
|
|
175
177
|
}
|
|
176
|
-
if (!watch) {
|
|
178
|
+
if (!watch && urlInfo.contentEtag) {
|
|
179
|
+
// file is not watched, check the filesystem
|
|
177
180
|
let fileContentAsBuffer
|
|
178
181
|
try {
|
|
179
182
|
fileContentAsBuffer = readFileSync(new URL(urlInfo.url))
|
|
180
183
|
} catch (e) {
|
|
181
184
|
if (e.code === "ENOENT") {
|
|
185
|
+
// we should consider calling urlGraph.deleteUrlInfo(urlInfo)
|
|
186
|
+
urlInfo.originalContentEtag = undefined
|
|
187
|
+
urlInfo.contentEtag = undefined
|
|
182
188
|
return false
|
|
183
189
|
}
|
|
184
190
|
return false
|
|
185
191
|
}
|
|
186
192
|
const fileContentEtag = bufferToEtag(fileContentAsBuffer)
|
|
187
193
|
if (fileContentEtag !== urlInfo.originalContentEtag) {
|
|
194
|
+
// we should consider calling urlGraph.considerModified(urlInfo)
|
|
195
|
+
urlInfo.originalContentEtag = undefined
|
|
196
|
+
urlInfo.contentEtag = undefined
|
|
188
197
|
return false
|
|
189
198
|
}
|
|
190
199
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# dev/
|
|
2
|
+
|
|
3
|
+
Code implementing jsenv dev server can be found here.
|
|
4
|
+
|
|
5
|
+
# Description
|
|
6
|
+
|
|
7
|
+
Jsenv dev server is a file server injecting code into HTML to autoreload when a file is saved.
|
|
8
|
+
|
|
9
|
+
It uses a plugin pattern allowing to control dev server behaviour
|
|
10
|
+
|
|
11
|
+
- Plugins can be used to transform file content before serving them; And many more things
|
|
12
|
+
- Some plugins are internal to jsenv and can be configured through parameters
|
|
13
|
+
- Some plugins are published in their own packages and can be passed via startDevServer "plugins" parameter
|
|
@@ -72,6 +72,7 @@ export const startDevServer = async ({
|
|
|
72
72
|
fileSystemMagicRedirection,
|
|
73
73
|
transpilation,
|
|
74
74
|
explorer = true, // see jsenv_plugin_explorer.js
|
|
75
|
+
cacheControl = true,
|
|
75
76
|
ribbon = true,
|
|
76
77
|
// toolbar = false,
|
|
77
78
|
|
|
@@ -209,6 +210,7 @@ export const startDevServer = async ({
|
|
|
209
210
|
clientMainFileUrl,
|
|
210
211
|
cooldownBetweenFileEvents,
|
|
211
212
|
explorer,
|
|
213
|
+
cacheControl,
|
|
212
214
|
ribbon,
|
|
213
215
|
sourcemaps,
|
|
214
216
|
sourcemapsSourcesProtocol,
|
package/src/execute/execute.js
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Export a function capable to execute a file on a runtime (browser or node) and return how it goes.
|
|
3
|
+
*
|
|
4
|
+
* - can be useful to execute a file in a browser/node.js programmatically
|
|
5
|
+
* - not documented
|
|
6
|
+
* - the most importants parts:
|
|
7
|
+
* - fileRelativeUrl: the file to execute inside rootDirectoryUrl
|
|
8
|
+
* - runtime: an object with a "run" method.
|
|
9
|
+
* The run method will start a browser/node process and execute file in it
|
|
10
|
+
* - Most of the logic lives in "./run.js" used by executeTestPlan to run tests
|
|
11
|
+
*/
|
|
12
|
+
|
|
1
13
|
import { Abort, raceProcessTeardownEvents } from "@jsenv/abort"
|
|
2
14
|
import { assertAndNormalizeDirectoryUrl } from "@jsenv/filesystem"
|
|
3
15
|
import { createLogger } from "@jsenv/log"
|
|
@@ -51,13 +63,13 @@ export const execute = async ({
|
|
|
51
63
|
if (runtime.type === "browser") {
|
|
52
64
|
if (!devServerOrigin) {
|
|
53
65
|
throw new TypeError(
|
|
54
|
-
`devServerOrigin is required
|
|
66
|
+
`devServerOrigin is required to execute file on a browser`,
|
|
55
67
|
)
|
|
56
68
|
}
|
|
57
69
|
const devServerStarted = await pingServer(devServerOrigin)
|
|
58
70
|
if (!devServerStarted) {
|
|
59
71
|
throw new Error(
|
|
60
|
-
`
|
|
72
|
+
`no server listening at ${devServerOrigin}. It is required to execute file`,
|
|
61
73
|
)
|
|
62
74
|
}
|
|
63
75
|
}
|
package/src/execute/run.js
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Export a function capable to run a file on a runtime.
|
|
3
|
+
*
|
|
4
|
+
* - Used internally by "executeTestPlan" part of the documented API
|
|
5
|
+
* - Used internally by "execute" an advanced API not documented
|
|
6
|
+
* - logs generated during file execution can be collected
|
|
7
|
+
* - logs generated during file execution can be mirrored (re-logged to the console)
|
|
8
|
+
* - File is given allocatedMs to complete
|
|
9
|
+
* - Errors are collected
|
|
10
|
+
* - File execution result is returned, it contains status/errors/namespace/consoleCalls
|
|
11
|
+
*/
|
|
12
|
+
|
|
1
13
|
import { createId } from "@paralleldrive/cuid2"
|
|
2
14
|
import { Abort, raceCallbacks } from "@jsenv/abort"
|
|
3
15
|
import { ensureParentDirectories } from "@jsenv/filesystem"
|
|
@@ -2,7 +2,9 @@ import { createRuntimeFromPlaywright } from "./from_playwright.js"
|
|
|
2
2
|
|
|
3
3
|
export const chromium = createRuntimeFromPlaywright({
|
|
4
4
|
browserName: "chromium",
|
|
5
|
-
browserVersion
|
|
5
|
+
// browserVersion will be set by "browser._initializer.version"
|
|
6
|
+
// see also https://github.com/microsoft/playwright/releases
|
|
7
|
+
browserVersion: "unset",
|
|
6
8
|
coveragePlaywrightAPIAvailable: true,
|
|
7
9
|
})
|
|
8
10
|
export const chromiumIsolatedTab = chromium.isolatedTab
|
|
@@ -2,6 +2,8 @@ import { createRuntimeFromPlaywright } from "./from_playwright.js"
|
|
|
2
2
|
|
|
3
3
|
export const firefox = createRuntimeFromPlaywright({
|
|
4
4
|
browserName: "firefox",
|
|
5
|
-
browserVersion
|
|
5
|
+
// browserVersion will be set by "browser._initializer.version"
|
|
6
|
+
// see also https://github.com/microsoft/playwright/releases
|
|
7
|
+
browserVersion: "unset",
|
|
6
8
|
})
|
|
7
9
|
export const firefoxIsolatedTab = firefox.isolatedTab
|
|
@@ -46,8 +46,8 @@ export const createRuntimeFromPlaywright = ({
|
|
|
46
46
|
keepRunning,
|
|
47
47
|
onConsole,
|
|
48
48
|
|
|
49
|
-
executablePath,
|
|
50
49
|
headful = keepRunning,
|
|
50
|
+
playwrightLaunchOptions = {},
|
|
51
51
|
ignoreHTTPSErrors = true,
|
|
52
52
|
}) => {
|
|
53
53
|
const cleanupCallbackList = createCallbackListNotifiedOnce()
|
|
@@ -62,11 +62,14 @@ export const createRuntimeFromPlaywright = ({
|
|
|
62
62
|
signal,
|
|
63
63
|
browserName,
|
|
64
64
|
stopOnExit: true,
|
|
65
|
-
|
|
65
|
+
playwrightLaunchOptions: {
|
|
66
|
+
...playwrightLaunchOptions,
|
|
66
67
|
headless: !headful,
|
|
67
|
-
executablePath,
|
|
68
68
|
},
|
|
69
69
|
})
|
|
70
|
+
if (browser._initializer.version) {
|
|
71
|
+
runtime.version = browser._initializer.version
|
|
72
|
+
}
|
|
70
73
|
const browserContext = await browser.newContext({ ignoreHTTPSErrors })
|
|
71
74
|
return { browser, browserContext }
|
|
72
75
|
})()
|
|
@@ -409,7 +412,7 @@ const launchBrowserUsingPlaywright = async ({
|
|
|
409
412
|
signal,
|
|
410
413
|
browserName,
|
|
411
414
|
stopOnExit,
|
|
412
|
-
|
|
415
|
+
playwrightLaunchOptions,
|
|
413
416
|
}) => {
|
|
414
417
|
const launchBrowserOperation = Abort.startOperation()
|
|
415
418
|
launchBrowserOperation.addAbortSignal(signal)
|
|
@@ -431,7 +434,7 @@ const launchBrowserUsingPlaywright = async ({
|
|
|
431
434
|
const browserClass = playwright[browserName]
|
|
432
435
|
try {
|
|
433
436
|
const browser = await browserClass.launch({
|
|
434
|
-
...
|
|
437
|
+
...playwrightLaunchOptions,
|
|
435
438
|
// let's handle them to close properly browser + remove listener
|
|
436
439
|
// instead of relying on playwright to do so
|
|
437
440
|
handleSIGINT: false,
|
|
@@ -2,7 +2,9 @@ import { createRuntimeFromPlaywright } from "./from_playwright.js"
|
|
|
2
2
|
|
|
3
3
|
export const webkit = createRuntimeFromPlaywright({
|
|
4
4
|
browserName: "webkit",
|
|
5
|
-
browserVersion
|
|
5
|
+
// browserVersion will be set by "browser._initializer.version"
|
|
6
|
+
// see also https://github.com/microsoft/playwright/releases
|
|
7
|
+
browserVersion: "unset",
|
|
6
8
|
ignoreErrorHook: (error) => {
|
|
7
9
|
// we catch error during execution but safari throw unhandled rejection
|
|
8
10
|
// in a non-deterministic way.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# runtimes/
|
|
2
|
+
|
|
3
|
+
Code implementing runtimes can be found here.
|
|
4
|
+
|
|
5
|
+
# Description
|
|
6
|
+
|
|
7
|
+
A runtime is an object with a "run" method.
|
|
8
|
+
The run method is roughly doing the following:
|
|
9
|
+
|
|
10
|
+
1. spawn a runtime (browser,Node.js)
|
|
11
|
+
2. set various listeners to monitor file execution
|
|
12
|
+
3. execute the file
|
|
13
|
+
4. return info about file execution (logs and errors for instance)
|
package/src/kitchen/kitchen.js
CHANGED
|
@@ -356,7 +356,7 @@ ${ANSI.color(normalizedReturnValue, ANSI.YELLOW)}
|
|
|
356
356
|
type,
|
|
357
357
|
subtype,
|
|
358
358
|
originalUrl,
|
|
359
|
-
originalContent,
|
|
359
|
+
originalContent = content,
|
|
360
360
|
sourcemap,
|
|
361
361
|
filename,
|
|
362
362
|
|
|
@@ -381,8 +381,13 @@ ${ANSI.color(normalizedReturnValue, ANSI.YELLOW)}
|
|
|
381
381
|
urlInfo.subtype = subtype || reference.expectedSubtype || ""
|
|
382
382
|
// during build urls info are reused and load returns originalUrl/originalContent
|
|
383
383
|
urlInfo.originalUrl = originalUrl || urlInfo.originalUrl
|
|
384
|
-
urlInfo.originalContent
|
|
385
|
-
|
|
384
|
+
if (originalContent !== urlInfo.originalContent) {
|
|
385
|
+
urlInfo.originalContentEtag = undefined // set by "initTransformations"
|
|
386
|
+
}
|
|
387
|
+
if (content !== urlInfo.content) {
|
|
388
|
+
urlInfo.contentEtag = undefined // set by "applyFinalTransformations"
|
|
389
|
+
}
|
|
390
|
+
urlInfo.originalContent = originalContent
|
|
386
391
|
urlInfo.content = content
|
|
387
392
|
urlInfo.sourcemap = sourcemap
|
|
388
393
|
if (data) {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# kitchen/
|
|
2
|
+
|
|
3
|
+
Code implementing jsenv kitchen can be found here.
|
|
4
|
+
|
|
5
|
+
# Description
|
|
6
|
+
|
|
7
|
+
Jsenv kitchen is an abstraction representing the place where files content is transformed to obtain something.
|
|
8
|
+
Jsenv dev server and build go through the kitchen to transform file content.
|
package/src/kitchen/url_graph.js
CHANGED
|
@@ -37,12 +37,30 @@ export const createUrlGraph = () => {
|
|
|
37
37
|
if (!parentUrlInfo) {
|
|
38
38
|
return null
|
|
39
39
|
}
|
|
40
|
-
const
|
|
41
|
-
|
|
40
|
+
const seen = []
|
|
41
|
+
const search = (urlInfo) => {
|
|
42
|
+
const firstReferenceFound = urlInfo.references.find((reference) => {
|
|
42
43
|
return urlSpecifierEncoding.decode(reference) === specifier
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
})
|
|
45
|
+
if (firstReferenceFound) {
|
|
46
|
+
return firstReferenceFound
|
|
47
|
+
}
|
|
48
|
+
for (const dependencyUrl of parentUrlInfo.dependencies) {
|
|
49
|
+
if (seen.includes(dependencyUrl)) {
|
|
50
|
+
continue
|
|
51
|
+
}
|
|
52
|
+
seen.push(dependencyUrl)
|
|
53
|
+
const dependencyUrlInfo = getUrlInfo(dependencyUrl)
|
|
54
|
+
if (dependencyUrlInfo.isInline) {
|
|
55
|
+
const firstRef = search(dependencyUrlInfo)
|
|
56
|
+
if (firstRef) {
|
|
57
|
+
return firstRef
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return null
|
|
62
|
+
}
|
|
63
|
+
return search(parentUrlInfo)
|
|
46
64
|
}
|
|
47
65
|
const findDependent = (urlInfo, visitor) => {
|
|
48
66
|
const seen = [urlInfo.url]
|
|
@@ -1,15 +1,19 @@
|
|
|
1
|
-
export const jsenvPluginCacheControl = (
|
|
1
|
+
export const jsenvPluginCacheControl = ({
|
|
2
|
+
versionedUrls = true,
|
|
3
|
+
maxAge = SECONDS_IN_30_DAYS,
|
|
4
|
+
}) => {
|
|
2
5
|
return {
|
|
3
6
|
name: "jsenv:cache_control",
|
|
4
7
|
appliesDuring: "dev",
|
|
5
8
|
augmentResponse: ({ reference }) => {
|
|
6
9
|
if (
|
|
10
|
+
versionedUrls &&
|
|
7
11
|
reference.searchParams.has("v") &&
|
|
8
12
|
!reference.searchParams.has("hmr")
|
|
9
13
|
) {
|
|
10
14
|
return {
|
|
11
15
|
headers: {
|
|
12
|
-
"cache-control": `private,max-age=${
|
|
16
|
+
"cache-control": `private,max-age=${maxAge},immutable`,
|
|
13
17
|
},
|
|
14
18
|
}
|
|
15
19
|
}
|
package/src/plugins/plugins.js
CHANGED
|
@@ -38,11 +38,15 @@ export const getCorePlugins = ({
|
|
|
38
38
|
clientFileChangeCallbackList,
|
|
39
39
|
clientFilesPruneCallbackList,
|
|
40
40
|
explorer,
|
|
41
|
+
cacheControl,
|
|
41
42
|
ribbon = true,
|
|
42
43
|
} = {}) => {
|
|
43
44
|
if (explorer === true) {
|
|
44
45
|
explorer = {}
|
|
45
46
|
}
|
|
47
|
+
if (cacheControl === true) {
|
|
48
|
+
cacheControl = {}
|
|
49
|
+
}
|
|
46
50
|
if (supervisor === true) {
|
|
47
51
|
supervisor = {}
|
|
48
52
|
}
|
|
@@ -59,6 +63,7 @@ export const getCorePlugins = ({
|
|
|
59
63
|
} else {
|
|
60
64
|
clientMainFileUrl = String(clientMainFileUrl)
|
|
61
65
|
}
|
|
66
|
+
|
|
62
67
|
if (ribbon === true) {
|
|
63
68
|
ribbon = {}
|
|
64
69
|
}
|
|
@@ -97,7 +102,7 @@ export const getCorePlugins = ({
|
|
|
97
102
|
}),
|
|
98
103
|
]
|
|
99
104
|
: []),
|
|
100
|
-
jsenvPluginCacheControl(),
|
|
105
|
+
...(cacheControl ? [jsenvPluginCacheControl(cacheControl)] : []),
|
|
101
106
|
...(explorer
|
|
102
107
|
? [jsenvPluginExplorer({ ...explorer, clientMainFileUrl })]
|
|
103
108
|
: []),
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# plugins/
|
|
2
|
+
|
|
3
|
+
Code implementing jsenv internal plugins can be found here.
|
|
4
|
+
|
|
5
|
+
# Description
|
|
6
|
+
|
|
7
|
+
These plugins can be configured using parameters of "startDevServer" and "build" functions.
|
|
8
|
+
|
|
9
|
+
A few examples of plugins that can be found here:
|
|
10
|
+
|
|
11
|
+
- inject code to autoreload during dev
|
|
12
|
+
- apply node module resolution on js imports
|
|
13
|
+
|
|
14
|
+
They are here and not inside a separate NPM package because:
|
|
15
|
+
|
|
16
|
+
1. they are considered useful enough to be available by default
|
|
17
|
+
2. putting them into a separate package would force people to enable a bunch of plugins
|
|
18
|
+
to obtain what they want instead of having sensible defaults
|
package/src/test/execute_plan.js
CHANGED