Skip to content

Commit

Permalink
Website & Docs Cleanup (#3002)
Browse files Browse the repository at this point in the history
Co-authored-by: Mario Guerra <[email protected]>
Co-authored-by: Libba Lawrence <[email protected]>
Co-authored-by: Allen Zhang <[email protected]>
Co-authored-by: Brian Terlson <[email protected]>
  • Loading branch information
5 people authored Mar 12, 2024
1 parent 57f0d47 commit d8576ef
Show file tree
Hide file tree
Showing 112 changed files with 1,519 additions and 1,586 deletions.
8 changes: 8 additions & 0 deletions .chronus/changes/website-doc-cleanup-2024-2-11-22-12-8.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
# Change versionKind to one of: internal, fix, dependencies, feature, deprecation, breaking
changeKind: internal
packages:
- "@typespec/compiler"
- "@typespec/http"
- "@typespec/openapi3"
---
108 changes: 54 additions & 54 deletions docs/extending-typespec/basics.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/extending-typespec/codefixes.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ reportDiagnostic({

## Test a diagnostic

[See here for testing a codefix inside a linter rule](./linters.md#test-a-codefix)
[See here for testing a codefix inside a linter rule](./linters.md#testing-linter-with-codefixes)

Testing a codefix is done by using the `expectCodeFixOnAst` function from the `@typespec/compiler/testing` package. It takes in the source code and a function that returns the codefix to apply.
It takes the input source code with a cursor defined by `` which will be used to resolve the node where the codefix should be applied. The callback function will receive that resolved node and is expected to return the codefix to test.
Expand Down
85 changes: 40 additions & 45 deletions docs/extending-typespec/create-decorators.md
Original file line number Diff line number Diff line change
@@ -1,33 +1,33 @@
---
id: create-decorators
title: Creating TypeSpec Decorators
title: How to create TypeSpec decorators
---

# Creating TypeSpec decorators
# How to create TypeSpec decorators

TypeSpec decorator are implemented as JavaScript function. Declarating a decorator can be done in 1 or 2 part:
TypeSpec decorators are implemented as JavaScript functions. The process of creating a decorator can be divided into two parts:

1. [(Optional) Declare the decorator signature in typespec](#declaring-a-decorator-signature)
2. [Implement the decorator in Javascript](#implement-the-decorator-in-js)
1. [(Optional) Declare the decorator signature in TypeSpec](#declare-the-decorator-signature)
2. [Implement the decorator in JavaScript](#implement-the-decorator-in-javascript)

## Declaring a decorator signature
## Declare the decorator signature

This part is optional but provides great value:
While this step is optional, it offers significant benefits:

- Type checking for the parameters
- IDE IntelliSense
- It enables type checking for the parameters
- It provides IDE IntelliSense

A decorator signature can be declared using the `dec` keyword. As we are implementing the decorator in JS (only choice right now), we must apply the `extern` modifier as well.
You can declare a decorator signature using the `dec` keyword. Since we're implementing the decorator in JavaScript (the only option currently), we need to use the `extern` modifier as well.

```typespec
extern dec logType(target: unknown, name: string);
```

## Decorator target
## Specifying the decorator target

The first parameter of the decorator represents the TypeSpec type(s) that the decorator can be applied on.
The first parameter of the decorator represents the TypeSpec type(s) that the decorator can be applied to.

You can specify multiple potential target type using an `union expression`
You can specify multiple potential target types using a `union expression`.

```typespec
using TypeSpec.Reflection;
Expand All @@ -37,31 +37,30 @@ extern dec track(target: Model | Enum);

### Optional parameters

A decorator parameter can be marked optional using `?`
You can mark a decorator parameter as optional using `?`.

```typespec
extern dec track(target: Model | Enum, name?: valueof string);
```

### REST parameters
### Rest parameters

A decorator's last parameter can be prefixed with `...` to collect all the remaining arguments. The type of that parameter must be an `array expression`
You can prefix the last parameter of a decorator with `...` to collect all the remaining arguments. The type of this parameter must be an `array expression`.

```typespec
extern dec track(target: Model | Enum, ...names: valueof string[]);
```

## Ask for a value type
## Requesting a value type

It is common that decorators parameter will expect a value(e.g. a string or a number). However just using `: string` as the type will also allow a user of the decorator to pass `string` itself or a custom scalar extending string as well as union of strings.
Instead the decorator can use `valueof <T>` to specify that it is expecting a value of that kind.
It's common for decorator parameters to expect a value (e.g., a string or a number). However, using `: string` as the type would also allow a user of the decorator to pass `string` itself or a custom scalar extending string, as well as a union of strings. Instead, the decorator can use `valueof <T>` to specify that it expects a value of that kind.

| Example | Description |
| ----------------- | ---------------- |
| `valueof string` | Expect a string |
| `valueof float64` | Expect a float |
| `valueof int32` | Expect a number |
| `valueof boolean` | Expect a boolean |
| Example | Description |
| ----------------- | ----------------- |
| `valueof string` | Expects a string |
| `valueof float64` | Expects a float |
| `valueof int32` | Expects a number |
| `valueof boolean` | Expects a boolean |

```tsp
extern dec tag(target: unknown, value: valueof string);
Expand All @@ -73,7 +72,7 @@ extern dec tag(target: unknown, value: valueof string);
@tag("This is the tag name")
```

## Implement the decorator in JS
## Implement the decorator in JavaScript

Decorators can be implemented in JavaScript by prefixing the function name with `$`. A decorator function must have the following parameters:

Expand All @@ -90,7 +89,7 @@ export function $logType(context: DecoratorContext, target: Type, name: valueof
}
```

or in pure JS
Or in pure JavaScript:

```ts
// model.js
Expand All @@ -99,7 +98,7 @@ export function $logType(context, target, name) {
}
```

The decorator can then be consumed this way
The decorator can then be used like this:

```typespec
// main.tsp
Expand All @@ -114,23 +113,23 @@ model Dog {

### Decorator parameter marshalling

For certain TypeSpec types (Literal types) the decorator do not receive the actual type but a marshalled value if the decorator parmaeter type is a `valueof`. This is to simplify the most common cases.
For certain TypeSpec types (Literal types), the decorator does not receive the actual type but a marshalled value if the decorator parameter type is a `valueof`. This simplifies the most common cases.

| TypeSpec Type | Marshalled value in JS |
| ----------------- | ---------------------- |
| `valueof string` | `string` |
| `valueof numeric` | `number` |
| `valueof boolean` | `boolean` |

for all the other types they are not transformed.
For all other types, they are not transformed.

Example:

```ts
export function $tag(
context: DecoratorContext,
target: Type,
stringArg: string, // Here instead of receiving a `StringLiteral` the string value is being sent.
stringArg: string, // Here instead of receiving a `StringLiteral`, the string value is being sent.
modelArg: Model // Model has no special handling so we receive the Model type
) {}
```
Expand All @@ -142,17 +141,13 @@ The TypeSpec type system will already validate the string template can be serial

```tsp
extern dec doc(target: unknown, name: valueof string);
alias world = "world!";
@doc("Hello ${world} ") // receive: "Hello world!"
@doc("Hello ${123} ") // receive: "Hello 123"
@doc("Hello ${true} ") // receive: "Hello true"
model Bar {}
@doc("Hello ${Bar} ") // not called error
^ String template cannot be serialized as a string.
```

#### Typescript type Reference
Expand All @@ -168,7 +163,7 @@ model Bar {}

### Adding metadata with decorators

Decorators can be used to register some metadata. For this you can use the `context.program.stateMap` or `context.program.stateSet` to insert data that will be tied to the current execution.
Decorators can be used to register some metadata. For this, you can use the `context.program.stateMap` or `context.program.stateSet` to insert data that will be tied to the current execution.

❌ Do not save the data in a global variable.

Expand Down Expand Up @@ -200,7 +195,7 @@ export const StateKeys = $lib.stateKeys;

### Reporting diagnostic on decorator or arguments

Decorator context provide the `decoratorTarget` and `getArgumentTarget` helpers
The decorator context provides the `decoratorTarget` and `getArgumentTarget` helpers.

```ts
import type { DecoratorContext, Type } from "@typespec/compiler";
Expand All @@ -218,9 +213,9 @@ export function $customName(context: DecoratorContext, target: Type, name: strin
}
```

## Declaration - implementation link
## Linking declaration and implementation

Decorator signatures are linked to the implementation of the same name in the same namespace
Decorator signatures are linked to the implementation of the same name in the same namespace.

```typespec
import "./lib.js";
Expand All @@ -231,7 +226,7 @@ namespace MyLib {
}
```

is linked the the following in `lib.js`
This is linked to the following in `lib.js`:

```ts
export function $customName(context: DecoratorContext, name: string) {}
Expand All @@ -242,12 +237,12 @@ setTypeSpecNamespace("MyLib", $tableName);

## Troubleshooting

### Extern declation must have an implementation in JS file
### Extern declaration must have an implementation in JS file

Potential issues:

- JS function is not prefixed with `$`. For a decorator called `@decorate` the JS function must be called `$decoratate`
- JS function is not in the same namespace as the the `extern dec`
- Error is only showing in the IDE? Restart the TypeSpec server or the IDE.
- The JS function is not prefixed with `$`. For a decorator called `@decorate`, the JS function must be called `$decorate`.
- The JS function is not in the same namespace as the `extern dec`.
- Is the error only showing in the IDE? Try restarting the TypeSpec server or the IDE.

You can use `--trace bind.js.decorator` to log debug information about decorator loading in JS file that should help pinning down which of those is the issue.
You can use `--trace bind.js.decorator` to log debug information about decorator loading in the JS file, which should help identify the issue.
34 changes: 17 additions & 17 deletions docs/extending-typespec/diagnostics.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,25 @@
title: Diagnostics
---

The TypeSpec compiler reports errors and warnings in the spec using the diagnostic API.
The TypeSpec compiler uses the diagnostic API to report errors and warnings in the specification.

## Best practices

-Do not use `throw` to report errors. Any exception thrown like this will be presented as a bug in your library to the user.
-Use diagnostic API to report expected errors and warnings.
-Use `reportDiagnostic` in a decorator, `$onValidate` or `$onEmit`
-Do not use `reportDiagnostic` in an accessor (A function meant to be consumed in another library or emitter). See [collect diagnostics section](#collect-diagnostics)
-Avoid using `throw` to report errors. Any exceptions thrown in this manner will be perceived as bugs in your library by the user.
-Utilize the diagnostic API to report anticipated errors and warnings.
-Employ `reportDiagnostic` in a decorator, `$onValidate` or `$onEmit`
-Refrain from using `reportDiagnostic` in an accessor (a function intended to be used in another library or emitter). Refer to the [section on collecting diagnostics](#collect-diagnostics) for more information.

## Diagnostic specification
## Diagnostic requirements

- Each diagnostic MUST have a `code`. The full code is the the library name followed by the declared code. (`<lib-name>/<local-code>`)
- Each diagnostic MUST have a `severity`. It can be `error`, `warning`. Errors cannot be suppressed
- Each diagnostics MUST have at least one message. Using `default` as the `messageId` will allow it to be the default selected.
- Each diagnostics message MAY have parameters to interpolate information into the message
- Each diagnostic MUST have a `code`. The complete code is the library name followed by the declared code. (`<lib-name>/<local-code>`)
- Each diagnostic MUST have a `severity`. It can be `error` or `warning`. Errors cannot be suppressed.
- Each diagnostic MUST have at least one message. Using `default` as the `messageId` will make it the default selection.
- Each diagnostic message MAY have parameters to interpolate information into the message.

## Usage
## How to use

### Declare the diagnostics you are reporting
### Declare the diagnostics you plan to report

```ts
import { createTypeSpecLibrary } from "@typespec/compiler";
Expand Down Expand Up @@ -56,11 +56,11 @@ export const $lib = createTypeSpecLibrary({
},
} as const);

// Rexport the helper functions to be able to just call them directly.
// Re-export the helper functions to be able to just call them directly.
export const { reportDiagnostic, createDiagnostic };
```

This will represent 3 different diagnostics with full name of
This will represent three different diagnostics with the full names of:

- `@typespec/my-lib/no-array`
- `@typespec/my-lib/duplicate-route`
Expand All @@ -87,16 +87,16 @@ reportDiagnostic(program, {
// Multiple messages
reportDiagnostic(program, {
code: "duplicate-name",
messageId: "parmaeter",
messageId: "parameter",
format: {value: "$select"},
target: diagnosticTarget,
});
```

### Collect diagnostics

When trying to report diagnostic in an accessor a good pattern is not to report the diagnostic to the program directly but return a tuple to let the user decide what to do.
This prevent duplicate diagnostics emitter if the accessor is called multiple times.
When attempting to report a diagnostic in an accessor, a good practice is not to report the diagnostic to the program directly, but return a tuple to let the user decide what to do.
This prevents duplicate diagnostics emitter if the accessor is called multiple times.

```ts
import { createDiagnosticCollector, Diagnostic } from "@typespec/compiler";
Expand Down
Loading

0 comments on commit d8576ef

Please sign in to comment.