For a TypeSpec beginner, it may be difficult to understand how to
configure or format tspconfig.yaml to tweak the different options
available. Adding a basic example should help folks get started.
Related to #3336
## Summary
This PR introduces 2 changes:
1. adds a new `setExtension` API on the `@typespec/json-schema` emitter
to make it easier to author custom extension decorators.
2. updates the `@extension` decorator to support taking values to
obviate the need for the `Json<Data>` model.
Example usage of writing a custom extension `@added` decorator:
```js
export function $added(context, target, value) {
setExtension(context.program, target, "x-added", value);
}
```
Example usage of passing in value literals to `@extension` decorator:
```tsp
@extension("x-current", Json<["foo"]>)
@extension("x-new", #["foo"])
model Foo {}
```
## Background
The `@typespec/json-schema` package exports an `@extension` decorator
that allows customers to specify additional properties to be present as
part of their emitted JSON schema.
For example, we can add a property - converted to a schema - that
indicates when a field was added to the model:
```tsp
@jsonSchema
model Pet {
@extension("x-added", "2024-01-01")
name: string;
}
```
The decorator also supports emitting the property's value as raw code by
wrapping the parameter in the `Json<Data>` template:
```tsp
@jsonSchema
model Pet {
@extension("x-added", Json<"2024-01-01">)
name: string;
}
```
### Comparison
Below is a diff between the two ways of calling the decorator for
clarity:
```diff
$schema: https://json-schema.org/draft/2020-12/schema
$id: Pet.yaml
type: object
properties:
name:
type: string
- x-added:
- type: string
- const: 2024-01-01
+ x-added: 2024-01-01
required:
- name
```
## Pain Points
Currently, `@extension` only supports raw values by wrapping them with
the `TypeSpec.JsonSchema.Json` template. Since this type isn't available
in JS code, this forces authors to manually create the wrapped type
themselves, e.g.:
```js
/**
* @param {import("@typespec/compiler").DecoratorContext} context
* @param {import("@typespec/compiler").Type} target
* @param {import("@typespec/compiler").StringLiteral} value
*/
export function $added(context, target, value) {
$extension(context, target, "x-added", {
kind: "Model",
name: "Json",
namespace: { name: "JsonSchema" },
properties: createRekeyableMap([["value", { type: value }]]),
});
}
```
## Update `@extension` to accept values directly and expose
`setExtension` API
This PR exposes a new `setExtension` API that accepts types and values
as input, instead of just `Type`. This also means
`ExtensionRecord.value` has been loosened:
```diff
export interface ExtensionRecord {
key: string;
- value: Type;
+ value: Type | unknown;
}
```
In addition, the `@extension` decorator is updated to support accepting
values directly:
```diff
- extern dec extension(target: unknown, key: valueof string, value: unknown);
+ extern dec extension(target: unknown, key: valueof string, value: unknown | (valueof unknown));
```
While this change introduces breaking changes to the `@extensions`
decorator and `getExtensions` function, it also allows the `@extension`
decorator to support [value
kinds](https://typespec.io/docs/language-basics/values) as input - a
feature language feature introduced in `0.57.0`.
This would not cause additional breaking changes (beyond
`getExtensions`) for users that already use the
`TypeSpec.JsonSchema.Json` template to pass in raw values. However,
there is 1 scenario that would now fail:
1. Scalar literals/null would now be treated as values instead of types.
(e.g. `@extension("x-bool-literal", true)` would need to be updated to
`@extension("x-bool-literal", typeof true)` to preserve current
behavior)
## Alternatives
### Solution - only expose `setExtension` API
This PR exposes a new `setExtension` API that accepts types and values
as input, instead of just `Type`. This also means
`ExtensionRecord.value` has been loosened:
```diff
export interface ExtensionRecord {
key: string;
- value: Type;
+ value: Type | any;
}
```
This does introduce a **breaking change** in the **getExtensions** API
since `ExtensionRecord.value` is no longer guaranteed to be `Type`.
However, it is likely this API is not used much if at all outside of
this emitter.
### Create new `setExtensionValue`/`getExtensionValues` functions and
alternative `@extension` decorator
This change has the benefit of not requiring any breaking changes.
However, we would now have 2 different but very similar APIs for
settings/getting extensions that may lead to confusion, as well as 2
very similar extensions to do essentially the same thing.
---------
Co-authored-by: Christopher Radek <Christopher.Radek@microsoft.com>
fix#2130
Generate the README.md for typespec libraries automatically using the
subset of the content as what gets included in the ref doc
To achieve this I reoganized the markdown rendering to be able to
provide a reusable system that can be cusomized for different markdown
engines.
- The markdown renderer will render a markdown compatible with Github
Flavored Markdown.
- The docusuaurs rendererer extends the markdown renderer and include a
few extra metadata.