@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.
@@ -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,
@@ -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 when running tests on browser(s)`,
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
- `dev server not started at ${devServerOrigin}. It is required to run tests`,
72
+ `no server listening at ${devServerOrigin}. It is required to execute file`,
61
73
  )
62
74
  }
63
75
  }
@@ -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: "110.0.5481.38", // to update, check https://github.com/microsoft/playwright/releases
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: "108.0.2", // to update, check https://github.com/microsoft/playwright/releases
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
- playwrightOptions: {
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
- playwrightOptions,
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
- ...playwrightOptions,
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: "16.4", // to update, check https://github.com/microsoft/playwright/releases
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)
@@ -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
- originalContent === undefined ? content : originalContent
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.
@@ -37,12 +37,30 @@ export const createUrlGraph = () => {
37
37
  if (!parentUrlInfo) {
38
38
  return null
39
39
  }
40
- const firstReferenceOnThatUrl = parentUrlInfo.references.find(
41
- (reference) => {
40
+ const seen = []
41
+ const search = (urlInfo) => {
42
+ const firstReferenceFound = urlInfo.references.find((reference) => {
42
43
  return urlSpecifierEncoding.decode(reference) === specifier
43
- },
44
- )
45
- return firstReferenceOnThatUrl
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=${SECONDS_IN_30_DAYS},immutable`,
16
+ "cache-control": `private,max-age=${maxAge},immutable`,
13
17
  },
14
18
  }
15
19
  }
@@ -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
@@ -1,5 +1,3 @@
1
- /* globals self */
2
-
3
1
  import { createWebSocketConnection } from "./web_socket_connection.js"
4
2
 
5
3
  const websocketScheme = self.location.protocol === "https:" ? "wss" : "ws"
@@ -12,7 +12,6 @@
12
12
 
13
13
  ;(function () {
14
14
  /* eslint-env browser */
15
- /* globals self */
16
15
 
17
16
  const loadRegistry = Object.create(null)
18
17
  const registerRegistry = Object.create(null)
@@ -326,6 +326,7 @@ export const executePlan = async (
326
326
 
327
327
  const afterExecutionInfo = {
328
328
  ...beforeExecutionInfo,
329
+ runtimeVersion: runtime.version,
329
330
  endMs: Date.now(),
330
331
  executionResult,
331
332
  }
@@ -0,0 +1,3 @@
1
+ # test/
2
+
3
+ Code implementing "executeTestPlan" can be found here