browsertime 23.4.5 → 24.0.0-alpha.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.
Files changed (102) hide show
  1. package/CHANGELOG.md +32 -1
  2. package/bin/browsertime.js +3 -2
  3. package/lib/android/gnirehtet.js +2 -2
  4. package/lib/android/index.js +2 -2
  5. package/lib/android/root.js +2 -2
  6. package/lib/chrome/chromeDevtoolsProtocol.js +4 -4
  7. package/lib/chrome/har.js +2 -2
  8. package/lib/chrome/networkManager.js +2 -2
  9. package/lib/chrome/parseCpuTrace.js +2 -2
  10. package/lib/chrome/traceCategoriesParser.js +2 -2
  11. package/lib/chrome/webdriver/builder.js +1 -2
  12. package/lib/chrome/webdriver/chromium.js +2 -2
  13. package/lib/chrome/webdriver/setupChromiumOptions.js +2 -2
  14. package/lib/chrome/webdriver/traceUtilities.js +2 -2
  15. package/lib/connectivity/humble.js +2 -2
  16. package/lib/connectivity/index.js +2 -2
  17. package/lib/core/engine/collector.js +2 -2
  18. package/lib/core/engine/command/addText.js +2 -2
  19. package/lib/core/engine/command/android.js +2 -2
  20. package/lib/core/engine/command/bidi.js +2 -2
  21. package/lib/core/engine/command/cache.js +2 -2
  22. package/lib/core/engine/command/chromeDevToolsProtocol.js +2 -2
  23. package/lib/core/engine/command/chromeTrace.js +2 -2
  24. package/lib/core/engine/command/click.js +2 -2
  25. package/lib/core/engine/command/debug.js +2 -2
  26. package/lib/core/engine/command/geckoProfiler.js +2 -2
  27. package/lib/core/engine/command/javaScript.js +2 -2
  28. package/lib/core/engine/command/measure.js +14 -3
  29. package/lib/core/engine/command/mouse/clickAndHold.js +2 -2
  30. package/lib/core/engine/command/mouse/contextClick.js +2 -2
  31. package/lib/core/engine/command/mouse/doubleClick.js +2 -2
  32. package/lib/core/engine/command/mouse/mouseMove.js +2 -2
  33. package/lib/core/engine/command/mouse/singleClick.js +2 -2
  34. package/lib/core/engine/command/navigation.js +2 -2
  35. package/lib/core/engine/command/screenshot.js +2 -2
  36. package/lib/core/engine/command/scroll.js +2 -2
  37. package/lib/core/engine/command/select.js +2 -2
  38. package/lib/core/engine/command/set.js +2 -2
  39. package/lib/core/engine/command/stopWatch.js +2 -2
  40. package/lib/core/engine/command/switch.js +2 -2
  41. package/lib/core/engine/command/wait.js +2 -2
  42. package/lib/core/engine/index.js +15 -16
  43. package/lib/core/engine/iteration.js +2 -2
  44. package/lib/core/seleniumRunner.js +8 -12
  45. package/lib/core/webdriver/index.js +1 -1
  46. package/lib/edge/webdriver/builder.js +3 -4
  47. package/lib/firefox/firefoxBidi.js +2 -2
  48. package/lib/firefox/geckoProfiler.js +2 -2
  49. package/lib/firefox/memoryReport.js +2 -2
  50. package/lib/firefox/networkManager.js +2 -2
  51. package/lib/firefox/perfStats.js +2 -2
  52. package/lib/firefox/webdriver/builder.js +3 -5
  53. package/lib/firefox/webdriver/firefox.js +2 -2
  54. package/lib/screenshot/index.js +7 -9
  55. package/lib/screenshot/loadCustomJimp.js +17 -0
  56. package/lib/support/browserScript.js +4 -5
  57. package/lib/support/cli.js +19 -5
  58. package/lib/support/dns.js +2 -2
  59. package/lib/support/engineUtils.js +4 -5
  60. package/lib/support/fileUtil.js +20 -3
  61. package/lib/support/getPort.js +25 -0
  62. package/lib/support/getViewPort.js +2 -2
  63. package/lib/support/har/index.js +5 -6
  64. package/lib/support/images/index.js +8 -17
  65. package/lib/support/logging.js +14 -39
  66. package/lib/support/pathToFolder.js +3 -3
  67. package/lib/support/preURL.js +2 -2
  68. package/lib/support/statistics.js +2 -2
  69. package/lib/support/stop.js +2 -2
  70. package/lib/support/storageManager.js +5 -5
  71. package/lib/support/userTiming.js +10 -0
  72. package/lib/support/util.js +73 -3
  73. package/lib/support/xvfb.js +55 -26
  74. package/lib/video/postprocessing/finetune/addTextToVideo.js +2 -2
  75. package/lib/video/postprocessing/finetune/convertFps.js +2 -2
  76. package/lib/video/postprocessing/finetune/index.js +2 -2
  77. package/lib/video/postprocessing/finetune/removeOrange.js +2 -2
  78. package/lib/video/postprocessing/visualmetrics/getVideoMetrics.js +2 -2
  79. package/lib/video/postprocessing/visualmetrics/visualMetrics.js +2 -2
  80. package/lib/video/screenRecording/android/recorder.js +2 -2
  81. package/lib/video/screenRecording/desktop/convert.js +2 -2
  82. package/lib/video/screenRecording/desktop/desktopRecorder.js +2 -2
  83. package/lib/video/screenRecording/desktop/ffmpegRecorder.js +2 -2
  84. package/lib/video/screenRecording/desktop/osx/getScreen.js +2 -2
  85. package/lib/video/screenRecording/firefox/firefoxWindowRecorder.js +2 -2
  86. package/lib/video/screenRecording/ios/convertToMp4.js +2 -2
  87. package/lib/video/screenRecording/ios/iosRecorder.js +2 -2
  88. package/lib/video/screenRecording/iosSimulator/convertToMp4.js +2 -2
  89. package/lib/video/screenRecording/iosSimulator/recorder.js +2 -2
  90. package/lib/video/screenRecording/setOrangeBackground.js +2 -2
  91. package/package.json +19 -24
  92. package/types/core/engine/command/measure.d.ts.map +1 -1
  93. package/types/support/engineUtils.d.ts.map +1 -1
  94. package/types/support/fileUtil.d.ts +1 -0
  95. package/types/support/fileUtil.d.ts.map +1 -1
  96. package/types/support/storageManager.d.ts +0 -1
  97. package/types/support/storageManager.d.ts.map +1 -1
  98. package/types/support/userTiming.d.ts +1 -0
  99. package/types/support/userTiming.d.ts.map +1 -1
  100. package/types/support/util.d.ts +3 -0
  101. package/types/support/util.d.ts.map +1 -1
  102. package/types/video/defaults.d.ts.map +1 -1
