@jsenv/core 30.3.9 → 30.4.1

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/main.js CHANGED
@@ -7093,10 +7093,30 @@ const createUrlGraph = () => {
7093
7093
  if (!parentUrlInfo) {
7094
7094
  return null;
7095
7095
  }
7096
- const firstReferenceOnThatUrl = parentUrlInfo.references.find(reference => {
7097
- return urlSpecifierEncoding.decode(reference) === specifier;
7098
- });
7099
- return firstReferenceOnThatUrl;
7096
+ const seen = [];
7097
+ const search = urlInfo => {
7098
+ const firstReferenceFound = urlInfo.references.find(reference => {
7099
+ return urlSpecifierEncoding.decode(reference) === specifier;
7100
+ });
7101
+ if (firstReferenceFound) {
7102
+ return firstReferenceFound;
7103
+ }
7104
+ for (const dependencyUrl of parentUrlInfo.dependencies) {
7105
+ if (seen.includes(dependencyUrl)) {
7106
+ continue;
7107
+ }
7108
+ seen.push(dependencyUrl);
7109
+ const dependencyUrlInfo = getUrlInfo(dependencyUrl);
7110
+ if (dependencyUrlInfo.isInline) {
7111
+ const firstRef = search(dependencyUrlInfo);
7112
+ if (firstRef) {
7113
+ return firstRef;
7114
+ }
7115
+ }
7116
+ }
7117
+ return null;
7118
+ };
7119
+ return search(parentUrlInfo);
7100
7120
  };
