browsertime 17.0.0-beta.3 → 17.0.0-beta.4

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/CHANGELOG.md CHANGED
@@ -3,10 +3,36 @@
3
3
  ## 17.0.0 - UNRELEASED
4
4
 
5
5
  ### Breaking changes
6
- * We moved the project to be a [pure ESM package](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c) [#1859](https://github.com/sitespeedio/browsertime/pull/1859). That helps us keep up to date with dependencies that already did the move.
6
+ * We moved the project to be a [pure ESM package](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c) [#1859](https://github.com/sitespeedio/browsertime/pull/1859). That helps us keep up to date with dependencies that already did the move and make sure we can always use the latest version of our dependencies.
7
7
 
8
- If you are a command line user, everything will work as before. If you import Browsertime in NodeJS
9
- we changed how you do your import.
8
+ #### CLI users
9
+ If you are a command line user and use scripting, you will need to do a change to your scripts or add a extra configuration.
10
+ The new browsertime version treat all JavaScript files that ends with *.js* as ESM modules, that means your old script files will not work out of the box. There's a couple of fixes for that:
11
+
12
+ **The best fix**:
13
+ Change your code so your scripts also follows ESM package. If you have simple scripts you probably just need to change your exports. The old way looked like this:
14
+
15
+ ```
16
+ module.exports = async function(context, commands) {
17
+ ...
18
+ }
19
+ ```
20
+
21
+ change that to:
22
+ ```
23
+ export default async function (context, commands) {
24
+ ...
25
+ }
26
+ ```
27
+
28
+ If you have more complicated scripts, follow the [ESM package guide](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c).
29
+
30
+ **The quick fix**: Rename your *.js* to *.cjs* that way NodeJS will treat your file as a common JS file and everthing will just work. For example if you have a file names `login.js` you can rename that to `login.cjs` and make sure you load that new file.
31
+
32
+ **Another quick fix alternative**: Add `--cjs` as a parameter to your test. That way the scripting file will be treated as a commonjs file. This is a hack, so to make sure it works, the user that runs Browsertime need to have write privileges to the folder where you have your scripting files.
33
+
34
+ #### Non cli users
35
+ If you import Browsertime in NodeJS we changed how you do your import.
10
36
 
11
37
  If you use ES modules you can import Browsertime like this:
12
38
  ```JavaScript
@@ -154,6 +154,7 @@ async function run(urls, options) {
154
154
  }
155
155
 
156
156
  let cliResult = parseCommandLine();
157
+ logging(cliResult.options);
157
158
 
158
159
  /*
159
160
  Each url can be:
@@ -169,8 +170,7 @@ const tests = [];
169
170
  for (const url of cliResult.urls) {
170
171
  // for each url, we try to load it as a script, that may contain
171
172
  // export a single function or an object containing setUp/test/tearDown functions.
172
- let testScript = await loadScript(url);
173
-
173
+ let testScript = await loadScript(url, cliResult.options);
174
174
  // if the value is an url or a not a single function, we can return the original value
175
175
  if (typeof testScript == 'string' || testScript instanceof AsyncFunction) {
176
176
  tests.push(url);
@@ -192,7 +192,4 @@ for (const url of cliResult.urls) {
192
192
  // here, url is the filename containing the script, and test the callable.
193
193
  tests.push([url, testScript.test]);
194
194
  }
195
-
196
- logging(cliResult.options);
197
-
198
195
  await run(tests, cliResult.options);
@@ -25,8 +25,8 @@ import Collector from './collector.js';
25
25
  import { Android, isAndroidConfigured } from '../../android/index.js';
26
26
  import RootedDevice from '../../android/root.js';
27
27
  import run from './run.js';
28
- import { loadScript } from '../../support/engineUtils.js';
29
28
  import IOSRecorder from '../../video/screenRecording/ios/iosRecorder.js';
29
+ import { loadPrePostScripts, loadScript } from '../../support/engineUtils.js';
30
30
  const log = intel.getLogger('browsertime');
31
31
  const defaults = {
32
32
  scripts: [],
@@ -252,11 +252,30 @@ export default class Engine {
252
252
  engineDelegate = new Safari(storageManager, options);
253
253
  }
254
254
  }
255
+
256
+ let preScripts, postScripts, postURLScripts, pageCompleteCheck;
257
+
258
+ try {
259
+ preScripts = await loadPrePostScripts(options.preScript, options);
260
+ postScripts = await loadPrePostScripts(options.postScript, options);
261
+ postURLScripts = await loadPrePostScripts(options.postURLScript, options);
262
+ pageCompleteCheck = options.pageCompleteCheck
263
+ ? await loadScript(options.pageCompleteCheck, options)
264
+ : undefined;
265
+ } catch (error) {
266
+ log.error(error.message);
267
+ throw error;
268
+ }
269
+
255
270
  const iteration = new Iteration(
256
271
  storageManager,
257
272
  engineDelegate,
258
273
  scriptsByCategory,
259
274
  asyncScriptsByCategory,
275
+ preScripts,
276
+ postScripts,
277
+ postURLScripts,
278
+ pageCompleteCheck,
260
279
  options
261
280
  );
262
281
 
@@ -425,8 +444,7 @@ export default class Engine {
425
444
  if (Array.isArray(urlOrFile)) {
426
445
  script = urlOrFile[1];
427
446
  }
428
-
429
- script = await loadScript(script, true);
447
+ script = await loadScript(script, options, true);
430
448
 
431
449
  if (script.setUp) {
432
450
  if (!options.preScript) {
@@ -7,10 +7,6 @@ import preURL from '../../support/preURL.js';
7
7
  import setResourceTimingBufferSize from '../../support/setResourceTimingBufferSize.js';
8
8
  import ScreenshotManager from '../../screenshot/index.js';
9
9
  import ExtensionServer from '../../extensionserver/index.js';
10
- import {
11
- loadPrePostScriptsSync,
12
- loadScriptSync
13
- } from '../../support/engineUtils.js';
14
10
  import Video from '../../video/video.js';
15
11
  import stop from '../../support/stop.js';
16
12
  import AddText from './command/addText.js';
@@ -61,19 +57,16 @@ export default class Iteration {
61
57
  engineDelegate,
62
58
  scriptsByCategory,
63
59
  asyncScriptsByCategory,
60
+ preScripts,
61
+ postScripts,
62
+ postURLScripts,
63
+ pageCompleteCheck,
64
64
  options
65
65
  ) {
66
- try {
67
- this.preScripts = loadPrePostScriptsSync(options.preScript);
68
- this.postScripts = loadPrePostScriptsSync(options.postScript);
69
- this.postURLScripts = loadPrePostScriptsSync(options.postURLScript);
70
- this.pageCompleteCheck = options.pageCompleteCheck
71
- ? loadScriptSync(options.pageCompleteCheck)
72
- : undefined;
73
- } catch (error) {
74
- log.error(error.message);
75
- throw error;
76
- }
66
+ this.preScripts = preScripts;
67
+ this.postScripts = postScripts;
68
+ this.postURLScripts = postURLScripts;
69
+ this.pageCompleteCheck = pageCompleteCheck;
77
70
  this.options = options;
78
71
  this.storageManager = storageManager;
79
72
  this.engineDelegate = engineDelegate;
@@ -1194,6 +1194,12 @@ export function parseCommandLine() {
1194
1194
  type: 'boolean',
1195
1195
  default: false
1196
1196
  })
1197
+ .option('cjs', {
1198
+ describe:
1199
+ 'Load scripting files that ends with .js as common js. Default (false) loads files as esmodules.',
1200
+ type: 'boolean',
1201
+ default: false
1202
+ })
1197
1203
  .option('browserRestartTries', {
1198
1204
  type: 'number',
1199
1205
  default: 3,
@@ -1,83 +1,70 @@
1
- import { resolve, join, sep } from 'node:path';
2
- import os from 'node:os';
3
- import { createRequire } from 'node:module';
1
+ import { resolve, dirname, join } from 'node:path';
2
+ import { promisify } from 'node:util';
3
+ import { writeFile as _writeFile, access, unlink as _unlink } from 'node:fs';
4
+ import { pathToFileURL } from 'node:url';
4
5
  import dayjs from 'dayjs';
5
6
  import intel from 'intel';
6
7
  import { toArray } from '../support/util.js';
7
- import { copyFile, copyFileSync } from './fileUtil.js';
8
- import { realpathSync, mkdtempSync, rmdirSync } from 'node:fs';
9
- const require = createRequire(import.meta.url);
10
8
  const log = intel.getLogger('browsertime');
9
+ const writeFile = promisify(_writeFile);
10
+ const unlink = promisify(_unlink);
11
11
 
12
12
  const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor;
13
13
 
14
- function getTmp() {
15
- return mkdtempSync(realpathSync(os.tmpdir()) + sep);
16
- }
17
-
18
- export function loadPrePostScriptsSync(scripts) {
19
- return toArray(scripts).map(script => {
20
- // if the script is an AsyncFunction, return it.
21
- if (script instanceof AsyncFunction) {
22
- return script;
23
- }
24
- try {
25
- if (script.endsWith('.js')) {
26
- const dir = getTmp();
27
- const name = join(dir, 'tmp.cjs');
28
- copyFileSync(script, name);
29
- const data = require(resolve(name));
30
- rmdirSync(dir, { recursive: true });
31
- return data;
32
- } else {
33
- return require(resolve(script));
34
- }
35
- } catch (error) {
36
- throw new Error(
37
- "Couldn't run pre/post script file: " + resolve(script) + ' ' + error
38
- );
39
- }
40
- });
14
+ async function exists(path) {
15
+ try {
16
+ await access(path);
17
+ return true;
18
+ } catch {
19
+ return false;
20
+ }
41
21
  }
42
22
 
43
- export function loadScriptSync(script) {
23
+ async function loadFile(script, options, throwError) {
24
+ // if the script is an AsyncFunction, return it.
44
25
  if (script instanceof AsyncFunction) {
45
26
  return script;
46
- }
47
- try {
48
- if (script.endsWith('.js')) {
49
- const dir = getTmp();
50
- const name = join(dir, 'tmp.cjs');
51
- copyFileSync(script, name);
52
- const data = require(resolve(name));
53
- rmdirSync(dir, { recursive: true });
54
- return data;
55
- } else {
56
- return require(resolve(script));
57
- }
58
- } catch {
59
- // assume the script is a valid inline script by default
27
+ } else if (!script.endsWith('js')) {
28
+ // This is an inline script
60
29
  return script;
61
30
  }
62
- }
63
-
64
- export async function loadScript(script, throwError) {
65
- if (script) {
66
- // if the script is an AsyncFunction, return it.
67
- if (script instanceof AsyncFunction) {
68
- return script;
69
- }
31
+ // If we follow correct naming for modules (.mjs) or commonjs (.cjs)
32
+ // we just loads it. .js files is loaded as commonjs if you set
33
+ // --cjs true
34
+ if (
35
+ options.cjs === false ||
36
+ script.endsWith('.mjs') ||
37
+ script.endsWith('.cjs')
38
+ ) {
39
+ let myFunction = await import(pathToFileURL(resolve(script)));
40
+ return myFunction.default ?? myFunction;
41
+ } else {
42
+ // Hack a way! Try to add a package.json file in the same folder as the
43
+ // script file. That way, the .js file will be treated as commonjs =
44
+ // working as before we moved Browsertime to module
45
+ let createdPackageJson = false;
70
46
  try {
71
- if (script.endsWith('.js')) {
72
- const dir = getTmp();
73
- const name = join(dir, 'tmp.cjs');
74
- await copyFile(script, name);
75
- const data = require(resolve(name));
76
- rmdirSync(dir, { recursive: true });
77
- return data;
47
+ const packageJson = join(dirname(resolve(script)), 'package.json');
48
+ if (!(await exists(packageJson))) {
49
+ await writeFile(packageJson, '{}');
50
+ createdPackageJson = true;
78
51
  } else {
79
- return require(resolve(script));
52
+ log.error(
53
+ 'Could not create package.json file, the %S file will be treated as a esmodule',
54
+ script
55
+ );
56
+ }
57
+ const myFunction = await import(resolve(script));
58
+
59
+ try {
60
+ if (createdPackageJson) {
61
+ await unlink(packageJson);
62
+ }
63
+ } catch (error) {
64
+ log.error(error);
80
65
  }
66
+
67
+ return myFunction.default ?? myFunction;
81
68
  } catch (error) {
82
69
  // assume the script is a valid inline script by default
83
70
  if (throwError) {
@@ -90,6 +77,21 @@ export async function loadScript(script, throwError) {
90
77
  }
91
78
  }
92
79
  }
80
+ }
81
+
82
+ export async function loadPrePostScripts(scripts, options) {
83
+ const readScripts = [];
84
+ for (let script of toArray(scripts)) {
85
+ const loadedScript = await loadFile(script, options, true);
86
+ readScripts.push(loadedScript);
87
+ }
88
+ return readScripts;
89
+ }
90
+
91
+ export async function loadScript(script, options, throwError) {
92
+ if (script) {
93
+ return loadFile(script, options, throwError);
94
+ }
93
95
  return script;
94
96
  }
95
97
  export function timestamp() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "description": "Browsertime",
3
- "version": "17.0.0-beta.3",
3
+ "version": "17.0.0-beta.4",
4
4
  "bin": "./bin/browsertime.js",
5
5
  "type": "module",
6
6
  "dependencies": {