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,10 @@
{
"changes": [
{
"packageName": "@typespec/compiler",
"comment": "Api: Added `sourceModel` and `sourceOperation` on `Model` and `Operation` respectively.",
"type": "none"
}
],
"packageName": "@typespec/compiler"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@typespec/html-program-viewer",
"comment": "Update to show new `sourceModel` and `sourceOperation`",
"type": "none"
}
],
"packageName": "@typespec/html-program-viewer"
}
6 changes: 4 additions & 2 deletions packages/compiler/core/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1602,14 +1602,14 @@ export function createChecker(program: Program): Checker {
let decorators: DecoratorApplication[] = [];

// Is this a definition or reference?
let parameters: Model, returnType: Type;
let parameters: Model, returnType: Type, sourceOperation: Operation | undefined;
if (node.signature.kind === SyntaxKind.OperationSignatureReference) {
// Attempt to resolve the operation
const baseOperation = checkOperationIs(node, node.signature.baseOperation, mapper);
if (!baseOperation) {
return errorType;
}

sourceOperation = baseOperation;
// Reference the same return type and create the parameters type
parameters = cloneType(baseOperation.parameters);
returnType = baseOperation.returnType;
Expand All @@ -1629,6 +1629,7 @@ export function createChecker(program: Program): Checker {
parameters,
returnType,
decorators,
sourceOperation,
interface: parentInterface,
});

Expand Down Expand Up @@ -2328,6 +2329,7 @@ export function createChecker(program: Program): Checker {
const isBase = checkModelIs(node, node.is, mapper);

if (isBase) {
type.sourceModel = isBase;
checkDeprecated(isBase, node.is!);
// copy decorators
decorators.push(...isBase.decorators);
Expand Down
7 changes: 7 additions & 0 deletions packages/compiler/core/projector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,9 @@ export function createProjector(
if (model.baseModel) {
projectedModel.baseModel = projectType(model.baseModel) as Model;
}
if (model.sourceModel) {
projectedModel.sourceModel = projectType(model.sourceModel) as Model;
}

if (model.indexer) {
projectedModel.indexer = {
Expand Down Expand Up @@ -406,6 +409,10 @@ export function createProjector(
projectedOp.templateArguments = mutate(projectedOp.templateMapper.args);
}

if (op.sourceOperation) {
projectedOp.sourceOperation = projectType(op.sourceOperation) as Operation;
}

if (op.namespace) {
projectedOp.namespace = projectedNamespaceScope();
}
Expand Down
10 changes: 10 additions & 0 deletions packages/compiler/core/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,11 @@ export interface Model extends BaseType, DecoratedType, TemplatedTypeBase {
*/
derivedModels: Model[];

/**
* The model that is referenced via `model is`.
*/
sourceModel?: Model;

/**
* Late-bound symbol of this model type.
* @internal
Expand Down Expand Up @@ -344,6 +349,11 @@ export interface Operation extends BaseType, DecoratedType, TemplatedTypeBase {
interface?: Interface;
parameters: Model;
returnType: Type;

/**
* The operation that is referenced via `op is`.
*/
sourceOperation?: Operation;
}

export interface Namespace extends BaseType, DecoratedType {
Expand Down
13 changes: 13 additions & 0 deletions packages/compiler/test/checker/model.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,19 @@ describe("compiler: models", () => {
});
});

it("keeps reference to source model", async () => {
testHost.addTypeSpecFile(
"main.tsp",
`
import "./dec.js";
@test model A { }
@test model B is A { };
`
);
const { A, B } = (await testHost.compile("main.tsp")) as { A: Model; B: Model };
strictEqual(B.sourceModel, A);
});

it("copies decorators", async () => {
testHost.addTypeSpecFile(
"main.tsp",
Expand Down
12 changes: 12 additions & 0 deletions packages/compiler/test/checker/operations.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,18 @@ describe("compiler: operations", () => {
strictEqual((foo.returnType as IntrinsicType).name, "void");
});

it("keeps reference to source operation", async () => {
testHost.addTypeSpecFile(
"main.tsp",
`
@test op a(): void;
@test op b is a;
`
);
const { a, b } = (await testHost.compile("main.tsp")) as { a: Operation; b: Operation };
strictEqual(b.sourceOperation, a);
});

it("can be templated and referenced to define other operations", async () => {
testHost.addTypeSpecFile(
"main.tsp",
Expand Down
2 changes: 2 additions & 0 deletions packages/html-program-viewer/src/ui.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ const OperationUI: FunctionComponent<{ type: Operation }> = ({ type }) => {
interface: "skip",
parameters: "nested",
returnType: "ref",
sourceOperation: "ref",
}}
/>
);
Expand All @@ -247,6 +248,7 @@ const ModelUI: FunctionComponent<{ type: Model }> = ({ type }) => {
baseModel: "ref",
derivedModels: "ref",
properties: "nested",
sourceModel: "ref",
}}
/>
);
Expand Down