@memlab/e2e 1.0.17 → 1.0.18
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/BaseSynthesizer.d.ts +2 -1
- package/dist/BaseSynthesizer.js +11 -0
- package/dist/E2EInteractionManager.js +3 -0
- package/dist/ScriptManager.d.ts +4 -0
- package/dist/ScriptManager.js +33 -4
- package/dist/__tests__/scope/lib.d.ts +13 -0
- package/dist/__tests__/scope/lib.js +33 -0
- package/dist/__tests__/scope/scope-analysis.test.d.ts +11 -0
- package/dist/__tests__/scope/scope-analysis.test.js +57 -0
- package/dist/code-analysis/Script.d.ts +32 -0
- package/dist/code-analysis/Script.js +164 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -1
- package/dist/instrumentation/BaseAstTransform.d.ts +2 -3
- package/dist/instrumentation/transforms/InjectSourceInfoTranform.d.ts +2 -12
- package/dist/instrumentation/transforms/InjectSourceInfoTransform.d.ts +2 -12
- package/package.json +3 -2
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* @lightSyntaxTransform
|
|
9
9
|
* @oncall web_perf_infra
|
|
10
10
|
*/
|
|
11
|
-
import type { AnyOptions, Config, E2EOperation, E2EStepInfo, IE2EScenarioVisitPlan, E2ESynthesizerOptions, IScenario, IE2EStepBasic, IE2EScenarioSynthesizer, CheckPageLoadCallback, Nullable } from '@memlab/core';
|
|
11
|
+
import type { AnyOptions, Config, E2EOperation, E2EStepInfo, IE2EScenarioVisitPlan, E2ESynthesizerOptions, IScenario, IE2EStepBasic, IE2EScenarioSynthesizer, CheckPageLoadCallback, Nullable, PageSetupCallback } from '@memlab/core';
|
|
12
12
|
declare class BaseSynthesizer implements IE2EScenarioSynthesizer {
|
|
13
13
|
protected config: Config;
|
|
14
14
|
protected steps: Map<string, IE2EStepBasic>;
|
|
@@ -41,6 +41,7 @@ declare class BaseSynthesizer implements IE2EScenarioSynthesizer {
|
|
|
41
41
|
getDevice(): string;
|
|
42
42
|
getExtraOperationsForStep(_stepInfo: E2EStepInfo): E2EOperation[];
|
|
43
43
|
getPageLoadChecker(): CheckPageLoadCallback;
|
|
44
|
+
getPageSetupCallback(): PageSetupCallback;
|
|
44
45
|
synthesis(baseline: string, target: string, intermediates: string[], options?: E2ESynthesizerOptions): IE2EScenarioVisitPlan;
|
|
45
46
|
private getActualNumberOfWarmup;
|
|
46
47
|
private synthesisInitPart;
|
package/dist/BaseSynthesizer.js
CHANGED
|
@@ -156,6 +156,12 @@ class BaseSynthesizer {
|
|
|
156
156
|
return true;
|
|
157
157
|
});
|
|
158
158
|
}
|
|
159
|
+
getPageSetupCallback() {
|
|
160
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
161
|
+
return (_page) => __awaiter(this, void 0, void 0, function* () {
|
|
162
|
+
return;
|
|
163
|
+
});
|
|
164
|
+
}
|
|
159
165
|
synthesis(baseline, target, intermediates, options = {}) {
|
|
160
166
|
const type = options.type;
|
|
161
167
|
if (!type || type === 'standard') {
|
|
@@ -257,6 +263,7 @@ class BaseSynthesizer {
|
|
|
257
263
|
numOfWarmup: this.getActualNumberOfWarmup(),
|
|
258
264
|
dataBuilder: options.dataBuilder,
|
|
259
265
|
isPageLoaded: this.getPageLoadChecker(),
|
|
266
|
+
pageSetup: this.getPageSetupCallback(),
|
|
260
267
|
};
|
|
261
268
|
}
|
|
262
269
|
synthesisStandard(baseline, target, intermediates, options = {}) {
|
|
@@ -278,6 +285,7 @@ class BaseSynthesizer {
|
|
|
278
285
|
numOfWarmup: this.getActualNumberOfWarmup(),
|
|
279
286
|
dataBuilder: options.dataBuilder,
|
|
280
287
|
isPageLoaded: this.getPageLoadChecker(),
|
|
288
|
+
pageSetup: this.getPageSetupCallback(),
|
|
281
289
|
};
|
|
282
290
|
}
|
|
283
291
|
processStepUrl(url) {
|
|
@@ -335,6 +343,7 @@ class BaseSynthesizer {
|
|
|
335
343
|
numOfWarmup: this.getActualNumberOfWarmup(),
|
|
336
344
|
dataBuilder: null,
|
|
337
345
|
isPageLoaded: (_a = scenario.isPageLoaded) !== null && _a !== void 0 ? _a : this.getPageLoadChecker(),
|
|
346
|
+
pageSetup: this.getPageSetupCallback(),
|
|
338
347
|
scenario,
|
|
339
348
|
};
|
|
340
349
|
}
|
|
@@ -356,6 +365,7 @@ class BaseSynthesizer {
|
|
|
356
365
|
numOfWarmup: this.getActualNumberOfWarmup(),
|
|
357
366
|
dataBuilder: options.dataBuilder,
|
|
358
367
|
isPageLoaded: this.getPageLoadChecker(),
|
|
368
|
+
pageSetup: this.getPageSetupCallback(),
|
|
359
369
|
};
|
|
360
370
|
}
|
|
361
371
|
synthesisInitialPageLoad(target, intermediates, options = {}) {
|
|
@@ -381,6 +391,7 @@ class BaseSynthesizer {
|
|
|
381
391
|
numOfWarmup: this.getActualNumberOfWarmup(),
|
|
382
392
|
dataBuilder: options.dataBuilder,
|
|
383
393
|
isPageLoaded: this.getPageLoadChecker(),
|
|
394
|
+
pageSetup: this.getPageSetupCallback(),
|
|
384
395
|
};
|
|
385
396
|
}
|
|
386
397
|
getActionFromStep(step, type, snapshot, screenshot) {
|
|
@@ -99,6 +99,9 @@ class E2EInteractionManager {
|
|
|
99
99
|
yield setPermissions(this.page, TestPlanner_1.default.getOrigin());
|
|
100
100
|
yield this.beforeInteractions();
|
|
101
101
|
const baseURL = core_1.utils.normalizeBaseUrl(visitPlan.baseURL);
|
|
102
|
+
if (visitPlan.pageSetup) {
|
|
103
|
+
yield visitPlan.pageSetup(this.page);
|
|
104
|
+
}
|
|
102
105
|
yield this.startTrackingHeap();
|
|
103
106
|
for (let i = 0; i < visitPlan.tabsOrder.length; i++) {
|
|
104
107
|
core_1.info.beginSection(`step-${i}`);
|
package/dist/ScriptManager.d.ts
CHANGED
|
@@ -7,7 +7,9 @@
|
|
|
7
7
|
* @format
|
|
8
8
|
* @oncall web_perf_infra
|
|
9
9
|
*/
|
|
10
|
+
import type { Nullable } from '@memlab/core';
|
|
10
11
|
import type { RewriteScriptOption } from './instrumentation/ScriptRewriteManager';
|
|
12
|
+
import type { ClosureScope } from './code-analysis/Script';
|
|
11
13
|
export default class ScriptManager {
|
|
12
14
|
private fileId;
|
|
13
15
|
private metaFileWriteTimeout;
|
|
@@ -17,6 +19,8 @@ export default class ScriptManager {
|
|
|
17
19
|
constructor();
|
|
18
20
|
private init;
|
|
19
21
|
loadFromFiles(): boolean;
|
|
22
|
+
loadCodeForUrl(url: string): Nullable<string>;
|
|
23
|
+
getClosureScopeTreeForUrl(url: string): Nullable<ClosureScope>;
|
|
20
24
|
rewriteScript(code: string, options?: RewriteScriptOption): Promise<string>;
|
|
21
25
|
logScript(url: string, code: string): Promise<void>;
|
|
22
26
|
private debounce;
|
package/dist/ScriptManager.js
CHANGED
|
@@ -24,6 +24,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
24
24
|
const fs_1 = __importDefault(require("fs"));
|
|
25
25
|
const path_1 = __importDefault(require("path"));
|
|
26
26
|
const core_1 = require("@memlab/core");
|
|
27
|
+
const Script_1 = __importDefault(require("./code-analysis/Script"));
|
|
27
28
|
const ScriptRewriteManager_1 = __importDefault(require("./instrumentation/ScriptRewriteManager"));
|
|
28
29
|
class ScriptManager {
|
|
29
30
|
constructor() {
|
|
@@ -47,9 +48,6 @@ class ScriptManager {
|
|
|
47
48
|
const metaContent = fs_1.default.readFileSync(webSourceMetaFile, 'UTF-8');
|
|
48
49
|
this.scriptInfos = JSON.parse(metaContent);
|
|
49
50
|
for (const scriptInfo of this.scriptInfos) {
|
|
50
|
-
if (!fs_1.default.existsSync(scriptInfo.codePath)) {
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
53
51
|
this.urlToScriptMap.set(scriptInfo.url, scriptInfo);
|
|
54
52
|
}
|
|
55
53
|
}
|
|
@@ -58,6 +56,37 @@ class ScriptManager {
|
|
|
58
56
|
}
|
|
59
57
|
return true;
|
|
60
58
|
}
|
|
59
|
+
loadCodeForUrl(url) {
|
|
60
|
+
var _a;
|
|
61
|
+
if (!this.urlToScriptMap.has(url)) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
const scriptInfo = this.urlToScriptMap.get(url);
|
|
65
|
+
if (scriptInfo.code) {
|
|
66
|
+
return scriptInfo.code;
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
scriptInfo.code = fs_1.default.readFileSync(scriptInfo.codePath, 'UTF-8');
|
|
70
|
+
}
|
|
71
|
+
catch (_b) {
|
|
72
|
+
// do nothing
|
|
73
|
+
}
|
|
74
|
+
return (_a = scriptInfo.code) !== null && _a !== void 0 ? _a : null;
|
|
75
|
+
}
|
|
76
|
+
getClosureScopeTreeForUrl(url) {
|
|
77
|
+
const code = this.loadCodeForUrl(url);
|
|
78
|
+
if (code == null) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
const script = new Script_1.default(code);
|
|
83
|
+
return script.getClosureScopeTree();
|
|
84
|
+
}
|
|
85
|
+
catch (_a) {
|
|
86
|
+
// do nothing
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
61
90
|
rewriteScript(code, options = {}) {
|
|
62
91
|
return __awaiter(this, void 0, void 0, function* () {
|
|
63
92
|
if (!core_1.config.instrumentJS) {
|
|
@@ -82,7 +111,7 @@ class ScriptManager {
|
|
|
82
111
|
this.urlToScriptMap.set(url, scriptInfo);
|
|
83
112
|
this.scriptInfos.push(scriptInfo);
|
|
84
113
|
fs_1.default.writeFile(file, code, 'UTF-8', () => {
|
|
85
|
-
// write
|
|
114
|
+
// async write, do nothing here
|
|
86
115
|
});
|
|
87
116
|
// only write the latest version of the meta file state
|
|
88
117
|
this.debounce(() => {
|
|
@@ -0,0 +1,13 @@
|
|
|
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 { AnyValue } from '@memlab/core';
|
|
11
|
+
export declare function testScopeAnalysis(code: string, expectedScope: AnyValue): void;
|
|
12
|
+
export declare function removeLoc(entity: AnyValue): AnyValue;
|
|
13
|
+
//# sourceMappingURL=lib.d.ts.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.removeLoc = exports.testScopeAnalysis = void 0;
|
|
7
|
+
const Script_1 = __importDefault(require("../../code-analysis/Script"));
|
|
8
|
+
function testScopeAnalysis(code, expectedScope) {
|
|
9
|
+
const script = new Script_1.default(code);
|
|
10
|
+
const scope = script.getClosureScopeTree();
|
|
11
|
+
expect(removeLoc(scope)).toEqual(removeLoc(expectedScope));
|
|
12
|
+
}
|
|
13
|
+
exports.testScopeAnalysis = testScopeAnalysis;
|
|
14
|
+
function removeLoc(entity) {
|
|
15
|
+
const visited = new Set();
|
|
16
|
+
function remove(e) {
|
|
17
|
+
if (!e || typeof e !== 'object' || visited.has(e)) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
visited.add(e);
|
|
21
|
+
for (const k of Object.keys(e)) {
|
|
22
|
+
if (k === 'loc') {
|
|
23
|
+
delete e.loc;
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
remove(e[k]);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
remove(entity);
|
|
31
|
+
return entity;
|
|
32
|
+
}
|
|
33
|
+
exports.removeLoc = removeLoc;
|
|
@@ -0,0 +1,11 @@
|
|
|
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 {};
|
|
11
|
+
//# sourceMappingURL=scope-analysis.test.d.ts.map
|
|
@@ -0,0 +1,57 @@
|
|
|
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 lib_1 = require("./lib");
|
|
22
|
+
const code = `
|
|
23
|
+
const v0 = 'abc';
|
|
24
|
+
function f() {
|
|
25
|
+
const v1 = 'test';
|
|
26
|
+
function f2() {
|
|
27
|
+
let v2 = 123;
|
|
28
|
+
console.log(v0, v1, v2);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
`;
|
|
32
|
+
const closureScope = {
|
|
33
|
+
functionName: null,
|
|
34
|
+
functionType: 'Program',
|
|
35
|
+
nestedClosures: [
|
|
36
|
+
{
|
|
37
|
+
functionName: 'f',
|
|
38
|
+
functionType: 'FunctionDeclaration',
|
|
39
|
+
nestedClosures: [
|
|
40
|
+
{
|
|
41
|
+
functionName: 'f2',
|
|
42
|
+
functionType: 'FunctionDeclaration',
|
|
43
|
+
nestedClosures: [],
|
|
44
|
+
usedVariablesFromParentScope: ['v1'],
|
|
45
|
+
variablesDefined: ['v2'],
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
usedVariablesFromParentScope: ['v0'],
|
|
49
|
+
variablesDefined: ['v1', 'f2'],
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
usedVariablesFromParentScope: [],
|
|
53
|
+
variablesDefined: ['v0', 'f'],
|
|
54
|
+
};
|
|
55
|
+
test('simple scope analysis works as expected', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
56
|
+
(0, lib_1.testScopeAnalysis)(code, closureScope);
|
|
57
|
+
}));
|
|
@@ -0,0 +1,32 @@
|
|
|
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 { Optional } from '@memlab/core';
|
|
11
|
+
import type { SourceLocation } from '@babel/types';
|
|
12
|
+
export declare type ClosureScope = {
|
|
13
|
+
functionName: Optional<string>;
|
|
14
|
+
functionType: string;
|
|
15
|
+
variablesDefined: string[];
|
|
16
|
+
usedVariablesFromParentScope: string[];
|
|
17
|
+
nestedClosures: ClosureScope[];
|
|
18
|
+
loc: Optional<SourceLocation>;
|
|
19
|
+
};
|
|
20
|
+
export default class Script {
|
|
21
|
+
private code;
|
|
22
|
+
private ast;
|
|
23
|
+
private closureScopeTree;
|
|
24
|
+
constructor(code: string);
|
|
25
|
+
getClosureScopeTree(): ClosureScope;
|
|
26
|
+
private locToStr;
|
|
27
|
+
private findParentFunctionPath;
|
|
28
|
+
private findParentFunctionClosureScope;
|
|
29
|
+
private findGrandparentFunctionClosureScope;
|
|
30
|
+
private buildClosureScopeTree;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=Script.d.ts.map
|
|
@@ -0,0 +1,164 @@
|
|
|
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 traverse_1 = __importDefault(require("@babel/traverse"));
|
|
16
|
+
const parser_1 = require("@babel/parser");
|
|
17
|
+
const core_1 = require("@memlab/core");
|
|
18
|
+
function isFunctionType(type) {
|
|
19
|
+
return (type === 'FunctionDeclaration' ||
|
|
20
|
+
type === 'FunctionExpression' ||
|
|
21
|
+
type === 'ObjectMethod' ||
|
|
22
|
+
type === 'ClassMethod' ||
|
|
23
|
+
type === 'ArrowFunctionExpression');
|
|
24
|
+
}
|
|
25
|
+
class Script {
|
|
26
|
+
constructor(code) {
|
|
27
|
+
this.code = code;
|
|
28
|
+
this.ast = (0, parser_1.parse)(code, {
|
|
29
|
+
sourceType: 'script',
|
|
30
|
+
});
|
|
31
|
+
this.closureScopeTree = this.buildClosureScopeTree(this.ast);
|
|
32
|
+
}
|
|
33
|
+
getClosureScopeTree() {
|
|
34
|
+
return this.closureScopeTree;
|
|
35
|
+
}
|
|
36
|
+
locToStr(loc) {
|
|
37
|
+
if (loc == null) {
|
|
38
|
+
throw core_1.utils.haltOrThrow(`location in ast is ${loc}`);
|
|
39
|
+
}
|
|
40
|
+
return JSON.stringify(loc);
|
|
41
|
+
}
|
|
42
|
+
findParentFunctionPath(path) {
|
|
43
|
+
let curPath = path.parentPath;
|
|
44
|
+
while (curPath) {
|
|
45
|
+
if (isFunctionType(curPath.node.type) ||
|
|
46
|
+
curPath.node.type === 'Program') {
|
|
47
|
+
return curPath;
|
|
48
|
+
}
|
|
49
|
+
if (curPath.parentPath == null) {
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
curPath = curPath.parentPath;
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
findParentFunctionClosureScope(locToClosureScopeMap, path) {
|
|
57
|
+
const parentPath = this.findParentFunctionPath(path);
|
|
58
|
+
if (!parentPath) {
|
|
59
|
+
throw core_1.utils.haltOrThrow('cannot find parent scope');
|
|
60
|
+
}
|
|
61
|
+
const parentClosureScope = locToClosureScopeMap.get(this.locToStr(parentPath.node.loc));
|
|
62
|
+
if (!parentClosureScope) {
|
|
63
|
+
throw core_1.utils.haltOrThrow('cannot find parent scope');
|
|
64
|
+
}
|
|
65
|
+
return parentClosureScope;
|
|
66
|
+
}
|
|
67
|
+
findGrandparentFunctionClosureScope(locToClosureScopeMap, path) {
|
|
68
|
+
const parentPath = this.findParentFunctionPath(path);
|
|
69
|
+
if (!parentPath) {
|
|
70
|
+
throw core_1.utils.haltOrThrow('cannot find parent scope');
|
|
71
|
+
}
|
|
72
|
+
const grandparentPath = this.findParentFunctionPath(parentPath);
|
|
73
|
+
if (!grandparentPath) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
const grandparentClosureScope = locToClosureScopeMap.get(this.locToStr(grandparentPath.node.loc));
|
|
77
|
+
if (!grandparentClosureScope) {
|
|
78
|
+
throw core_1.utils.haltOrThrow('cannot find parent scope');
|
|
79
|
+
}
|
|
80
|
+
return grandparentClosureScope;
|
|
81
|
+
}
|
|
82
|
+
buildClosureScopeTree(ast) {
|
|
83
|
+
const root = {
|
|
84
|
+
functionName: null,
|
|
85
|
+
functionType: ast.program.type,
|
|
86
|
+
variablesDefined: [],
|
|
87
|
+
usedVariablesFromParentScope: [],
|
|
88
|
+
nestedClosures: [],
|
|
89
|
+
loc: ast.program.loc,
|
|
90
|
+
};
|
|
91
|
+
const locToClosureScopeMap = new Map();
|
|
92
|
+
locToClosureScopeMap.set(this.locToStr(ast.program.loc), root);
|
|
93
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
94
|
+
const self = this;
|
|
95
|
+
// build closure scope hierarchy
|
|
96
|
+
const buildClosureScopeFromFunction = function (path) {
|
|
97
|
+
var _a, _b;
|
|
98
|
+
const closureScope = {
|
|
99
|
+
functionName: 'id' in path.node ? (_b = (_a = path.node) === null || _a === void 0 ? void 0 : _a.id) === null || _b === void 0 ? void 0 : _b.name : null,
|
|
100
|
+
functionType: path.node.type,
|
|
101
|
+
variablesDefined: Object.keys(path.scope.bindings),
|
|
102
|
+
usedVariablesFromParentScope: [],
|
|
103
|
+
nestedClosures: [],
|
|
104
|
+
loc: path.node.loc,
|
|
105
|
+
};
|
|
106
|
+
locToClosureScopeMap.set(self.locToStr(path.node.loc), closureScope);
|
|
107
|
+
// find and connect to parent closure scope
|
|
108
|
+
const parentClosureScope = self.findParentFunctionClosureScope(locToClosureScopeMap, path);
|
|
109
|
+
parentClosureScope.nestedClosures.push(closureScope);
|
|
110
|
+
};
|
|
111
|
+
// Traverse the parent scope of the containing scope
|
|
112
|
+
// to find out if both of the conditions are true:
|
|
113
|
+
// 1. This is a var use in the containing scope
|
|
114
|
+
// 2. This is a var defined in the direct parent scope
|
|
115
|
+
// of the containing scope
|
|
116
|
+
// If true, add the identifer name to the usedVariablesFromParentScope
|
|
117
|
+
// of the containing scope
|
|
118
|
+
const fillInVarInfo = function (path) {
|
|
119
|
+
// if the identifier is a function name of a function definition
|
|
120
|
+
if (isFunctionType(path.parentPath.node.type)) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const name = path.node.name;
|
|
124
|
+
let parentPath = self.findParentFunctionPath(path);
|
|
125
|
+
let parentClosureScope = parentPath
|
|
126
|
+
? locToClosureScopeMap.get(self.locToStr(parentPath.node.loc))
|
|
127
|
+
: null;
|
|
128
|
+
// eslint-disable-next-line no-constant-condition
|
|
129
|
+
while (true) {
|
|
130
|
+
if (!parentPath || !parentClosureScope) {
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
const grandparentPath = self.findParentFunctionPath(parentPath);
|
|
134
|
+
if (!grandparentPath) {
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
const grandparentClosureScope = locToClosureScopeMap.get(self.locToStr(grandparentPath.node.loc));
|
|
138
|
+
if (!grandparentClosureScope) {
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
if (grandparentClosureScope.variablesDefined.includes(name) &&
|
|
142
|
+
!parentClosureScope.usedVariablesFromParentScope.includes(name)) {
|
|
143
|
+
parentClosureScope.usedVariablesFromParentScope.push(name);
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
parentPath = grandparentPath;
|
|
147
|
+
parentClosureScope = grandparentClosureScope;
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
(0, traverse_1.default)(ast, {
|
|
151
|
+
FunctionDeclaration: buildClosureScopeFromFunction,
|
|
152
|
+
FunctionExpression: buildClosureScopeFromFunction,
|
|
153
|
+
ObjectMethod: buildClosureScopeFromFunction,
|
|
154
|
+
ClassMethod: buildClosureScopeFromFunction,
|
|
155
|
+
ArrowFunctionExpression: buildClosureScopeFromFunction,
|
|
156
|
+
Identifier: fillInVarInfo,
|
|
157
|
+
Program: (path) => {
|
|
158
|
+
root.variablesDefined = Object.keys(path.scope.bindings);
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
return root;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
exports.default = Script;
|
package/dist/index.d.ts
CHANGED
|
@@ -15,4 +15,8 @@ export { default as Xvfb } from './lib/operations/XVirtualFrameBuffer';
|
|
|
15
15
|
export { default as E2EInteractionManager } from './E2EInteractionManager';
|
|
16
16
|
export { default as BaseSynthesizer } from './BaseSynthesizer';
|
|
17
17
|
export { default as E2EUtils } from './lib/E2EUtils';
|
|
18
|
+
/** @internal */
|
|
19
|
+
export { default as ScriptManager } from './ScriptManager';
|
|
20
|
+
/** @internal */
|
|
21
|
+
export type { ClosureScope } from './code-analysis/Script';
|
|
18
22
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -35,7 +35,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
35
35
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
36
36
|
};
|
|
37
37
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
|
-
exports.E2EUtils = exports.BaseSynthesizer = exports.E2EInteractionManager = exports.Xvfb = exports.defaultTestPlanner = exports.registerPackage = void 0;
|
|
38
|
+
exports.ScriptManager = exports.E2EUtils = exports.BaseSynthesizer = exports.E2EInteractionManager = exports.Xvfb = exports.defaultTestPlanner = exports.registerPackage = void 0;
|
|
39
39
|
const path_1 = __importDefault(require("path"));
|
|
40
40
|
const core_1 = require("@memlab/core");
|
|
41
41
|
/** @internal */
|
|
@@ -56,3 +56,6 @@ var BaseSynthesizer_1 = require("./BaseSynthesizer");
|
|
|
56
56
|
Object.defineProperty(exports, "BaseSynthesizer", { enumerable: true, get: function () { return __importDefault(BaseSynthesizer_1).default; } });
|
|
57
57
|
var E2EUtils_1 = require("./lib/E2EUtils");
|
|
58
58
|
Object.defineProperty(exports, "E2EUtils", { enumerable: true, get: function () { return __importDefault(E2EUtils_1).default; } });
|
|
59
|
+
/** @internal */
|
|
60
|
+
var ScriptManager_1 = require("./ScriptManager");
|
|
61
|
+
Object.defineProperty(exports, "ScriptManager", { enumerable: true, get: function () { return __importDefault(ScriptManager_1).default; } });
|
|
@@ -7,10 +7,9 @@
|
|
|
7
7
|
* @format
|
|
8
8
|
* @oncall web_perf_infra
|
|
9
9
|
*/
|
|
10
|
-
import type { ParseResult } from '@babel/
|
|
11
|
-
import type { File } from '@babel/types';
|
|
10
|
+
import type { ParseResult } from '@babel/core';
|
|
12
11
|
import type { RewriteScriptOption } from './ScriptRewriteManager';
|
|
13
12
|
export default abstract class BaseAstTransform {
|
|
14
|
-
transform(ast: ParseResult
|
|
13
|
+
transform(ast: ParseResult, options?: RewriteScriptOption): Promise<void>;
|
|
15
14
|
}
|
|
16
15
|
//# sourceMappingURL=BaseAstTransform.d.ts.map
|
|
@@ -1,17 +1,7 @@
|
|
|
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';
|
|
1
|
+
import type { ParseResult } from '@babel/core';
|
|
12
2
|
import type { RewriteScriptOption } from '../ScriptRewriteManager';
|
|
13
3
|
import BaseAstTransform from '../BaseAstTransform';
|
|
14
4
|
export default class InjectSourceInfoTranform extends BaseAstTransform {
|
|
15
|
-
transform(ast: ParseResult
|
|
5
|
+
transform(ast: ParseResult, options?: RewriteScriptOption): Promise<void>;
|
|
16
6
|
}
|
|
17
7
|
//# sourceMappingURL=InjectSourceInfoTranform.d.ts.map
|
|
@@ -1,17 +1,7 @@
|
|
|
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';
|
|
1
|
+
import type { ParseResult } from '@babel/core';
|
|
12
2
|
import type { RewriteScriptOption } from '../ScriptRewriteManager';
|
|
13
3
|
import BaseAstTransform from '../BaseAstTransform';
|
|
14
4
|
export default class InjectSourceInfoTransform extends BaseAstTransform {
|
|
15
|
-
transform(ast: ParseResult
|
|
5
|
+
transform(ast: ParseResult, options?: RewriteScriptOption): Promise<void>;
|
|
16
6
|
}
|
|
17
7
|
//# sourceMappingURL=InjectSourceInfoTransform.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memlab/e2e",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.18",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "memlab browser E2E interaction libraries",
|
|
6
6
|
"author": "Liang Gong <lgong@fb.com>",
|
|
@@ -55,7 +55,8 @@
|
|
|
55
55
|
},
|
|
56
56
|
"scripts": {
|
|
57
57
|
"build-pkg": "tsc",
|
|
58
|
-
"
|
|
58
|
+
"test-pkg": "jest .",
|
|
59
|
+
"publish-patch": "npm publish",
|
|
59
60
|
"clean-pkg": "rm -rf ./dist && rm -rf ./node_modules && rm -f ./tsconfig.tsbuildinfo"
|
|
60
61
|
},
|
|
61
62
|
"bugs": {
|