@memlab/e2e 1.0.13 → 1.0.16

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 (104) hide show
  1. package/README.md +2 -2
  2. package/dist/BaseSynthesizer.d.ts +1 -1
  3. package/dist/E2EInteractionManager.d.ts +2 -1
  4. package/dist/E2EInteractionManager.js +17 -7
  5. package/dist/NetworkManager.d.ts +21 -0
  6. package/dist/NetworkManager.js +110 -0
  7. package/dist/ScriptManager.d.ts +24 -0
  8. package/dist/ScriptManager.js +104 -0
  9. package/dist/TestRunnerLoader.d.ts +1 -1
  10. package/dist/TestRunnerLoader.js +1 -1
  11. package/dist/TypesThirdParty.d.ts +1 -1
  12. package/dist/TypesThirdParty.js +1 -1
  13. package/dist/index.d.ts +1 -1
  14. package/dist/index.js +1 -1
  15. package/dist/instrumentation/BaseAstTransform.d.ts +16 -0
  16. package/dist/instrumentation/BaseAstTransform.js +31 -0
  17. package/dist/instrumentation/ScriptRewriteManager.d.ts +17 -0
  18. package/dist/instrumentation/ScriptRewriteManager.js +45 -0
  19. package/dist/instrumentation/TransformLoader.d.ts +14 -0
  20. package/dist/instrumentation/TransformLoader.js +21 -0
  21. package/dist/instrumentation/transforms/InjectSourceInfoTranform.d.ts +17 -0
  22. package/dist/instrumentation/transforms/InjectSourceInfoTranform.js +62 -0
  23. package/dist/instrumentation/transforms/InjectSourceInfoTransform.d.ts +17 -0
  24. package/dist/instrumentation/transforms/InjectSourceInfoTransform.js +63 -0
  25. package/dist/lib/E2EUtils.d.ts +1 -1
  26. package/dist/lib/E2EUtils.js +1 -1
  27. package/dist/lib/SynthesisUtils.d.ts +1 -1
  28. package/dist/lib/SynthesisUtils.js +1 -1
  29. package/dist/lib/operations/BackOperation.d.ts +1 -1
  30. package/dist/lib/operations/BackOperation.js +1 -1
  31. package/dist/lib/operations/BackspaceOperation.d.ts +1 -1
  32. package/dist/lib/operations/BackspaceOperation.js +1 -1
  33. package/dist/lib/operations/BaseOperation.d.ts +1 -1
  34. package/dist/lib/operations/BaseOperation.js +1 -1
  35. package/dist/lib/operations/CachePageContent.d.ts +1 -1
  36. package/dist/lib/operations/CachePageContent.js +1 -1
  37. package/dist/lib/operations/ClickOperation.d.ts +1 -1
  38. package/dist/lib/operations/ClickOperation.js +1 -1
  39. package/dist/lib/operations/CompoundOperation.d.ts +1 -1
  40. package/dist/lib/operations/CompoundOperation.js +1 -1
  41. package/dist/lib/operations/EnterOperation.d.ts +1 -1
  42. package/dist/lib/operations/EnterOperation.js +1 -1
  43. package/dist/lib/operations/EscOperation.d.ts +1 -1
  44. package/dist/lib/operations/EscOperation.js +1 -1
  45. package/dist/lib/operations/HoverOperation.d.ts +1 -1
  46. package/dist/lib/operations/HoverOperation.js +1 -1
  47. package/dist/lib/operations/InteractionUtils.d.ts +1 -1
  48. package/dist/lib/operations/InteractionUtils.js +1 -1
  49. package/dist/lib/operations/Operations.d.ts +1 -1
  50. package/dist/lib/operations/Operations.js +1 -1
  51. package/dist/lib/operations/RevertOperation.d.ts +1 -1
  52. package/dist/lib/operations/RevertOperation.js +1 -1
  53. package/dist/lib/operations/RunJSCode.d.ts +1 -1
  54. package/dist/lib/operations/RunJSCode.js +1 -1
  55. package/dist/lib/operations/ScrollOperation.d.ts +1 -1
  56. package/dist/lib/operations/ScrollOperation.js +1 -1
  57. package/dist/lib/operations/TargetExtraOperation.d.ts +1 -1
  58. package/dist/lib/operations/TargetExtraOperation.js +1 -1
  59. package/dist/lib/operations/TestPlanner.d.ts +1 -1
  60. package/dist/lib/operations/TestPlanner.js +1 -1
  61. package/dist/lib/operations/TypeOperation.d.ts +1 -1
  62. package/dist/lib/operations/TypeOperation.js +1 -1
  63. package/dist/lib/operations/UploadOperation.d.ts +1 -1
  64. package/dist/lib/operations/UploadOperation.js +1 -1
  65. package/dist/lib/operations/WaitForElementOperation.d.ts +1 -1
  66. package/dist/lib/operations/WaitForElementOperation.js +1 -1
  67. package/dist/lib/operations/WaitOperation.d.ts +1 -1
  68. package/dist/lib/operations/WaitOperation.js +1 -1
  69. package/dist/lib/operations/WithCachedPageContent.d.ts +1 -1
  70. package/dist/lib/operations/WithCachedPageContent.js +1 -1
  71. package/dist/lib/operations/XVirtualFrameBuffer.d.ts +1 -1
  72. package/dist/lib/operations/XVirtualFrameBuffer.js +1 -1
  73. package/dist/plugins/DefaultScenarioSynthesizer.d.ts +1 -1
  74. package/dist/plugins/DefaultScenarioSynthesizer.js +1 -1
  75. package/dist/plugins/TestSPAVisitSynthesizer.d.ts +1 -1
  76. package/dist/plugins/TestSPAVisitSynthesizer.js +2 -2
  77. package/dist/plugins/scenarios/test-airbnb.d.ts +1 -1
  78. package/dist/plugins/scenarios/test-airbnb.js +1 -1
  79. package/dist/plugins/scenarios/test-databricks.d.ts +1 -1
  80. package/dist/plugins/scenarios/test-databricks.js +1 -1
  81. package/dist/plugins/scenarios/test-ebay.d.ts +1 -1
  82. package/dist/plugins/scenarios/test-ebay.js +1 -1
  83. package/dist/plugins/scenarios/test-google-maps.d.ts +1 -1
  84. package/dist/plugins/scenarios/test-google-maps.js +1 -1
  85. package/dist/plugins/scenarios/test-shopify.d.ts +1 -1
  86. package/dist/plugins/scenarios/test-shopify.js +1 -1
  87. package/dist/plugins/scenarios/test-youtube.d.ts +1 -1
  88. package/dist/plugins/scenarios/test-youtube.js +1 -1
  89. package/package.json +11 -5
  90. package/static/example/components/layout.jsx +1 -1
  91. package/static/example/leak-filters/filter-by-retained-size.js +7 -1
  92. package/static/example/next.config.js +4 -3
  93. package/static/example/pages/_app.js +1 -1
  94. package/static/example/pages/_document.js +1 -1
  95. package/static/example/pages/examples/detached-dom.jsx +9 -3
  96. package/static/example/pages/examples/oversized-object.jsx +8 -2
  97. package/static/example/pages/index.js +1 -1
  98. package/static/example/scenarios/detached-dom.js +7 -6
  99. package/static/example/scenarios/oversized-leak-filter.js +7 -2
  100. package/static/example/scenarios/oversized-object-with-filter.js +7 -2
  101. package/static/example/scenarios/oversized-object.js +4 -4
  102. package/static/links/min.js +1 -0
  103. package/static/links/src/App.js +1 -1
  104. package/static/links/src/index.js +1 -1
