RFC: (Engineering) Introduce A Reflectable Deserialization Helper
See original GitHub issueCurrent State
We have several places where we validate/deserialize JSON. Validation can fail with error messages, defaults need to be inserted and a JSON schema is also usually provided.
Here are some examples of how we do it currently:
-
Language Configuration
- Validation: https://github.com/microsoft/vscode/blob/0f4df3d63b33adc850f6908bcf9276c342da99f9/src/vs/workbench/contrib/codeEditor/browser/languageConfigurationExtensionPoint.ts#L167
- JSON Schema: https://github.com/microsoft/vscode/blob/0f4df3d63b33adc850f6908bcf9276c342da99f9/src/vs/workbench/contrib/codeEditor/browser/languageConfigurationExtensionPoint.ts#L545
- Defaults?
-
Editor Settings
- TypeScript-Type-Def: https://github.com/microsoft/vscode/blob/0f4df3d63b33adc850f6908bcf9276c342da99f9/src/vs/editor/common/config/editorOptions.ts#L2535
- Validation: https://github.com/microsoft/vscode/blob/0f4df3d63b33adc850f6908bcf9276c342da99f9/src/vs/editor/common/config/editorOptions.ts#L2637
- Default: https://github.com/microsoft/vscode/blob/0f4df3d63b33adc850f6908bcf9276c342da99f9/src/vs/editor/common/config/editorOptions.ts#L2570
- JSON Schema: https://github.com/microsoft/vscode/blob/0f4df3d63b33adc850f6908bcf9276c342da99f9/src/vs/editor/common/config/editorOptions.ts#L2595
-
Processing of
args
in commands
Problem
The current style of doing JSON deserialization/validation is very verbose and inconsistent. There is no guarantee that the JSON-Schema matches the validation (though sometimes this is done on purpose).
Proposed Solution
I suggest to explore introducing a generic, descriptive deserialization helper that allows to specify the typescript type, the validation logic, the default value and human readable descriptions at a single place. The JSON schema can be derived from that.
The outcome should be an internal helper (not an external library) that is similar to io-ts or my own library hediet/semantic-json (example).
In case of editor.minimap.side
, the code should look like this:
const editorMinimap = objectDeserializer({
properties: {
...
side: property(enumDeserializer(['left', 'right']), {
defaultValue: 'left',
description: nls.localize('minimap.side', "Controls the side where to render the minimap.")
})
...
}
});
const editorSettings = objectDeserializer({
properties: {
...
minimap: editorMinimap
...
}
});
assert.deepStrictEqual(flatten(objectDeserializer({ properties: { editor: editorSettings } })).getJSONSchema(), {
...
"editor.minimap.side": {
type: 'string',
enum: ['left', 'right'],
default: 'left',
description: "Controls the side where to render the minimap."
...
}
...
});
People interested in this might be: @alexdima @connor4312 @aeschli
I’m open to explore this idea further if there is interest in this.
Issue Analytics
- State:
- Created 2 years ago
- Comments:6 (6 by maintainers)
Top GitHub Comments
In general, I think we should explore code generation. All our settings could be described in one or more JSON files (possibly using JSON schema or JSON schema++) and then type definitions (with nice JSDoc comments) or efficient type validation code could be generated. But this needs to be discussed in the team and agreed team-wide.
From the editor options point of view, I am very interested in seeing a clear proposal that addresses the following:
monaco.d.ts
because that is what editor embedders end up compiling against. We also sometimes instantiate editors from our codebase (e.g. the repl editor) and we need type validation there.updateOptions
updateOptions
should end up creating an option change event that is queryable for the option that has changed.If we go with a code generation approach, which would solve the .d.ts problem, I would vote to simply use JSON schema. I’ve actually done this when building service APIs before and like it a lot – use the JSON schema as the point of truth and way to validate input, and derive types from it. Plus, there’s lots of existing tooling around JSON schemas.
Also, while this design focuses on settings, something adjacent to this and possibly solvable with the same toolchain is extension host communication. Currently communication to and from the extension host is done mostly though JSON serialization with small bits of special handling. As I referenced in https://github.com/microsoft/vscode/pull/133200, the abstractions we currently have here are leaky and there can be benefits, even without straying from JSON, to knowing the shape of serialized data ahead of time. If we use a system that provides runtime information for the protocol types, there’s opportunity for improvement.