@@ -1,4 +1,4 @@
1
- import intel from 'intel';
1
+ import { getLogger } from '@sitespeed.io/log';
2
2
  import merge from 'lodash.merge';
3
3
  import get from 'lodash.get';
4
4
  import { Condition } from 'selenium-webdriver';
@@ -9,7 +9,7 @@ import { getViewPort } from '../support/getViewPort.js';
9
9
  import { defaultPageCompleteCheck } from './pageCompleteChecks/defaultPageCompleteCheck.js';
10
10
  import { pageCompleteCheckByInactivity } from './pageCompleteChecks/pageCompleteCheckByInactivity.js';
11
11
  import { spaInactivity as spaCheck } from './pageCompleteChecks/spaInactivity.js';
12
- const log = intel.getLogger('browsertime');
12
+ const log = getLogger('browsertime');
13
13
 
14
14
  const defaults = {
15
15
  timeouts: {
@@ -402,11 +402,8 @@ export class SeleniumRunner {
402
402
  async runScript(script, name, arguments_) {
403
403
  let scriptTimeout = this.options.timeouts.script;
404
404
 
405
- if (log.isEnabledFor(log.TRACE)) {
406
- log.verbose('Executing script %s', script);
407
- } else if (log.isEnabledFor(log.VERBOSE)) {
408
- log.verbose('Executing script %s', name);
409
- }
405
+ log.verbose('Executing script %s', script);
406
+ log.verbose('Executing script %s', name);
410
407
 
411
408
  try {
412
409
  const result = await timeout(
@@ -463,11 +460,10 @@ export class SeleniumRunner {
463
460
  * @throws {BrowserError}
464
461
  */
465
462
  async runAsyncScript(script, name, arguments_) {
466
- if (log.isEnabledFor(log.TRACE)) {
467
- log.verbose('Executing async script %s', script);
468
- } else if (log.isEnabledFor(log.VERBOSE)) {
469
- log.verbose('Executing async script %s', name);
470
- }
463
+ log.verbose('Executing async script %s', script);
464
+
465
+ log.verbose('Executing async script %s', name);
466
+
471
467
  return this.driver.executeAsyncScript(script, arguments_);
472
468
  }
473
469
 
@@ -1,9 +1,9 @@
1
1
  import { Builder } from 'selenium-webdriver';
2
- import isEmpty from 'lodash.isempty';
3
2
  import { configureBuilder as configureBuilderChrome } from '../../chrome/webdriver/builder.js';
4
3
  import { configureBuilder as configureBuilderEdge } from '../../edge/webdriver/builder.js';
5
4
  import { configureBuilder as configureBuilderFirefox } from '../../firefox/webdriver/builder.js';
6
5
  import { configureBuilder as configureBuilderSafari } from '../../safari/webdriver/builder.js';
6
+ import { isEmpty } from '../../support/util.js';
7
7
 
8
8
  /**
9
9
  * Create a new WebDriver instance based on the specified options.
@@ -1,12 +1,11 @@
1
1
  import { Options, ServiceBuilder } from 'selenium-webdriver/edge.js';
2
- import intel from 'intel';
2
+ import { getLogger } from '@sitespeed.io/log';
3
3
  import get from 'lodash.get';
4
- import pick from 'lodash.pick';
5
- import isEmpty from 'lodash.isempty';
6
4
  import { logging } from 'selenium-webdriver';
7
5
  import { setupChromiumOptions } from '../../chrome/webdriver/setupChromiumOptions.js';
8
- const log = intel.getLogger('browsertime.edge');
6
+ const log = getLogger('browsertime.edge');
9
7
  const { pac, manual } = 'selenium-webdriver/proxy.js';
8
+ import { pick, isEmpty } from '../../support/util.js';
10
9
 
11
10
  export async function configureBuilder(builder, baseDir, options) {
12
11
  const edgeConfig = options.edge || {};
@@ -1,7 +1,7 @@
1
1
  import { Bidi } from '../core/engine/command/bidi.js';
2
- import intel from 'intel';
2
+ import { getLogger } from '@sitespeed.io/log';
3
3
  import { toArray } from '../support/util.js';
4
- const log = intel.getLogger('browsertime.firefox.bidi');
4
+ const log = getLogger('browsertime.firefox.bidi');
5
5
 
6
6
  export class FirefoxBidi {
7
7
  constructor(bidi, browsingContextId, driver, options) {
@@ -1,12 +1,12 @@
1
1
  import path from 'node:path';
2
2
  import get from 'lodash.get';
3
- import intel from 'intel';
3
+ import { getLogger } from '@sitespeed.io/log';
4
4
  import { geckoProfilerDefaults } from './settings/geckoProfilerDefaults.js';
5
5
  import { isAndroidConfigured, Android } from '../android/index.js';
6
6
  import { pathToFolder } from '../support/pathToFolder.js';
7
7
  import { BrowserError } from '../support/errors.js';
8
8
  const delay = ms => new Promise(res => setTimeout(res, ms));
9
- const log = intel.getLogger('browsertime.firefox');
9
+ const log = getLogger('browsertime.firefox');
10
10
 
11
11
  // Return power usage in Wh for a single counter.
12
12
  function computeCounterPowerSum(counter) {
@@ -1,10 +1,10 @@
1
1
  import path from 'node:path';
2
- import intel from 'intel';
2
+ import { getLogger } from '@sitespeed.io/log';
3
3
  import { pathToFolder } from '../support/pathToFolder.js';
4
4
  import { BrowserError } from '../support/errors.js';
5
5
  import { isAndroidConfigured, Android } from '../android/index.js';
6
6
  const delay = ms => new Promise(res => setTimeout(res, ms));
7
- const log = intel.getLogger('browsertime.firefox');
7
+ const log = getLogger('browsertime.firefox');
8
8
 
9
9
  /**
10
10
  * Timeout a promise after ms. Use promise.race to compete
@@ -1,6 +1,6 @@
1
- import intel from 'intel';
1
+ import { getLogger } from '@sitespeed.io/log';
2
2
  import get from 'lodash.get';
3
- const log = intel.getLogger('browsertime.chrome.network');
3
+ const log = getLogger('browsertime.chrome.network');
4
4
 
5
5
  export class NetworkManager {
6
6
  constructor(bidi, browsingContextIds, options) {
@@ -1,5 +1,5 @@
1
- import intel from 'intel';
2
- const log = intel.getLogger('browsertime.firefox');
1
+ import { getLogger } from '@sitespeed.io/log';
2
+ const log = getLogger('browsertime.firefox');
3
3
 
4
4
  export class PerfStats {
5
5
  constructor(runner, firefoxConfig) {
@@ -4,17 +4,15 @@ import {
4
4
  Options,
5
5
  Channel
6
6
  } from 'selenium-webdriver/firefox.js';
7
- import intel from 'intel';
7
+ import { getLogger } from '@sitespeed.io/log';
8
8
  import { pac, manual } from 'selenium-webdriver/proxy.js';
9
- import pick from 'lodash.pick';
10
- import isEmpty from 'lodash.isempty';
11
9
  import get from 'lodash.get';
12
10
  import { defaultFirefoxPreferences } from '../settings/firefoxPreferences.js';
13
11
  import { disableSafeBrowsingPreferences } from '../settings/disableSafeBrowsingPreferences.js';
14
12
  import { disableTrackingProtectionPreferences } from '../settings/disableTrackingProtectionPreferences.js';
15
- import { toArray } from '../../support/util.js';
13
+ import { toArray, pick, isEmpty } from '../../support/util.js';
16
14
  import { Android, isAndroidConfigured } from '../../android/index.js';
17
- const log = intel.getLogger('browsertime.firefox');
15
+ const log = getLogger('browsertime.firefox');
18
16
 
19
17
  export async function configureBuilder(builder, baseDir, options) {
20
18
  // Here we configure everything that we need to start Firefox
@@ -1,7 +1,7 @@
1
1
  import { rename as _rename } from 'node:fs';
2
2
  import { promisify } from 'node:util';
3
3
  import path from 'node:path';
4
- import intel from 'intel';
4
+ import { getLogger } from '@sitespeed.io/log';
5
5
  import get from 'lodash.get';
6
6
  import usbPowerProfiler from 'usb-power-profiling/usb-power-profiling.js';
7
7
  import { adapters } from 'ff-test-bidi-har-export';
@@ -16,7 +16,7 @@ import { MemoryReport } from '../memoryReport.js';
16
16
  import { PerfStats } from '../perfStats.js';
17
17
  import { NetworkManager } from '../networkManager.js';
18
18
 
19
- const log = intel.getLogger('browsertime.firefox');
19
+ const log = getLogger('browsertime.firefox');
20
20
  const rename = promisify(_rename);
21
21
  export class Firefox {
22
22
  constructor(storageManager, options) {
@@ -1,14 +1,17 @@
1
1
  import path from 'node:path';
2
2
  import merge from 'lodash.merge';
3
- import intel from 'intel';
3
+ import { getLogger } from '@sitespeed.io/log';
4
4
  import { screenshotDefaults } from './defaults.js';
5
5
  import {
6
6
  savePngWithoutResize,
7
7
  savePng,
8
8
  saveJpg
9
9
  } from '../support/images/index.js';
10
+ import { loadCustomJimp } from './loadCustomJimp.js';
11
+
10
12
  const SCREENSHOT_DIR = 'screenshots';
11
- const log = intel.getLogger('browsertime.screenshot');
13
+ const log = getLogger('browsertime.screenshot');
14
+
12
15
  export class ScreenshotManager {
13
16
  constructor(storageManager, options) {
14
17
  this.storageManager = storageManager;
@@ -18,17 +21,12 @@ export class ScreenshotManager {
18
21
  }
19
22
 
20
23
  async save(name, data, url, index) {
21
- let jimp;
22
- try {
23
- jimp = await import('jimp');
24
- } catch {
25
- jimp = undefined;
26
- }
24
+ const jimp = await loadCustomJimp();
27
25
 
28
26
  if (!jimp) {
29
27
  if (this.config.type === 'jpg') {
30
28
  log.info(
31
- 'Missing sharp dependency so you can only save images as png at viewport size'
29
+ 'Missing jimp dependency so you can only save images as png at viewport size'
32
30
  );
33
31
  }
34
32
  const pathAndName = await savePngWithoutResize(
@@ -0,0 +1,17 @@
1
+ export async function loadCustomJimp() {
2
+ try {
3
+ const { default: configure } = await import('@jimp/custom');
4
+ const { default: pluginPng } = await import('@jimp/png');
5
+ const { default: pluginJpeg } = await import('@jimp/jpeg');
6
+ const { default: pluginScale } = await import('@jimp/plugin-scale');
7
+ // The scale plugin use resize
8
+ const { default: pluginResize } = await import('@jimp/plugin-resize');
9
+ const jimp = configure({
10
+ types: [pluginPng, pluginJpeg],
11
+ plugins: [pluginResize, pluginScale]
12
+ });
13
+ return jimp;
14
+ } catch {
15
+ return;
16
+ }
17
+ }
@@ -3,7 +3,7 @@ import path from 'node:path';
3
3
  import { promisify } from 'node:util';
4
4
  import { createRequire } from 'node:module';
5
5
  import { fileURLToPath } from 'node:url';
6
- import intel from 'intel';
6
+ import { getLogger } from '@sitespeed.io/log';
7
7
  import { onlyFiles, onlyWithExtension, onlyDirectories } from './filters.js';
8
8
  const require = createRequire(import.meta.url);
9
9
  const readdir = promisify(fs.readdir);
@@ -11,7 +11,7 @@ const readFile = promisify(fs.readFile);
11
11
  const stat = promisify(fs.stat);
12
12
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
13
13
  const rootFolder = path.resolve(__dirname, '..', '..', 'browserscripts');
14
- const log = intel.getLogger('browsertime');
14
+ const log = getLogger('browsertime');
15
15
 
16
16
  function toFullPath(filename, root) {
17
17
  return path.join(root, filename);
@@ -93,9 +93,8 @@ function generateScriptObject(name, path, contents) {
93
93
  // Use this as a signal to fall back to an old-style script, but don't
94
94
  // perform any action
95
95
  }
96
- if (log.isEnabledFor(log.TRACE)) {
97
- log.verbose(name + ' is an old-style script object.');
98
- }
96
+ log.verbose(name + ' is an old-style script object.');
97
+
99
98
  return {
100
99
  name: name,
101
100
  content: contents,
@@ -2,10 +2,8 @@ import { format } from 'node:util';
2
2
  import { readFileSync, statSync } from 'node:fs';
3
3
  import yargs from 'yargs';
4
4
  import { hideBin } from 'yargs/helpers';
5
- import { findUpSync } from 'find-up';
6
5
  import get from 'lodash.get';
7
6
  import set from 'lodash.set';
8
- import hasbin from 'hasbin';
9
7
  import {
10
8
  framerate,
11
9
  crf,
@@ -15,6 +13,8 @@ import {
15
13
  } from '../video/defaults.js';
16
14
  import { screenshotDefaults } from '../screenshot/defaults.js';
17
15
  import { geckoProfilerDefaults } from '../firefox/settings/geckoProfilerDefaults.js';
16
+ import { findUpSync } from './fileUtil.js';
17
+ import { execaSync } from 'execa';
18
18
 
19
19
  const configPath = findUpSync(['.browsertime.json']);
20
20
 
@@ -33,6 +33,16 @@ try {
33
33
  throw error;
34
34
  }
35
35
 
36
+ function hasbin(bin) {
37
+ try {
38
+ const cmd = process.platform === 'win32' ? 'where' : 'which';
39
+ execaSync(cmd, bin);
40
+ return true;
41
+ } catch {
42
+ return false;
43
+ }
44
+ }
45
+
36
46
  function validateInput(argv) {
37
47
  // Check NodeJS major version
38
48
  const fullVersion = process.versions.node;
@@ -70,7 +80,7 @@ function validateInput(argv) {
70
80
  }
71
81
  }
72
82
 
73
- if ((argv.video || argv.visualMetrics) && !hasbin.sync(['ffmpeg'])) {
83
+ if ((argv.video || argv.visualMetrics) && !hasbin(['ffmpeg'])) {
74
84
  return 'You need to have ffmpeg in your path to be able to record a video.';
75
85
  }
76
86
 
@@ -90,11 +100,11 @@ function validateInput(argv) {
90
100
  return 'There is no benefit running debug mode inside a Docker container.';
91
101
  }
92
102
 
93
- if (argv.tcpdump && !hasbin.sync(['tcpdump'])) {
103
+ if (argv.tcpdump && !hasbin(['tcpdump'])) {
94
104
  return 'You need to have tcpdump in your path to be able to record a tcpdump.';
95
105
  }
96
106
 
97
- if (argv.gnirehtet && !hasbin.sync(['gnirehtet'])) {
107
+ if (argv.gnirehtet && !hasbin(['gnirehtet'])) {
98
108
  return 'You need to have gnirehtet in your path to be able to use it. If you run on a Mac you can install it using Homebrew: brew install gnirehtet';
99
109
  }
100
110
 
@@ -1255,6 +1265,10 @@ export function parseCommandLine() {
1255
1265
  describe:
1256
1266
  'All userTimings are captured by default this option takes a regex that will allow which userTimings to capture in the results.'
1257
1267
  })
1268
+ .option('userTimingBlockList', {
1269
+ describe:
1270
+ 'All userTimings are captured by default this option takes a regex that will block some usertimings in the results.'
1271
+ })
1258
1272
  .option('headless', {
1259
1273
  type: 'boolean',
1260
1274
  default: false,
@@ -1,7 +1,7 @@
1
1
  import { execa } from 'execa';
2
- import intel from 'intel';
2
+ import { getLogger } from '@sitespeed.io/log';
3
3
  import { isAndroidConfigured } from '../android/index.js';
4
- const log = intel.getLogger('browsertime.dns');
4
+ const log = getLogger('browsertime.dns');
5
5
 
6
6
  export async function flushDNS(options) {
7
7
  if (isAndroidConfigured(options)) {
@@ -6,10 +6,9 @@ import {
6
6
  readFile as _readFile
7
7
  } from 'node:fs';
8
8
  import { pathToFileURL } from 'node:url';
9
- import dayjs from 'dayjs';
10
- import intel from 'intel';
11
- import { toArray } from '../support/util.js';
12
- const log = intel.getLogger('browsertime');
9
+ import { getLogger } from '@sitespeed.io/log';
10
+ import { toArray, localTime } from '../support/util.js';
11
+ const log = getLogger('browsertime');
13
12
  const writeFile = promisify(_writeFile);
14
13
  const readFile = promisify(_readFile);
15
14
  const unlink = promisify(_unlink);
@@ -104,5 +103,5 @@ export async function loadScript(script, options, throwError) {
104
103
  return script;
105
104
  }
106
105
  export function timestamp() {
107
- return dayjs().format();
106
+ return localTime();
108
107
  }
@@ -9,9 +9,10 @@ import {
9
9
  rename as _rename,
10
10
  readFile as _readFile,
11
11
  copyFileSync as _copyFileSync,
12
- unlinkSync as _unlinkSync
12
+ unlinkSync as _unlinkSync,
13
+ existsSync
13
14
  } from 'node:fs';
14
- import intel from 'intel';
15
+ import { getLogger } from '@sitespeed.io/log';
15
16
  import { onlyFiles, onlyWithExtension } from './filters.js';
16
17
  const readdir = promisify(_readdir);
17
18
  const lstat = promisify(_lstat);
@@ -20,7 +21,7 @@ const rmdir = promisify(_rmdir);
20
21
  const copyFile2 = promisify(_copyFile);
21
22
  const rename2 = promisify(_rename);
22
23
  const readFile2 = promisify(_readFile);
23
- const log = intel.getLogger('browsertime');
24
+ const log = getLogger('browsertime');
24
25
 
25
26
  export async function rename(old, newName) {
26
27
  return rename2(old, newName);
@@ -83,3 +84,19 @@ export async function findFiles(dir, filter) {
83
84
  });
84
85
  return filePaths;
85
86
  }
87
+
88
+ export function findUpSync(filenames, startDir = process.cwd()) {
89
+ let currentDir = startDir;
90
+
91
+ while (currentDir !== path.dirname(currentDir)) {
92
+ for (const filename of filenames) {
93
+ const filePath = path.resolve(currentDir, filename);
94
+ if (existsSync(filePath)) {
95
+ return filePath;
96
+ }
97
+ }
98
+ currentDir = path.dirname(currentDir);
99
+ }
100
+
101
+ return;
102
+ }
@@ -0,0 +1,25 @@
1
+ import { createServer } from 'node:net';
2
+
3
+ export async function getAvailablePort(portRange, host = '127.0.0.1') {
4
+ const startPort = portRange[0];
5
+ const endPort = portRange[1];
6
+
7
+ for (let port = startPort; port <= endPort; port++) {
8
+ if (await isPortAvailable(port, host)) {
9
+ return port;
10
+ }
11
+ }
12
+
13
+ throw new Error(`No available ports found in range ${startPort}-${endPort}`);
14
+ }
15
+
16
+ function isPortAvailable(port, host) {
17
+ return new Promise(resolve => {
18
+ const server = createServer();
19
+ server.unref();
20
+ server.on('error', () => resolve(false));
21
+ server.listen(port, host, () => {
22
+ server.close(() => resolve(true));
23
+ });
24
+ });
25
+ }
@@ -1,7 +1,7 @@
1
1
  import get from 'lodash.get';
2
- import intel from 'intel';
2
+ import { getLogger } from '@sitespeed.io/log';
3
3
  import { isAndroidConfigured } from '../android/index.js';
4
- const log = intel.getLogger('browsertime');
4
+ const log = getLogger('browsertime');
5
5
 
6
6
  // Most use screen size as of 2018
7
7
  // https://www.w3schools.com/browsers/browsers_display.asp
@@ -1,14 +1,13 @@
1
1
  import { createRequire } from 'node:module';
2
2
  import merge from 'lodash.merge';
3
- import pick from 'lodash.pick';
4
- import isEmpty from 'lodash.isempty';
5
3
  import get from 'lodash.get';
6
- import dayjs from 'dayjs';
7
4
  import { pathToFolder } from '../pathToFolder.js';
8
- import intel from 'intel';
9
- const log = intel.getLogger('browsertime');
5
+ import { localTime } from '../util.js';
6
+ import { getLogger } from '@sitespeed.io/log';
7
+ const log = getLogger('browsertime');
10
8
  const require = createRequire(import.meta.url);
11
9
  const version = require('../../../package.json').version;
10
+ import { pick, isEmpty } from '../../support/util.js';
12
11
 
13
12
  function generateUniquePageId(baseId, existingIdMap) {
14
13
  let newId = baseId;
@@ -288,7 +287,7 @@ export function getEmptyHAR(url, browser) {
288
287
  },
289
288
  pages: [
290
289
  {
291
- startedDateTime: dayjs().format(),
290
+ startedDateTime: localTime(),
292
291
  id: 'failing_page',
293
292
  title: url,
294
293
  pageTimings: {},
@@ -1,6 +1,6 @@
1
1
  import path from 'node:path';
2
2
  import { pathToFolder } from '../pathToFolder.js';
3
-
3
+ import { loadCustomJimp } from '../../screenshot/loadCustomJimp.js';
4
4
  export async function savePngWithoutResize(
5
5
  name,
6
6
  data,
@@ -24,18 +24,13 @@ export async function savePng(
24
24
  dir,
25
25
  options
26
26
  ) {
27
- let jimp;
28
- try {
29
- jimp = await import('jimp');
30
- } catch {
31
- jimp = undefined;
32
- }
27
+ const jimp = await loadCustomJimp();
33
28
 
34
29
  if (jimp) {
35
- const image = await jimp.default.read(data);
30
+ const image = await jimp.read(data);
36
31
  const buffer = await image
37
32
  .deflateLevel(config.png.compressionLevel)
38
- .scaleToFit(config.maxSize, config.maxSize, jimp.default.RESIZE_HERMITE)
33
+ .scaleToFit(config.maxSize, config.maxSize, jimp.RESIZE_HERMITE)
39
34
  .getBufferAsync('image/png');
40
35
 
41
36
  return storageManager.writeData(
@@ -54,18 +49,14 @@ export async function saveJpg(
54
49
  dir,
55
50
  options
56
51
  ) {
57
- let jimp;
58
- try {
59
- jimp = await import('jimp');
60
- } catch {
61
- jimp = undefined;
62
- }
52
+ const jimp = await loadCustomJimp();
53
+
63
54
  if (jimp) {
64
- const image = await jimp.default.read(data);
55
+ const image = await jimp.read(data);
65
56
  // https://github.com/sitespeedio/sitespeed.io/issues/3922
66
57
  const buffer = await image
67
58
  .quality(config.jpg.quality)
68
- .scaleToFit(config.maxSize, config.maxSize, jimp.default.RESIZE_HERMITE)
59
+ .scaleToFit(config.maxSize, config.maxSize, jimp.RESIZE_HERMITE)
69
60
  .getBufferAsync('image/jpeg');
70
61
  return storageManager.writeData(
71
62
  `${name}.jpg`,
@@ -1,41 +1,16 @@
1
- import intel from 'intel';
2
- const { INFO, DEBUG, VERBOSE, TRACE, NONE, basicConfig } = intel;
1
+ // configure.js
2
+ import { configureLog } from '@sitespeed.io/log';
3
3
 
4
- export function configure(options) {
5
- options = options || {};
6
-
7
- let level = INFO;
8
- switch (options.verbose) {
9
- case 1: {
10
- level = DEBUG;
11
- break;
12
- }
13
- case 2: {
14
- level = VERBOSE;
15
- break;
16
- }
17
- case 3: {
18
- level = TRACE;
19
- break;
20
- }
21
- default: {
22
- break;
23
- }
24
- }
25
-
26
- if (options.silent) {
27
- level = NONE;
28
- }
29
-
30
- if (level === INFO) {
31
- basicConfig({
32
- format: '[%(date)s] %(levelname)s: %(message)s',
33
- level: level
34
- });
35
- } else {
36
- basicConfig({
37
- format: '[%(date)s] %(levelname)s: [%(name)s] %(message)s',
38
- level: level
39
- });
40
- }
4
+ /**
5
+ * Adapted from old intel-based configure logic.
6
+ *
7
+ * @param {Object} options
8
+ * @param {number} [options.verbose=0] - 0: Info, 1: Debug, 2: Verbose, 3: Trace
9
+ * @param {boolean} [options.silent=false] - If true, disables logging
10
+ */
11
+ export function configure(options = {}) {
12
+ configureLog({
13
+ verbose: options.verbose ?? 0,
14
+ silent: options.silent ?? false
15
+ });
41
16
  }
@@ -1,8 +1,8 @@
1
1
  import { parse } from 'node:url';
2
2
  import { createHash } from 'node:crypto';
3
- import isEmpty from 'lodash.isempty';
4
- import intel from 'intel';
5
- const log = intel.getLogger('browsertime');
3
+ import { getLogger } from '@sitespeed.io/log';
4
+ import { isEmpty } from './util.js';
5
+ const log = getLogger('browsertime');
6
6
 
7
7
  function toSafeKey(key) {
8
8
  // U+2013 : EN DASH – as used on https://en.wikipedia.org/wiki/2019–20_coronavirus_pandemic
@@ -1,5 +1,5 @@
1
- import intel from 'intel';
2
- const log = intel.getLogger('browsertime');
1
+ import { getLogger } from '@sitespeed.io/log';
2
+ const log = getLogger('browsertime');
3
3
  const delay = ms => new Promise(res => setTimeout(res, ms));
4
4
 
5
5
  export async function preURL(browser, options) {
@@ -1,8 +1,8 @@
1
1
  import get from 'lodash.get';
2
2
  import set from 'lodash.set';
3
3
  import { Stats } from 'fast-stats';
4
- import intel from 'intel';
5
- const log = intel.getLogger('browsertime');
4
+ import { getLogger } from '@sitespeed.io/log';
5
+ const log = getLogger('browsertime');
6
6
 
7
7
  function validateType(value, type, message) {
8
8
  if (typeof value !== type) {
@@ -1,6 +1,6 @@
1
1
  import { execa } from 'execa';
2
- import intel from 'intel';
3
- const log = intel.getLogger('browsertime');
2
+ import { getLogger } from '@sitespeed.io/log';
3
+ const log = getLogger('browsertime');
4
4
 
5
5
  export async function stop(processName) {
6
6
  const scriptArguments = ['-9', processName];
@@ -11,19 +11,19 @@ import {
11
11
  createReadStream,
12
12
  createWriteStream
13
13
  } from 'node:fs';
14
- import isEmpty from 'lodash.isempty';
15
- import intel from 'intel';
16
- import dayjs from 'dayjs';
14
+ import { getLogger } from '@sitespeed.io/log';
15
+ import { localTime, isEmpty } from './util.js';
16
+
17
17
  const gunzip = promisify(_gunzip);
18
18
  const writeFile = promisify(_writeFile);
19
19
  const readFile = promisify(_readFile);
20
20
  const gzip = promisify(_gzip);
21
21
  const unlink = promisify(_unlink);
22
22
  const mkdir = promisify(_mkdir);
23
- const log = intel.getLogger('browsertime');
23
+ const log = getLogger('browsertime');
24
24
 
25
25
  const defaultDir = 'browsertime-results';
26
- let timestamp = dayjs().format().replaceAll(':', '');
26
+ let timestamp = localTime().replaceAll(':', '');
27
27
 
28
28
  function pathNameFromUrl(url) {
29
29
  const parsedUrl = parse(url),