package/README.md CHANGED
@@ -4,5 +4,5 @@ This is the memlab E2E testing framework for headless/headful Chromium. It is ma
4
4
  for controling/interacting with browser, recording heap snapshots, and logging other execution meta data.
5
5
 
6
6
  ## Online Resources
7
- * [Official Website and Demo](https://facebookincubator.github.io/memlab)
8
- * [Documentation](https://facebookincubator.github.io/memlab/docs/intro)
7
+ * [Official Website and Demo](https://facebook.github.io/memlab)
8
+ * [Documentation](https://facebook.github.io/memlab/docs/intro)
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * @format
8
8
  * @lightSyntaxTransform
9
- * @oncall ws_labs
9
+ * @oncall web_perf_infra
10
10
  */
11
11
  import type { AnyOptions, Config, E2EOperation, E2EStepInfo, IE2EScenarioVisitPlan, E2ESynthesizerOptions, IScenario, IE2EStepBasic, IE2EScenarioSynthesizer, CheckPageLoadCallback, Nullable } from '@memlab/core';
12
12
  declare class BaseSynthesizer implements IE2EScenarioSynthesizer {
@@ -5,7 +5,7 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  *
7
7
  * @format
8
- * @oncall ws_labs
8
+ * @oncall web_perf_infra
9
9
  */
10
10
  import type { AnyFunction, OperationArgs, MemLabConfig } from '@memlab/core';
11
11
  import type { CDPSession, Page } from 'puppeteer';
@@ -19,6 +19,7 @@ export default class E2EInteractionManager {
19
19
  private page;
20
20
  private pageHistoryLength;
21
21
  private evalFuncAfterInitLoad;
22
+ private networkManager;
22
23
  constructor(page: Page);
23
24
  getCDPSession(): Promise<CDPSession>;
24
25
  clearCDPSession(): void;
@@ -6,7 +6,7 @@
6
6
  * LICENSE file in the root directory of this source tree.
7
7
  *
8
8
  * @format
9
- * @oncall ws_labs
9
+ * @oncall web_perf_infra
10
10
  */
11
11
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
12
12
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
@@ -27,17 +27,20 @@ const E2EUtils_1 = __importDefault(require("./lib/E2EUtils"));
27
27
  const core_1 = require("@memlab/core");
28
28
  const InteractionUtils_1 = __importDefault(require("./lib/operations/InteractionUtils"));
29
29
  const TestPlanner_1 = __importDefault(require("./lib/operations/TestPlanner"));
30
+ const NetworkManager_1 = __importDefault(require("./NetworkManager"));
30
31
  const { logMetaData, setPermissions, logTabProgress, maybeWaitForConsoleInput, applyAsyncWithRetry, compareURL, waitExtraForTab, checkURL, injectPageReloadChecker, checkPageReload, dispatchOperation, clearConsole, getNavigationHistoryLength, checkLastSnapshotChunk, getURLParameter, } = E2EUtils_1.default;
31
32
  class E2EInteractionManager {
32
33
  constructor(page) {
33
34
  this.pageHistoryLength = [];
34
35
  this.evalFuncAfterInitLoad = null;
35
36
  this.page = page;
37
+ this.networkManager = new NetworkManager_1.default(page);
36
38
  }
37
39
  getCDPSession() {
38
40
  return __awaiter(this, void 0, void 0, function* () {
39
41
  if (!this.cdpsession) {
40
42
  this.cdpsession = yield this.page.target().createCDPSession();
43
+ this.networkManager.setCDPSession(this.cdpsession);
41
44
  }
42
45
  return this.cdpsession;
43
46
  });
@@ -70,6 +73,9 @@ class E2EInteractionManager {
70
73
  }
71
74
  beforeInteractions() {
72
75
  return __awaiter(this, void 0, void 0, function* () {
76
+ const session = yield this.getCDPSession();
77
+ this.networkManager.setCDPSession(session);
78
+ // await this.networkManager.interceptScript();
73
79
  if (core_1.config.verbose) {
74
80
  const browserVersion = yield this.page.browser().version();
75
81
  core_1.info.lowLevel(`Browser version: ${browserVersion}`);
@@ -98,7 +104,7 @@ class E2EInteractionManager {
98
104
  core_1.info.lowLevel(new Date().toString());
99
105
  }
100
106
  const tab = visitPlan.tabsOrder[i];
101
- const subUrl = tab.url.substr(tab.url.startsWith('/') ? 1 : 0);
107
+ const subUrl = tab.url.substring(tab.url.startsWith('/') ? 1 : 0);
102
108
  const url = `${baseURL}${subUrl}` + getURLParameter(tab, visitPlan);
103
109
  logTabProgress(i, visitPlan);
104
110
  yield maybeWaitForConsoleInput(i + 1);
@@ -179,13 +185,17 @@ class E2EInteractionManager {
179
185
  });
180
186
  }
181
187
  getPageStatistics(url, tabInfo, opArgs = {}) {
182
- var _a;
188
+ var _a, _b;
183
189
  return __awaiter(this, void 0, void 0, function* () {
184
190
  if (core_1.config.verbose) {
185
191
  core_1.info.lowLevel('url: ' + url);
186
192
  }
187
193
  // visit the URL of the first step
188
194
  if (tabInfo.idx === 1) {
195
+ const beforeInitLoad = (_a = opArgs.scenario) === null || _a === void 0 ? void 0 : _a.beforeInitialPageLoad;
196
+ if (beforeInitLoad) {
197
+ yield beforeInitLoad(this.page);
198
+ }
189
199
  yield applyAsyncWithRetry(this.initialLoad, this, [this.page, url, opArgs], {
190
200
  retry: core_1.config.initialLoadFailRetry,
191
201
  delayBeforeRetry: 3000,
@@ -202,7 +212,7 @@ class E2EInteractionManager {
202
212
  // inject marker, which checks if the page is reloaded
203
213
  if (tabInfo.idx === 1) {
204
214
  // call setup callback if the scenario has one
205
- const setup = (_a = opArgs.scenario) === null || _a === void 0 ? void 0 : _a.setup;
215
+ const setup = (_b = opArgs.scenario) === null || _b === void 0 ? void 0 : _b.setup;
206
216
  if (setup) {
207
217
  yield setup(this.page);
208
218
  }
@@ -267,14 +277,14 @@ class E2EInteractionManager {
267
277
  writeStream.write(data.chunk);
268
278
  lastChunk = data.chunk;
269
279
  };
270
- const progressHander = (data) => {
280
+ const progressHandler = (data) => {
271
281
  const percent = ((100 * data.done) / data.total) | 0;
272
282
  if (!core_1.config.isContinuousTest) {
273
283
  core_1.info.overwrite(`heap snapshot ${percent}% complete`);
274
284
  }
275
285
  };
276
286
  session.on('HeapProfiler.addHeapSnapshotChunk', dataHandler);
277
- session.on('HeapProfiler.reportHeapSnapshotProgress', progressHander);
287
+ session.on('HeapProfiler.reportHeapSnapshotProgress', progressHandler);
278
288
  // start taking heap snapshot
279
289
  yield session.send('HeapProfiler.takeHeapSnapshot', {
280
290
  reportProgress: true,
@@ -282,7 +292,7 @@ class E2EInteractionManager {
282
292
  });
283
293
  checkLastSnapshotChunk(lastChunk);
284
294
  session.removeListener('HeapProfiler.addHeapSnapshotChunk', dataHandler);
285
- session.removeListener('HeapProfiler.reportHeapSnapshotProgress', progressHander);
295
+ session.removeListener('HeapProfiler.reportHeapSnapshotProgress', progressHandler);
286
296
  writeStream.end();
287
297
  });
288
298
  }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @format
8
+ * @oncall web_perf_infra
9
+ */
10
+ import type { CDPSession, Page } from 'puppeteer';
11
+ export default class NetworkManager {
12
+ private cdpSession;
13
+ private page;
14
+ private requestCache;
15
+ private scriptManager;
16
+ constructor(page: Page);
17
+ setCDPSession(session: CDPSession): void;
18
+ private networkLog;
19
+ interceptScript(): Promise<void>;
20
+ }
21
+ //# sourceMappingURL=NetworkManager.d.ts.map
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ *
8
+ * @format
9
+ * @oncall web_perf_infra
10
+ */
11
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
12
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
13
+ return new (P || (P = Promise))(function (resolve, reject) {
14
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
15
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
16
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
17
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
18
+ });
19
+ };
20
+ var __importDefault = (this && this.__importDefault) || function (mod) {
21
+ return (mod && mod.__esModule) ? mod : { "default": mod };
22
+ };
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ const core_1 = require("@memlab/core");
25
+ const ScriptManager_1 = __importDefault(require("./ScriptManager"));
26
+ const patterns = ['*'];
27
+ class NetworkManager {
28
+ constructor(page) {
29
+ this.cdpSession = null;
30
+ this.page = page;
31
+ this.requestCache = new Map();
32
+ this.scriptManager = new ScriptManager_1.default();
33
+ }
34
+ setCDPSession(session) {
35
+ this.cdpSession = session;
36
+ }
37
+ networkLog(msg) {
38
+ if (core_1.config.verbose) {
39
+ core_1.info.lowLevel(msg);
40
+ }
41
+ }
42
+ interceptScript() {
43
+ return __awaiter(this, void 0, void 0, function* () {
44
+ const session = this.cdpSession;
45
+ if (!session) {
46
+ core_1.info.warning('Network manager does not have connection to Chrome DevTools.');
47
+ return;
48
+ }
49
+ const requestCache = this.requestCache;
50
+ yield session.send('Network.enable');
51
+ yield session.send('Network.setRequestInterception', {
52
+ patterns: patterns.map(pattern => ({
53
+ urlPattern: pattern,
54
+ resourceType: 'Script',
55
+ interceptionStage: 'HeadersReceived',
56
+ })),
57
+ });
58
+ session.on('Network.requestIntercepted', ({ interceptionId, request, responseHeaders, resourceType }) => __awaiter(this, void 0, void 0, function* () {
59
+ var _a;
60
+ this.networkLog(`Intercepted ${request.url} {interception id: ${interceptionId}}`);
61
+ const response = (yield session.send('Network.getResponseBodyForInterception', { interceptionId }));
62
+ // responseHeaders could be null if requesting from
63
+ // local files (i.e., urls starting with 'file:///')
64
+ let contentType = null;
65
+ if (responseHeaders) {
66
+ const contentTypeHeader = (_a = Object.keys(responseHeaders).find(k => k.toLowerCase() === 'content-type')) !== null && _a !== void 0 ? _a : '';
67
+ contentType = responseHeaders[contentTypeHeader];
68
+ }
69
+ let newBody = '';
70
+ if (requestCache.has(response.body)) {
71
+ newBody = requestCache.get(response.body);
72
+ }
73
+ else {
74
+ const bodyData = response.base64Encoded
75
+ ? atob(response.body)
76
+ : response.body;
77
+ newBody = bodyData;
78
+ try {
79
+ if (resourceType === 'Script') {
80
+ newBody = yield this.scriptManager.rewriteScript(bodyData, {
81
+ url: request.url,
82
+ resourceType,
83
+ });
84
+ }
85
+ }
86
+ catch (e) {
87
+ this.networkLog(`Failed to process ${request.url} {interception id: ${interceptionId}}: ${e}`);
88
+ }
89
+ requestCache.set(response.body, newBody);
90
+ this.scriptManager.logScript(request.url, newBody);
91
+ }
92
+ const newHeaders = [
93
+ 'Date: ' + new Date().toUTCString(),
94
+ 'Connection: closed',
95
+ 'Content-Length: ' + newBody.length,
96
+ ];
97
+ if (contentType) {
98
+ newHeaders.push('Content-Type: ' + contentType);
99
+ }
100
+ this.networkLog(`Continuing interception ${interceptionId}`);
101
+ const rawResponse = btoa(['HTTP/1.1 200 OK', ...newHeaders, '', newBody].join('\r\n'));
102
+ session.send('Network.continueInterceptedRequest', {
103
+ interceptionId,
104
+ rawResponse,
105
+ });
106
+ }));
107
+ });
108
+ }
109
+ }
110
+ exports.default = NetworkManager;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @format
8
+ * @oncall web_perf_infra
9
+ */
10
+ import type { RewriteScriptOption } from './instrumentation/ScriptRewriteManager';
11
+ export default class ScriptManager {
12
+ private fileId;
13
+ private metaFileWriteTimeout;
14
+ private scriptRewriteManager;
15
+ private urlToScriptMap;
16
+ private scriptInfos;
17
+ constructor();
18
+ private init;
19
+ loadFromFiles(): boolean;
20
+ rewriteScript(code: string, options?: RewriteScriptOption): Promise<string>;
21
+ logScript(url: string, code: string): Promise<void>;
22
+ private debounce;
23
+ }
24
+ //# sourceMappingURL=ScriptManager.d.ts.map
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ *
8
+ * @format
9
+ * @oncall web_perf_infra
10
+ */
11
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
12
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
13
+ return new (P || (P = Promise))(function (resolve, reject) {
14
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
15
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
16
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
17
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
18
+ });
19
+ };
20
+ var __importDefault = (this && this.__importDefault) || function (mod) {
21
+ return (mod && mod.__esModule) ? mod : { "default": mod };
22
+ };
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ const fs_1 = __importDefault(require("fs"));
25
+ const path_1 = __importDefault(require("path"));
26
+ const core_1 = require("@memlab/core");
27
+ const ScriptRewriteManager_1 = __importDefault(require("./instrumentation/ScriptRewriteManager"));
28
+ class ScriptManager {
29
+ constructor() {
30
+ this.fileId = 0;
31
+ this.metaFileWriteTimeout = null;
32
+ this.init();
33
+ }
34
+ init() {
35
+ this.urlToScriptMap = new Map();
36
+ this.scriptInfos = [];
37
+ this.scriptRewriteManager = new ScriptRewriteManager_1.default();
38
+ }
39
+ loadFromFiles() {
40
+ this.init();
41
+ const webSourceMetaFile = core_1.fileManager.getWebSourceMetaFile();
42
+ const webSourceDir = core_1.fileManager.getWebSourceDir();
43
+ if (!fs_1.default.existsSync(webSourceMetaFile) || !fs_1.default.existsSync(webSourceDir)) {
44
+ return false;
45
+ }
46
+ try {
47
+ const metaContent = fs_1.default.readFileSync(webSourceMetaFile, 'UTF-8');
48
+ this.scriptInfos = JSON.parse(metaContent);
49
+ for (const scriptInfo of this.scriptInfos) {
50
+ if (!fs_1.default.existsSync(scriptInfo.codePath)) {
51
+ return false;
52
+ }
53
+ this.urlToScriptMap.set(scriptInfo.url, scriptInfo);
54
+ }
55
+ }
56
+ catch (_a) {
57
+ return false;
58
+ }
59
+ return true;
60
+ }
61
+ rewriteScript(code, options = {}) {
62
+ return __awaiter(this, void 0, void 0, function* () {
63
+ if (!core_1.config.instrumentJS) {
64
+ return code;
65
+ }
66
+ const newCode = yield this.scriptRewriteManager.rewriteScript(code, options);
67
+ return newCode;
68
+ });
69
+ }
70
+ logScript(url, code) {
71
+ return __awaiter(this, void 0, void 0, function* () {
72
+ if (this.urlToScriptMap.has(url)) {
73
+ return;
74
+ }
75
+ const metaFile = core_1.fileManager.getWebSourceMetaFile();
76
+ const file = path_1.default.join(core_1.fileManager.getWebSourceDir(), `${++this.fileId}.js`);
77
+ const scriptInfo = {
78
+ url,
79
+ fileId: this.fileId,
80
+ codePath: file,
81
+ };
82
+ this.urlToScriptMap.set(url, scriptInfo);
83
+ this.scriptInfos.push(scriptInfo);
84
+ fs_1.default.writeFile(file, code, 'UTF-8', () => {
85
+ // write file asynchroniously
86
+ });
87
+ // only write the latest version of the meta file state
88
+ this.debounce(() => {
89
+ fs_1.default.writeFileSync(metaFile, JSON.stringify(this.scriptInfos, undefined, 2), 'UTF-8');
90
+ }, 1000);
91
+ });
92
+ }
93
+ debounce(callback, timeout) {
94
+ if (this.metaFileWriteTimeout) {
95
+ clearTimeout(this.metaFileWriteTimeout);
96
+ this.metaFileWriteTimeout = null;
97
+ }
98
+ this.metaFileWriteTimeout = setTimeout(() => {
99
+ callback();
100
+ this.metaFileWriteTimeout = null;
101
+ }, timeout);
102
+ }
103
+ }
104
+ exports.default = ScriptManager;
@@ -5,7 +5,7 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  *
7
7
  * @format
8
- * @oncall ws_labs
8
+ * @oncall web_perf_infra
9
9
  */
10
10
  import type { E2EScenarioSynthesizerConstructor } from '@memlab/core';
11
11
  declare class TestRunnerLoader {
@@ -6,7 +6,7 @@
6
6
  * LICENSE file in the root directory of this source tree.
7
7
  *
8
8
  * @format
9
- * @oncall ws_labs
9
+ * @oncall web_perf_infra
10
10
  */
11
11
  var __importDefault = (this && this.__importDefault) || function (mod) {
12
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
@@ -5,7 +5,7 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  *
7
7
  * @format
8
- * @oncall ws_labs
8
+ * @oncall web_perf_infra
9
9
  */
10
10
  declare module 'xvfb' {
11
11
  interface XvfbParams {
@@ -6,5 +6,5 @@
6
6
  * LICENSE file in the root directory of this source tree.
7
7
  *
8
8
  * @format
9
- * @oncall ws_labs
9
+ * @oncall web_perf_infra
10
10
  */
package/dist/index.d.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  *
7
7
  * @format
8
- * @oncall ws_labs
8
+ * @oncall web_perf_infra
9
9
  */
10
10
  /** @internal */
11
11
  export declare function registerPackage(): Promise<void>;
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@
6
6
  * LICENSE file in the root directory of this source tree.
7
7
  *
8
8
  * @format
9
- * @oncall ws_labs
9
+ * @oncall web_perf_infra
10
10
  */
11
11
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
12
12
  if (k2 === undefined) k2 = k;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @format
8
+ * @oncall web_perf_infra
9
+ */
10
+ import type { ParseResult } from '@babel/parser';
11
+ import type { File } from '@babel/types';
12
+ import type { RewriteScriptOption } from './ScriptRewriteManager';
13
+ export default abstract class BaseAstTransform {
14
+ transform(ast: ParseResult<File>, options?: RewriteScriptOption): Promise<void>;
15
+ }
16
+ //# sourceMappingURL=BaseAstTransform.d.ts.map
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ *
8
+ * @format
9
+ * @oncall web_perf_infra
10
+ */
11
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
12
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
13
+ return new (P || (P = Promise))(function (resolve, reject) {
14
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
15
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
16
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
17
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
18
+ });
19
+ };
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ const core_1 = require("@memlab/core");
22
+ class BaseAstTransform {
23
+ transform(ast,
24
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
25
+ options = {}) {
26
+ return __awaiter(this, void 0, void 0, function* () {
27
+ throw core_1.utils.haltOrThrow('BaseAstTransform.transform is not implemented');
28
+ });
29
+ }
30
+ }
31
+ exports.default = BaseAstTransform;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @format
8
+ * @oncall web_perf_infra
9
+ */
10
+ export declare type RewriteScriptOption = {
11
+ url?: string;
12
+ resourceType?: string;
13
+ };
14
+ export default class ScriptRewriteManager {
15
+ rewriteScript(code: string, options?: RewriteScriptOption): Promise<string>;
16
+ }
17
+ //# sourceMappingURL=ScriptRewriteManager.d.ts.map
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ *
8
+ * @format
9
+ * @oncall web_perf_infra
10
+ */
11
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
12
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
13
+ return new (P || (P = Promise))(function (resolve, reject) {
14
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
15
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
16
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
17
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
18
+ });
19
+ };
20
+ var __importDefault = (this && this.__importDefault) || function (mod) {
21
+ return (mod && mod.__esModule) ? mod : { "default": mod };
22
+ };
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ const parser_1 = require("@babel/parser");
25
+ const generator_1 = __importDefault(require("@babel/generator"));
26
+ const TransformLoader_1 = __importDefault(require("./TransformLoader"));
27
+ class ScriptRewriteManager {
28
+ rewriteScript(code, options = {}) {
29
+ return __awaiter(this, void 0, void 0, function* () {
30
+ // parse code
31
+ const ast = (0, parser_1.parse)(code, {
32
+ sourceType: 'script',
33
+ });
34
+ // transform ast
35
+ const transforms = TransformLoader_1.default.loadAllTransforms();
36
+ for (const transform of transforms) {
37
+ yield transform.transform(ast, options);
38
+ }
39
+ // generate code
40
+ const generateResult = (0, generator_1.default)(ast);
41
+ return generateResult.code;
42
+ });
43
+ }
44
+ }
45
+ exports.default = ScriptRewriteManager;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @format
8
+ * @oncall web_perf_infra
9
+ */
10
+ import type BaseAstTransform from './BaseAstTransform';
11
+ export default class TransformLoader {
12
+ static loadAllTransforms(): BaseAstTransform[];
13
+ }
14
+ //# sourceMappingURL=TransformLoader.d.ts.map
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ *
8
+ * @format
9
+ * @oncall web_perf_infra
10
+ */
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const InjectSourceInfoTransform_1 = __importDefault(require("./transforms/InjectSourceInfoTransform"));
16
+ class TransformLoader {
17
+ static loadAllTransforms() {
18
+ return [new InjectSourceInfoTransform_1.default()];
19
+ }
20
+ }
21
+ exports.default = TransformLoader;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @format
8
+ * @oncall web_perf_infra
9
+ */
10
+ import type { File } from '@babel/types';
11
+ import type { ParseResult } from '@babel/parser';
12
+ import type { RewriteScriptOption } from '../ScriptRewriteManager';
13
+ import BaseAstTransform from '../BaseAstTransform';
14
+ export default class InjectSourceInfoTranform extends BaseAstTransform {
15
+ transform(ast: ParseResult<File>, options?: RewriteScriptOption): Promise<void>;
16
+ }
17
+ //# sourceMappingURL=InjectSourceInfoTranform.d.ts.map