7101
7121
  const findDependent = (urlInfo, visitor) => {
7102
7122
  const seen = [urlInfo.url];
@@ -8852,7 +8872,7 @@ ${ANSI.color(normalizedReturnValue, ANSI.YELLOW)}
8852
8872
  type,
8853
8873
  subtype,
8854
8874
  originalUrl,
8855
- originalContent,
8875
+ originalContent = content,
8856
8876
  sourcemap,
8857
8877
  filename,
8858
8878
  status = 200,
@@ -8875,7 +8895,15 @@ ${ANSI.color(normalizedReturnValue, ANSI.YELLOW)}
8875
8895
  urlInfo.subtype = subtype || reference.expectedSubtype || "";
8876
8896
  // during build urls info are reused and load returns originalUrl/originalContent
8877
8897
  urlInfo.originalUrl = originalUrl || urlInfo.originalUrl;
8878
- urlInfo.originalContent = originalContent === undefined ? content : originalContent;
8898
+ if (originalContent !== urlInfo.originalContent) {
8899
+ urlInfo.originalContentEtag = undefined; // set by "initTransformations"
8900
+ }
8901
+
8902
+ if (content !== urlInfo.content) {
8903
+ urlInfo.contentEtag = undefined; // set by "applyFinalTransformations"
8904
+ }
8905
+
8906
+ urlInfo.originalContent = originalContent;
8879
8907
  urlInfo.content = content;
8880
8908
  urlInfo.sourcemap = sourcemap;
8881
8909
  if (data) {
@@ -19180,17 +19208,6 @@ const jsenvPluginBabel = ({
19180
19208
  return {
19181
19209
  name: "jsenv:babel",
19182
19210
  appliesDuring: "*",
19183
- transformUrlContent: urlInfo => {
19184
- if (urlInfo.url === regeneratorRuntimeClientFileUrl) {
19185
- urlInfo.data.isBabelClientFile = true;
19186
- }
19187
- if (urlInfo.url === globalThisClientFileUrl) {
19188
- urlInfo.data.isBabelClientFile = true;
19189
- }
19190
- if (urlInfo.url === newStylesheetClientFileUrl) {
19191
- urlInfo.data.isBabelClientFile = true;
19192
- }
19193
- },
19194
19211
  finalizeUrlContent: {
19195
19212
  js_classic: transformWithBabel,
19196
19213
  js_module: transformWithBabel
@@ -20017,17 +20034,20 @@ const jsenvPluginAutoreload = ({
20017
20034
  })];
20018
20035
  };
20019
20036
 
20020
- const jsenvPluginCacheControl = () => {
20037
+ const jsenvPluginCacheControl = ({
20038
+ versionedUrls = true,
20039
+ maxAge = SECONDS_IN_30_DAYS$1
20040
+ }) => {
20021
20041
  return {
20022
20042
  name: "jsenv:cache_control",
20023
20043
  appliesDuring: "dev",
20024
20044
  augmentResponse: ({
20025
20045
  reference
20026
20046
  }) => {
20027
- if (reference.searchParams.has("v") && !reference.searchParams.has("hmr")) {
20047
+ if (versionedUrls && reference.searchParams.has("v") && !reference.searchParams.has("hmr")) {
20028
20048
  return {
20029
20049
  headers: {
20030
- "cache-control": `private,max-age=${SECONDS_IN_30_DAYS$1},immutable`
20050
+ "cache-control": `private,max-age=${maxAge},immutable`
20031
20051
  }
20032
20052
  };
20033
20053
  }
@@ -20169,11 +20189,15 @@ const getCorePlugins = ({
20169
20189
  clientFileChangeCallbackList,
20170
20190
  clientFilesPruneCallbackList,
20171
20191
  explorer,
20192
+ cacheControl,
20172
20193
  ribbon = true
20173
20194
  } = {}) => {
20174
20195
  if (explorer === true) {
20175
20196
  explorer = {};
20176
20197
  }
20198
+ if (cacheControl === true) {
20199
+ cacheControl = {};
20200
+ }
20177
20201
  if (supervisor === true) {
20178
20202
  supervisor = {};
20179
20203
  }
@@ -20214,7 +20238,7 @@ const getCorePlugins = ({
20214
20238
  ...clientAutoreload,
20215
20239
  clientFileChangeCallbackList,
20216
20240
  clientFilesPruneCallbackList
20217
- })] : []), jsenvPluginCacheControl(), ...(explorer ? [jsenvPluginExplorer({
20241
+ })] : []), ...(cacheControl ? [jsenvPluginCacheControl(cacheControl)] : []), ...(explorer ? [jsenvPluginExplorer({
20218
20242
  ...explorer,
20219
20243
  clientMainFileUrl
20220
20244
  })] : []), ...(ribbon ? [jsenvPluginRibbon({
@@ -21392,7 +21416,7 @@ ${ANSI.color(buildUrl, ANSI.MAGENTA)}
21392
21416
  }
21393
21417
  if (preferWithoutVersioning(reference)) {
21394
21418
  // when versioning is dynamic no need to take into account
21395
- // happend for:
21419
+ // happens for:
21396
21420
  // - specifier mapped by window.__v__()
21397
21421
  // - specifier mapped by importmap
21398
21422
  return null;
@@ -21752,7 +21776,7 @@ ${ANSI.color(buildUrl, ANSI.MAGENTA)}
21752
21776
  return finalUrlInfo.subtype === "service_worker" && finalUrlInfo.isEntryPoint;
21753
21777
  });
21754
21778
  if (serviceWorkerEntryUrlInfos.length > 0) {
21755
- const serviceWorkerUrls = {};
21779
+ const serviceWorkerResources = {};
21756
21780
  GRAPH.forEach(finalGraph, urlInfo => {
21757
21781
  if (urlInfo.isInline || !urlInfo.shouldHandle) {
21758
21782
  return;
@@ -21765,26 +21789,27 @@ ${ANSI.color(buildUrl, ANSI.MAGENTA)}
21765
21789
  // so that service worker source still changes and navigator
21766
21790
  // detect there is a change
21767
21791
  const specifier = findKey(buildUrls, urlInfo.url);
21768
- serviceWorkerUrls[specifier] = {
21769
- versioned: false,
21792
+ serviceWorkerResources[specifier] = {
21770
21793
  version: versionMap.get(urlInfo.url)
21771
21794
  };
21772
21795
  return;
21773
21796
  }
21797
+ const specifier = findKey(buildUrls, urlInfo.url);
21774
21798
  const versionedUrl = versionedUrlMap.get(urlInfo.url);
21775
21799
  const versionedSpecifier = findKey(buildUrls, versionedUrl);
21776
- serviceWorkerUrls[versionedSpecifier] = {
21777
- versioned: true
21800
+ serviceWorkerResources[specifier] = {
21801
+ version: versionMap.get(urlInfo.url),
21802
+ versionedUrl: versionedSpecifier
21778
21803
  };
21779
21804
  });
21780
21805
  serviceWorkerEntryUrlInfos.forEach(serviceWorkerEntryUrlInfo => {
21781
21806
  const magicSource = createMagicSource(serviceWorkerEntryUrlInfo.content);
21782
- const urlsWithoutSelf = {
21783
- ...serviceWorkerUrls
21807
+ const serviceWorkerResourcesWithoutSwScriptItSelf = {
21808
+ ...serviceWorkerResources
21784
21809
  };
21785
21810
  const serviceWorkerSpecifier = findKey(buildUrls, serviceWorkerEntryUrlInfo.url);
21786
- delete urlsWithoutSelf[serviceWorkerSpecifier];
21787
- magicSource.prepend(`\nself.serviceWorkerUrls = ${JSON.stringify(urlsWithoutSelf, null, " ")};\n`);
21811
+ delete serviceWorkerResourcesWithoutSwScriptItSelf[serviceWorkerSpecifier];
21812
+ magicSource.prepend(`\nself.resourcesFromJsenvBuild = ${JSON.stringify(serviceWorkerResourcesWithoutSwScriptItSelf, null, " ")};\n`);
21788
21813
  const {
21789
21814
  content,
21790
21815
  sourcemap
@@ -22157,6 +22182,7 @@ const createFileService = ({
22157
22182
  clientMainFileUrl,
22158
22183
  cooldownBetweenFileEvents,
22159
22184
  explorer,
22185
+ cacheControl,
22160
22186
  ribbon,
22161
22187
  sourcemaps,
22162
22188
  sourcemapsSourcesProtocol,
@@ -22264,6 +22290,7 @@ const createFileService = ({
22264
22290
  clientFileChangeCallbackList,
22265
22291
  clientFilesPruneCallbackList,
22266
22292
  explorer,
22293
+ cacheControl,
22267
22294
  ribbon
22268
22295
  })],
22269
22296
  minification: false,
@@ -22293,18 +22320,25 @@ const createFileService = ({
22293
22320
  // when file is modified
22294
22321
  return false;
22295
22322
  }
22296
- if (!watch) {
22323
+ if (!watch && urlInfo.contentEtag) {
22324
+ // file is not watched, check the filesystem
22297
22325
  let fileContentAsBuffer;
22298
22326
  try {
22299
22327
  fileContentAsBuffer = readFileSync$1(new URL(urlInfo.url));
22300
22328
  } catch (e) {
22301
22329
  if (e.code === "ENOENT") {
22330
+ // we should consider calling urlGraph.deleteUrlInfo(urlInfo)
22331
+ urlInfo.originalContentEtag = undefined;
22332
+ urlInfo.contentEtag = undefined;
22302
22333
  return false;
22303
22334
  }
22304
22335
  return false;
22305
22336
  }
22306
22337
  const fileContentEtag = bufferToEtag$1(fileContentAsBuffer);
22307
22338
  if (fileContentEtag !== urlInfo.originalContentEtag) {
22339
+ // we should consider calling urlGraph.considerModified(urlInfo)
22340
+ urlInfo.originalContentEtag = undefined;
22341
+ urlInfo.contentEtag = undefined;
22308
22342
  return false;
22309
22343
  }
22310
22344
  }
@@ -22622,6 +22656,7 @@ const startDevServer = async ({
22622
22656
  transpilation,
22623
22657
  explorer = true,
22624
22658
  // see jsenv_plugin_explorer.js
22659
+ cacheControl = true,
22625
22660
  ribbon = true,
22626
22661
  // toolbar = false,
22627
22662
 
@@ -22760,6 +22795,7 @@ const startDevServer = async ({
22760
22795
  clientMainFileUrl,
22761
22796
  cooldownBetweenFileEvents,
22762
22797
  explorer,
22798
+ cacheControl,
22763
22799
  ribbon,
22764
22800
  sourcemaps,
22765
22801
  sourcemapsSourcesProtocol,
@@ -23394,6 +23430,17 @@ const getCoverageFromReport = async ({
23394
23430
  };
23395
23431
  const isV8Coverage = coverage => Boolean(coverage.result);
23396
23432
 
23433
+ /*
23434
+ * Export a function capable to run a file on a runtime.
23435
+ *
23436
+ * - Used internally by "executeTestPlan" part of the documented API
23437
+ * - Used internally by "execute" an advanced API not documented
23438
+ * - logs generated during file execution can be collected
23439
+ * - logs generated during file execution can be mirrored (re-logged to the console)
23440
+ * - File is given allocatedMs to complete
23441
+ * - Errors are collected
23442
+ * - File execution result is returned, it contains status/errors/namespace/consoleCalls
23443
+ */
23397
23444
  const run = async ({
23398
23445
  signal = new AbortController().signal,
23399
23446
  logger,
@@ -24244,6 +24291,7 @@ const executePlan = async (plan, {
24244
24291
  }
24245
24292
  const afterExecutionInfo = {
24246
24293
  ...beforeExecutionInfo,
24294
+ runtimeVersion: runtime.version,
24247
24295
  endMs: Date.now(),
24248
24296
  executionResult
24249
24297
  };
@@ -24636,8 +24684,8 @@ const createRuntimeFromPlaywright = ({
24636
24684
  stopSignal,
24637
24685
  keepRunning,
24638
24686
  onConsole,
24639
- executablePath,
24640
24687
  headful = keepRunning,
24688
+ playwrightLaunchOptions = {},
24641
24689
  ignoreHTTPSErrors = true
24642
24690
  }) => {
24643
24691
  const cleanupCallbackList = createCallbackListNotifiedOnce();
@@ -24653,11 +24701,14 @@ const createRuntimeFromPlaywright = ({
24653
24701
  signal,
24654
24702
  browserName,
24655
24703
  stopOnExit: true,
24656
- playwrightOptions: {
24657
- headless: !headful,
24658
- executablePath
24704
+ playwrightLaunchOptions: {
24705
+ ...playwrightLaunchOptions,
24706
+ headless: !headful
24659
24707
  }
24660
24708
  });
24709
+ if (browser._initializer.version) {
24710
+ runtime.version = browser._initializer.version;
24711
+ }
24661
24712
  const browserContext = await browser.newContext({
24662
24713
  ignoreHTTPSErrors
24663
24714
  });
@@ -24974,7 +25025,7 @@ const launchBrowserUsingPlaywright = async ({
24974
25025
  signal,
24975
25026
  browserName,
24976
25027
  stopOnExit,
24977
- playwrightOptions
25028
+ playwrightLaunchOptions
24978
25029
  }) => {
24979
25030
  const launchBrowserOperation = Abort.startOperation();
24980
25031
  launchBrowserOperation.addAbortSignal(signal);
@@ -24995,7 +25046,7 @@ const launchBrowserUsingPlaywright = async ({
24995
25046
  const browserClass = playwright[browserName];
24996
25047
  try {
24997
25048
  const browser = await browserClass.launch({
24998
- ...playwrightOptions,
25049
+ ...playwrightLaunchOptions,
24999
25050
  // let's handle them to close properly browser + remove listener
25000
25051
  // instead of relying on playwright to do so
25001
25052
  handleSIGINT: false,
@@ -25086,23 +25137,26 @@ const registerEvent = ({
25086
25137
 
25087
25138
  const chromium = createRuntimeFromPlaywright({
25088
25139
  browserName: "chromium",
25089
- browserVersion: "110.0.5481.38",
25090
- // to update, check https://github.com/microsoft/playwright/releases
25140
+ // browserVersion will be set by "browser._initializer.version"
25141
+ // see also https://github.com/microsoft/playwright/releases
25142
+ browserVersion: "unset",
25091
25143
  coveragePlaywrightAPIAvailable: true
25092
25144
  });
25093
25145
  const chromiumIsolatedTab = chromium.isolatedTab;
25094
25146
 
25095
25147
  const firefox = createRuntimeFromPlaywright({
25096
25148
  browserName: "firefox",
25097
- browserVersion: "108.0.2" // to update, check https://github.com/microsoft/playwright/releases
25149
+ // browserVersion will be set by "browser._initializer.version"
25150
+ // see also https://github.com/microsoft/playwright/releases
25151
+ browserVersion: "unset"
25098
25152
  });
25099
-
25100
25153
  const firefoxIsolatedTab = firefox.isolatedTab;
25101
25154
 
25102
25155
  const webkit = createRuntimeFromPlaywright({
25103
25156
  browserName: "webkit",
25104
- browserVersion: "16.4",
25105
- // to update, check https://github.com/microsoft/playwright/releases
25157
+ // browserVersion will be set by "browser._initializer.version"
25158
+ // see also https://github.com/microsoft/playwright/releases
25159
+ browserVersion: "unset",
25106
25160
  ignoreErrorHook: error => {
25107
25161
  // we catch error during execution but safari throw unhandled rejection
25108
25162
  // in a non-deterministic way.
@@ -26176,6 +26230,17 @@ const createBuildFilesService = ({
26176
26230
  };
26177
26231
  const SECONDS_IN_30_DAYS = 60 * 60 * 24 * 30;
26178
26232
 
26233
+ /*
26234
+ * Export a function capable to execute a file on a runtime (browser or node) and return how it goes.
26235
+ *
26236
+ * - can be useful to execute a file in a browser/node.js programmatically
26237
+ * - not documented
26238
+ * - the most importants parts:
26239
+ * - fileRelativeUrl: the file to execute inside rootDirectoryUrl
26240
+ * - runtime: an object with a "run" method.
26241
+ * The run method will start a browser/node process and execute file in it
26242
+ * - Most of the logic lives in "./run.js" used by executeTestPlan to run tests
26243
+ */
26179
26244
  const execute = async ({
26180
26245
  signal = new AbortController().signal,
26181
26246
  handleSIGINT = true,
@@ -26216,11 +26281,11 @@ const execute = async ({
26216
26281
  };
26217
26282
  if (runtime.type === "browser") {
26218
26283
  if (!devServerOrigin) {
26219
- throw new TypeError(`devServerOrigin is required when running tests on browser(s)`);
26284
+ throw new TypeError(`devServerOrigin is required to execute file on a browser`);
26220
26285
  }
26221
26286
  const devServerStarted = await pingServer(devServerOrigin);
26222
26287
  if (!devServerStarted) {
26223
- throw new Error(`dev server not started at ${devServerOrigin}. It is required to run tests`);
26288
+ throw new Error(`no server listening at ${devServerOrigin}. It is required to execute file`);
26224
26289
  }
26225
26290
  }
26226
26291
  let result = await run({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/core",
3
- "version": "30.3.9",
3
+ "version": "30.4.1",
4
4
  "description": "Tool to develop, test and build js projects",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -44,8 +44,8 @@
44
44
  "dev": "node --conditions=development ./scripts/dev/dev.mjs",
45
45
  "test": "node --conditions=development ./scripts/test/test.mjs",
46
46
  "test:resource_hints": "npm run test -- --only-resource-hints",
47
- "test:workspace": "npm run test --workspaces --if-present -- --workspace",
48
47
  "build": "node --conditions=development ./scripts/build/build.mjs",
48
+ "workspace:test": "npm run test --workspaces --if-present -- --workspace",
49
49
  "workspace:versions": "node ./scripts/publish/workspace_versions.mjs",
50
50
  "workspace:publish": "node ./scripts/publish/workspace_publish.mjs",
51
51
  "performances": "node --expose-gc ./scripts/performance/generate_performance_report.mjs --log --once",
@@ -68,8 +68,8 @@
68
68
  "@financial-times/polyfill-useragent-normaliser": "1.10.2",
69
69
  "@jsenv/abort": "4.2.4",
70
70
  "@jsenv/ast": "3.0.2",
71
- "@jsenv/babel-plugins": "1.1.3",
72
- "@jsenv/plugin-bundling": "1.1.2",
71
+ "@jsenv/babel-plugins": "1.1.4",
72
+ "@jsenv/plugin-bundling": "1.2.1",
73
73
  "@jsenv/filesystem": "4.1.9",
74
74
  "@jsenv/importmap": "1.2.1",
75
75
  "@jsenv/integrity": "0.0.1",
@@ -107,11 +107,11 @@
107
107
  "@jsenv/plugin-globals": "./packages/jsenv-plugin-globals/",
108
108
  "@jsenv/plugin-placeholders": "./packages/jsenv-plugin-placeholders/",
109
109
  "@jsenv/performance-impact": "4.1.0",
110
- "eslint": "8.32.0",
110
+ "eslint": "8.35.0",
111
111
  "eslint-plugin-html": "7.1.0",
112
112
  "eslint-plugin-import": "2.27.5",
113
- "eslint-plugin-react": "7.32.1",
114
- "playwright": "1.30.0",
115
- "prettier": "2.8.3"
113
+ "eslint-plugin-react": "7.32.2",
114
+ "playwright": "1.31.2",
115
+ "prettier": "2.8.4"
116
116
  }
117
117
  }
@@ -1042,7 +1042,7 @@ ${ANSI.color(buildUrl, ANSI.MAGENTA)}
1042
1042
  }
1043
1043
  if (preferWithoutVersioning(reference)) {
1044
1044
  // when versioning is dynamic no need to take into account
1045
- // happend for:
1045
+ // happens for:
1046
1046
  // - specifier mapped by window.__v__()
1047
1047
  // - specifier mapped by importmap
1048
1048
  return null
@@ -1477,7 +1477,7 @@ ${ANSI.color(buildUrl, ANSI.MAGENTA)}
1477
1477
  },
1478
1478
  )
1479
1479
  if (serviceWorkerEntryUrlInfos.length > 0) {
1480
- const serviceWorkerUrls = {}
1480
+ const serviceWorkerResources = {}
1481
1481
  GRAPH.forEach(finalGraph, (urlInfo) => {
1482
1482
  if (urlInfo.isInline || !urlInfo.shouldHandle) {
1483
1483
  return
@@ -1490,31 +1490,36 @@ ${ANSI.color(buildUrl, ANSI.MAGENTA)}
1490
1490
  // so that service worker source still changes and navigator
1491
1491
  // detect there is a change
1492
1492
  const specifier = findKey(buildUrls, urlInfo.url)
1493
- serviceWorkerUrls[specifier] = {
1494
- versioned: false,
1493
+ serviceWorkerResources[specifier] = {
1495
1494
  version: versionMap.get(urlInfo.url),
1496
1495
  }
1497
1496
  return
1498
1497
  }
1498
+ const specifier = findKey(buildUrls, urlInfo.url)
1499
1499
  const versionedUrl = versionedUrlMap.get(urlInfo.url)
1500
1500
  const versionedSpecifier = findKey(buildUrls, versionedUrl)
1501
- serviceWorkerUrls[versionedSpecifier] = { versioned: true }
1501
+ serviceWorkerResources[specifier] = {
1502
+ version: versionMap.get(urlInfo.url),
1503
+ versionedUrl: versionedSpecifier,
1504
+ }
1502
1505
  })
1503
1506
  serviceWorkerEntryUrlInfos.forEach((serviceWorkerEntryUrlInfo) => {
1504
1507
  const magicSource = createMagicSource(
1505
1508
  serviceWorkerEntryUrlInfo.content,
1506
1509
  )
1507
- const urlsWithoutSelf = {
1508
- ...serviceWorkerUrls,
1510
+ const serviceWorkerResourcesWithoutSwScriptItSelf = {
1511
+ ...serviceWorkerResources,
1509
1512
  }
1510
1513
  const serviceWorkerSpecifier = findKey(
1511
1514
  buildUrls,
1512
1515
  serviceWorkerEntryUrlInfo.url,
1513
1516
  )
1514
- delete urlsWithoutSelf[serviceWorkerSpecifier]
1517
+ delete serviceWorkerResourcesWithoutSwScriptItSelf[
1518
+ serviceWorkerSpecifier
1519
+ ]
1515
1520
  magicSource.prepend(
1516
- `\nself.serviceWorkerUrls = ${JSON.stringify(
1517
- urlsWithoutSelf,
1521
+ `\nself.resourcesFromJsenvBuild = ${JSON.stringify(
1522
+ serviceWorkerResourcesWithoutSwScriptItSelf,
1518
1523
  null,
1519
1524
  " ",
1520
1525
  )};\n`,
@@ -0,0 +1,3 @@
1
+ # build/
2
+
3
+ Code implementing jsenv build can be found here.
@@ -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,