-
Notifications
You must be signed in to change notification settings - Fork 228
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
update protocol-dts-generator to add command and event to types mapping #113
Conversation
One new thing is that this uses the new tuple features in TypeScript 3.0, so I've kept the mappings in a new file in case users of older versions of typescript need the base object types. The new tuple syntax allow bypassing a lot of the backflips we had to do because typescript struggles at the boundary of function overloading and type unions. This was an issue because we're building the types up dynamically but some commands have parameters and some don't (and some have parameters that are entirely optional). . Now |
cc @nojvek @JoelEinbinder wanna take a look? |
types/protocol.d.ts
Outdated
@@ -2,94 +2,14 @@ | |||
* Auto-generated by protocol-dts-generator.ts, do not edit manually. * | |||
**********************************************************************/ | |||
|
|||
export namespace Protocol { | |||
export module Protocol { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the diff gets a little wonky depending on how far you scroll but the only changes in here are
s/namespace/module
DomainApi
interfaces deleted- whitespace adjustments
- descriptions for event types now included in jsdoc
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why module but not namespaces?
Initially added the DomainApi so the types work with noice-rpc-json out of the box. I believe vscode-chrome-debugger also uses this info so this could be a breaking change for some clients.
@roblourens please confirm.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks like vscode-chrome-debug-core
does indeed depend on these. We'll figure out what we should do
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why module but not namespaces?
yeah, looks like you're right
scripts/protocol-dts-generator.ts
Outdated
const eventDefs = domain.events ? domain.events.map(e => emitEvent(e, domainName)) : [] | ||
emitOpenBlock(`export module ${domainName}`) | ||
if (domain.types) domain.types.forEach(emitDomainType) | ||
if (domain.commands) domain.commands.forEach(c => emitCommand(c)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
forEach(emitCommand) right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah yeah, good call
scripts/protocol-dts-generator.ts
Outdated
emitOpenBlock(`export module ${domainName}`) | ||
if (domain.types) domain.types.forEach(emitDomainType) | ||
if (domain.commands) domain.commands.forEach(c => emitCommand(c)) | ||
if (domain.events) domain.events.forEach(e => emitEvent(e)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
forEach(emitEvent)
return `${getPropertyType(prop.items)}[]` | ||
else if (prop.type == 'object') | ||
return `any` | ||
else if (prop.type == 'function') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just checking: function and lambda dont need to be handled any longer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just checking: function and lambda dont need to be handled any longer?
right. They were types constructed just by the dts generator itself (in the old versions of emitCommand
and emitEvent
) to be able to use the same emitInterface
pipeline to output the generated command and event listener functions.
(similar to the new getCommandMapping
and getEventMapping
, but they just reuse the RefType
that the protocol already uses for cross references within itself)
Update to use these definitions in Lighthouse: GoogleChrome/lighthouse@049af19...7488a41
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm
anyone else want to look before we merge?
scripts/protocol-dts-generator.ts
Outdated
} | ||
const getPropertyDef = (prop: P.PropertyType): string => { | ||
// Quote key if it has a . in it. | ||
const propName = /\./.test(prop.name) ? `'${prop.name}'` : prop.name |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i'm a sucker for str.includes but w/e
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd agree that str.includes would be more readable
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd agree that str.includes would be more readable
lol ok fine
Concept looks sound. |
I’d like to take a pass. As a new dad I don’t get as much time for OS
tings. I’ll do it in the next couple of hours though.
On an unrelated note: I’m quite envious of Google parental leave.
|
"ts-node": "^5.0.1", | ||
"typescript": "^2.7.2" | ||
"ts-node": "^7.0.0", | ||
"typescript": "3.0.1" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❤️
scripts/protocol-dts-generator.ts
Outdated
type: 'object', | ||
name: `${domainName}.${command.name}`, | ||
// TODO(bckenny): does having a description help anywhere? | ||
// description: command.description, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what's the harm in having description?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what's the harm in having description?
no harm except file size and maybe ts parse time? The main thing is I couldn't find any place where the descriptions end up surfaced anyways since the mapping objects can't really be used directly, so it didn't seem worth it. I left them commented out so if it turns out we do want them you don't have to wade into the code again.
It seemed maybe better to take the command descriptions and somehow modify them to apply them to the CommandRequest
and CommandResponse
types, as those do show up in completions and intellisense, but I didn't see an easy way to modify the strings to apply the types. "The request parameters for a command that..." or something :)
} | ||
|
||
type ParamOrFunction = P.ParameterType | P.FunctionType | ||
const jsProtocol: IProtocol = require('../json/js_protocol.json') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you 🙏 . If latest tsc does indeed validate the require
-ed json against the IProtocol type, you can remove the TODO on line 5.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If latest tsc does indeed validate the require-ed json against the IProtocol type, you can remove the TODO on line 5.
sadly it does not, the require()
comes in as any
, but if I do "esModuleInterop": true
in tsconfig and go back to import jsProtocol from '../json/js_protocol.json';
I can't apply an interface to the import statement (and if you apply the interface later, things like property type
fields have been widened to string
instead of their literal values so it fails).
Weirdly the js version of this works fine
/** @typedef {import('./protocol-schema').IProtocol} IProtocol */
/** @type {IProtocol} */
const jsProtocol = require('../json/js_protocol.json')
and any changes to the schema or json are flagged as type errors.
So, leaving the TODO for now, but I can unassign you if you'd like :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
works for me as is. Don't remove the TODO just yet.
I do quite like the work tsc team having doing regarding jsdoc type checking and //@ts-check
. I know Google's lingua franca is Js. So if we want this file to be JS with jsdoc type annotations, I don't mind it at all.
Doesn't have to be this PR right away.
emitLine() | ||
} | ||
|
||
const emitModule = (moduleName:string, domains: P.Domain[]) => { | ||
const emitModule = (moduleName: string, domains: P.Domain[]) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ha! spaces! We should just get this tslinted so you don't have to curse me everytime you see a missing space.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should just get this tslinted
agreed :)
// TODO: actually 'any'? or can use generic '[key: string]: string'? | ||
return `any` | ||
} else { | ||
// hack: access indent, \n directly |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could make this into a less hacky version by implementing a startSubEmit()
and endSubEmit()
. startSubEmit saves old str and indent in a stack. all emitX calls go to a new str, so you can you use emitLine()
, emiOpenBlock
and emitCloseBlock
without guilt. endSubEmit() would return the str that was generated by the emitCalls.
I know you're only using it here, but I imagine the next dev may follow hack pattern to make more hacks. Up-to-you though, don't wanna bikeshed too much.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could make this into a less hacky version by implementing a startSubEmit() and endSubEmit()
In the original form of this PR I reworked the whole emit system just for this one spot, but it ended up seeming like way too many changes for what's just a simple reach into the indent state, so I reverted all that and did this instead.
I know you're only using it here, but I imagine the next dev may follow hack pattern to make more hacks.
Agreed, but maybe the (eventual) need for a second hack will be a better justification for the refactor, and give better insight on what form it should take.
/** | ||
* Line number in the resource that generated this message (1-based). | ||
*/ | ||
line?: integer; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't the spaces make the file more readable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't the spaces make the file more readable?
It was way too much whitespace in the mappings file and removing it from interfaces in the protocol file seemed still readable since most have a description comment block. And I didn't want to pipe in a boolean for extra lines or not :)
Can re-add spacing if others agree it's better with it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no strong feelings but i'm fine without the spaces. it's dense but the comments already give things plenty of room.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
W4m, minor nits and ask that we don't break any existing clients.
ok, I added back the The compromise is that they're now in a different file than Also happy to bikeshed on naming. It might be a bit weird to call it |
I propose we call it I assume this will be a major release for devtools-protocol since it's probably a breaking change for some clients as files have moved. Release notes on what changed would be nice too. Thanks for grinding through this @brendankenny . Feels good to see puppeteer will be using something that started as an evening hack. |
sorry for the pause here. I ran into a significant typescript performance issue with the approach in
@paulirish thoughts on file naming?
@paulirish on this too. Looks like the changelog is updated automatically. Should I just insert a note in there anyways or is there a better spot? |
naming
changelogchangelog generation will overwrite it. that changelog is only about the protocol anyway, not the package. lets just put something the readme. |
done |
published to |
thanks so much to @brendankenny for taking this on! |
@@ -6,5 +6,8 @@ Please [file issues](https://github.com/ChromeDevTools/devtools-protocol/issues) | |||
Use the [protocol viewer](https://chromedevtools.github.io/devtools-protocol/) for navigating the protocol. File issues at [its repo](https://github.com/ChromeDevTools/debugger-protocol-viewer) if you have a bug with the documentation webapp. | |||
|
|||
|
|||
TypeScript definitions for the protocol's types are available in ['types/protocol.d.ts'](https://github.com/ChromeDevTools/devtools-protocol/tree/master/types). Mappings from Commands and events to these types are available in either generated `DomainApi` style in [`types/protocol-proxy-api.d.ts`](https://github.com/ChromeDevTools/devtools-protocol/blob/master/types/protocol-proxy-api.d.ts) or in simple name-to-type-interface style in [`types/protocol-mapping.d.ts`](https://github.com/ChromeDevTools/devtools-protocol/blob/master/types/protocol-mapping.d.ts). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😍
Previously the protocol's typescript definition included not just the types used by the protocol but also a set of generated methods that a theoretical interface for the API could use, e.g. the protocol command
Console.clearMessages
has a matching interface entryProtocol.ConsoleApi.clearMessages(): Promise<void>;
.That can be confusing because all the different clients have exposed the protocol in different ways. e.g. lighthouse supports
await sendCommand('Console.clearMessages')
, chrome-remote-interface hasawait client.Console.clearMessages()
, etc.This PR removes those methods (leaving all the type definitions), and instead builds foundational types that map events to their payloads and commands to their parameters and responses. e.g.
Libraries that use the protocol can then uses these mappings to build their own type definitions (in their own repos or in DefinitelyTyped)
This is similar to the model Lighthouse and Puppeteer currently use. In Lighthouse we take this mapping and then transform it for different purposes (for instance, this defines the union of possible JSON responses from sending protocol commands).
Also cleans up the dts generator a bit.