Remove hermes-inspector-msggen (#48465)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48465

Removes the `packages/hermes-inspector-msggen` workspace. Since https://github.com/facebook/react-native/pull/39300, this is no longer referenced in React Native, and is now part of the Hermes repo.

Changelog: [Internal]

Reviewed By: cortinico, hoxyq

Differential Revision: D67791612

fbshipit-source-id: 73da135b264d8df632fefe87cc4e3101075ae98c
This commit is contained in:
Alex Hunt
2025-01-03 04:29:09 -08:00
committed by Facebook GitHub Bot
parent f20486cc77
commit 3e0fc8899b
24 changed files with 8 additions and 2600 deletions
@@ -1,8 +0,0 @@
{
"presets": [ "@babel/preset-flow", ["@babel/preset-env", {
"targets": {
"node": "current"
}
}]
],
}
@@ -1,3 +0,0 @@
.DS_Store
bin/
node_modules/
@@ -1,21 +0,0 @@
# @react-native/hermes-inspector-msggen
[![Version][version-badge]][package]
## Installation
```
yarn add --dev @babel/cli @babel/core @babel/preset-env @babel/preset-flow jest @react-native/hermes-inspector-msggen
```
*Note: We're using `yarn` to install deps. Feel free to change commands to use `npm` 3+ and `npx` if you like*
[version-badge]: https://img.shields.io/npm/v/@react-native/hermes-inspector-msggen?style=flat-square
[package]: https://www.npmjs.com/package/@react-native/hermes-inspector-msggen
## Testing
To run the tests in this package, run the following commands from the React Native root folder:
1. `yarn` to install the dependencies. You just need to run this once
2. `yarn jest packages/hermes-inspector-msggen`.
@@ -1,62 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import {Command} from '../src/Command.js';
test('parses simple command', () => {
let obj = {
'name': 'setBreakpointsActive',
'parameters': [
{ 'name': 'active', 'type': 'boolean', 'description': 'New value for breakpoints active state.' },
],
'description': 'Activates / deactivates all breakpoints on the page.',
};
let command = Command.create('Debugger', obj, false);
expect(command.domain).toBe('Debugger');
expect(command.name).toBe('setBreakpointsActive');
expect(command.description).toBe('Activates / deactivates all breakpoints on the page.');
expect(command.parameters.map(p => p.name)).toEqual(['active']);
expect(command.returns.length).toBe(0);
expect(command.getDebuggerName()).toBe('Debugger.setBreakpointsActive');
expect(command.getCppNamespace()).toBe('debugger');
expect(command.getRequestCppType()).toBe('SetBreakpointsActiveRequest');
expect(command.getResponseCppType()).toBeUndefined();
expect(command.getForwardDecls()).toEqual(['struct SetBreakpointsActiveRequest;']);
});
test('parses command with return', () => {
let obj = {
'name': 'setBreakpoint',
'parameters': [
{ 'name': 'location', '$ref': 'Location', 'description': 'Location to set breakpoint in.' },
{ 'name': 'condition', 'type': 'string', 'optional': true, 'description': 'Expression to use as a breakpoint condition. When specified, debugger will only stop on the breakpoint if this expression evaluates to true.' },
],
'returns': [
{ 'name': 'breakpointId', '$ref': 'BreakpointId', 'description': 'Id of the created breakpoint for further reference.' },
{ 'name': 'actualLocation', '$ref': 'Location', 'description': 'Location this breakpoint resolved into.' },
],
'description': 'Sets JavaScript breakpoint at a given location.',
};
let command = Command.create('Debugger', obj, false);
expect(command.domain).toBe('Debugger');
expect(command.name).toBe('setBreakpoint');
expect(command.description).toBe('Sets JavaScript breakpoint at a given location.');
expect(command.parameters.map(p => p.name)).toEqual(['location', 'condition']);
expect(command.returns.map(p => p.name)).toEqual(['breakpointId', 'actualLocation']);
expect(command.getDebuggerName()).toBe('Debugger.setBreakpoint');
expect(command.getCppNamespace()).toBe('debugger');
expect(command.getRequestCppType()).toBe('SetBreakpointRequest');
expect(command.getResponseCppType()).toBe('SetBreakpointResponse');
expect(command.getForwardDecls()).toEqual([
'struct SetBreakpointRequest;',
'struct SetBreakpointResponse;',
]);
});
@@ -1,47 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import {Event} from '../src/Event.js';
test('parses simple event', () => {
let obj = {
'name': 'resumed',
'description': 'Fired when the virtual machine resumed execution.',
};
let event = Event.create('Debugger', obj, false);
expect(event.domain).toBe('Debugger');
expect(event.name).toBe('resumed');
expect(event.description).toBe('Fired when the virtual machine resumed execution.');
expect(event.getDebuggerName()).toBe('Debugger.resumed');
expect(event.getCppNamespace()).toBe('debugger');
expect(event.getCppType()).toBe('ResumedNotification');
expect(event.getForwardDecls()).toEqual(['struct ResumedNotification;']);
});
test('parses event with params', () => {
let obj = {
'name': 'breakpointResolved',
'parameters': [
{ 'name': 'breakpointId', '$ref': 'BreakpointId', 'description': 'Breakpoint unique identifier.' },
{ 'name': 'location', '$ref': 'Location', 'description': 'Actual breakpoint location.' },
],
'description': 'Fired when breakpoint is resolved to an actual script and location.',
};
let event = Event.create('Debugger', obj, false);
expect(event.domain).toBe('Debugger');
expect(event.name).toBe('breakpointResolved');
expect(event.description).toBe('Fired when breakpoint is resolved to an actual script and location.');
expect(event.parameters.map(p => p.name)).toEqual(['breakpointId', 'location']);
expect(event.getDebuggerName()).toBe('Debugger.breakpointResolved');
expect(event.getCppNamespace()).toBe('debugger');
expect(event.getCppType()).toBe('BreakpointResolvedNotification');
expect(event.getForwardDecls()).toEqual(['struct BreakpointResolvedNotification;']);
});
@@ -1,80 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import {Graph} from '../src/Graph.js';
// graph looks like this before test: https://pxl.cl/9k8t
let graph = null;
beforeEach(() => {
graph = new Graph();
graph.addEdge('A1', 'B1');
graph.addEdge('A2', 'B1');
graph.addEdge('A2', 'B2');
graph.addEdge('A3', 'B2');
graph.addEdge('A3', 'C3');
graph.addEdge('B1', 'C1');
graph.addEdge('B1', 'C2');
graph.addEdge('B1', 'C3');
graph.addEdge('B2', 'C2');
});
// checks id1 occurs after id2 in arr
function expectOccursAfter(arr, id1, id2) {
const idx1 = arr.indexOf(id1);
const idx2 = arr.indexOf(id2);
expect(idx1).not.toBe(-1);
expect(idx2).not.toBe(-1);
expect(idx1).toBeGreaterThan(idx2);
}
test('detects cycle', () => {
graph.addEdge('C2', 'A1');
const {cycles} = graph.traverse(['A2']);
expect(cycles).toContainEqual({from: 'A1', to: 'B1'});
});
test('checks for presence of root', () => {
expect(() => graph.traverse(['A1', 'NX'])).toThrow(/^No node/);
});
test('traverses partial graph', () => {
const {nodes: ids} = graph.traverse(['B1', 'A3']);
// Check that expected nodes are there
const sortedIds = ids.slice().sort();
expect(sortedIds).toEqual(['A3', 'B1', 'B2', 'C1', 'C2', 'C3']);
// Check that the result is topologically sorted
expectOccursAfter(ids, 'A3', 'B2');
expectOccursAfter(ids, 'A3', 'C3');
expectOccursAfter(ids, 'B1', 'C1');
expectOccursAfter(ids, 'B1', 'C2');
expectOccursAfter(ids, 'B1', 'C3');
expectOccursAfter(ids, 'B2', 'C2');
});
test('traverses complete graph', () => {
const {nodes: ids} = graph.traverse(['A1', 'A2', 'A3']);
// Check that expected nodes are there
const sortedIds = ids.slice().sort();
expect(sortedIds).toEqual(['A1', 'A2', 'A3', 'B1', 'B2', 'C1', 'C2', 'C3']);
// Check that the result is topologically sorted
expectOccursAfter(ids, 'A1', 'B1');
expectOccursAfter(ids, 'A2', 'B1');
expectOccursAfter(ids, 'A2', 'B2');
expectOccursAfter(ids, 'A3', 'B2');
expectOccursAfter(ids, 'A3', 'C3');
expectOccursAfter(ids, 'B1', 'C1');
expectOccursAfter(ids, 'B1', 'C2');
expectOccursAfter(ids, 'B1', 'C3');
expectOccursAfter(ids, 'B2', 'C2');
});
@@ -1,132 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import { Command } from '../src/Command';
import { Event } from '../src/Event';
import {
emitNotificationDecl,
emitRequestDecl,
emitResponseDecl,
emitTypeDecl,
} from '../src/HeaderWriter';
import {FakeWritable, expectCodeIsEqual} from '../src/TestHelpers';
import { Type } from '../src/Type';
let stream = null;
beforeEach(() => {
stream = new FakeWritable();
});
test('emits type decl', () => {
let obj = {
'id': 'Location',
'type': 'object',
'properties': [
{ 'name': 'scriptId', '$ref': 'Runtime.ScriptId', 'description': 'Script identifier as reported in the <code>Debugger.scriptParsed</code>.' },
{ 'name': 'lineNumber', 'type': 'integer', 'description': 'Line number in the script (0-based).' },
{ 'name': 'columnNumber', 'type': 'integer', 'optional': true, 'description': 'Column number in the script (0-based).' },
],
'description': 'Location in the source code.',
};
let type = Type.create('Debugger', obj);
emitTypeDecl(stream, type);
expectCodeIsEqual(stream.get(), `
struct debugger::Location : public Serializable {
Location() = default;
Location(Location&&) = default;
Location(const Location&) = delete;
explicit Location(const folly::dynamic &obj);
folly::dynamic toDynamic() const override;
Location& operator=(const Location&) = delete;
Location& operator=(Location&&) = default;
runtime::ScriptId scriptId{};
int lineNumber{};
std::optional<int> columnNumber;
};
`);
});
test('emits request decl', () => {
let obj = {
'name': 'getScriptSource',
'parameters': [
{ 'name': 'scriptId', '$ref': 'Runtime.ScriptId', 'description': 'Id of the script to get source for.' },
],
'returns': [
{ 'name': 'scriptSource', 'type': 'string', 'description': 'Script source.' },
],
'description': 'Returns source for the script with given id.',
};
let command = Command.create('Debugger', obj);
emitRequestDecl(stream, command);
expectCodeIsEqual(stream.get(), `
struct debugger::GetScriptSourceRequest : public Request {
GetScriptSourceRequest();
explicit GetScriptSourceRequest(const folly::dynamic &obj);
folly::dynamic toDynamic() const override;
void accept(RequestHandler &handler) const override;
runtime::ScriptId scriptId{};
};
`);
});
test('emits response decl', () => {
let obj = {
'name': 'getScriptSource',
'parameters': [
{ 'name': 'scriptId', '$ref': 'Runtime.ScriptId', 'description': 'Id of the script to get source for.' },
],
'returns': [
{ 'name': 'scriptSource', 'type': 'string', 'description': 'Script source.' },
],
'description': 'Returns source for the script with given id.',
};
let command = Command.create('Debugger', obj);
emitResponseDecl(stream, command);
expectCodeIsEqual(stream.get(), `
struct debugger::GetScriptSourceResponse : public Response {
GetScriptSourceResponse() = default;
explicit GetScriptSourceResponse(const folly::dynamic &obj);
folly::dynamic toDynamic() const override;
std::string scriptSource;
};
`);
});
test('emits notification decl', () => {
let obj = {
'name': 'messageAdded',
'parameters': [
{ 'name': 'message', '$ref': 'ConsoleMessage', 'description': 'Console message that has been added.' },
],
'description': 'Issued when new console message is added.',
};
let event = Event.create('Console', obj);
emitNotificationDecl(stream, event);
expectCodeIsEqual(stream.get(), `
struct console::MessageAddedNotification : public Notification {
MessageAddedNotification();
explicit MessageAddedNotification(const folly::dynamic &obj);
folly::dynamic toDynamic() const override;
console::ConsoleMessage message{};
};
`);
});
@@ -1,171 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import { Command } from '../src/Command';
import { Event } from '../src/Event';
import {
emitNotificationDef,
emitRequestDef,
emitResponseDef,
emitTypeDef,
} from '../src/ImplementationWriter';
import {FakeWritable, expectCodeIsEqual} from '../src/TestHelpers';
import { Type } from '../src/Type';
let stream = null;
beforeEach(() => {
stream = new FakeWritable();
});
test('emits type def', () => {
let obj = {
'id': 'Location',
'type': 'object',
'properties': [
{ 'name': 'scriptId', '$ref': 'Runtime.ScriptId', 'description': 'Script identifier as reported in the <code>Debugger.scriptParsed</code>.' },
{ 'name': 'lineNumber', 'type': 'integer', 'description': 'Line number in the script (0-based).' },
{ 'name': 'columnNumber', 'type': 'integer', 'optional': true, 'description': 'Column number in the script (0-based).' },
],
'description': 'Location in the source code.',
};
let type = Type.create('Debugger', obj);
emitTypeDef(stream, type);
expectCodeIsEqual(stream.get(), `
debugger::Location::Location(const dynamic &obj) {
assign(scriptId, obj, "scriptId");
assign(lineNumber, obj, "lineNumber");
assign(columnNumber, obj, "columnNumber");
}
dynamic debugger::Location::toDynamic() const {
dynamic obj = dynamic::object;
put(obj, "scriptId", scriptId);
put(obj, "lineNumber", lineNumber);
put(obj, "columnNumber", columnNumber);
return obj;
}
`);
});
test('emits request def', () => {
let obj = {
'name': 'getScriptSource',
'parameters': [
{ 'name': 'scriptId', '$ref': 'Runtime.ScriptId', 'description': 'Id of the script to get source for.' },
],
'returns': [
{ 'name': 'scriptSource', 'type': 'string', 'description': 'Script source.' },
],
'description': 'Returns source for the script with given id.',
};
let command = Command.create('Debugger', obj);
emitRequestDef(stream, command);
expectCodeIsEqual(stream.get(), `
debugger::GetScriptSourceRequest::GetScriptSourceRequest()
: Request("Debugger.getScriptSource") {}
debugger::GetScriptSourceRequest::GetScriptSourceRequest(const dynamic &obj)
: Request("Debugger.getScriptSource") {
assign(id, obj, "id");
assign(method, obj, "method");
dynamic params = obj.at("params");
assign(scriptId, params, "scriptId");
}
dynamic debugger::GetScriptSourceRequest::toDynamic() const {
dynamic params = dynamic::object;
put(params, "scriptId", scriptId);
dynamic obj = dynamic::object;
put(obj, "id", id);
put(obj, "method", method);
put(obj, "params", std::move(params));
return obj;
}
void debugger::GetScriptSourceRequest::accept(RequestHandler &handler) const {
handler.handle(*this);
}
`);
});
test('emits response def', () => {
let obj = {
'name': 'getScriptSource',
'parameters': [
{ 'name': 'scriptId', '$ref': 'Runtime.ScriptId', 'description': 'Id of the script to get source for.' },
],
'returns': [
{ 'name': 'scriptSource', 'type': 'string', 'description': 'Script source.' },
],
'description': 'Returns source for the script with given id.',
};
let command = Command.create('Debugger', obj);
emitResponseDef(stream, command);
expectCodeIsEqual(stream.get(), `
debugger::GetScriptSourceResponse::GetScriptSourceResponse(const dynamic &obj) {
assign(id, obj, "id");
dynamic res = obj.at("result");
assign(scriptSource, res, "scriptSource");
}
dynamic debugger::GetScriptSourceResponse::toDynamic() const {
dynamic res = dynamic::object;
put(res, "scriptSource", scriptSource);
dynamic obj = dynamic::object;
put(obj, "id", id);
put(obj, "result", std::move(res));
return obj;
}
`);
});
test('emits notification def', () => {
let obj = {
'name': 'messageAdded',
'parameters': [
{ 'name': 'message', '$ref': 'ConsoleMessage', 'description': 'Console message that has been added.' },
],
'description': 'Issued when new console message is added.',
};
let event = Event.create('Console', obj);
emitNotificationDef(stream, event);
expectCodeIsEqual(stream.get(), `
console::MessageAddedNotification::MessageAddedNotification()
: Notification("Console.messageAdded") {}
console::MessageAddedNotification::MessageAddedNotification(const dynamic &obj)
: Notification("Console.messageAdded") {
assign(method, obj, "method");
dynamic params = obj.at("params");
assign(message, params, "message");
}
dynamic console::MessageAddedNotification::toDynamic() const {
dynamic params = dynamic::object;
put(params, "message", message);
dynamic obj = dynamic::object;
put(obj, "method", method);
put(obj, "params", std::move(params));
return obj;
}
`);
});
@@ -1,131 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import {Property} from '../src/Property.js';
test('parses required primitive prop', () => {
let obj = {
'name': 'lineNumber',
'type': 'integer',
'description': 'Line number in the script (0-based).',
};
let prop = Property.create('Debugger', obj);
expect(prop.domain).toBe('Debugger');
expect(prop.name).toBe('lineNumber');
expect(prop.type).toBe('integer');
expect(prop.optional).toBeUndefined();
expect(prop.description).toBe('Line number in the script (0-based).');
expect(prop.getFullCppType()).toBe('int');
expect(prop.getCppIdentifier()).toBe('lineNumber');
expect(prop.getInitializer()).toBe('{}');
});
test('parses optional primitive prop', () => {
let obj = {
'name': 'samplingInterval',
'type': 'number',
'optional': true,
'description': 'Average sample interval in bytes.',
};
let prop = Property.create('HeapProfiler', obj);
expect(prop.domain).toBe('HeapProfiler');
expect(prop.name).toBe('samplingInterval');
expect(prop.type).toBe('number');
expect(prop.optional).toBe(true);
expect(prop.description).toBe('Average sample interval in bytes.');
expect(prop.getFullCppType()).toBe('std::optional<double>');
expect(prop.getCppIdentifier()).toBe('samplingInterval');
expect(prop.getInitializer()).toBe('');
});
test('parses optional ref prop', () => {
let obj = {
'name': 'exceptionDetails',
'optional': true,
'$ref': 'Runtime.ExceptionDetails',
'description': 'Exception details if any.',
};
let prop = Property.create('Debugger', obj);
expect(prop.domain).toBe('Debugger');
expect(prop.name).toBe('exceptionDetails');
expect(prop.optional).toBe(true);
expect(prop.$ref).toBe('Runtime.ExceptionDetails');
expect(prop.description).toBe('Exception details if any.');
expect(prop.getFullCppType()).toBe('std::optional<runtime::ExceptionDetails>');
expect(prop.getCppIdentifier()).toBe('exceptionDetails');
expect(prop.getInitializer()).toBe('');
});
test('parses recursive ref prop', () => {
let obj = {
'name': 'parent',
'$ref': 'StackTrace',
'optional': true,
'recursive': true,
'description': 'Asynchronous JavaScript stack trace...',
};
let prop = Property.create('Runtime', obj);
expect(prop.domain).toBe('Runtime');
expect(prop.name).toBe('parent');
expect(prop.optional).toBe(true);
expect(prop.recursive).toBe(true);
expect(prop.$ref).toBe('StackTrace');
expect(prop.description).toBe('Asynchronous JavaScript stack trace...');
expect(prop.getFullCppType()).toBe('std::unique_ptr<runtime::StackTrace>');
expect(prop.getCppIdentifier()).toBe('parent');
expect(prop.getInitializer()).toBe('');
});
test('parses optional array items prop', () => {
let obj = {
'name': 'hitBreakpoints',
'type': 'array',
'optional': true,
'items': { 'type': 'string' },
'description': 'Hit breakpoints IDs',
};
let prop = Property.create('Debugger', obj);
expect(prop.domain).toBe('Debugger');
expect(prop.name).toBe('hitBreakpoints');
expect(prop.type).toBe('array');
expect(prop.optional).toBe(true);
expect(prop.items).toEqual({ 'type': 'string' });
expect(prop.description).toBe('Hit breakpoints IDs');
expect(prop.getFullCppType()).toBe('std::optional<std::vector<std::string>>');
expect(prop.getCppIdentifier()).toBe('hitBreakpoints');
expect(prop.getInitializer()).toBe('');
});
test('parses array ref prop', () => {
let obj = {
'name': 'domains',
'type': 'array',
'items': { '$ref': 'Domain' },
'description': 'List of supported domains.',
};
let prop = Property.create('Schema', obj);
expect(prop.domain).toBe('Schema');
expect(prop.name).toBe('domains');
expect(prop.type).toBe('array');
expect(prop.items).toEqual({ $ref: 'Domain' });
expect(prop.description).toBe('List of supported domains.');
expect(prop.getFullCppType()).toBe('std::vector<schema::Domain>');
expect(prop.getCppIdentifier()).toBe('domains');
expect(prop.getInitializer()).toBe('');
});
@@ -1,50 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import {Type} from '../src/Type.js';
test('parses primitive type', () => {
let obj = {
'id': 'Timestamp',
'type': 'number',
'description': 'Number of milliseconds since epoch.',
};
let type = Type.create('Runtime', obj, false);
expect(type.domain).toBe('Runtime');
expect(type.id).toBe('Timestamp');
expect(type.type).toBe('number');
expect(type.description).toBe('Number of milliseconds since epoch.');
expect(type.getCppNamespace()).toBe('runtime');
expect(type.getCppType()).toBe('Timestamp');
expect(type.getForwardDecls()).toEqual(['using Timestamp = double;']);
});
test('parses object type', () => {
let obj = {
'id': 'Location',
'type': 'object',
'properties': [
{ 'name': 'scriptId', '$ref': 'Runtime.ScriptId', 'description': 'Script identifier as reported in the <code>Debugger.scriptParsed</code>.' },
{ 'name': 'lineNumber', 'type': 'integer', 'description': 'Line number in the script (0-based).' },
{ 'name': 'columnNumber', 'type': 'integer', 'optional': true, 'description': 'Column number in the script (0-based).' },
],
'description': 'Location in the source code.',
};
let type = Type.create('Debugger', obj, false);
expect(type.domain).toBe('Debugger');
expect(type.id).toBe('Location');
expect(type.type).toBe('object');
expect(type.properties.map(p => p.name)).toEqual(['scriptId', 'lineNumber', 'columnNumber']);
expect(type.description).toBe('Location in the source code.');
expect(type.getCppNamespace()).toBe('debugger');
expect(type.getCppType()).toBe('Location');
expect(type.getForwardDecls()).toEqual(['struct Location;']);
});
@@ -1,40 +0,0 @@
{
"name": "@react-native/hermes-inspector-msggen",
"version": "0.77.0-main",
"private": true,
"description": "Hermes Inspector Message Generator for React Native",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/facebook/react-native.git",
"directory": "packages/hermes-inspector-msggen"
},
"bin": {
"msggen": "bin/index.js"
},
"engines": {
"node": ">=18"
},
"scripts": {
"flow": "flow",
"build": "babel src --out-dir bin --source-maps",
"watch": "babel src --out-dir bin --source-maps --watch",
"test": "jest"
},
"dependencies": {
"devtools-protocol": "0.0.1107588",
"yargs": "^17.6.2"
},
"devDependencies": {
"@babel/cli": "^7.24.8",
"@babel/core": "^7.25.2",
"@babel/preset-env": "^7.25.3",
"@babel/preset-flow": "^7.24.7",
"jest": "^29.6.3"
},
"jest": {
"transform": {
".*": "<rootDir>/node_modules/babel-jest"
}
}
}
@@ -1,91 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
import {toCppNamespace, toCppType} from './Converters';
import {Property} from './Property';
export class Command {
domain: string;
name: string;
description: ?string;
experimental: ?boolean;
parameters: Array<Property>;
returns: Array<Property>;
static create(
domain: string,
obj: any,
ignoreExperimental: boolean,
includeExperimental: Set<string>,
): ?Command {
return ignoreExperimental &&
obj.experimental &&
!includeExperimental.has(domain + '.' + obj.name)
? null
: new Command(domain, obj, ignoreExperimental, includeExperimental);
}
constructor(
domain: string,
obj: any,
ignoreExperimental: boolean,
includeExperimental: Set<string>,
) {
this.domain = domain;
this.name = obj.name;
this.description = obj.description;
this.experimental = obj.experimental;
this.parameters = Property.createArray(
domain,
obj.name,
obj.parameters || [],
ignoreExperimental,
includeExperimental,
);
this.returns = Property.createArray(
domain,
obj.name,
obj.returns || [],
ignoreExperimental,
includeExperimental,
);
}
getDebuggerName(): string {
return `${this.domain}.${this.name}`;
}
getCppNamespace(): string {
return toCppNamespace(this.domain);
}
getRequestCppType(): string {
return toCppType(this.name + 'Request');
}
getResponseCppType(): ?string {
if (this.returns && this.returns.length > 0) {
return toCppType(this.name + 'Response');
}
}
getForwardDecls(): Array<string> {
const decls = [`struct ${this.getRequestCppType()};`];
const respCppType = this.getResponseCppType();
if (respCppType) {
decls.push(`struct ${respCppType};`);
}
return decls;
}
getForwardDeclSortKey(): string {
return this.getRequestCppType();
}
}
@@ -1,39 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
export function toCppNamespace(domain: string): string {
return domain.slice(0, 1).toLowerCase() + domain.slice(1);
}
export function toCppType(type: string): string {
return type.slice(0, 1).toUpperCase() + type.slice(1);
}
export type JsTypeString =
| 'any'
| 'boolean'
| 'integer'
| 'number'
| 'object'
| 'string';
const jsTypeMappings = {
any: 'folly::dynamic',
array: 'folly::dynamic',
boolean: 'bool',
integer: 'int',
number: 'double',
object: 'folly::dynamic',
string: 'std::string',
};
export function jsTypeToCppType(jsTypeStr: JsTypeString): string {
return jsTypeMappings[jsTypeStr];
}
@@ -1,71 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
import {toCppNamespace, toCppType} from './Converters';
import {Property} from './Property';
export class Event {
domain: string;
name: string;
description: ?string;
experimental: ?boolean;
parameters: Array<Property>;
static create(
domain: string,
obj: any,
ignoreExperimental: boolean,
includeExperimental: Set<string>,
): ?Event {
return ignoreExperimental &&
obj.experimental &&
!includeExperimental.has(domain + '.' + obj.name)
? null
: new Event(domain, obj, ignoreExperimental, includeExperimental);
}
constructor(
domain: string,
obj: any,
ignoreExperimental: boolean,
includeExperimental: Set<string>,
) {
this.domain = domain;
this.name = obj.name;
this.description = obj.description;
this.parameters = Property.createArray(
domain,
obj.name,
obj.parameters || [],
ignoreExperimental,
includeExperimental,
);
}
getDebuggerName(): string {
return `${this.domain}.${this.name}`;
}
getCppNamespace(): string {
return toCppNamespace(this.domain);
}
getCppType(): string {
return toCppType(this.name + 'Notification');
}
getForwardDecls(): Array<string> {
return [`struct ${this.getCppType()};`];
}
getForwardDeclSortKey(): string {
return this.getCppType();
}
}
@@ -1,16 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
'use strict';
// placeholder token that will be replaced by signedsource script
export const GeneratedHeader: string =
'// Copyright (c) Meta Platforms, Inc. and affiliates. All Rights Reserved.\n' +
'// @generated <<SignedSource::*O*zOeWoEQle#+L!plEphiEmie@IsG>>';
@@ -1,98 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import invariant from 'assert';
type NodeId = string;
type NodeCycle = {from: NodeId, to: NodeId};
class Node {
id: NodeId;
children: Set<Node>;
state: 'none' | 'visiting' | 'visited';
constructor(id: NodeId) {
this.id = id;
this.children = new Set();
this.state = 'none';
}
}
export class Graph {
nodes: Map<NodeId, Node>;
constructor() {
this.nodes = new Map();
}
addNode(nodeId: NodeId): Node {
let node = this.nodes.get(nodeId);
if (!node) {
node = new Node(nodeId);
this.nodes.set(nodeId, node);
}
return node;
}
addEdge(srcId: NodeId, dstId: NodeId) {
const src = this.addNode(srcId);
const dst = this.addNode(dstId);
src.children.add(dst);
}
// traverse returns all nodes in the graph reachable from the given rootIds.
// the returned nodes are topologically sorted, with the deepest nodes
// returned first.
traverse(rootIds: Array<NodeId>): {
nodes: Array<NodeId>,
cycles: Array<NodeCycle>,
} {
// clear marks
for (const node of this.nodes.values()) {
node.state = 'none';
}
// make a fake root node that points to all the provided rootIds
const root = new Node('root');
for (const id of rootIds) {
const node = this.nodes.get(id);
invariant(node != null, `No node ${id} in graph`);
root.children.add(node);
}
const nodes: Array<NodeId> = [];
const cycles: Array<NodeCycle> = [];
postorder(root, nodes, cycles);
// remove fake root node
nodes.splice(-1);
return {nodes: nodes, cycles: cycles};
}
}
function postorder(node: Node, nodes: Array<NodeId>, cycles: Array<NodeCycle>) {
if (node.state === 'visited') {
return;
}
node.state = 'visiting';
for (const child of node.children) {
if (child.state === 'visiting') {
// This will lead to a cycle; we need to mark that and move on
cycles.push({from: node.id, to: child.id});
} else {
postorder(child, nodes, cycles);
}
}
node.state = 'visited';
nodes.push(node.id);
}
@@ -1,327 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
import {Command} from './Command';
import {toCppNamespace} from './Converters';
import {Event} from './Event';
import {GeneratedHeader} from './GeneratedHeader';
import {Property} from './Property';
import {PropsType, Type} from './Type';
import {Writable} from 'stream';
export class HeaderWriter {
stream: Writable;
types: Array<Type>;
commands: Array<Command>;
events: Array<Event>;
constructor(
stream: Writable,
types: Array<Type>,
commands: Array<Command>,
events: Array<Event>,
) {
this.stream = stream;
this.types = types;
this.commands = commands;
this.events = events;
}
write() {
this.writePrologue();
this.writeForwardDecls();
this.writeRequestHandlerDecls();
this.writeTypeDecls();
this.writeRequestDecls();
this.writeResponseDecls();
this.writeNotificationDecls();
this.writeEpilogue();
}
writePrologue() {
this.stream.write(`${GeneratedHeader}
#pragma once
#include <hermes/inspector-modern/chrome/MessageInterfaces.h>
#include <optional>
#include <vector>
namespace facebook {
namespace hermes {
namespace inspector_modern {
namespace chrome {
namespace message {
template<typename T>
void deleter(T* p);
`);
}
writeForwardDecls() {
this.stream.write('struct UnknownRequest;\n\n');
const namespaceMap: Map<string, Array<Type | Command | Event>> = new Map();
const addToMap = function (type: Type | Command | Event) {
const domain = type.domain;
let types = namespaceMap.get(domain);
if (!types) {
types = [];
namespaceMap.set(domain, types);
}
types.push(type);
};
this.types.forEach(addToMap);
this.commands.forEach(addToMap);
this.events.forEach(addToMap);
for (const [domain, types] of namespaceMap) {
types.sort((a, b) => {
const nameA = a.getForwardDeclSortKey();
const nameB = b.getForwardDeclSortKey();
return nameA < nameB ? -1 : nameA > nameB ? 1 : 0;
});
const ns = toCppNamespace(domain);
this.stream.write(`namespace ${ns} {\n`);
for (const type of types) {
for (const decl of type.getForwardDecls()) {
this.stream.write(`${decl}\n`);
}
}
this.stream.write(`} // namespace ${ns}\n\n`);
}
}
writeRequestHandlerDecls() {
this.stream.write(
'\n/// RequestHandler handles requests via the visitor pattern.\n',
);
emitRequestHandlerDecl(this.stream, this.commands);
this.stream.write(
'\n/// NoopRequestHandler can be subclassed to only handle some requests.\n',
);
emitNoopRequestHandlerDecl(this.stream, this.commands);
}
writeTypeDecls() {
this.stream.write('\n/// Types\n');
for (const type of this.types) {
if (type instanceof PropsType) {
emitTypeDecl(this.stream, type);
}
}
}
writeRequestDecls() {
this.stream.write('\n/// Requests\n');
emitUnknownRequestDecl(this.stream);
for (const command of this.commands) {
emitRequestDecl(this.stream, command);
}
}
writeResponseDecls() {
this.stream.write('\n/// Responses\n');
emitErrorResponseDecl(this.stream);
emitOkResponseDecl(this.stream);
for (const command of this.commands) {
emitResponseDecl(this.stream, command);
}
}
writeNotificationDecls() {
this.stream.write('\n/// Notifications\n');
for (const event of this.events) {
emitNotificationDecl(this.stream, event);
}
}
writeEpilogue() {
this.stream.write(`
} // namespace message
} // namespace chrome
} // namespace inspector_modern
} // namespace hermes
} // namespace facebook
`);
}
}
function emitRequestHandlerDecl(stream: Writable, commands: Array<Command>) {
stream.write(`struct RequestHandler {
virtual ~RequestHandler() = default;
virtual void handle(const UnknownRequest &req) = 0;
`);
for (const command of commands) {
const cppNs = command.getCppNamespace();
const cppType = command.getRequestCppType();
stream.write(`virtual void handle(const ${cppNs}::${cppType} &req) = 0;`);
}
stream.write('};\n');
}
function emitNoopRequestHandlerDecl(
stream: Writable,
commands: Array<Command>,
) {
stream.write(`struct NoopRequestHandler : public RequestHandler {
void handle(const UnknownRequest &req) override {}
`);
for (const command of commands) {
const cppNs = command.getCppNamespace();
const cppType = command.getRequestCppType();
stream.write(`void handle(const ${cppNs}::${cppType} &req) override {}`);
}
stream.write('};\n');
}
function emitProps(stream: Writable, props: ?Array<Property>) {
if (!props || props.length === 0) {
return;
}
stream.write('\n');
for (const prop of props) {
const fullCppType = prop.getFullCppType();
const cppId = prop.getCppIdentifier();
const init = prop.getInitializer();
stream.write(` ${fullCppType} ${cppId}${init};\n`);
}
}
export function emitTypeDecl(stream: Writable, type: PropsType) {
const cppNs = type.getCppNamespace();
const cppType = type.getCppType();
stream.write(`struct ${cppNs}::${cppType} : public Serializable {
${cppType}() = default;
${cppType}(${cppType}&&) = default;
${cppType}(const ${cppType}&) = delete;
explicit ${cppType}(const folly::dynamic &obj);
folly::dynamic toDynamic() const override;
${cppType}& operator=(const ${cppType}&) = delete;
${cppType}& operator=(${cppType}&&) = default;
`);
if (type instanceof PropsType) {
emitProps(stream, type.properties);
}
stream.write('};\n\n');
}
function emitUnknownRequestDecl(stream: Writable) {
stream.write(`struct UnknownRequest : public Request {
UnknownRequest();
explicit UnknownRequest(const folly::dynamic &obj);
folly::dynamic toDynamic() const override;
void accept(RequestHandler &handler) const override;
std::optional<folly::dynamic> params;
};
`);
}
export function emitRequestDecl(stream: Writable, command: Command) {
const cppNs = command.getCppNamespace();
const cppType = command.getRequestCppType();
stream.write(`struct ${cppNs}::${cppType} : public Request {
${cppType}();
explicit ${cppType}(const folly::dynamic &obj);
folly::dynamic toDynamic() const override;
void accept(RequestHandler &handler) const override;
`);
emitProps(stream, command.parameters);
stream.write('};\n\n');
}
function emitErrorResponseDecl(stream: Writable) {
stream.write(`struct ErrorResponse : public Response {
ErrorResponse() = default;
explicit ErrorResponse(const folly::dynamic &obj);
folly::dynamic toDynamic() const override;
int code;
std::string message;
std::optional<folly::dynamic> data;
};
`);
}
function emitOkResponseDecl(stream: Writable) {
stream.write(`struct OkResponse : public Response {
OkResponse() = default;
explicit OkResponse(const folly::dynamic &obj);
folly::dynamic toDynamic() const override;
};
`);
}
export function emitResponseDecl(stream: Writable, command: Command) {
const cppNs = command.getCppNamespace();
const cppType = command.getResponseCppType();
if (!cppType) {
return;
}
stream.write(`struct ${cppNs}::${cppType} : public Response {
${cppType}() = default;
explicit ${cppType}(const folly::dynamic &obj);
folly::dynamic toDynamic() const override;
`);
emitProps(stream, command.returns);
stream.write('};\n\n');
}
export function emitNotificationDecl(stream: Writable, event: Event) {
const cppNs = event.getCppNamespace();
const cppType = event.getCppType();
stream.write(`struct ${cppNs}::${cppType} : public Notification {
${cppType}();
explicit ${cppType}(const folly::dynamic &obj);
folly::dynamic toDynamic() const override;
`);
emitProps(stream, event.parameters);
stream.write('};\n\n');
}
@@ -1,420 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
import {Command} from './Command';
import {Event} from './Event';
import {GeneratedHeader} from './GeneratedHeader';
import {PropsType, Type} from './Type';
import {Writable} from 'stream';
export class ImplementationWriter {
stream: Writable;
types: Array<Type>;
commands: Array<Command>;
events: Array<Event>;
constructor(
stream: Writable,
types: Array<Type>,
commands: Array<Command>,
events: Array<Event>,
) {
this.stream = stream;
this.types = types;
this.commands = commands;
this.events = events;
}
write() {
this.writePrologue();
this.writeRequestParser();
this.writeTypeDefs();
this.writeRequestDefs();
this.writeResponseDefs();
this.writeNotificationDefs();
this.writeEpilogue();
}
writePrologue() {
this.stream.write(`${GeneratedHeader}
#include "MessageTypes.h"
#include "MessageTypesInlines.h"
namespace facebook {
namespace hermes {
namespace inspector_modern {
namespace chrome {
namespace message {
`);
}
writeRequestParser() {
emitRequestParser(this.stream, this.commands);
}
writeTypeDefs() {
this.stream.write('\n/// Types\n');
for (const type of this.types) {
if (type instanceof PropsType) {
emitTypeDef(this.stream, type);
}
}
}
writeRequestDefs() {
this.stream.write('\n/// Requests\n');
emitUnknownRequestDef(this.stream);
for (const command of this.commands) {
emitRequestDef(this.stream, command);
}
}
writeResponseDefs() {
this.stream.write('\n/// Responses\n');
emitErrorResponseDef(this.stream);
emitOkResponseDef(this.stream);
for (const command of this.commands) {
emitResponseDef(this.stream, command);
}
}
writeNotificationDefs() {
this.stream.write('\n/// Notifications\n');
for (const event of this.events) {
emitNotificationDef(this.stream, event);
}
}
writeEpilogue() {
this.stream.write(`
} // namespace message
} // namespace chrome
} // namespace inspector_modern
} // namespace hermes
} // namespace facebook
`);
}
}
function emitRequestParser(stream: Writable, commands: Array<Command>) {
stream.write(`
using RequestBuilder = std::unique_ptr<Request> (*)(const dynamic &);
namespace {
template <typename T>
std::unique_ptr<Request> makeUnique(const dynamic &obj) {
return std::make_unique<T>(obj);
}
} // namespace
std::unique_ptr<Request> Request::fromJsonThrowOnError(const std::string &str) {
static std::unordered_map<std::string, RequestBuilder> builders = {
`);
for (const command of commands) {
const cppNs = command.getCppNamespace();
const cppType = command.getRequestCppType();
const dbgName = command.getDebuggerName();
stream.write(`{"${dbgName}", makeUnique<${cppNs}::${cppType}>},\n`);
}
stream.write(`};
dynamic obj = folly::parseJson(str);
std::string method = obj.at("method").asString();
auto it = builders.find(method);
if (it == builders.end()) {
return std::make_unique<UnknownRequest>(obj);
}
auto builder = it->second;
return builder(obj);
}
folly::Try<std::unique_ptr<Request>> Request::fromJson(const std::string &str) {
return folly::makeTryWith(
[&str] { return Request::fromJsonThrowOnError(str); });
}\n\n`);
stream.write('\n');
}
export function emitTypeDef(stream: Writable, type: PropsType) {
const cppNs = type.getCppNamespace();
const cppType = type.getCppType();
const props = type.properties || [];
// From-dynamic constructor
stream.write(`${cppNs}::${cppType}::${cppType}(const dynamic &obj) {\n`);
for (const prop of props) {
const id = prop.getCppIdentifier();
const name = prop.name;
stream.write(`assign(${id}, obj, "${name}");\n`);
}
stream.write('}\n\n');
// toDynamic
stream.write(`dynamic ${cppNs}::${cppType}::toDynamic() const {
dynamic obj = dynamic::object;\n\n`);
for (const prop of props) {
const id = prop.getCppIdentifier();
const name = prop.name;
stream.write(`put(obj, "${name}", ${id});\n`);
}
stream.write('return obj;\n}\n\n');
}
function emitErrorResponseDef(stream: Writable) {
stream.write(`ErrorResponse::ErrorResponse(const dynamic &obj) {
assign(id, obj, "id");
dynamic error = obj.at("error");
assign(code, error, "code");
assign(message, error, "message");
assign(data, error, "data");
}
dynamic ErrorResponse::toDynamic() const {
dynamic error = dynamic::object;
put(error, "code", code);
put(error, "message", message);
put(error, "data", data);
dynamic obj = dynamic::object;
put(obj, "id", id);
put(obj, "error", std::move(error));
return obj;
}\n\n`);
}
function emitOkResponseDef(stream: Writable) {
stream.write(`OkResponse::OkResponse(const dynamic &obj) {
assign(id, obj, "id");
}
dynamic OkResponse::toDynamic() const {
dynamic result = dynamic::object;
dynamic obj = dynamic::object;
put(obj, "id", id);
put(obj, "result", std::move(result));
return obj;
}\n\n`);
}
function emitUnknownRequestDef(stream: Writable) {
stream.write(`UnknownRequest::UnknownRequest() {}
UnknownRequest::UnknownRequest(const dynamic &obj) {
assign(id, obj, "id");
assign(method, obj, "method");
assign(params, obj, "params");
}
dynamic UnknownRequest::toDynamic() const {
dynamic obj = dynamic::object;
put(obj, "id", id);
put(obj, "method", method);
put(obj, "params", params);
return obj;
}
void UnknownRequest::accept(RequestHandler &handler) const {
handler.handle(*this);
}\n\n`);
}
export function emitRequestDef(stream: Writable, command: Command) {
const cppNs = command.getCppNamespace();
const cppType = command.getRequestCppType();
const dbgName = command.getDebuggerName();
const props = command.parameters || [];
// Default constructor
stream.write(`${cppNs}::${cppType}::${cppType}()
: Request("${dbgName}") {}\n\n`);
// From-dynamic constructor
stream.write(`${cppNs}::${cppType}::${cppType}(const dynamic &obj)
: Request("${dbgName}") {
assign(id, obj, "id");
assign(method, obj, "method");\n\n`);
if (props.length > 0) {
const optionalParams = props.every(p => p.optional);
if (optionalParams) {
stream.write(`
auto it = obj.find("params");
if (it != obj.items().end()) {
dynamic params = it->second;
`);
} else {
stream.write('dynamic params = obj.at("params");\n');
}
for (const prop of props) {
const id = prop.getCppIdentifier();
const name = prop.name;
stream.write(`assign(${id}, params, "${name}");\n`);
}
if (optionalParams) {
stream.write('}');
}
}
stream.write('}\n\n');
// toDynamic
stream.write(`dynamic ${cppNs}::${cppType}::toDynamic() const {\n`);
if (props.length > 0) {
stream.write('dynamic params = dynamic::object;\n');
for (const prop of props) {
const id = prop.getCppIdentifier();
const name = prop.name;
stream.write(`put(params, "${name}", ${id});\n`);
}
}
stream.write(`
dynamic obj = dynamic::object;
put(obj, "id", id);
put(obj, "method", method);
`);
if (props.length > 0) {
stream.write('put(obj, "params", std::move(params));\n');
}
stream.write(`return obj;
}\n\n`);
// visitor
stream.write(`void ${cppNs}::${cppType}::accept(RequestHandler &handler) const {
handler.handle(*this);
}\n\n`);
}
export function emitResponseDef(stream: Writable, command: Command) {
const cppNs = command.getCppNamespace();
const cppType = command.getResponseCppType();
if (!cppType) {
return;
}
// From-dynamic constructor
stream.write(`${cppNs}::${cppType}::${cppType}(const dynamic &obj) {
assign(id, obj, "id");\n\n`);
const props = command.returns || [];
if (props.length > 0) {
stream.write('dynamic res = obj.at("result");\n');
for (const prop of props) {
const id = prop.getCppIdentifier();
const name = prop.name;
stream.write(`assign(${id}, res, "${name}");\n`);
}
}
stream.write('}\n\n');
// toDynamic
stream.write(`dynamic ${cppNs}::${cppType}::toDynamic() const {\n`);
if (props.length > 0) {
stream.write('dynamic res = dynamic::object;\n');
for (const prop of props) {
const id = prop.getCppIdentifier();
const name = prop.name;
stream.write(`put(res, "${name}", ${id});\n`);
}
}
stream.write(`
dynamic obj = dynamic::object;
put(obj, "id", id);
put(obj, "result", std::move(res));
return obj;
}\n\n`);
}
export function emitNotificationDef(stream: Writable, event: Event) {
const cppNs = event.getCppNamespace();
const cppType = event.getCppType();
const dbgName = event.getDebuggerName();
const props = event.parameters || [];
// Default constructor
stream.write(`${cppNs}::${cppType}::${cppType}()
: Notification("${dbgName}") {}\n\n`);
// From-dynamic constructor
stream.write(`${cppNs}::${cppType}::${cppType}(const dynamic &obj)
: Notification("${dbgName}") {
assign(method, obj, "method");\n\n`);
if (props.length > 0) {
stream.write('dynamic params = obj.at("params");\n');
for (const prop of props) {
const id = prop.getCppIdentifier();
const name = prop.name;
stream.write(`assign(${id}, params, "${name}");\n`);
}
}
stream.write('}\n\n');
// toDynamic
stream.write(`dynamic ${cppNs}::${cppType}::toDynamic() const {\n`);
if (props.length > 0) {
stream.write('dynamic params = dynamic::object;\n');
for (const prop of props) {
const id = prop.getCppIdentifier();
const name = prop.name;
stream.write(`put(params, "${name}", ${id});\n`);
}
}
stream.write(`
dynamic obj = dynamic::object;
put(obj, "method", method);
`);
if (props.length > 0) {
stream.write('put(obj, "params", std::move(params));\n');
}
stream.write(`return obj;
}\n\n`);
}
@@ -1,244 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
import {
type JsTypeString,
jsTypeToCppType,
toCppNamespace,
toCppType,
} from './Converters';
export class Property {
domain: string;
name: string;
description: ?string;
exported: ?boolean;
experimental: ?boolean;
optional: ?boolean;
static create(domain: string, obj: any): Property {
if (obj.$ref) {
return new RefProperty(domain, obj);
} else if (obj.type === 'array') {
return new ArrayProperty(domain, obj);
}
return new PrimitiveProperty(domain, obj);
}
static createArray(
domain: string,
parent: string,
elements: Array<any>,
ignoreExperimental: boolean,
includeExperimental: Set<string>,
): Array<Property> {
let props = elements.map(elem => Property.create(domain, elem));
if (ignoreExperimental) {
props = props.filter(prop => {
return (
!prop.experimental ||
includeExperimental.has(domain + '.' + parent + '.' + prop.name)
);
});
}
return props;
}
constructor(domain: string, obj: any) {
this.domain = domain;
this.name = obj.name;
this.description = obj.description;
this.exported = obj.exported;
this.experimental = obj.experimental;
this.optional = obj.optional;
}
getRefDebuggerName(): ?string {
throw new Error('subclass must implement');
}
getFullCppType(): string {
throw new Error('subclass must implement');
}
getCppIdentifier(): string {
// need to munge identifier if it matches a C++ keyword like "this"
if (this.name === 'this') {
return 'thisObj';
}
return this.name;
}
getInitializer(): string {
throw new Error('subclass must implement');
}
}
function maybeWrapOptional(
type: string,
optional: ?boolean,
recursive: ?boolean,
cyclical: ?boolean,
) {
if (cyclical) {
// n.b. need to use unique_ptr with a custom deleter since
// due to the type cycle we only have the fwd def here and
// the type is incomplete
return `std::unique_ptr<${type}, std::function<void(${type}*)>>`;
} else if (optional) {
return recursive ? `std::unique_ptr<${type}>` : `std::optional<${type}>`;
}
return type;
}
function toDomainAndId(
curDomain: string,
absOrRelRef: string,
): [string, string] {
let [domain, id] = ['', ''];
// absOrRelRef can be:
// 1) absolute ref with a "." referencing a type from another namespace, like
// "Runtime.ExceptionDetails"
// 2) relative ref without a "." referencing a type in current domain, like
// "Domain"
const i = absOrRelRef.indexOf('.');
if (i === -1) {
domain = curDomain;
id = absOrRelRef;
} else {
domain = absOrRelRef.slice(0, i);
id = absOrRelRef.slice(i + 1);
}
return [domain, id];
}
function toFullCppType(curDomain: string, absOrRelRef: string) {
const [domain, id] = toDomainAndId(curDomain, absOrRelRef);
return `${toCppNamespace(domain)}::${toCppType(id)}`;
}
class PrimitiveProperty extends Property {
type: JsTypeString;
constructor(domain: string, obj: any) {
super(domain, obj);
this.type = obj.type;
}
getRefDebuggerName(): ?string {
return undefined;
}
getFullCppType(): string {
return maybeWrapOptional(jsTypeToCppType(this.type), this.optional);
}
getInitializer(): string {
// std::optional doesn't need to be explicitly zero-init
if (this.optional) {
return '';
}
// we want to explicitly zero-init bool, int, and double
const type = this.type;
if (type === 'boolean' || type === 'integer' || type === 'number') {
return '{}';
}
// everything else (folly::dynamic and std::string) has sensible default
// constructor, no need to explicitly zero-init
return '';
}
}
class RefProperty extends Property {
$ref: string;
recursive: ?boolean;
cyclical: ?boolean;
constructor(domain: string, obj: any) {
super(domain, obj);
this.$ref = obj.$ref;
this.recursive = obj.recursive;
this.cyclical = obj.cyclical;
}
getRefDebuggerName(): ?string {
const [domain, id] = toDomainAndId(this.domain, this.$ref);
return `${domain}.${id}`;
}
getFullCppType(): string {
const fullCppType = toFullCppType(this.domain, this.$ref);
return maybeWrapOptional(
`${fullCppType}`,
this.optional,
this.recursive,
this.cyclical,
);
}
getInitializer(): string {
// must zero-init non-optional ref props since the ref could just be an
// alias to a C++ primitive type like int which we always want to zero-init
const fullCppType = toFullCppType(this.domain, this.$ref);
return this.cyclical
? `{nullptr, deleter<${fullCppType}>}`
: this.optional
? ''
: '{}';
}
}
class ArrayProperty extends Property {
type: 'array';
items:
| {|type: JsTypeString, recursive: false|}
| {|$ref: string, recursive: ?boolean|};
constructor(domain: string, obj: any) {
super(domain, obj);
this.type = obj.type;
this.items = obj.items;
}
getRefDebuggerName(): ?string {
if (this.items && this.items.$ref && !this.items.recursive) {
const [domain, id] = toDomainAndId(this.domain, this.items.$ref);
return `${domain}.${id}`;
}
}
getFullCppType(): string {
let elemType: string = 'folly::dynamic';
let recursive: ?(false | boolean) = false;
if (this.items) {
if (this.items.type) {
elemType = jsTypeToCppType(this.items.type);
} else if (this.items.$ref) {
elemType = toFullCppType(this.domain, this.items.$ref);
recursive = this.items.recursive;
}
}
return maybeWrapOptional(
`std::vector<${elemType}>`,
this.optional,
recursive,
);
}
getInitializer(): string {
return '';
}
}
@@ -1,40 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*global expect*/
// munges string so that it's nice to look at in a test diff
function strip(str) {
// Trim leading and trailing WS
str = str.replace(/^\s+/, '');
str = str.replace(/\s+$/, '');
// Collapse all repeating newlines (possibly with spaces in between) into a
// single newline
str = str.replace(/\n(\s*)/g, '\n');
// Collapse all non-newline whitespace into a single space
return str.replace(/[^\S\n]+/g, ' ');
}
export function expectCodeIsEqual(actual, expected) {
expect(strip(actual)).toBe(strip(expected));
}
export class FakeWritable {
constructor() {
this.result = '';
}
write(str) {
this.result += str;
}
get() {
return this.result;
}
}
@@ -1,117 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
import {jsTypeToCppType, toCppNamespace, toCppType} from './Converters';
import {Property} from './Property';
export class Type {
domain: string;
id: string;
description: ?string;
exported: ?boolean;
experimental: ?boolean;
static create(
domain: string,
obj: any,
ignoreExperimental: boolean,
includeExperimental: Set<string>,
): ?Type {
let type = null;
if (obj.type === 'object' && obj.properties) {
type = new PropsType(
domain,
obj,
ignoreExperimental,
includeExperimental,
);
} else if (obj.type) {
type = new PrimitiveType(domain, obj, ignoreExperimental);
} else {
throw new TypeError('Type requires `type` property.');
}
if (ignoreExperimental && type.experimental) {
if (!includeExperimental.has(domain + '.' + type.id)) {
type = null;
}
}
return type;
}
constructor(domain: string, obj: any) {
this.domain = domain;
this.id = obj.id;
this.description = obj.description;
this.exported = obj.exported;
this.experimental = obj.experimental;
}
getDebuggerName(): string {
return `${this.domain}.${this.id}`;
}
getCppNamespace(): string {
return toCppNamespace(this.domain);
}
getCppType(): string {
return toCppType(this.id);
}
getForwardDecls(): Array<string> {
throw new Error('subclass must implement');
}
getForwardDeclSortKey(): string {
return this.getCppType();
}
}
export class PrimitiveType extends Type {
type: 'integer' | 'number' | 'object' | 'string';
constructor(domain: string, obj: any, ignoreExperimental: boolean) {
super(domain, obj);
this.type = obj.type;
}
getForwardDecls(): Array<string> {
return [`using ${this.getCppType()} = ${jsTypeToCppType(this.type)};`];
}
}
export class PropsType extends Type {
type: 'object';
properties: Array<Property>;
constructor(
domain: string,
obj: any,
ignoreExperimental: boolean,
includeExperimental: Set<string>,
) {
super(domain, obj);
this.type = obj.type;
this.properties = Property.createArray(
domain,
obj.id,
obj.properties || [],
ignoreExperimental,
includeExperimental,
);
}
getForwardDecls(): Array<string> {
return [`struct ${this.getCppType()};`];
}
}
@@ -1,26 +0,0 @@
{
"domains": [
{
"domain": "Hermes",
"description": "Hermes specific messages",
"commands": [
{
"name": "setPauseOnLoad",
"description": "Pause VM when new scripts are loaded (reason='load')",
"parameters": [
{
"name": "state",
"description": "Pause on script load mode",
"type": "string",
"enum": [
"none",
"smart",
"all"
]
}
]
}
]
}
]
}
@@ -1,283 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
import {Command} from './Command';
import {Event} from './Event';
import {Graph} from './Graph';
import {HeaderWriter} from './HeaderWriter';
import {ImplementationWriter} from './ImplementationWriter';
import {Property} from './Property';
import {PropsType, Type} from './Type';
import fs from 'fs';
import yargs from 'yargs';
const custom = require('../src/custom.json');
// $FlowFixMe[cannot-resolve-module] : this isn't a module, just a JSON file.
const standard = require('devtools-protocol/json/js_protocol.json');
type Descriptor = {|
types: Array<Type>,
commands: Array<Command>,
events: Array<Event>,
|};
function mergeDomains(original: any, extra: any) {
return {...original, domains: original.domains.concat(extra.domains)};
}
const proto = mergeDomains(standard, custom);
function parseDomains(
domainObjs: Array<any>,
ignoreExperimental: boolean,
includeExperimental: Set<string>,
): Descriptor {
const desc = {
types: ([]: Array<Type>),
commands: ([]: Array<Command>),
events: ([]: Array<Event>),
};
for (const obj of domainObjs) {
const domain = obj.domain;
for (const typeObj of obj.types || []) {
const type = Type.create(
domain,
typeObj,
ignoreExperimental,
includeExperimental,
);
if (type) {
desc.types.push(type);
}
}
for (const commandObj of obj.commands || []) {
const command = Command.create(
domain,
commandObj,
!includeExperimental.has(`${domain}.${commandObj.name}`) &&
ignoreExperimental,
includeExperimental,
);
if (command) {
desc.commands.push(command);
}
}
for (const eventObj of obj.events || []) {
const event = Event.create(
domain,
eventObj,
ignoreExperimental,
includeExperimental,
);
if (event) {
desc.events.push(event);
}
}
}
return desc;
}
function buildGraph(desc: Descriptor): Graph {
const graph = new Graph();
const types = desc.types;
const commands = desc.commands;
const events = desc.events;
const maybeAddPropEdges = function (nodeId: string, props: ?Array<Property>) {
if (props) {
for (const prop of props) {
const refId = prop.getRefDebuggerName();
(prop: Object).recursive = refId && refId === nodeId;
if (refId && refId !== nodeId) {
// Don't add edges for recursive properties.
graph.addEdge(nodeId, refId);
}
}
}
};
for (const type of types) {
graph.addNode(type.getDebuggerName());
if (type instanceof PropsType) {
maybeAddPropEdges(type.getDebuggerName(), type.properties);
}
}
for (const command of commands) {
graph.addNode(command.getDebuggerName());
maybeAddPropEdges(command.getDebuggerName(), command.parameters);
maybeAddPropEdges(command.getDebuggerName(), command.returns);
}
for (const event of events) {
graph.addNode(event.getDebuggerName());
maybeAddPropEdges(event.getDebuggerName(), event.parameters);
}
return graph;
}
function parseRoots(desc: Descriptor, rootsPath: ?string): Array<string> {
const roots = [];
if (rootsPath) {
const buf = fs.readFileSync(rootsPath);
for (let line of buf.toString().split('\n')) {
line = line.trim();
// ignore comments and blank lines
if (!line.match(/\s*#/) && line.length > 0) {
roots.push(line);
}
}
} else {
for (const type of desc.types) {
roots.push(type.getDebuggerName());
}
for (const command of desc.commands) {
roots.push(command.getDebuggerName());
}
for (const event of desc.events) {
roots.push(event.getDebuggerName());
}
}
return roots;
}
// only include types, commands, events that can be reached from the given
// root messages
function filterReachableFromRoots(
desc: Descriptor,
graph: Graph,
roots: Array<string>,
): Descriptor {
const traversal = graph.traverse(roots);
const topoSortedIds = traversal.nodes;
// Types can include other types by value, so they need to be topologically
// sorted in the header.
const typeMap: Map<string, Type> = new Map();
for (const type of desc.types) {
typeMap.set(type.getDebuggerName(), type);
}
const types = [];
for (const id of topoSortedIds) {
const type = typeMap.get(id);
if (type) {
types.push(type);
}
}
const cycles: Set<string> = new Set();
for (const cycle of traversal.cycles) {
const from = typeMap.get(cycle.from);
const to = typeMap.get(cycle.to);
if (from && to) {
cycles.add(from.id + ' > ' + to.id);
}
}
for (const type of types) {
if (type instanceof PropsType) {
for (const prop of type.properties) {
const ref = (prop: Object).$ref;
if (ref) {
const cycleKey = `${type.id} > ${ref}`;
if (cycles.has(cycleKey)) {
(prop: Object).cyclical = true;
}
}
}
}
}
// Commands and events don't depend on each other, so just emit them in the
// order we got them from the JSON file.
const ids = new Set(topoSortedIds);
const commands = desc.commands.filter(cmd => ids.has(cmd.getDebuggerName()));
const events = desc.events.filter(event => ids.has(event.getDebuggerName()));
// Sort commands and events so the code is easier to read. Types have to be
// topologically sorted as explained above.
const comparator = (a: Command | Event, b: Command | Event) => {
const id1 = a.getDebuggerName();
const id2 = b.getDebuggerName();
return id1 < id2 ? -1 : id1 > id2 ? 1 : 0;
};
commands.sort(comparator);
events.sort(comparator);
return {types, commands, events};
}
async function main(): Promise<void> {
const args = await yargs
.usage('Usage: msggen <header_path> <cpp_path>')
.alias('h', 'help')
.help('h')
.boolean('e')
.alias('e', 'ignore-experimental')
.describe('e', 'ignore experimental commands, props, and types')
.alias('i', 'include-experimental')
.describe('i', 'experimental commands to include')
.alias('r', 'roots')
.describe('r', 'path to a file listing root types, events, and commands')
.nargs('r', 1)
.demandCommand(2, 2).argv;
const ignoreExperimental = !!args.e;
const includeExperimental = new Set(
typeof args.i === 'string' ? args.i.split(',') : [],
);
const [headerPath, implPath] = args._;
const headerStream = fs.createWriteStream(headerPath);
const implStream = fs.createWriteStream(implPath);
const desc = parseDomains(
proto.domains,
ignoreExperimental,
includeExperimental,
);
const graph = buildGraph(desc);
const roots = parseRoots(desc, String(args.roots));
const reachable = filterReachableFromRoots(desc, graph, roots);
const hw = new HeaderWriter(
headerStream,
reachable.types,
reachable.commands,
reachable.events,
);
hw.write();
const iw = new ImplementationWriter(
implStream,
reachable.types,
reachable.commands,
reachable.events,
);
iw.write();
}
// $FlowFixMe[unused-promise]
main();
+8 -83
View File
@@ -10,22 +10,6 @@
"@jridgewell/gen-mapping" "^0.3.5"
"@jridgewell/trace-mapping" "^0.3.24"
"@babel/cli@^7.24.8":
version "7.25.6"
resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.25.6.tgz#bc35561adc78ade43ac9c09a690768493ab9ed95"
integrity sha512-Z+Doemr4VtvSD2SNHTrkiFZ1LX+JI6tyRXAAOb4N9khIuPyoEPmTPJarPm8ljJV1D6bnMQjyHMWTT9NeKbQuXA==
dependencies:
"@jridgewell/trace-mapping" "^0.3.25"
commander "^6.2.0"
convert-source-map "^2.0.0"
fs-readdir-recursive "^1.1.0"
glob "^7.2.0"
make-dir "^2.1.0"
slash "^2.0.0"
optionalDependencies:
"@nicolo-ribaudo/chokidar-2" "2.1.8-no-fsevents.3"
chokidar "^3.6.0"
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.0", "@babel/code-frame@^7.24.7":
version "7.24.7"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.7.tgz#882fd9e09e8ee324e496bd040401c6f046ef4465"
@@ -1589,11 +1573,6 @@
"@jridgewell/resolve-uri" "^3.1.0"
"@jridgewell/sourcemap-codec" "^1.4.14"
"@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3":
version "2.1.8-no-fsevents.3"
resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz#323d72dd25103d0c4fbdce89dadf574a787b1f9b"
integrity sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==
"@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1":
version "5.1.1-v1"
resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz#dbf733a965ca47b1973177dc0bb6c889edcfb129"
@@ -2478,7 +2457,7 @@ ansi-styles@^6.0.0, ansi-styles@^6.2.1:
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5"
integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==
anymatch@^3.0.3, anymatch@~3.1.2:
anymatch@^3.0.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e"
integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==
@@ -2879,11 +2858,6 @@ before-after-hook@^2.2.0:
resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.3.tgz#c51e809c81a4e354084422b9b26bad88249c517c"
integrity sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==
binary-extensions@^2.0.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522"
integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==
bl@^4.0.3, bl@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a"
@@ -2908,7 +2882,7 @@ brace-expansion@^2.0.1:
dependencies:
balanced-match "^1.0.0"
braces@^3.0.3, braces@~3.0.2:
braces@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789"
integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
@@ -3083,21 +3057,6 @@ charm@^1.0.2:
dependencies:
inherits "^2.0.1"
chokidar@^3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b"
integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==
dependencies:
anymatch "~3.1.2"
braces "~3.0.2"
glob-parent "~5.1.2"
is-binary-path "~2.1.0"
is-glob "~4.0.1"
normalize-path "~3.0.0"
readdirp "~3.6.0"
optionalDependencies:
fsevents "~2.3.2"
chownr@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece"
@@ -3296,11 +3255,6 @@ commander@^2.12.1, commander@^2.18.0, commander@^2.20.0:
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
commander@^6.2.0:
version "6.2.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c"
integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==
commander@^9.4.1:
version "9.5.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30"
@@ -3680,11 +3634,6 @@ detect-newline@^3.0.0:
resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651"
integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==
devtools-protocol@0.0.1107588:
version "0.0.1107588"
resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1107588.tgz#f8cac707840b97cc30b029359341bcbbb0ad8ffa"
integrity sha512-yIR+pG9x65Xko7bErCUSQaDLrO/P1p3JUzEk7JCU4DowPcGHkTGUGQapcfcLc4qj0UaALwZ+cr0riFgiqpixcg==
diff-sequences@^29.6.3:
version "29.6.3"
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921"
@@ -4553,17 +4502,12 @@ fs-minipass@^2.0.0:
dependencies:
minipass "^3.0.0"
fs-readdir-recursive@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz#e32fc030a2ccee44a6b5371308da54be0b397d27"
integrity sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==
fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
fsevents@^2.3.2, fsevents@~2.3.2:
fsevents@^2.3.2:
version "2.3.3"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
@@ -4685,7 +4629,7 @@ git-config-path@^1.0.1:
fs-exists-sync "^0.1.0"
homedir-polyfill "^1.0.0"
glob-parent@^5.1.2, glob-parent@~5.1.2:
glob-parent@^5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
@@ -4699,7 +4643,7 @@ glob-parent@^6.0.2:
dependencies:
is-glob "^4.0.3"
glob@^7.0.0, glob@^7.1.1, glob@^7.1.3, glob@^7.1.4, glob@^7.2.0:
glob@^7.0.0, glob@^7.1.1, glob@^7.1.3, glob@^7.1.4:
version "7.2.3"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
@@ -5134,13 +5078,6 @@ is-bigint@^1.0.1:
dependencies:
has-bigints "^1.0.1"
is-binary-path@~2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
dependencies:
binary-extensions "^2.0.0"
is-boolean-object@^1.1.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719"
@@ -5243,7 +5180,7 @@ is-generator-function@^1.0.10:
dependencies:
has-tostringtag "^1.0.0"
is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1:
is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3:
version "4.0.3"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
@@ -6818,7 +6755,7 @@ node-stream-zip@^1.9.1:
semver "^7.3.4"
validate-npm-package-license "^3.0.1"
normalize-path@^3.0.0, normalize-path@~3.0.0:
normalize-path@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
@@ -7180,7 +7117,7 @@ picocolors@^1.0.0, picocolors@^1.0.1:
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.0.tgz#5358b76a78cde483ba5cef6a9dc9671440b27d59"
integrity sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1:
picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
@@ -7431,13 +7368,6 @@ readable-stream@^3.0.2, readable-stream@^3.1.1, readable-stream@^3.4.0:
string_decoder "^1.1.1"
util-deprecate "^1.0.1"
readdirp@~3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==
dependencies:
picomatch "^2.2.1"
readline-sync@^1.4.9:
version "1.4.10"
resolved "https://registry.yarnpkg.com/readline-sync/-/readline-sync-1.4.10.tgz#41df7fbb4b6312d673011594145705bf56d8873b"
@@ -7953,11 +7883,6 @@ sisteransi@^1.0.5:
resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed"
integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==
slash@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44"
integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==
slash@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"