Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
changeKind: internal
packages:
- "@azure-tools/typespec-ts"
---

Clean up vestigial RLC technical debt in the emitter now that only Modular generation is supported: remove the dead `rlc-common` subfolder and relocate its live code into the main source tree, de-RLC internal type/function names (for example `RLCModel` -> `ClientModel`, `ModularClientOptions` -> `ClientModuleInfo`), decouple the client code model build from source generation, collapse the redundant `modularSourcesDir`, remove the dead `compatibility-query-multi-format` emitter option, and refresh the README and CONTRIBUTING docs. No changes to the generated output.
160 changes: 73 additions & 87 deletions packages/typespec-ts/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,70 +1,80 @@
# How to Contribute

## Prerequisite
## Prerequisites

Please follow the **[Prerequisite](../../CONTRIBUTING.md#prerequisites)** part to install the dependencies.
Follow the repository-level **[Prerequisites](../../CONTRIBUTING.md#prerequisites)** to install
the required tooling (Node.js and `pnpm`). The command scripts under `test/commands/*` run on
`node` directly (no `tsx`), which requires **Node >= 22.18**.

## Steps to clone, build & test
## Clone, build & test

1. Use the following command to clone the Typescript/Javascript SDK generator repository:
1. Clone the `typespec-azure` monorepo (this emitter lives in `packages/typespec-ts`). The repo
uses a git submodule for `core/`, so initialize submodules when cloning:

```
git clone https://github.com/Azure/autorest.typescript.git
```
```bash
git clone --recurse-submodules https://github.com/Azure/typespec-azure.git
```

2. Use the following commands to build the SDK generator:
If you already cloned without `--recurse-submodules`, run `git submodule update --init`.

```
pnpm install
pnpm build
```
2. Install dependencies and build from the repo root:

3. There are also 3 test-suites in the RLC generator:
1. Unit tests (which could be found at `test/unit/*`)
2. Integration tests (which could be found at `test/integration/*`)
3. Smoke tests (which could be found at `../../packages/typespec-test`)
```bash
pnpm install
pnpm build
```

1. You can run the Unit tests & Integration tests using the following command:
To build only this package and its dependencies:

```
npm run test
```
```bash
pnpm -r --filter "@azure-tools/typespec-ts..." build
```

Running the command above will do the following things:
3. Run the commands below from `packages/typespec-ts`.

- Start TestServer
- Build TypeSpec TS
- Generate all scenarios in parallel (i.e. Dictionary, Extensible Enums, Models, Resiliency)
- Run all the tests under test/integration
- Stop TestServer
## Test suites

**_Note_**: If your development environment is Windows, then run the command `npm run start-test-server`(in a seperate window) before running `npm run test` and run the command `npm run stop-test-server` after. (In non windows machines, we could run the test-server in the background automatically. But, in Windows machines, it has to be done manually.)
The package is tested through three vitest projects (configured in `vitest.config.ts`):

5. You can run the Smoke tests using the following command:
| Project | Location | Covers | Command |
| --------------------------- | ----------------------------------- | -------------------------------- | ---------------------------------------- |
| `test-next` | `test-next/**` | Modern unit tests | `pnpm test-next` |
| `unit-modular` | `test/modular-unit/**` | Modular unit tests | `pnpm unit-test` |
| `integration-azure-modular` | `test/azure-modular-integration/**` | Modular spector end-to-end tests | `pnpm integration-test-ci:azure-modular` |

```shell
cd ../../packages/typespec-test
npm run smoke-test
```
`pnpm lint` runs ESLint with `--max-warnings=0`.

## How to add an integration test case
The integration suite generates real clients from specs and runs them against a local spector
test server; `pnpm integration-test-ci:azure-modular` starts the server, generates, and runs the
assertions for you. To (re)generate the tracked baselines without running the tests:

Whenever you work on adding a feature/fixing a bug, this would probably be your first step. You create a test case and then run it through the generator, see the result, modify the generator, run it again and so on, until you get the desired output.
```bash
pnpm copy:typespec # assemble ./temp/specs from the http-specs packages
pnpm generate-tsp-only # regenerate all Azure modular baselines (client + declarations)
pnpm check:tree # fails if regeneration left the git tree dirty
```

1. Pick up a typespec as your test input in spector. Below are some examples
> Only `src/index.d.ts` is tracked per generated package, so `check:tree` is what guards the
> generated API surface in CI. See `.github/instructions/typespec-ts.instructions.md` for the
> full pipeline details.

Let us say your test input is `authentication/api-key/main.tsp` in @typespec/http-specs or @azure-tools/azure-http-specs.
## How to add an integration (spector) test case

1. Now add an entry to the TypeSpecRanchConfig to the file [`spector-list.js`](./test/commands/spector-list.js). In the file, add the following to the array.
1. Pick a spec from `@typespec/http-specs` or `@azure-tools/azure-http-specs` as your input
(for example `authentication/api-key`).

```typescript
{
outputPath: "authentication/apiKey",
inputPath: "authentication/api-key"
},
2. Add an entry to the `azureModularTsps` array in
[`test/commands/spector-list.js`](./test/commands/spector-list.js):

```js
{
outputPath: "authentication/apiKey",
inputPath: "authentication/api-key",
},
```

1. Create a tspconfig.yaml in `./test/integration/generated/authentication/apiKey` folder and put the following content in it.
3. Create a `tspconfig.yaml` under
`test/azure-modular-integration/generated/authentication/apiKey/`:

```yaml
emit:
Expand All @@ -73,60 +83,36 @@ Whenever you work on adding a feature/fixing a bug, this would probably be your
"@azure-tools/typespec-ts":
emitter-output-dir: "{project-root}"
generate-metadata: true
generate-test: true
include-shortcuts: true
add-credentials: false
generate-test: false
is-typespec-test: true
title: AuthApiKeyClient
hierarchy-client: false
package-details:
name: "@msinternal/auth-apikey"
description: "Auth api key Test Service"
version: "1.0.0"
```

1. Now, You can generate the RLC for your test case with the following command: (Initially, during your development, you do not want to run all the cases during every step of your development, you can comment out other test cases. But, once your code changes are complete for your case, then you need to run the entire suite to ensure that your changes did not cause any unwanted changes.)
4. Generate the client for your case. During development you can temporarily trim
`azureModularTsps` to just your entry for a faster loop, and emit only the importable sources:

```shell
npm run generate-tsp-only
```bash
pnpm copy:typespec
pnpm generate-tsp-only:azure-modular:client
```

1. Once you are satisfied with the generated code, you can add a spec file such as `testUserCaseRest.spec.ts` file [here](./test/integration). You can find several examples in the same place.

## How to debug
Once your change is complete, regenerate the whole suite and verify there are no unexpected
diffs:

### `generate-tsp-only` step

If you would like to debug the `generate-tsp-only` step for our test input, Open the repository in VS Code -> Select `Generate code for TypeSpec Emitter` section -> Click `Attach`.

### Spec file

If you would like to debug the `testUserCase.spec.ts` file (after the SDK is generated), Open the repository in VS Code -> Open the `testUserCase.spec.ts` file -> Select `Run and Debug` section -> Click `IntegrationTests - Current File`.

### How to debug an unit test case

- In VS Code, We have created a Debugging profile for UnitTests to start debugging:
1. Go to the debugger tab
2. Select the "[TypeSpec] - Unit Test" Profile
3. Click the "Play" button

- Your breakpoints will start hitting, you can set breakpoints in either Test or Generator code

### Integration Tests

- In order to debug integration tests you need to start the test server, by running:

npm run start-test-server:v1

- Once the Test Server is running
1. In VSCode go to the debugger tab
2. Select the "[TypeSpec] - Integration Test" profile from the drop down
3. Click the "Play" button

- **\*\***IMPORTANT**\*\***: Running Integration Tests for debugging, does not re-generate the test clients so make sure that after each change you do:
- Re-generate all the test swaggers
```bash
pnpm generate-tsp-only
pnpm check:tree
```

npm run generate-tsp-only -- --build
5. Add a `*.test.ts` file under `test/azure-modular-integration/` with your assertions. There are
many existing examples in that folder (e.g. `auth-api-key.test.ts`).

- Re-generate a specific swagger
## How to debug

npm run generate-tsp-only -- -i bodyComplexRest -b
The repo ships a **Debug Current Test File** VS Code launch profile (`.vscode/launch.json`) that
runs the currently open file through vitest with the debugger attached. Open any `*.test.ts`
file (unit or integration), select that profile, and press play — breakpoints work in both the
test and the emitter `src/`.
8 changes: 1 addition & 7 deletions packages/typespec-ts/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# @azure-tools/typespec-ts

An experimental TypeSpec emitter for TypeScript
A TypeSpec emitter for TypeScript

## Install

Expand Down Expand Up @@ -192,12 +192,6 @@ The emitter will use camel case to normalize the property name, to ignore this n

The emitter has a normalization logic for enum member key, to ignore this normalization, you can set this option to true

### `compatibility-query-multi-format`

**Type:** `boolean`

Whether to generate the backward-compatible code for query parameter serialization for array types in RLC. Defaults to `false`

### `typespec-title-map`

**Type:** `object`
Expand Down
2 changes: 1 addition & 1 deletion packages/typespec-ts/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@azure-tools/typespec-ts",
"version": "0.55.0",
"description": "An experimental TypeSpec emitter for TypeScript",
"description": "A TypeSpec emitter for TypeScript",
"main": "dist/src/index.js",
"type": "module",
"exports": {
Expand Down
4 changes: 2 additions & 2 deletions packages/typespec-ts/src/context-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Project, SourceFile } from "ts-morph";
import { ExternalDependencies } from "./framework/dependency.js";
import { Binder } from "./framework/hooks/binder.js";
import { SdkTypeContext } from "./framework/hooks/sdk-types.js";
import { RlcMetaTree } from "./meta-tree.js";
import { ClientTypeMetaTree } from "./meta-tree.js";

/**
* Contexts Object Guidelines
Expand All @@ -20,7 +20,7 @@ import { RlcMetaTree } from "./meta-tree.js";
* Remember, adding too many contexts can lead to complex dependencies and harder-to-maintain code. Always evaluate if the context is truly necessary or if there are better alternatives such as localized state management or passing props for simpler scenarios.
*/
type Contexts = {
rlcMetaTree: RlcMetaTree; // Context for RLC types metadata.
clientTypeMetaTree: ClientTypeMetaTree; // Context for client type metadata.
outputProject: Project; // The TS-Morph root project context for code generation.
symbolMap: Map<string, SourceFile>; // Mapping of symbols to their corresponding source files.
sdkTypes: SdkTypeContext;
Expand Down
2 changes: 1 addition & 1 deletion packages/typespec-ts/src/framework/hooks/sdk-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import { reportDiagnostic } from "../../lib.js";
import { visitPackageTypes } from "../../modular/emit-models.js";
import { getAllAncestors, getAllProperties } from "../../modular/helpers/operation-helpers.js";
import { normalizeModelPropertyName } from "../../modular/type-expressions/get-type-expression.js";
import { NameType, normalizeName } from "../../rlc-common/index.js";
import { SdkContext } from "../../utils/interfaces.js";
import { NameType, normalizeName } from "../../utils/name-utils.js";

export const emitQueue: Set<SdkType> = new Set<SdkType>();
export const flattenPropertyModelMap: Map<SdkModelPropertyType, SdkModelType> = new Map<
Expand Down
Loading
Loading