@memlab/cli 1.0.23 → 1.0.25
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/BaseCommand.d.ts +4 -3
- package/dist/BaseCommand.js +11 -1
- package/dist/Dispatcher.js +6 -0
- package/dist/commands/MemLabRunCommand.d.ts +2 -2
- package/dist/commands/RunMeasureCommand.d.ts +2 -2
- package/dist/commands/RunMeasureCommand.js +3 -2
- package/dist/commands/WarmupAppCommand.d.ts +2 -2
- package/dist/commands/WarmupAppCommand.js +2 -0
- package/dist/commands/heap/CheckLeakCommand.d.ts +12 -1
- package/dist/commands/heap/CheckLeakCommand.js +71 -9
- package/dist/commands/heap/DiffLeakCommand.d.ts +13 -2
- package/dist/commands/heap/DiffLeakCommand.js +49 -25
- package/dist/commands/heap/GetRetainerTraceCommand.d.ts +2 -2
- package/dist/commands/heap/HeapAnalysisCommand.d.ts +2 -2
- package/dist/commands/heap/interactive/InteractiveHeapCommand.d.ts +2 -2
- package/dist/commands/heap/interactive/InteractiveHeapExploreCommand.d.ts +2 -2
- package/dist/commands/heap/interactive/ui-components/CliScreen.d.ts +5 -0
- package/dist/commands/heap/interactive/ui-components/CliScreen.js +51 -11
- package/dist/commands/heap/interactive/ui-components/HeapViewController.d.ts +2 -0
- package/dist/commands/heap/interactive/ui-components/HeapViewController.js +5 -1
- package/dist/commands/heap/interactive/ui-components/HeapViewUtils.d.ts +2 -0
- package/dist/commands/heap/interactive/ui-components/HeapViewUtils.js +25 -21
- package/dist/commands/heap/interactive/ui-components/ListComponent.js +2 -1
- package/dist/commands/helper/GenerateCLIDocCommand.js +16 -8
- package/dist/commands/helper/HelperCommand.d.ts +7 -4
- package/dist/commands/helper/HelperCommand.js +34 -27
- package/dist/commands/helper/lib/DocUtils.d.ts +19 -0
- package/dist/commands/helper/lib/DocUtils.js +39 -0
- package/dist/commands/snapshot/TakeSnapshotCommand.d.ts +2 -2
- package/dist/commands/snapshot/TakeSnapshotCommand.js +2 -0
- package/dist/commands/snapshot/WarmupAndSnapshotCommand.d.ts +3 -1
- package/dist/commands/snapshot/WarmupAndSnapshotCommand.js +23 -0
- package/dist/lib/CLIUtils.d.ts +22 -0
- package/dist/lib/CLIUtils.js +129 -0
- package/dist/options/MLClusteringOption.d.ts +1 -0
- package/dist/options/MLClusteringOption.js +4 -0
- package/dist/options/NumberOfRunsOption.d.ts +4 -2
- package/dist/options/NumberOfRunsOption.js +12 -4
- package/dist/options/SetMaxClusterSampleSizeOption.d.ts +19 -0
- package/dist/options/SetMaxClusterSampleSizeOption.js +49 -0
- package/dist/options/e2e/SetChromiumBinaryOption.d.ts +18 -0
- package/dist/options/e2e/SetChromiumBinaryOption.js +43 -0
- package/dist/options/experiment/SetControlWorkDirOption.d.ts +3 -2
- package/dist/options/experiment/SetControlWorkDirOption.js +25 -6
- package/dist/options/experiment/SetTreatmentWorkDirOption.js +6 -0
- package/dist/options/heap/SetTraceContainsFilterOption.d.ts +18 -0
- package/dist/options/heap/SetTraceContainsFilterOption.js +42 -0
- package/dist/options/heap/TraceAllObjectsOption.d.ts +4 -2
- package/dist/options/heap/TraceAllObjectsOption.js +32 -4
- package/dist/options/lib/OptionConstant.d.ts +6 -0
- package/dist/options/lib/OptionConstant.js +3 -0
- package/package.json +1 -1
|
@@ -40,6 +40,7 @@ function positionToNumber(info) {
|
|
|
40
40
|
class CliScreen {
|
|
41
41
|
constructor(title, heap, objectCategory) {
|
|
42
42
|
this.currentFocuseKey = 1;
|
|
43
|
+
this.fullScreenComponent = null;
|
|
43
44
|
this.heapController = new HeapViewController_1.default(heap, objectCategory);
|
|
44
45
|
this.screen = this.initScreen(title);
|
|
45
46
|
const callbacks = this.initCallbacks(this.heapController, this.screen);
|
|
@@ -118,16 +119,20 @@ class CliScreen {
|
|
|
118
119
|
}
|
|
119
120
|
registerScreenResize() {
|
|
120
121
|
const screen = this.screen;
|
|
121
|
-
screen.on('resize', ()
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
122
|
+
screen.on('resize', this.updateAllComponentsSize.bind(this));
|
|
123
|
+
}
|
|
124
|
+
updateAllComponentsSize() {
|
|
125
|
+
// all boxes/lists needs to resize
|
|
126
|
+
this.updateComponentSize(this.clusteredObjectBox, this.getClusteredObjectBoxSize());
|
|
127
|
+
this.updateComponentSize(this.referrerBox, this.getReferrerBoxSize());
|
|
128
|
+
this.updateComponentSize(this.objectBox, this.getObjectBoxSize());
|
|
129
|
+
this.updateComponentSize(this.objectPropertyBox, this.getObjectPropertyBoxSize());
|
|
130
|
+
this.updateComponentSize(this.referenceBox, this.getReferenceBoxSize());
|
|
131
|
+
this.updateComponentSize(this.retainerTraceBox, this.getRetainerTraceBoxSize());
|
|
132
|
+
if (this.fullScreenComponent != null) {
|
|
133
|
+
this.updateComponentSize(this.fullScreenComponent, this.getComponentFullScreenSize());
|
|
134
|
+
}
|
|
135
|
+
this.updateElementSize(this.helperTextElement, this.getHelperTextSize());
|
|
131
136
|
}
|
|
132
137
|
updateComponentSize(component, size) {
|
|
133
138
|
this.updateElementSize(component.element, size);
|
|
@@ -141,7 +146,16 @@ class CliScreen {
|
|
|
141
146
|
registerKeys() {
|
|
142
147
|
const screen = this.screen;
|
|
143
148
|
// Quit on Escape, q, or Control-C.
|
|
144
|
-
screen.key(['escape', 'q', 'C-c'], () =>
|
|
149
|
+
screen.key(['escape', 'q', 'C-c'], () => {
|
|
150
|
+
if (this.fullScreenComponent != null) {
|
|
151
|
+
// exit component full screen mode
|
|
152
|
+
this.makeNoComponentFullScreen();
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
// quit the program
|
|
156
|
+
process.exit(0);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
145
159
|
const keyToComponent = this.keyToComponent;
|
|
146
160
|
const heapController = this.heapController;
|
|
147
161
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
@@ -150,13 +164,30 @@ class CliScreen {
|
|
|
150
164
|
// focus on the selected element
|
|
151
165
|
const component = keyToComponent.get(char);
|
|
152
166
|
if (component) {
|
|
167
|
+
if (component !== this.fullScreenComponent) {
|
|
168
|
+
// quit full screen mode if the component to focus
|
|
169
|
+
// is not the current full screen component
|
|
170
|
+
this.makeNoComponentFullScreen();
|
|
171
|
+
}
|
|
153
172
|
heapController.focusOnComponent(component.id);
|
|
154
173
|
screen.render();
|
|
155
174
|
}
|
|
156
175
|
}
|
|
176
|
+
// enter full screen
|
|
177
|
+
if (char === 'f') {
|
|
178
|
+
this.makeComponentFullScreen(heapController.getFocusedComponent());
|
|
179
|
+
}
|
|
157
180
|
};
|
|
158
181
|
screen.on('keypress', callback);
|
|
159
182
|
}
|
|
183
|
+
makeComponentFullScreen(component) {
|
|
184
|
+
this.fullScreenComponent = component;
|
|
185
|
+
this.updateAllComponentsSize();
|
|
186
|
+
}
|
|
187
|
+
makeNoComponentFullScreen() {
|
|
188
|
+
this.fullScreenComponent = null;
|
|
189
|
+
this.updateAllComponentsSize();
|
|
190
|
+
}
|
|
160
191
|
addComponentToFocusKeyMap(component) {
|
|
161
192
|
const key = `${this.currentFocuseKey++}`;
|
|
162
193
|
this.keyToComponent.set(key, component);
|
|
@@ -208,6 +239,14 @@ class CliScreen {
|
|
|
208
239
|
this.addComponentToFocusKeyMap(box);
|
|
209
240
|
return box;
|
|
210
241
|
}
|
|
242
|
+
getComponentFullScreenSize() {
|
|
243
|
+
return {
|
|
244
|
+
width: positionToNumber(this.screen.width) - 4,
|
|
245
|
+
height: positionToNumber(this.screen.height) - 5,
|
|
246
|
+
top: 2,
|
|
247
|
+
left: 2,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
211
250
|
getObjectBoxSize() {
|
|
212
251
|
return {
|
|
213
252
|
width: Math.floor(positionToNumber(this.screen.width) / 3),
|
|
@@ -282,6 +321,7 @@ class CliScreen {
|
|
|
282
321
|
'←': '',
|
|
283
322
|
'→': '',
|
|
284
323
|
Enter: 'select',
|
|
324
|
+
f: 'full screen',
|
|
285
325
|
q: 'quit',
|
|
286
326
|
};
|
|
287
327
|
const keysToFocus = Array.from(this.keyToComponent.keys());
|
|
@@ -22,6 +22,7 @@ export default class HeapViewController {
|
|
|
22
22
|
private componentIdToDataMap;
|
|
23
23
|
private componentIdToComponentMap;
|
|
24
24
|
private heap;
|
|
25
|
+
private focusedComponent;
|
|
25
26
|
private clusteredBox;
|
|
26
27
|
private referrerBox;
|
|
27
28
|
private objectBox;
|
|
@@ -30,6 +31,7 @@ export default class HeapViewController {
|
|
|
30
31
|
private retainerTracePropertyBox;
|
|
31
32
|
private scriptManager;
|
|
32
33
|
constructor(heap: IHeapSnapshot, objectCategory: ObjectCategory);
|
|
34
|
+
getFocusedComponent(): ListComponent;
|
|
33
35
|
private getFlattenHeapObjectsInfo;
|
|
34
36
|
private getFlattenClusteredObjectsInfo;
|
|
35
37
|
private shouldClusterCategory;
|
|
@@ -27,6 +27,9 @@ class HeapViewController {
|
|
|
27
27
|
this.scriptManager = new e2e_1.ScriptManager();
|
|
28
28
|
this.scriptManager.loadFromFiles();
|
|
29
29
|
}
|
|
30
|
+
getFocusedComponent() {
|
|
31
|
+
return this.focusedComponent;
|
|
32
|
+
}
|
|
30
33
|
getFlattenHeapObjectsInfo(objectCategory) {
|
|
31
34
|
let ret = [];
|
|
32
35
|
for (const category of objectCategory.keys()) {
|
|
@@ -341,7 +344,7 @@ class HeapViewController {
|
|
|
341
344
|
this.objectBox.setLabel('Objects');
|
|
342
345
|
this.setSelectedHeapObject(node);
|
|
343
346
|
if (!options.skipFocus) {
|
|
344
|
-
this.focusOnComponent(this.
|
|
347
|
+
this.focusOnComponent(this.clusteredBox.id);
|
|
345
348
|
}
|
|
346
349
|
}
|
|
347
350
|
focusOnComponent(componentId) {
|
|
@@ -349,6 +352,7 @@ class HeapViewController {
|
|
|
349
352
|
for (const component of this.componentIdToComponentMap.values()) {
|
|
350
353
|
if (component.id === componentId) {
|
|
351
354
|
component.focus();
|
|
355
|
+
this.focusedComponent = component;
|
|
352
356
|
const data = this.componentIdToDataMap.get(componentId);
|
|
353
357
|
const selectIndex = (_a = (data && data.selectedIdx)) !== null && _a !== void 0 ? _a : -1;
|
|
354
358
|
this.setSelectedHeapObjectFromComponent(componentId, selectIndex);
|
|
@@ -17,6 +17,8 @@ export declare class ComponentDataItem {
|
|
|
17
17
|
type?: string;
|
|
18
18
|
details?: Map<string, string>;
|
|
19
19
|
static getTextForDisplay(data: ComponentDataItem): string;
|
|
20
|
+
private static getHeapObjectTextContent;
|
|
21
|
+
private static getHeapEdgeTextContent;
|
|
20
22
|
private static getTextContent;
|
|
21
23
|
}
|
|
22
24
|
export declare class ComponentData {
|
|
@@ -66,6 +66,28 @@ class ComponentDataItem {
|
|
|
66
66
|
}
|
|
67
67
|
return chalk_1.default.grey(content);
|
|
68
68
|
}
|
|
69
|
+
static getHeapObjectTextContent(node) {
|
|
70
|
+
const objectType = chalk_1.default.grey(`(${node.type})`);
|
|
71
|
+
const objectId = chalk_1.default.grey(` @${node.id}`);
|
|
72
|
+
const size = core_1.utils.getReadableBytes(node.retainedSize);
|
|
73
|
+
const sizeInfo = chalk_1.default.grey(' [') + chalk_1.default.bold(chalk_1.default.blue(size)) + chalk_1.default.grey(']');
|
|
74
|
+
const objectTitle = node.isString ? '<string>' : node.name;
|
|
75
|
+
return (chalk_1.default.green('[') +
|
|
76
|
+
(isUsefulObjectForDebugging(node)
|
|
77
|
+
? chalk_1.default.green(objectTitle)
|
|
78
|
+
: chalk_1.default.bold(chalk_1.default.grey(objectTitle))) +
|
|
79
|
+
chalk_1.default.green(']') +
|
|
80
|
+
objectType +
|
|
81
|
+
objectId +
|
|
82
|
+
sizeInfo);
|
|
83
|
+
}
|
|
84
|
+
static getHeapEdgeTextContent(edge) {
|
|
85
|
+
const arrowPrefix = chalk_1.default.grey('--');
|
|
86
|
+
const arrowSuffix = chalk_1.default.grey('---') + '>';
|
|
87
|
+
const edgeType = chalk_1.default.grey(`(${edge.type})`);
|
|
88
|
+
const edgeName = edge.name_or_index;
|
|
89
|
+
return `${arrowPrefix}${edgeName}${edgeType}${arrowSuffix} `;
|
|
90
|
+
}
|
|
69
91
|
static getTextContent(data) {
|
|
70
92
|
let ret = '';
|
|
71
93
|
if (data.tag) {
|
|
@@ -74,32 +96,14 @@ class ComponentDataItem {
|
|
|
74
96
|
if (data.stringContent) {
|
|
75
97
|
ret += data.stringContent;
|
|
76
98
|
}
|
|
77
|
-
const arrowPrefix = chalk_1.default.grey('--');
|
|
78
|
-
const arrowSuffix = chalk_1.default.grey('---') + '>';
|
|
79
99
|
if (data.referrerEdge) {
|
|
80
|
-
|
|
81
|
-
const edgeName = data.referrerEdge.name_or_index;
|
|
82
|
-
ret += `${arrowPrefix}${edgeName}${edgeType}${arrowSuffix} `;
|
|
100
|
+
ret += this.getHeapEdgeTextContent(data.referrerEdge);
|
|
83
101
|
}
|
|
84
102
|
if (data.heapObject) {
|
|
85
|
-
|
|
86
|
-
const objectId = chalk_1.default.grey(` @${data.heapObject.id}`);
|
|
87
|
-
const size = core_1.utils.getReadableBytes(data.heapObject.retainedSize);
|
|
88
|
-
const sizeInfo = chalk_1.default.grey(' [') + chalk_1.default.bold(chalk_1.default.blue(size)) + chalk_1.default.grey(']');
|
|
89
|
-
ret +=
|
|
90
|
-
chalk_1.default.green('[') +
|
|
91
|
-
(isUsefulObjectForDebugging(data.heapObject)
|
|
92
|
-
? chalk_1.default.green(data.heapObject.name)
|
|
93
|
-
: chalk_1.default.bold(chalk_1.default.grey(data.heapObject.name))) +
|
|
94
|
-
chalk_1.default.green(']') +
|
|
95
|
-
objectType +
|
|
96
|
-
objectId +
|
|
97
|
-
sizeInfo;
|
|
103
|
+
ret += this.getHeapObjectTextContent(data.heapObject);
|
|
98
104
|
}
|
|
99
105
|
if (data.referenceEdge) {
|
|
100
|
-
|
|
101
|
-
const edgeName = data.referenceEdge.name_or_index;
|
|
102
|
-
ret += ` ${arrowPrefix}${edgeName}${edgeType}${arrowSuffix} `;
|
|
106
|
+
ret += this.getHeapEdgeTextContent(data.referenceEdge);
|
|
103
107
|
}
|
|
104
108
|
return ret === '' ? chalk_1.default.grey('<undefinied>') : ret;
|
|
105
109
|
}
|
|
@@ -145,6 +145,7 @@ class ListComponent {
|
|
|
145
145
|
}
|
|
146
146
|
focus() {
|
|
147
147
|
this.element.focus();
|
|
148
|
+
this.element.setFront();
|
|
148
149
|
this.element.style.border.fg = 'white';
|
|
149
150
|
this.element.style.selected = {
|
|
150
151
|
bg: 'grey',
|
|
@@ -202,7 +203,7 @@ class ListComponent {
|
|
|
202
203
|
this.element.pushItem(content[i]);
|
|
203
204
|
++this.displayedItems;
|
|
204
205
|
}
|
|
205
|
-
this.content = content;
|
|
206
|
+
this.content = content.map(v => v.replace(/\n/g, '\\n'));
|
|
206
207
|
this.horizonScrollPositionMap.clear();
|
|
207
208
|
this.insertDisplayMoreEntry();
|
|
208
209
|
this.updateContent(oldContent, this.content);
|
|
@@ -46,6 +46,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
46
46
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
47
47
|
const path_1 = __importDefault(require("path"));
|
|
48
48
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
49
|
+
const DocUtils_1 = __importDefault(require("./lib/DocUtils"));
|
|
49
50
|
const BaseCommand_1 = __importStar(require("../../BaseCommand"));
|
|
50
51
|
const core_1 = require("@memlab/core");
|
|
51
52
|
const UniversalOptions_1 = __importDefault(require("../../options/lib/UniversalOptions"));
|
|
@@ -86,6 +87,10 @@ class GenerateCLIDocCommand extends BaseCommand_1.default {
|
|
|
86
87
|
}
|
|
87
88
|
writeCommandCategories(docFile) {
|
|
88
89
|
this.writeTextWithNewLine(docFile, '# Command Line Interface');
|
|
90
|
+
this.writeTextWithNewLine(docFile, `Install the memlab command line tool with npm:
|
|
91
|
+
\`\`\`bash
|
|
92
|
+
npm install -g memlab
|
|
93
|
+
\`\`\``);
|
|
89
94
|
for (const category in BaseCommand_1.CommandCategory) {
|
|
90
95
|
const commandsToPrintFirst = [];
|
|
91
96
|
this.writeCategory(docFile, category, commandsToPrintFirst);
|
|
@@ -131,25 +136,28 @@ class GenerateCLIDocCommand extends BaseCommand_1.default {
|
|
|
131
136
|
this.writeTextWithNewLine(docFile, `\n## ${categoryName} Commands\n`);
|
|
132
137
|
}
|
|
133
138
|
writeCommand(docFile, command, indent = '') {
|
|
139
|
+
var _a, _b;
|
|
134
140
|
const name = command.getFullCommand();
|
|
135
|
-
const desc = core_1.utils.upperCaseFirstCharacter(command.getDescription());
|
|
141
|
+
const desc = core_1.utils.upperCaseFirstCharacter(command.getDescription().trim());
|
|
142
|
+
const cmdDoc = command.getDocumenation().trim();
|
|
136
143
|
// write command title
|
|
137
144
|
this.writeTextWithNewLine(docFile, `\n###${indent} memlab ${name}\n`);
|
|
138
145
|
// write description
|
|
139
146
|
this.writeTextWithNewLine(docFile, `${desc}\n`);
|
|
147
|
+
// write detailed command documentation
|
|
148
|
+
if (cmdDoc.length > 0) {
|
|
149
|
+
this.writeTextWithNewLine(docFile, `${cmdDoc}\n`);
|
|
150
|
+
}
|
|
140
151
|
// get example
|
|
141
152
|
const examples = command.getExamples();
|
|
142
|
-
|
|
143
|
-
if (examples.length > 0) {
|
|
144
|
-
example = examples[0].trim();
|
|
145
|
-
}
|
|
153
|
+
const example = (_a = examples[0]) !== null && _a !== void 0 ? _a : '';
|
|
146
154
|
// write command synopsis
|
|
147
|
-
const cmd =
|
|
155
|
+
const cmd = DocUtils_1.default.generateExampleCommand(name, example);
|
|
148
156
|
this.writeCodeBlock(docFile, cmd, 'bash');
|
|
149
157
|
// write command examples if there is any
|
|
150
158
|
const exampleBlock = examples
|
|
151
159
|
.slice(1)
|
|
152
|
-
.map(example =>
|
|
160
|
+
.map(example => DocUtils_1.default.generateExampleCommand(name, example))
|
|
153
161
|
.join('\n');
|
|
154
162
|
if (exampleBlock.length > 0) {
|
|
155
163
|
this.writeTextWithNewLine(docFile, '\n#### examples\n');
|
|
@@ -157,7 +165,7 @@ class GenerateCLIDocCommand extends BaseCommand_1.default {
|
|
|
157
165
|
}
|
|
158
166
|
// write options
|
|
159
167
|
this.writeCommandOptions(docFile, command);
|
|
160
|
-
const subCommands = command.getSubCommands();
|
|
168
|
+
const subCommands = (_b = command.getSubCommands()) !== null && _b !== void 0 ? _b : [];
|
|
161
169
|
for (const subCommand of subCommands) {
|
|
162
170
|
this.writeCommand(docFile, subCommand, indent + '#');
|
|
163
171
|
}
|
|
@@ -7,20 +7,23 @@
|
|
|
7
7
|
* @format
|
|
8
8
|
* @oncall web_perf_infra
|
|
9
9
|
*/
|
|
10
|
-
import type { BaseOption, CLIOptions } from '@memlab/core';
|
|
10
|
+
import type { BaseOption, CLIOptions, CommandOptionExample } from '@memlab/core';
|
|
11
11
|
import BaseCommand from '../../BaseCommand';
|
|
12
|
-
declare type
|
|
12
|
+
declare type PrintCommandOptions = {
|
|
13
|
+
printOptions?: boolean;
|
|
14
|
+
printDoc?: boolean;
|
|
15
|
+
};
|
|
16
|
+
declare type HelperOption = CLIOptions & PrintCommandOptions & {
|
|
13
17
|
modules: Map<string, BaseCommand>;
|
|
14
18
|
command: BaseCommand | null;
|
|
15
19
|
indent?: string;
|
|
16
|
-
printOptions?: boolean;
|
|
17
20
|
};
|
|
18
21
|
export default class HelperCommand extends BaseCommand {
|
|
19
22
|
private printedCommand;
|
|
20
23
|
private universalOptions;
|
|
21
24
|
getCommandName(): string;
|
|
22
25
|
getDescription(): string;
|
|
23
|
-
getExamples():
|
|
26
|
+
getExamples(): CommandOptionExample[];
|
|
24
27
|
setUniversalOptions(options: BaseOption[]): void;
|
|
25
28
|
private printHeader;
|
|
26
29
|
private printCommandCategories;
|
|
@@ -48,10 +48,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
48
48
|
const chalk_1 = __importDefault(require("chalk"));
|
|
49
49
|
const string_width_1 = __importDefault(require("string-width"));
|
|
50
50
|
const core_1 = require("@memlab/core");
|
|
51
|
+
const heap_analysis_1 = require("@memlab/heap-analysis");
|
|
52
|
+
const DocUtils_1 = __importDefault(require("./lib/DocUtils"));
|
|
51
53
|
const CommandOrder_1 = __importDefault(require("./lib/CommandOrder"));
|
|
52
54
|
const BaseCommand_1 = __importStar(require("../../BaseCommand"));
|
|
53
55
|
const UniversalOptions_1 = __importDefault(require("../../options/lib/UniversalOptions"));
|
|
54
|
-
const
|
|
56
|
+
const CLIUtils_1 = require("../../lib/CLIUtils");
|
|
55
57
|
class HelperCommand extends BaseCommand_1.default {
|
|
56
58
|
constructor() {
|
|
57
59
|
super(...arguments);
|
|
@@ -137,7 +139,7 @@ class HelperCommand extends BaseCommand_1.default {
|
|
|
137
139
|
if (options.length === 0) {
|
|
138
140
|
return '';
|
|
139
141
|
}
|
|
140
|
-
const width = Math.min(
|
|
142
|
+
const width = Math.min(CLIUtils_1.READABLE_CMD_FLAG_WIDTH, process.stdout.columns);
|
|
141
143
|
let summary = '';
|
|
142
144
|
let curLine = chalk_1.default.bold(`${indent}Options:`);
|
|
143
145
|
for (const option of options) {
|
|
@@ -177,37 +179,40 @@ class HelperCommand extends BaseCommand_1.default {
|
|
|
177
179
|
}
|
|
178
180
|
formatOptionText(optionText, indent, headerLength) {
|
|
179
181
|
const header = chalk_1.default.green(optionText.header);
|
|
180
|
-
const prefix =
|
|
182
|
+
const prefix = (0, CLIUtils_1.getBlankSpaceString)(headerLength - optionText.header.length);
|
|
181
183
|
const headerString = `${indent}${prefix}${header} `;
|
|
182
184
|
const headerStringWidth = (0, string_width_1.default)(headerString);
|
|
183
|
-
const maxWidth = Math.min(process.stdout.columns,
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
descString += core_1.utils.repeat(' ', headerStringWidth);
|
|
190
|
-
descString += descStringRemain.substring(0, descMaxWidth);
|
|
191
|
-
descStringRemain = descStringRemain.substring(descMaxWidth);
|
|
192
|
-
}
|
|
193
|
-
return `${headerString}${descString}`;
|
|
185
|
+
const maxWidth = Math.min(process.stdout.columns, CLIUtils_1.READABLE_TEXT_WIDTH);
|
|
186
|
+
const descString = (0, CLIUtils_1.alignTextInBlock)(optionText.desc, {
|
|
187
|
+
leftIndent: headerStringWidth,
|
|
188
|
+
lineLength: maxWidth,
|
|
189
|
+
});
|
|
190
|
+
return `${headerString}${descString.substring(headerStringWidth)}`;
|
|
194
191
|
}
|
|
195
|
-
printCommand(command, extraIndent = '',
|
|
192
|
+
printCommand(command, extraIndent = '', options = {}) {
|
|
193
|
+
var _a;
|
|
196
194
|
const indent = ' ' + extraIndent;
|
|
197
195
|
const name = command.getFullCommand();
|
|
198
|
-
const desc = core_1.utils.upperCaseFirstCharacter(command.getDescription());
|
|
196
|
+
const desc = core_1.utils.upperCaseFirstCharacter(command.getDescription().trim());
|
|
197
|
+
const cmdDoc = command.getDocumenation().trim();
|
|
199
198
|
// get example
|
|
200
199
|
const examples = command.getExamples();
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
200
|
+
const example = (_a = examples[0]) !== null && _a !== void 0 ? _a : '';
|
|
201
|
+
// write command synopsis
|
|
202
|
+
const cmd = DocUtils_1.default.generateExampleCommand(name, example, {
|
|
203
|
+
descriptionAsBashComment: false,
|
|
204
|
+
});
|
|
206
205
|
let msg = `${indent}${cmd}`;
|
|
207
206
|
msg += `\n${indent}${desc}`;
|
|
207
|
+
if (options.printOptions && cmdDoc.length > 0) {
|
|
208
|
+
const cmdDocBlock = (0, CLIUtils_1.alignTextInBlock)(cmdDoc, {
|
|
209
|
+
leftIndent: indent.length + 2,
|
|
210
|
+
});
|
|
211
|
+
msg += `\n\n${chalk_1.default.grey(cmdDocBlock)}`;
|
|
212
|
+
}
|
|
208
213
|
core_1.info.topLevel(msg);
|
|
209
214
|
// print options info
|
|
210
|
-
if (printOptions) {
|
|
215
|
+
if (options.printOptions) {
|
|
211
216
|
// print full options description
|
|
212
217
|
this.printOptions(command, indent);
|
|
213
218
|
}
|
|
@@ -221,11 +226,12 @@ class HelperCommand extends BaseCommand_1.default {
|
|
|
221
226
|
core_1.info.topLevel('');
|
|
222
227
|
}
|
|
223
228
|
printHelperTextForCommand(command, options) {
|
|
229
|
+
var _a;
|
|
224
230
|
return __awaiter(this, void 0, void 0, function* () {
|
|
225
231
|
// print helper text for a specific command
|
|
226
|
-
this.printCommand(command, options.indent, options
|
|
232
|
+
this.printCommand(command, options.indent, options);
|
|
227
233
|
// print helper text for its subcommands
|
|
228
|
-
const subcommands = command.getSubCommands();
|
|
234
|
+
const subcommands = (_a = command.getSubCommands()) !== null && _a !== void 0 ? _a : [];
|
|
229
235
|
const subOptions = Object.assign({}, options);
|
|
230
236
|
subOptions.indent = (subOptions.indent || '') + ' ';
|
|
231
237
|
for (const subcommand of subcommands) {
|
|
@@ -235,6 +241,7 @@ class HelperCommand extends BaseCommand_1.default {
|
|
|
235
241
|
});
|
|
236
242
|
}
|
|
237
243
|
printFullHelperTextForCommand(args, modules) {
|
|
244
|
+
var _a, _b;
|
|
238
245
|
return __awaiter(this, void 0, void 0, function* () {
|
|
239
246
|
// get the command to print
|
|
240
247
|
let map = modules;
|
|
@@ -249,7 +256,7 @@ class HelperCommand extends BaseCommand_1.default {
|
|
|
249
256
|
if (!command) {
|
|
250
257
|
break;
|
|
251
258
|
}
|
|
252
|
-
const subCommands = command.getSubCommands();
|
|
259
|
+
const subCommands = (_a = command.getSubCommands()) !== null && _a !== void 0 ? _a : [];
|
|
253
260
|
map = new Map(subCommands.map((cmd) => [cmd.getCommandName(), cmd]));
|
|
254
261
|
}
|
|
255
262
|
if (!command) {
|
|
@@ -257,9 +264,9 @@ class HelperCommand extends BaseCommand_1.default {
|
|
|
257
264
|
}
|
|
258
265
|
// print the helper text of the command
|
|
259
266
|
core_1.info.topLevel('');
|
|
260
|
-
this.printCommand(command, '', true);
|
|
267
|
+
this.printCommand(command, '', { printOptions: true, printDoc: true });
|
|
261
268
|
// print the helper text of the subcommands
|
|
262
|
-
const subCommands = command.getSubCommands();
|
|
269
|
+
const subCommands = (_b = command.getSubCommands()) !== null && _b !== void 0 ? _b : [];
|
|
263
270
|
if (subCommands.length > 0) {
|
|
264
271
|
core_1.info.topLevel(chalk_1.default.bold(' SUB-COMMANDS\n'));
|
|
265
272
|
for (const subCommand of subCommands) {
|
|
@@ -0,0 +1,19 @@
|
|
|
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 { CommandOptionExample } from '@memlab/core';
|
|
11
|
+
declare type GenerateExampleCommandOption = {
|
|
12
|
+
descriptionAsBashComment?: boolean;
|
|
13
|
+
};
|
|
14
|
+
declare function generateExampleCommand(command: string, cliExample: CommandOptionExample, options?: GenerateExampleCommandOption): string;
|
|
15
|
+
declare const _default: {
|
|
16
|
+
generateExampleCommand: typeof generateExampleCommand;
|
|
17
|
+
};
|
|
18
|
+
export default _default;
|
|
19
|
+
//# sourceMappingURL=DocUtils.d.ts.map
|
|
@@ -0,0 +1,39 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
function generateExampleCommand(command, cliExample, options = {}) {
|
|
13
|
+
if (typeof cliExample === 'string') {
|
|
14
|
+
return exampleFromCliOptionString(command, cliExample);
|
|
15
|
+
}
|
|
16
|
+
let commandExample = '';
|
|
17
|
+
if (cliExample.description != null &&
|
|
18
|
+
// if it's not null, undefined, or true
|
|
19
|
+
options.descriptionAsBashComment !== false) {
|
|
20
|
+
const desc = cliExample.description.trim();
|
|
21
|
+
if (desc.length > 0) {
|
|
22
|
+
// inject the description as a bash command in the bash example
|
|
23
|
+
const bashText = cliExample.description
|
|
24
|
+
.trim()
|
|
25
|
+
.split('\n')
|
|
26
|
+
.map(line => `# ${line.trim()}`)
|
|
27
|
+
.join('\n');
|
|
28
|
+
commandExample += bashText + '\n';
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
commandExample += exampleFromCliOptionString(command, cliExample.cliOptionExample);
|
|
32
|
+
return commandExample;
|
|
33
|
+
}
|
|
34
|
+
function exampleFromCliOptionString(command, cliExample) {
|
|
35
|
+
return `memlab ${command} ${cliExample.trim()}`;
|
|
36
|
+
}
|
|
37
|
+
exports.default = {
|
|
38
|
+
generateExampleCommand,
|
|
39
|
+
};
|
|
@@ -7,14 +7,14 @@
|
|
|
7
7
|
* @format
|
|
8
8
|
* @oncall web_perf_infra
|
|
9
9
|
*/
|
|
10
|
-
import type { CLIOptions } from '@memlab/core';
|
|
10
|
+
import type { CLIOptions, CommandOptionExample } from '@memlab/core';
|
|
11
11
|
import BaseCommand from '../../BaseCommand';
|
|
12
12
|
import { BaseOption } from '@memlab/core';
|
|
13
13
|
export default class TakeSnapshotCommand extends BaseCommand {
|
|
14
14
|
getCommandName(): string;
|
|
15
15
|
getDescription(): string;
|
|
16
16
|
getPrerequisites(): BaseCommand[];
|
|
17
|
-
getExamples():
|
|
17
|
+
getExamples(): CommandOptionExample[];
|
|
18
18
|
getOptions(): BaseOption[];
|
|
19
19
|
run(_options: CLIOptions): Promise<void>;
|
|
20
20
|
}
|
|
@@ -45,6 +45,7 @@ const DisableWebSecurityOption_1 = __importDefault(require("../../options/e2e/Di
|
|
|
45
45
|
const EnableJSRewriteOption_1 = __importDefault(require("../../options/e2e/EnableJSRewriteOption"));
|
|
46
46
|
const EnableJSInterceptOption_1 = __importDefault(require("../../options/e2e/EnableJSInterceptOption"));
|
|
47
47
|
const TargetWorkerOption_1 = __importDefault(require("../../options/e2e/TargetWorkerOption"));
|
|
48
|
+
const SetChromiumBinaryOption_1 = __importDefault(require("../../options/e2e/SetChromiumBinaryOption"));
|
|
48
49
|
class TakeSnapshotCommand extends BaseCommand_1.default {
|
|
49
50
|
getCommandName() {
|
|
50
51
|
return 'snapshot';
|
|
@@ -80,6 +81,7 @@ class TakeSnapshotCommand extends BaseCommand_1.default {
|
|
|
80
81
|
new RunningModeOption_1.default(),
|
|
81
82
|
new RemoteBrowserDebugOption_1.default(),
|
|
82
83
|
new ScenarioFileOption_1.default(),
|
|
84
|
+
new SetChromiumBinaryOption_1.default(),
|
|
83
85
|
new SetDeviceOption_1.default(),
|
|
84
86
|
new SetUserAgentOption_1.default(),
|
|
85
87
|
new DisableXvfbOption_1.default(),
|
|
@@ -7,11 +7,13 @@
|
|
|
7
7
|
* @format
|
|
8
8
|
* @oncall web_perf_infra
|
|
9
9
|
*/
|
|
10
|
-
import type { CLIOptions } from '@memlab/core';
|
|
10
|
+
import type { CLIOptions, CommandOptionExample } from '@memlab/core';
|
|
11
11
|
import BaseCommand from '../../BaseCommand';
|
|
12
12
|
export default class WarmupAndSnapshotCommand extends BaseCommand {
|
|
13
13
|
getCommandName(): string;
|
|
14
14
|
getDescription(): string;
|
|
15
|
+
getDocumenation(): string;
|
|
16
|
+
getExamples(): CommandOptionExample[];
|
|
15
17
|
getPrerequisites(): BaseCommand[];
|
|
16
18
|
run(_options: CLIOptions): Promise<void>;
|
|
17
19
|
}
|
|
@@ -22,6 +22,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
22
22
|
};
|
|
23
23
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
24
|
const BaseCommand_1 = __importDefault(require("../../BaseCommand"));
|
|
25
|
+
const SetWorkingDirectoryOption_1 = __importDefault(require("../../options/SetWorkingDirectoryOption"));
|
|
25
26
|
const InitDirectoryCommand_1 = __importDefault(require("../InitDirectoryCommand"));
|
|
26
27
|
const WarmupAppCommand_1 = __importDefault(require("../WarmupAppCommand"));
|
|
27
28
|
const TakeSnapshotCommand_1 = __importDefault(require("./TakeSnapshotCommand"));
|
|
@@ -32,6 +33,28 @@ class WarmupAndSnapshotCommand extends BaseCommand_1.default {
|
|
|
32
33
|
getDescription() {
|
|
33
34
|
return 'Warm up server and take heap snapshots';
|
|
34
35
|
}
|
|
36
|
+
getDocumenation() {
|
|
37
|
+
const warmupCommand = new WarmupAppCommand_1.default();
|
|
38
|
+
const warmupCLI = `memlab ${warmupCommand.getCommandName()}`;
|
|
39
|
+
const takeSnapshotCommand = new TakeSnapshotCommand_1.default();
|
|
40
|
+
const snapshotCLI = `memlab ${takeSnapshotCommand.getCommandName()}`;
|
|
41
|
+
return ('This is equivalent to running ' +
|
|
42
|
+
`\`${warmupCLI}\` and \`${snapshotCLI}\`.`);
|
|
43
|
+
}
|
|
44
|
+
getExamples() {
|
|
45
|
+
return [
|
|
46
|
+
{
|
|
47
|
+
description: 'specify a test scenario file, memlab will ' +
|
|
48
|
+
'warm up the server and take heap snapshots',
|
|
49
|
+
cliOptionExample: '--scenario <TEST_SCENARIO_FILE>',
|
|
50
|
+
},
|
|
51
|
+
'--scenario /tmp/test-scenario.js',
|
|
52
|
+
{
|
|
53
|
+
description: new SetWorkingDirectoryOption_1.default().getDescription(),
|
|
54
|
+
cliOptionExample: '--scenario /tmp/test-scenario.js --work-dir /tmp/test-1/',
|
|
55
|
+
},
|
|
56
|
+
];
|
|
57
|
+
}
|
|
35
58
|
getPrerequisites() {
|
|
36
59
|
return [
|
|
37
60
|
new InitDirectoryCommand_1.default(),
|
|
@@ -0,0 +1,22 @@
|
|
|
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 { ParsedArgs } from 'minimist';
|
|
11
|
+
import type { AnyRecord } from '@memlab/core';
|
|
12
|
+
export declare type BlockTextOption = {
|
|
13
|
+
leftIndent?: number;
|
|
14
|
+
lineLength?: number;
|
|
15
|
+
};
|
|
16
|
+
export declare const READABLE_CMD_FLAG_WIDTH = 70;
|
|
17
|
+
export declare const READABLE_TEXT_WIDTH = 150;
|
|
18
|
+
export declare function filterAndGetUndefinedArgs(cliArgs: ParsedArgs): AnyRecord;
|
|
19
|
+
export declare function argsToString(args: AnyRecord): string;
|
|
20
|
+
export declare function getBlankSpaceString(length: number): string;
|
|
21
|
+
export declare function alignTextInBlock(text: string, options: BlockTextOption): string;
|
|
22
|
+
//# sourceMappingURL=CLIUtils.d.ts.map
|