diff --git a/.chronus/changes/decl-provider-2026-0-15-15-41-29.md b/.chronus/changes/decl-provider-2026-0-15-15-41-29.md new file mode 100644 index 00000000000..6c10ed85c8e --- /dev/null +++ b/.chronus/changes/decl-provider-2026-0-15-15-41-29.md @@ -0,0 +1,7 @@ +--- +changeKind: feature +packages: + - "@typespec/emitter-framework" +--- + +Add DeclarationProvider, a mechanism to reactively track declarations discovered as they are emitted. \ No newline at end of file diff --git a/.chronus/changes/decl-provider-2026-0-15-15-43-16.md b/.chronus/changes/decl-provider-2026-0-15-15-43-16.md new file mode 100644 index 00000000000..a656c1a38fa --- /dev/null +++ b/.chronus/changes/decl-provider-2026-0-15-15-43-16.md @@ -0,0 +1,7 @@ +--- +changeKind: breaking +packages: + - "@typespec/emitter-framework" +--- + +TypeScript: Adopt DeclarationProvider, which changes how refkeys to declarations are assigned and referenced. The current provider vends fresh refkeys when needed, so `refkey(type)` is no longer a viable way to refer to declarations created by the emitter framework. Replace all forms of `refkey(type)` with `dp.getRefkey(type)`. \ No newline at end of file diff --git a/.chronus/changes/decl-provider-2026-0-15-15-43-33.md b/.chronus/changes/decl-provider-2026-0-15-15-43-33.md new file mode 100644 index 00000000000..04544234568 --- /dev/null +++ b/.chronus/changes/decl-provider-2026-0-15-15-43-33.md @@ -0,0 +1,7 @@ +--- +changeKind: internal +packages: + - "@typespec/http-client-js" +--- + +Adopt EF changes. \ No newline at end of file diff --git a/.chronus/changes/decl-provider-2026-0-15-15-44-18.md b/.chronus/changes/decl-provider-2026-0-15-15-44-18.md new file mode 100644 index 00000000000..e8885b6a213 --- /dev/null +++ b/.chronus/changes/decl-provider-2026-0-15-15-44-18.md @@ -0,0 +1,7 @@ +--- +changeKind: fix +packages: + - "@typespec/http-client-js" +--- + +Don't emit types for built-ins like floats and such (which were unused anyway). \ No newline at end of file diff --git a/.chronus/changes/decl-provider-2026-0-15-15-45-2.md b/.chronus/changes/decl-provider-2026-0-15-15-45-2.md new file mode 100644 index 00000000000..ccb4bf6cc52 --- /dev/null +++ b/.chronus/changes/decl-provider-2026-0-15-15-45-2.md @@ -0,0 +1,7 @@ +--- +changeKind: fix +packages: + - "@typespec/http-client-js" +--- + +Emit types for model is `Record`. \ No newline at end of file diff --git a/.chronus/changes/decl-provider-2026-0-15-15-49-58.md b/.chronus/changes/decl-provider-2026-0-15-15-49-58.md new file mode 100644 index 00000000000..2e9f058effb --- /dev/null +++ b/.chronus/changes/decl-provider-2026-0-15-15-49-58.md @@ -0,0 +1,7 @@ +--- +changeKind: breaking +packages: + - "@typespec/emitter-framework" +--- + +TypeScript: remove notion of suffix refkey. Since its purpose is to create a unique refkey based on the suffix, users can just provide such a refkey instead of providing the suffix. \ No newline at end of file diff --git a/.chronus/changes/decl-provider-2026-0-15-15-51-26.md b/.chronus/changes/decl-provider-2026-0-15-15-51-26.md new file mode 100644 index 00000000000..2616cc9e70f --- /dev/null +++ b/.chronus/changes/decl-provider-2026-0-15-15-51-26.md @@ -0,0 +1,7 @@ +--- +changeKind: feature +packages: + - "@typespec/emitter-framework" +--- + +Add `refkey` option to `buildParameterDescriptor`. \ No newline at end of file diff --git a/packages/emitter-framework/package.json b/packages/emitter-framework/package.json index a9da96f0d74..a73f3951a8d 100644 --- a/packages/emitter-framework/package.json +++ b/packages/emitter-framework/package.json @@ -23,18 +23,23 @@ }, "exports": { ".": { + "development": "./src/core/index.ts", "import": "./dist/src/core/index.js" }, "./csharp": { + "development": "./src/csharp/index.ts", "import": "./dist/src/csharp/index.js" }, "./typescript": { + "development": "./src/typescript/index.ts", "import": "./dist/src/typescript/index.js" }, "./python": { + "development": "./src/python/index.ts", "import": "./dist/src/python/index.js" }, "./testing": { + "development": "./src/testing/index.ts", "import": "./dist/src/testing/index.js" } }, diff --git a/packages/emitter-framework/src/core/context/declaration-provider.ts b/packages/emitter-framework/src/core/context/declaration-provider.ts new file mode 100644 index 00000000000..7c2c569dadf --- /dev/null +++ b/packages/emitter-framework/src/core/context/declaration-provider.ts @@ -0,0 +1,25 @@ +import { DeclarationProvider } from "#core/declaration-provider.js"; +import { type ComponentContext, createNamedContext, useContext } from "@alloy-js/core"; +import type { Typekit } from "@typespec/compiler/typekit"; +import { useTsp } from "./tsp-context.js"; + +/** + * Provides the declaration provider that is used to get refkeys for + * declarations and determine if a type should be declared or referenced. + */ +export const DeclarationProviderContext: ComponentContext = + createNamedContext("DeclarationProviderContext"); + +export function useDeclarationProvider(): DeclarationProvider { + return useContext(DeclarationProviderContext) ?? getDefaultDeclarationProvider(useTsp().$); +} + +const knownDeclarationProviders = new WeakMap(); +function getDefaultDeclarationProvider($: Typekit) { + let provider = knownDeclarationProviders.get($); + if (!provider) { + provider = new DeclarationProvider($); + knownDeclarationProviders.set($, provider); + } + return provider; +} diff --git a/packages/emitter-framework/src/core/context/index.ts b/packages/emitter-framework/src/core/context/index.ts index a4820a0403c..47ad8e0dcf2 100644 --- a/packages/emitter-framework/src/core/context/index.ts +++ b/packages/emitter-framework/src/core/context/index.ts @@ -1,2 +1,3 @@ +export * from "./declaration-provider.js"; export * from "./name-policy-context.js"; export * from "./tsp-context.js"; diff --git a/packages/emitter-framework/src/core/declaration-provider.test.tsx b/packages/emitter-framework/src/core/declaration-provider.test.tsx new file mode 100644 index 00000000000..4e45c52b6b0 --- /dev/null +++ b/packages/emitter-framework/src/core/declaration-provider.test.tsx @@ -0,0 +1,306 @@ +import { Tester } from "#test/test-host.js"; +import { effect, Output, renderAsync } from "@alloy-js/core"; +import type { Enum, Model, Union } from "@typespec/compiler"; +import { $ as typekit } from "@typespec/compiler/typekit"; +import { describe, expect, it } from "vitest"; +import { DeclarationProvider } from "./declaration-provider.js"; + +describe("isDeclaration", () => { + interface DeclarationTestCase { + description: string; + code: string; + expected: boolean; + } + + async function assertDeclaration(code: string, expected: boolean) { + const runner = await Tester.createInstance(); + const { program } = await runner.compile(code); + const $ = typekit(program); + const provider = new DeclarationProvider($); + + const type = program.resolveTypeReference("Test")[0]!; + expect(type, "Type Test should exist").toBeDefined(); + expect(provider.isDeclaration(type)).toBe(expected); + } + + async function assertDeclarationExpression(expression: string, expected: boolean, setup = "") { + const code = `${setup}model Test { prop: ${expression}; }`; + const runner = await Tester.createInstance(); + const { program } = await runner.compile(code); + const $ = typekit(program); + const provider = new DeclarationProvider($); + + const test = program.resolveTypeReference("Test")[0] as Model; + const type = test.properties.get("prop")!.type; + expect(type, "Property type should exist").toBeDefined(); + expect(provider.isDeclaration(type)).toBe(expected); + } + + const declarationTestCases: DeclarationTestCase[] = [ + { + description: "named model", + code: `model Test { name: string; }`, + expected: true, + }, + { + description: "namespace", + code: `namespace Test { }`, + expected: true, + }, + { + description: "interface", + code: `interface Test { getPet(): string; }`, + expected: true, + }, + { + description: "enum", + code: `enum Test { Red, Green, Blue }`, + expected: true, + }, + { + description: "operation", + code: `op Test(): string;`, + expected: true, + }, + { + description: "named union", + code: `union Test { string, int32 }`, + expected: true, + }, + { + description: "scalar", + code: `scalar Test extends string;`, + expected: true, + }, + ]; + + declarationTestCases.forEach(({ description, code, expected }) => { + it(`treats ${description} as declaration`, async () => { + await assertDeclaration(code, expected); + }); + }); + + interface ExpressionTestCase { + description: string; + expression: string; + expected: boolean; + setup?: string; + } + + const expressionTestCases: ExpressionTestCase[] = [ + { + description: "array", + expression: "string[]", + expected: false, + }, + { + description: "record", + expression: "Record", + expected: false, + }, + { + description: "anonymous union", + expression: '"active" | "inactive"', + expected: false, + }, + { + description: "anonymous model", + expression: "{ name: string }", + expected: false, + }, + { + description: "template instance", + expression: "Array", + expected: false, + }, + { + description: "scalar (string)", + expression: "string", + expected: false, + }, + { + description: "custom template instance", + expression: "Container", + expected: true, + setup: "model Container { item: T; }", + }, + { + description: "intrinsic (void)", + expression: "void", + expected: false, + }, + { + description: "intrinsic (never)", + expression: "never", + expected: false, + }, + ]; + + expressionTestCases.forEach(({ description, expression, expected, setup }) => { + it(`treats ${description} as ${expected ? "declaration" : "non-declaration"}`, async () => { + await assertDeclarationExpression(expression, expected, setup); + }); + }); + + it("treats a model property as a non-declaration", async () => { + const runner = await Tester.createInstance(); + const { program } = await runner.compile(`model Test { a: string, b: int32 }`); + const $ = typekit(program); + const provider = new DeclarationProvider($); + + const model = program.resolveTypeReference("Test")[0]! as Model; + const property = model.properties.get("a")!; + expect(provider.isDeclaration(property)).toBe(false); + }); + + it("treats union variant as non-declaration", async () => { + const runner = await Tester.createInstance(); + const { program } = await runner.compile(`union Test { a: string, b: int32 }`); + const $ = typekit(program); + const provider = new DeclarationProvider($); + + const union = program.resolveTypeReference("Test")[0]! as Union; + const variant = union.variants.get("a")!; + expect(provider.isDeclaration(variant)).toBe(false); + }); + + it("treats enum member as non-declaration", async () => { + const runner = await Tester.createInstance(); + const { program } = await runner.compile(`enum Test { Red, Green, Blue }`); + const $ = typekit(program); + const provider = new DeclarationProvider($); + + const enumType = program.resolveTypeReference("Test")[0]! as Enum; + const member = enumType.members.get("Red")!; + expect(provider.isDeclaration(member)).toBe(false); + }); +}); + +describe("getRefkey", () => { + it("creates a refkey for a declaration type", async () => { + const runner = await Tester.createInstance(); + const { program } = await runner.compile(` + model Pet { + name: string; + } + `); + const $ = typekit(program); + const provider = new DeclarationProvider($); + + const pet = program.resolveTypeReference("Pet")[0]!; + const key = provider.getRefkey(pet); + + expect(key).toBeDefined(); + }); + + it("returns the same refkey for the same type", async () => { + const runner = await Tester.createInstance(); + const { program } = await runner.compile(` + model Pet { + name: string; + } + `); + const $ = typekit(program); + const provider = new DeclarationProvider($); + + const pet = program.resolveTypeReference("Pet")[0]!; + const key1 = provider.getRefkey(pet); + const key2 = provider.getRefkey(pet); + + expect(key1).toBe(key2); + }); + + it("returns different refkeys for different types", async () => { + const runner = await Tester.createInstance(); + const { program } = await runner.compile(` + model Pet { + name: string; + } + model Owner { + name: string; + } + `); + const $ = typekit(program); + const provider = new DeclarationProvider($); + + const pet = program.resolveTypeReference("Pet")[0]!; + const owner = program.resolveTypeReference("Owner")[0]!; + const petKey = provider.getRefkey(pet); + const ownerKey = provider.getRefkey(owner); + + expect(petKey).not.toBe(ownerKey); + }); + + it("throws for non-declaration types", async () => { + const runner = await Tester.createInstance(); + const { program } = await runner.compile(` + model Pet { + name: string; + } + `); + const $ = typekit(program); + const provider = new DeclarationProvider($); + + const pet = program.resolveTypeReference("Pet")[0] as Model; + const prop = pet.properties.get("name")!; + + expect(() => provider.getRefkey(prop)).toThrow(/Type ModelProperty is not a declaration/); + }); + + it("adds type to declarations map", async () => { + const runner = await Tester.createInstance(); + const { program } = await runner.compile(` + model Pet { + name: string; + } + `); + const $ = typekit(program); + const provider = new DeclarationProvider($); + + const pet = program.resolveTypeReference("Pet")[0]!; + + // Initially not in the map + expect(provider.declarations.has(pet)).toBe(false); + + // Get refkey should add it + const key = provider.getRefkey(pet); + + // Now it should be in the map + expect(provider.declarations.has(pet)).toBe(true); + expect(provider.declarations.get(pet)).toBe(key); + }); + + it("declarations map is reactive", async () => { + const runner = await Tester.createInstance(); + const { program } = await runner.compile(` + model Pet { + name: string; + } + `); + const $ = typekit(program); + const provider = new DeclarationProvider($); + + const pet = program.resolveTypeReference("Pet")[0]!; + let effectRan = false; + let capturedKey: any = undefined; + + // Set up an effect that reacts to the declarations map + effect(() => { + if (provider.declarations.has(pet)) { + effectRan = true; + capturedKey = provider.declarations.get(pet); + } + }); + + // Initially effect should not have run (map is empty) + expect(effectRan).toBe(false); + + // Get refkey should trigger the effect + const key = provider.getRefkey(pet); + await renderAsync(); // Allow reactivity to flush + + // Effect should have run and captured the key + expect(effectRan).toBe(true); + expect(capturedKey).toBe(key); + }); +}); diff --git a/packages/emitter-framework/src/core/declaration-provider.ts b/packages/emitter-framework/src/core/declaration-provider.ts new file mode 100644 index 00000000000..a341ec2ccf2 --- /dev/null +++ b/packages/emitter-framework/src/core/declaration-provider.ts @@ -0,0 +1,149 @@ +import { refkey, shallowReactive, type Refkey } from "@alloy-js/core"; +import { getLocationContext, isVoidType, type MemberType, type Type } from "@typespec/compiler"; +import type { Typekit } from "@typespec/compiler/typekit"; + +/** + * This class tracks determines what types should be declared in the emit output + * and vends refkeys for them. The emitter framework will call `getRefkey` on + * the current declaration provider (see DeclarationProviderContext) to get + * refkeys for declarations it needs. Types passed to `getRefkey` and the refkey + * ultimately returned are added to the reactive `declarations` map. All types + * in this map should be emitted as declarations by the emitter. + */ +export class DeclarationProvider { + $: Typekit; + /** + * Reactive map of types to refkeys for all types which should be emitted + * as declarations. + */ + declarations: Map = shallowReactive(new Map()); + #staticMemberRefkeys: Map = new Map(); + + constructor($: Typekit) { + this.$ = $; + } + + /** + * Get a refkey for the given type, creating one if one has not been created + * for it already. + * + * @throws if the type is not a declaration or static member. + * + * @remarks + * + * If you need a static member refkey for a type which is not otherwise a + * static member (e.g. because you are rendering a union which can't usually + * have its variants referenced as an enum which can), you can call + * `getStaticMemberRefkey` directly. + */ + public getRefkey(type: Type): Refkey { + if (this.isDeclaration(type)) { + return this.getDeclarationRefkey(type); + } + + if (this.isStaticMember(type)) { + return this.getStaticMemberRefkey(type as MemberType); + } + + throw new Error( + `Type ${type.kind} is not a declaration or static member and cannot have a refkey.`, + ); + } + + /** + * Whether the given type should be referenced via refkey. True for things + * which are declarations and static members of declarations. + */ + public shouldReference(type: Type): boolean { + return this.isDeclaration(type) || this.isStaticMember(type); + } + + /** + * Get a refkey for the given declaration, creating one if one has not been + * created for it already. + * + * @throws if the type is not a declaration type. + */ + public getDeclarationRefkey(type: Type): Refkey { + const existing = this.declarations.get(type); + + if (existing) { + return existing; + } + + if (!this.isDeclaration(type)) { + throw new Error(`Type ${type.kind} is not a declaration type and cannot have a refkey.`); + } + + const key = refkey(); + this.declarations.set(type, key); + return key; + } + + /** + * Whether the given type should be emitted as a declaration. + * + * @remarks + * + * By default, things which are declarations in TypeSpec but not in the + * compiler are considered declarations. For example, `string` is not a + * declaration but `scalar customScalar extends string` is. Likewise, + * `Array` is not a declaration because Array is built-in, but + * `MyModel` is a declaration because it's not part of the compiler and + * has a TypeSpec declaration. + */ + public isDeclaration(type: Type): boolean { + const location = getLocationContext(this.$.program, type).type; + + if (location === "compiler") { + return false; + } + + // BUG: intrinsic.is doesn't capture void... + if (this.$.intrinsic.is(type) || isVoidType(type)) { + return false; + } + + if (!("name" in type) || type.name === undefined || type.name === "") { + return false; + } + + if ( + this.$.unionVariant.is(type) || + this.$.enumMember.is(type) || + this.$.modelProperty.is(type) || + this.$.intrinsic.is(type) + ) { + return false; + } + + if (location === "synthetic" && (type.name === "Record" || type.name === "Array")) { + return false; + } + + return true; + } + + /** + * Whether the given type is a static member that can be referenced directly + * (i.e. without instantiation). + */ + public isStaticMember(type: Type): boolean { + return this.$.enumMember.is(type); + } + + /** + * Get a refkey for the given static member, creating one if one has not been + * created for it already. + */ + public getStaticMemberRefkey(type: MemberType): Refkey { + const existing = this.#staticMemberRefkeys.get(type); + if (existing) { + return existing; + } + + const key = refkey(); + this.#staticMemberRefkeys.set(type, key); + return key; + } +} diff --git a/packages/emitter-framework/src/core/index.ts b/packages/emitter-framework/src/core/index.ts index 6b3d6fb8a5a..8844ee0eb80 100644 --- a/packages/emitter-framework/src/core/index.ts +++ b/packages/emitter-framework/src/core/index.ts @@ -1,5 +1,6 @@ export * from "./components/index.js"; export * from "./context/index.js"; +export * from "./declaration-provider.js"; export * from "./scc-set.js"; export * from "./transport-name-policy.js"; export * from "./type-connector.js"; diff --git a/packages/emitter-framework/src/typescript/components/enum-declaration.tsx b/packages/emitter-framework/src/typescript/components/enum-declaration.tsx index 814824ebfab..dcffb06cb2e 100644 --- a/packages/emitter-framework/src/typescript/components/enum-declaration.tsx +++ b/packages/emitter-framework/src/typescript/components/enum-declaration.tsx @@ -1,9 +1,10 @@ +import { useDeclarationProvider } from "#core/context/declaration-provider.js"; +import { joinRefkeys } from "#typescript/utils/refkey.js"; import { type Children, For, type Refkey } from "@alloy-js/core"; import * as ts from "@alloy-js/typescript"; import type { Enum, EnumMember as TspEnumMember, Union } from "@typespec/compiler"; import { useTsp } from "../../core/context/tsp-context.js"; import { reportDiagnostic } from "../../lib.js"; -import { declarationRefkeys, efRefkey } from "../utils/refkey.js"; export interface EnumDeclarationProps extends Omit { name?: string; @@ -25,29 +26,32 @@ export function EnumDeclaration(props: EnumDeclarationProps) { if (!props.type.name || props.type.name === "") { reportDiagnostic($.program, { code: "type-declaration-missing-name", target: props.type }); } - const refkeys = declarationRefkeys(props.refkey, props.type); - const name = props.name ?? ts.useTSNamePolicy().getName(props.type.name!, "enum"); - const members = Array.from(type.members.entries()); + const dp = useDeclarationProvider(); + const refkeys = joinRefkeys(props.refkey, dp.getRefkey(props.type)); + const members = type.members.values(); const doc = props.doc ?? $.type.getDoc(type); return ( - {([key, value]) => { - const memberDoc = $.type.getDoc(value); + {(member) => { + const memberDoc = $.type.getDoc(member); + + const originalMember = $.union.is(props.type) + ? props.type.variants.get(member.name)! + : member; + return ( ); }} diff --git a/packages/emitter-framework/src/typescript/components/function-declaration.tsx b/packages/emitter-framework/src/typescript/components/function-declaration.tsx index 41917a65e15..13740629ba0 100644 --- a/packages/emitter-framework/src/typescript/components/function-declaration.tsx +++ b/packages/emitter-framework/src/typescript/components/function-declaration.tsx @@ -1,8 +1,9 @@ +import { useDeclarationProvider } from "#core/context/declaration-provider.js"; +import { joinRefkeys } from "#typescript/utils/refkey.js"; import * as ts from "@alloy-js/typescript"; import type { Model, Operation } from "@typespec/compiler"; import { useTsp } from "../../core/index.js"; import { buildParameterDescriptors, getReturnType } from "../utils/operation.js"; -import { declarationRefkeys } from "../utils/refkey.js"; import { TypeExpression } from "./type-expression.js"; export interface FunctionDeclarationPropsWithType extends Omit< @@ -30,7 +31,8 @@ export function FunctionDeclaration(props: FunctionDeclarationProps) { return ; } - const refkeys = declarationRefkeys(props.refkey, props.type); + const dp = useDeclarationProvider(); + const refkeys = joinRefkeys(props.refkey, dp.getRefkey(props.type)); let name = props.name ? props.name : ts.useTSNamePolicy().getName(props.type.name, "function"); diff --git a/packages/emitter-framework/src/typescript/components/interface-declaration.tsx b/packages/emitter-framework/src/typescript/components/interface-declaration.tsx index cb1cc107919..75f1a8d36b8 100644 --- a/packages/emitter-framework/src/typescript/components/interface-declaration.tsx +++ b/packages/emitter-framework/src/typescript/components/interface-declaration.tsx @@ -1,3 +1,5 @@ +import { useDeclarationProvider } from "#core/context/declaration-provider.js"; +import { joinRefkeys } from "#typescript/utils/refkey.js"; import { type Children, For, mapJoin } from "@alloy-js/core"; import * as ts from "@alloy-js/typescript"; import { @@ -12,7 +14,6 @@ import type { Typekit } from "@typespec/compiler/typekit"; import { createRekeyableMap } from "@typespec/compiler/utils"; import { useTsp } from "../../core/context/tsp-context.js"; import { reportDiagnostic } from "../../lib.js"; -import { declarationRefkeys, efRefkey } from "../utils/refkey.js"; import { InterfaceMember } from "./interface-member.js"; import { TypeExpression } from "./type-expression.jsx"; export interface TypedInterfaceDeclarationProps extends Omit { @@ -40,8 +41,8 @@ export function InterfaceDeclaration(props: InterfaceDeclarationProps) { } name = namePolicy.getName(name, "interface"); - - const refkeys = declarationRefkeys(props.refkey, props.type); + const dp = useDeclarationProvider(); + const refkeys = joinRefkeys(props.refkey, dp.getRefkey(props.type)); const extendsType = props.extends ?? getExtendsType($, props.type); const doc = props.doc ?? $.type.getDoc(props.type); @@ -83,7 +84,7 @@ function getExtendsType($: Typekit, type: Model | Interface): Children | undefin if (!$.model.is(type)) { return undefined; } - + const dp = useDeclarationProvider(); const extending: Children[] = []; if (type.baseModel) { @@ -94,7 +95,7 @@ function getExtendsType($: Typekit, type: Model | Interface): Children | undefin // Instead of extending we need to create an envelope property // do nothing here. } else { - extending.push(efRefkey(type.baseModel)); + extending.push(dp.getRefkey(type.baseModel)); } } diff --git a/packages/emitter-framework/src/typescript/components/static-serializers.tsx b/packages/emitter-framework/src/typescript/components/static-serializers.tsx index 991c09e5b9f..f03ab38b677 100644 --- a/packages/emitter-framework/src/typescript/components/static-serializers.tsx +++ b/packages/emitter-framework/src/typescript/components/static-serializers.tsx @@ -1,8 +1,7 @@ -import { code } from "@alloy-js/core"; +import { code, refkey } from "@alloy-js/core"; import * as ts from "@alloy-js/typescript"; -import { efRefkey } from "../utils/refkey.js"; -export const DateRfc3339SerializerRefkey = efRefkey(); +export const DateRfc3339SerializerRefkey = refkey(); export function DateRfc3339Serializer() { return ( `; const convertFnType = `(item: any) => any`; @@ -161,7 +160,7 @@ export function RecordSerializer() { ); } -export const ArraySerializerRefkey = efRefkey(); +export const ArraySerializerRefkey = refkey(); export function ArraySerializer() { const arrayType = `any[]`; const convertFnType = `(item: any) => any`; diff --git a/packages/emitter-framework/src/typescript/components/type-alias-declaration.tsx b/packages/emitter-framework/src/typescript/components/type-alias-declaration.tsx index 17bac4e0c83..f71ffa7c59d 100644 --- a/packages/emitter-framework/src/typescript/components/type-alias-declaration.tsx +++ b/packages/emitter-framework/src/typescript/components/type-alias-declaration.tsx @@ -1,8 +1,9 @@ +import { useDeclarationProvider } from "#core/context/declaration-provider.js"; +import { joinRefkeys } from "#typescript/utils/refkey.js"; import * as ts from "@alloy-js/typescript"; import type { Type } from "@typespec/compiler"; import { useTsp } from "../../core/context/tsp-context.js"; import { reportDiagnostic } from "../../lib.js"; -import { declarationRefkeys } from "../utils/refkey.js"; import { TypeExpression } from "./type-expression.jsx"; export interface TypedAliasDeclarationProps extends Omit { @@ -31,7 +32,8 @@ export function TypeAliasDeclaration(props: TypeAliasDeclarationProps) { } const doc = props.doc ?? $.type.getDoc(props.type); - const refkeys = declarationRefkeys(props.refkey, props.type); + const dp = useDeclarationProvider(); + const refkeys = joinRefkeys(props.refkey, dp.getRefkey(props.type)); const name = ts.useTSNamePolicy().getName(originalName, "type"); return ( diff --git a/packages/emitter-framework/src/typescript/components/type-declaration.tsx b/packages/emitter-framework/src/typescript/components/type-declaration.tsx index ac62e5b76af..9f4224afef3 100644 --- a/packages/emitter-framework/src/typescript/components/type-declaration.tsx +++ b/packages/emitter-framework/src/typescript/components/type-declaration.tsx @@ -1,7 +1,6 @@ import * as ts from "@alloy-js/typescript"; import type { Type } from "@typespec/compiler"; import { useTsp } from "../../core/index.js"; -import { declarationRefkeys } from "../utils/refkey.js"; import { EnumDeclaration } from "./enum-declaration.js"; import { InterfaceDeclaration } from "./interface-declaration.jsx"; import { TypeAliasDeclaration } from "./type-alias-declaration.jsx"; @@ -17,13 +16,7 @@ export type WithRequired = T & { [P in K]-?: T[P] }; export function TypeDeclaration(props: TypeDeclarationProps) { const { $ } = useTsp(); if (!props.type) { - const refkeys = declarationRefkeys(props.refkey, props.type); - return ( - )} - refkey={refkeys} - /> - ); + return )} />; } const { type, ...restProps } = props; diff --git a/packages/emitter-framework/src/typescript/components/type-expression.tsx b/packages/emitter-framework/src/typescript/components/type-expression.tsx index 18123f64258..87816ec3ac9 100644 --- a/packages/emitter-framework/src/typescript/components/type-expression.tsx +++ b/packages/emitter-framework/src/typescript/components/type-expression.tsx @@ -1,11 +1,11 @@ +import { useDeclarationProvider } from "#core/context/declaration-provider.js"; import { For } from "@alloy-js/core"; -import { Reference, ValueExpression } from "@alloy-js/typescript"; +import { ValueExpression } from "@alloy-js/typescript"; import type { IntrinsicType, Model, Scalar, Type } from "@typespec/compiler"; import type { Typekit } from "@typespec/compiler/typekit"; import { Experimental_OverridableComponent } from "../../core/components/overrides/component-overrides.jsx"; import { useTsp } from "../../core/context/tsp-context.js"; import { reportTypescriptDiagnostic } from "../../typescript/lib.js"; -import { efRefkey } from "../utils/refkey.js"; import { ArrayExpression } from "./array-expression.js"; import { FunctionType } from "./function-type.js"; import { InterfaceExpression } from "./interface-declaration.js"; @@ -30,11 +30,9 @@ export function TypeExpression(props: TypeExpressionProps) { return ( {() => { - if (!props.noReference && isDeclaration($, type)) { - // todo: probably need abstraction around deciding what's a declaration in the output - // (it may not correspond to things which are declarations in TypeSpec?) - return ; - //throw new Error("Reference not implemented"); + const dp = useDeclarationProvider(); + if (!props.noReference && dp.shouldReference(type)) { + return dp.getRefkey(type); } // TODO: Make sure this is an exhaustive switch, including EnumMember and such @@ -159,27 +157,3 @@ function getScalarIntrinsicExpression($: Typekit, type: Scalar | IntrinsicType): return tsType; } - -function isDeclaration($: Typekit, type: Type): boolean { - switch (type.kind) { - case "Namespace": - case "Interface": - case "Enum": - case "Operation": - case "EnumMember": - return true; - case "UnionVariant": - return false; - - case "Model": - if ($.array.is(type) || $.record.is(type)) { - return false; - } - - return Boolean(type.name); - case "Union": - return Boolean(type.name); - default: - return false; - } -} diff --git a/packages/emitter-framework/src/typescript/components/type-transform.tsx b/packages/emitter-framework/src/typescript/components/type-transform.tsx index 0e629db42e6..539976bd9df 100644 --- a/packages/emitter-framework/src/typescript/components/type-transform.tsx +++ b/packages/emitter-framework/src/typescript/components/type-transform.tsx @@ -1,4 +1,4 @@ -import { type Children, code, For, mapJoin, type Refkey } from "@alloy-js/core"; +import { type Children, code, For, mapJoin, refkey, type Refkey } from "@alloy-js/core"; import * as ts from "@alloy-js/typescript"; import type { Discriminator, @@ -14,7 +14,8 @@ import { createRekeyableMap } from "@typespec/compiler/utils"; import { useTsp } from "../../core/context/tsp-context.js"; import { reportDiagnostic } from "../../lib.js"; import { reportTypescriptDiagnostic } from "../../typescript/lib.js"; -import { efRefkey } from "../utils/refkey.js"; + +import { useDeclarationProvider } from "#core/context/declaration-provider.js"; import { ArraySerializerRefkey, DateDeserializerRefkey, @@ -122,11 +123,12 @@ export function TypeTransformDeclaration(props: TypeTransformProps) { }); } + const dp = useDeclarationProvider(); const baseName = namePolicy.getName(originalName!, "function"); const functionSuffix = props.target === "application" ? "ToApplication" : "ToTransport"; const functionName = props.name ? props.name : `${baseName}${functionSuffix}`; const itemType = - props.target === "application" ? "any" : ; + props.target === "application" ? "any" : ; let transformExpression: Children; if ($.model.is(props.type)) { @@ -153,7 +155,7 @@ export function TypeTransformDeclaration(props: TypeTransformProps) { }); } - const returnType = props.target === "application" ? efRefkey(props.type) : "any"; + const returnType = props.target === "application" ? dp.getRefkey(props.type) : "any"; const ref = props.refkey ?? getTypeTransformerRefkey(props.type, props.target); @@ -177,7 +179,7 @@ export function TypeTransformDeclaration(props: TypeTransformProps) { * @returns the refkey for the TypeTransformer function */ export function getTypeTransformerRefkey(type: Type, target: "application" | "transport") { - return efRefkey(type, target); + return refkey(type, target); } export interface ModelTransformExpressionProps { @@ -363,8 +365,9 @@ export function TypeTransformCall(props: TypeTransformCallProps): Children { itemPath.unshift(`${props.optionsBagName}?`); } let itemName: Children = itemPath.join("."); + const dp = useDeclarationProvider(); if (props.castInput) { - itemName = code`${itemName} as ${efRefkey(props.type)}`; + itemName = code`${itemName} as ${dp.getRefkey(props.type)}`; } const transformType = collapsedProperty?.type ?? props.type; if ($.model.is(transformType) && $.array.is(transformType)) { diff --git a/packages/emitter-framework/src/typescript/components/union/declaration.tsx b/packages/emitter-framework/src/typescript/components/union/declaration.tsx index 7c444c5565a..22def245b69 100644 --- a/packages/emitter-framework/src/typescript/components/union/declaration.tsx +++ b/packages/emitter-framework/src/typescript/components/union/declaration.tsx @@ -1,5 +1,6 @@ -import { declarationRefkeys } from "#typescript/utils/refkey.js"; -import { type Children } from "@alloy-js/core"; +import { useDeclarationProvider } from "#core/context/declaration-provider.js"; +import { joinRefkeys } from "#typescript/utils/refkey.js"; +import { splitProps, type Children } from "@alloy-js/core"; import * as ts from "@alloy-js/typescript"; import type { Enum, Union } from "@typespec/compiler"; import { useTsp } from "../../../core/context/tsp-context.js"; @@ -14,26 +15,29 @@ export interface TypedUnionDeclarationProps extends Omit{props.children}; + return ; } - const { type, ...coreProps } = props; - const refkeys = declarationRefkeys(props.refkey, props.type); - - const originalName = coreProps.name ?? type.name; + const [typeProp, coreProps] = splitProps(props, ["type"]); + const type = typeProp.type; + const originalName = coreProps.name ?? type.name ?? ""; - if (!originalName || originalName === "") { + if (originalName === "") { reportDiagnostic($.program, { code: "type-declaration-missing-name", target: type }); } - const name = ts.useTSNamePolicy().getName(originalName!, "type"); - + const dp = useDeclarationProvider(); + const refkey = joinRefkeys(props.refkey, dp.getRefkey(type)); const doc = props.doc ?? $.type.getDoc(type); return ( - + {coreProps.children} ); diff --git a/packages/emitter-framework/src/typescript/components/union/expression.tsx b/packages/emitter-framework/src/typescript/components/union/expression.tsx index 2d8df922dd5..e250b3af5d8 100644 --- a/packages/emitter-framework/src/typescript/components/union/expression.tsx +++ b/packages/emitter-framework/src/typescript/components/union/expression.tsx @@ -1,3 +1,4 @@ +import { useDeclarationProvider } from "#core/context/declaration-provider.js"; import { type Children, For, List } from "@alloy-js/core"; import * as ts from "@alloy-js/typescript"; import { @@ -8,7 +9,6 @@ import { type UnionVariant, } from "@typespec/compiler"; import { useTsp } from "../../../core/context/tsp-context.js"; -import { efRefkey } from "../../utils/refkey.js"; import { TypeExpression } from "../type-expression.jsx"; export interface UnionExpressionProps { @@ -125,6 +125,8 @@ function NoneEnvelope(props: NoneEnvelopeProps) { return ; } + const dp = useDeclarationProvider(); + return ( @@ -133,7 +135,7 @@ function NoneEnvelope(props: NoneEnvelopeProps) { value={} /> - <>{efRefkey(props.type.type)} + <>{dp.getRefkey(props.type.type)} ); } diff --git a/packages/emitter-framework/src/typescript/utils/operation.ts b/packages/emitter-framework/src/typescript/utils/operation.ts index aa73b346ad2..11285f965a1 100644 --- a/packages/emitter-framework/src/typescript/utils/operation.ts +++ b/packages/emitter-framework/src/typescript/utils/operation.ts @@ -1,9 +1,8 @@ -import { refkey, type Refkey } from "@alloy-js/core"; +import { type Refkey } from "@alloy-js/core"; import * as ts from "@alloy-js/typescript"; import type { Model, ModelProperty, Operation, Type } from "@typespec/compiler"; import { useTsp } from "../../core/index.js"; import { TypeExpression } from "../components/type-expression.jsx"; -import { efRefkey } from "./refkey.js"; export function getReturnType( type: Operation, @@ -30,7 +29,6 @@ export function buildParameterDescriptors( options: BuildParameterDescriptorsOptions = {}, ): ts.ParameterDescriptor[] | undefined { const { $ } = useTsp(); - const suffixRefkey = options.suffixRefkey ?? refkey(); const optionsParams = normalizeParameters(options.params); if (options.mode === "replace") { @@ -38,9 +36,7 @@ export function buildParameterDescriptors( } const modelProperties = $.model.getProperties(type); - const operationParams = [...modelProperties.values()].map((m) => - buildParameterDescriptor(m, suffixRefkey), - ); + const operationParams = [...modelProperties.values()].map((m) => buildParameterDescriptor(m)); // Merge parameters based on location const allParams = @@ -51,9 +47,12 @@ export function buildParameterDescriptors( return allParams; } +/** + * Create a parameter descriptor from a model property type. + */ export function buildParameterDescriptor( modelProperty: ModelProperty, - suffixRefkey: Refkey, + options: { refkey?: Refkey } = {}, ): ts.ParameterDescriptor { const { $ } = useTsp(); const namePolicy = ts.useTSNamePolicy(); @@ -63,9 +62,9 @@ export function buildParameterDescriptor( return { doc, name: paramName, - refkey: efRefkey(modelProperty, suffixRefkey), optional: isOptional, type: TypeExpression({ type: modelProperty.type }), + refkey: options.refkey, }; } diff --git a/packages/emitter-framework/src/typescript/utils/refkey.ts b/packages/emitter-framework/src/typescript/utils/refkey.ts index 6cf84fb5154..bb6149d02bb 100644 --- a/packages/emitter-framework/src/typescript/utils/refkey.ts +++ b/packages/emitter-framework/src/typescript/utils/refkey.ts @@ -1,36 +1,5 @@ -import { refkey as ayRefkey, type Refkey } from "@alloy-js/core"; +import type { Refkey } from "@alloy-js/core"; -const refKeyPrefix = Symbol.for("emitter-framework:typescript"); - -/** - * A wrapper around `refkey` that uses a custom symbol to avoid collisions with - * other libraries that use `refkey`. - * - * @remarks - * - * The underlying refkey function is called with the {@link refKeyPrefix} symbol as the first argument. - * - * @param args The parameters of the refkey. - * @returns A refkey object that can be used to identify the value. - */ -export function efRefkey(...args: unknown[]): Refkey { - if (args.length === 0) { - return ayRefkey(); // Generates a unique refkey - } - return ayRefkey(refKeyPrefix, ...args); -} - -/** - * Creates a refkey for a declaration by combining the provided refkey with an internal - * refkey generated from the provided arguments. - * - * @param refkey The refkey provided by the user to be passed as is. - * @param args The parameters of the refkey. - * @returns An array of refkeys that can be passed to an Alloy declaration. - */ -export function declarationRefkeys(refkey?: Refkey | Refkey[], ...args: unknown[]): Refkey[] { - if (refkey) { - return [refkey, efRefkey(...args)].flat(); - } - return [efRefkey(...args)]; +export function joinRefkeys(...refkeys: (Refkey | Refkey[] | undefined)[]): Refkey[] { + return refkeys.filter((v) => !!v).flat(); } diff --git a/packages/emitter-framework/test/typescript/components/enum-declaration.test.tsx b/packages/emitter-framework/test/typescript/components/enum-declaration.test.tsx index 5e224c45856..d1d4921bd41 100644 --- a/packages/emitter-framework/test/typescript/components/enum-declaration.test.tsx +++ b/packages/emitter-framework/test/typescript/components/enum-declaration.test.tsx @@ -1,10 +1,12 @@ +import { DeclarationProviderContext } from "#core/context/declaration-provider.js"; +import { DeclarationProvider } from "#core/declaration-provider.js"; import { List, StatementList } from "@alloy-js/core"; import { d } from "@alloy-js/core/testing"; -import type { Enum, Union } from "@typespec/compiler"; +import type { Enum, Program, Union } from "@typespec/compiler"; +import { $ } from "@typespec/compiler/typekit"; import { describe, expect, it } from "vitest"; import { TspContext } from "../../../src/core/index.js"; import { EnumDeclaration } from "../../../src/typescript/components/enum-declaration.js"; -import { efRefkey } from "../../../src/typescript/utils/refkey.js"; import { getEmitOutput } from "../../utils.js"; describe("Typescript Enum Declaration", () => { @@ -141,19 +143,26 @@ describe("Typescript Enum Declaration", () => { } `; - const output = await getEmitOutput(code, (program) => { - const Foo = program.resolveTypeReference("Foo")[0]! as Enum; + function Test(props: { program: Program }) { + const dp = new DeclarationProvider($(props.program)); + const Foo = props.program.resolveTypeReference("Foo")[0]! as Enum; return ( - - - - - {efRefkey(Foo)} - {efRefkey(Foo.members.get("one"))} - - - + + + + + + {dp.getRefkey(Foo)} + {dp.getStaticMemberRefkey(Foo.members.get("one")!)} + + + + ); + } + + const output = await getEmitOutput(code, (program) => { + return ; }); expect(output).toBe(d` @@ -176,19 +185,26 @@ describe("Typescript Enum Declaration", () => { } `; - const output = await getEmitOutput(code, (program) => { - const Foo = program.resolveTypeReference("Foo")[0]! as Union; + function Test(props: { program: Program }) { + const dp = new DeclarationProvider($(props.program)); + const Foo = props.program.resolveTypeReference("Foo")[0]! as Union; return ( - - - - - {efRefkey(Foo)} - {efRefkey(Foo.variants.get("one"))} - - - + + + + + + {dp.getRefkey(Foo)} + {dp.getStaticMemberRefkey(Foo.variants.get("one")!)} + + + + ); + } + + const output = await getEmitOutput(code, (program) => { + return ; }); expect(output).toBe(d` diff --git a/packages/http-client-js/src/components/models.tsx b/packages/http-client-js/src/components/models.tsx index 8c165096b40..1b5ccb8efe1 100644 --- a/packages/http-client-js/src/components/models.tsx +++ b/packages/http-client-js/src/components/models.tsx @@ -1,6 +1,6 @@ -import { For, refkey } from "@alloy-js/core"; +import { For } from "@alloy-js/core"; import * as ts from "@alloy-js/typescript"; -import { useTsp } from "@typespec/emitter-framework"; +import { useDeclarationProvider } from "@typespec/emitter-framework"; import * as ef from "@typespec/emitter-framework/typescript"; import { useClientLibrary } from "@typespec/http-client"; @@ -9,16 +9,18 @@ export interface ModelsProps { } export function Models(props: ModelsProps) { - const { $ } = useTsp(); const clientLibrary = useClientLibrary(); const dataTypes = clientLibrary.dataTypes; + const dp = useDeclarationProvider(); return ( {(type) => { - return $.array.is(type) || $.record.is(type) ? null : ( - - ); + if (!dp.isDeclaration(type)) { + return; + } + + return ; }} diff --git a/packages/http-client-js/src/components/output.tsx b/packages/http-client-js/src/components/output.tsx index 5336221b9af..1f8a1a8d77c 100644 --- a/packages/http-client-js/src/components/output.tsx +++ b/packages/http-client-js/src/components/output.tsx @@ -1,4 +1,4 @@ -import { Children } from "@alloy-js/core/jsx-runtime"; +import { Children } from "@alloy-js/core"; import * as ts from "@alloy-js/typescript"; import { Program } from "@typespec/compiler"; import { Output as EFOutput, TransformNamePolicyContext } from "@typespec/emitter-framework"; diff --git a/packages/http-client-js/src/components/static-helpers/bytes-encoding.tsx b/packages/http-client-js/src/components/static-helpers/bytes-encoding.tsx index d994628a20f..0c31b3e8cf5 100644 --- a/packages/http-client-js/src/components/static-helpers/bytes-encoding.tsx +++ b/packages/http-client-js/src/components/static-helpers/bytes-encoding.tsx @@ -1,5 +1,4 @@ -import { code, Refkey, refkey } from "@alloy-js/core"; -import { Children } from "@alloy-js/core/jsx-runtime"; +import { Children, code, Refkey, refkey } from "@alloy-js/core"; import * as ts from "@alloy-js/typescript"; export function getEncodeUint8ArrayRef(): Refkey { diff --git a/packages/http-client-js/src/components/transforms/json/json-model-transform.tsx b/packages/http-client-js/src/components/transforms/json/json-model-transform.tsx index 284fb98572f..84338faaab4 100644 --- a/packages/http-client-js/src/components/transforms/json/json-model-transform.tsx +++ b/packages/http-client-js/src/components/transforms/json/json-model-transform.tsx @@ -2,7 +2,7 @@ import * as ts from "@alloy-js/typescript"; import { type Children, code, For, type Refkey, refkey } from "@alloy-js/core"; import type { Model } from "@typespec/compiler"; -import { useTsp } from "@typespec/emitter-framework"; +import { useDeclarationProvider, useTsp } from "@typespec/emitter-framework"; import { JsonAdditionalPropertiesTransform } from "./json-model-additional-properties-transform.jsx"; import { JsonModelPropertyTransform } from "./json-model-property-transform.jsx"; import { JsonRecordTransformDeclaration } from "./json-record-transform.jsx"; @@ -70,13 +70,14 @@ export interface JsonModelTransformDeclarationProps { export function JsonModelTransformDeclaration(props: JsonModelTransformDeclarationProps): Children { const { $ } = useTsp(); const namePolicy = ts.useTSNamePolicy(); + const dp = useDeclarationProvider(); const transformName = namePolicy.getName( `json_${props.type.name}_to_${props.target}_transform`, "function", ); - const returnType = props.target === "transport" ? "any" : refkey(props.type); - const inputType = props.target === "transport" ? <>{refkey(props.type)} | null : "any"; + const returnType = props.target === "transport" ? "any" : dp.getRefkey(props.type); + const inputType = props.target === "transport" ? <>{dp.getRefkey(props.type)} | null : "any"; const inputRef = refkey(); const parameters: ts.ParameterDescriptor[] = [ diff --git a/packages/http-client-js/src/components/transforms/json/json-transform-discriminator.tsx b/packages/http-client-js/src/components/transforms/json/json-transform-discriminator.tsx index ffb0e656ce9..0ced5d707d5 100644 --- a/packages/http-client-js/src/components/transforms/json/json-transform-discriminator.tsx +++ b/packages/http-client-js/src/components/transforms/json/json-transform-discriminator.tsx @@ -1,7 +1,7 @@ import { code, mapJoin, refkey, useNamePolicy, type Children, type Refkey } from "@alloy-js/core"; import * as ts from "@alloy-js/typescript"; import { Discriminator, Model, Union } from "@typespec/compiler"; -import { useTsp } from "@typespec/emitter-framework"; +import { useDeclarationProvider, useTsp } from "@typespec/emitter-framework"; import { JsonTransform } from "./json-transform.jsx"; export interface JsonTransformDiscriminatorProps { @@ -82,8 +82,8 @@ export function JsonTransformDiscriminatorDeclaration( `json_${props.type.name}_to_${props.target}_discriminator`, "function", ); - - const typeRef = refkey(props.type); + const dp = useDeclarationProvider(); + const typeRef = dp.getRefkey(props.type); const returnType = props.target === "transport" ? "any" : typeRef; const inputType = props.target === "transport" ? typeRef : "any"; const inputRef = refkey(); diff --git a/packages/http-client-js/src/components/transforms/json/json-transform.tsx b/packages/http-client-js/src/components/transforms/json/json-transform.tsx index edf4c78ceda..3faca211ce2 100644 --- a/packages/http-client-js/src/components/transforms/json/json-transform.tsx +++ b/packages/http-client-js/src/components/transforms/json/json-transform.tsx @@ -1,5 +1,4 @@ -import { code, Refkey } from "@alloy-js/core"; -import { Children } from "@alloy-js/core/jsx-runtime"; +import { Children, code, Refkey } from "@alloy-js/core"; import type { Type } from "@typespec/compiler"; import { useTsp } from "@typespec/emitter-framework"; import { ScalarDataTransform } from "../data-transform.jsx"; diff --git a/packages/http-client-js/src/components/transforms/json/union-transform.tsx b/packages/http-client-js/src/components/transforms/json/union-transform.tsx index d546cf021a9..3ddc403681a 100644 --- a/packages/http-client-js/src/components/transforms/json/union-transform.tsx +++ b/packages/http-client-js/src/components/transforms/json/union-transform.tsx @@ -1,7 +1,7 @@ import { Children, code, refkey, Refkey } from "@alloy-js/core"; import * as ts from "@alloy-js/typescript"; import type { Union } from "@typespec/compiler"; -import { useTsp } from "@typespec/emitter-framework"; +import { useDeclarationProvider, useTsp } from "@typespec/emitter-framework"; import { getJsonTransformDiscriminatorRefkey, JsonTransformDiscriminatorDeclaration, @@ -53,8 +53,8 @@ export function JsonUnionTransformDeclaration(props: JsonUnionTransformDeclarati `json_${props.type.name}_to_${props.target}_transform`, "function", ); - - const typeRef = refkey(props.type); + const dp = useDeclarationProvider(); + const typeRef = dp.getRefkey(props.type); const returnType = props.target === "transport" ? "any" : typeRef; const inputType = props.target === "transport" ? <>{typeRef} | null : "any"; const inputRef = refkey(); diff --git a/packages/http-client-js/src/components/transforms/multipart/array-part-transform.tsx b/packages/http-client-js/src/components/transforms/multipart/array-part-transform.tsx index b402dab172c..78bac56ba18 100644 --- a/packages/http-client-js/src/components/transforms/multipart/array-part-transform.tsx +++ b/packages/http-client-js/src/components/transforms/multipart/array-part-transform.tsx @@ -1,5 +1,4 @@ -import { code } from "@alloy-js/core"; -import { Children } from "@alloy-js/core/jsx-runtime"; +import { Children, code } from "@alloy-js/core"; import * as ts from "@alloy-js/typescript"; import { HttpOperationPart } from "@typespec/http"; import { HttpPartTransform } from "./part-transform.jsx"; diff --git a/packages/http-client-js/src/utils/parameters.tsx b/packages/http-client-js/src/utils/parameters.tsx index af058b37e73..a6eef170df5 100644 --- a/packages/http-client-js/src/utils/parameters.tsx +++ b/packages/http-client-js/src/utils/parameters.tsx @@ -49,7 +49,7 @@ function buildClientParameterDescriptor( ): ts.ParameterDescriptor | undefined { const { $ } = useTsp(); const authSchemes = $.modelProperty.getCredentialAuth(modelProperty); - + const key = refkey(modelProperty, suffixRefkey); if (authSchemes) { if (authSchemes.length === 1 && authSchemes[0].type === "noAuth") { return undefined; @@ -60,7 +60,7 @@ function buildClientParameterDescriptor( ); return { name: "credential", - refkey: refkey(modelProperty, suffixRefkey), + refkey: key, optional: modelProperty.optional, type: mapJoin( () => credentialType, @@ -70,7 +70,7 @@ function buildClientParameterDescriptor( }; } - return buildParameterDescriptor(modelProperty, suffixRefkey); + return buildParameterDescriptor(modelProperty, { refkey: key }); } const oauth2FlowRefs: Record = { diff --git a/packages/http-client-js/test/scenarios/additional-properties/is.md b/packages/http-client-js/test/scenarios/additional-properties/is.md index b1ebf098998..f51cf726383 100644 --- a/packages/http-client-js/test/scenarios/additional-properties/is.md +++ b/packages/http-client-js/test/scenarios/additional-properties/is.md @@ -21,10 +21,7 @@ Should not create model and treat it as a Record. Should just treat it as a Record ```ts src/api/testClientOperations.ts function foo -export async function foo( - client: TestClientContext, - options?: FooOptions, -): Promise> { +export async function foo(client: TestClientContext, options?: FooOptions): Promise { const path = parse("/").expand({}); const httpRequestOptions = { headers: {}, diff --git a/packages/http-client-js/test/scenarios/serializers/scalars.md b/packages/http-client-js/test/scenarios/serializers/scalars.md index d3d3c871b52..c888252596c 100644 --- a/packages/http-client-js/test/scenarios/serializers/scalars.md +++ b/packages/http-client-js/test/scenarios/serializers/scalars.md @@ -19,15 +19,7 @@ op foo(a: MyDate, b: MyUtcDate, c: MyIsoDate, d: MyUnixDate): void; ## TypeScript ```ts src/models/models.ts -/** - * A sequence of textual characters. - */ -export type String = string; export type MyDate = Date; -/** - * An instant in coordinated universal time (UTC)" - */ -export type UtcDateTime = Date; export type MyUtcDate = Date; export type MyIsoDate = Date; export type MyUnixDate = Date;