diff --git a/.chronus/changes/add_on_validate-2024-1-26-14-11-57.md b/.chronus/changes/add_on_validate-2024-1-26-14-11-57.md new file mode 100644 index 0000000000..bd8dd46b3c --- /dev/null +++ b/.chronus/changes/add_on_validate-2024-1-26-14-11-57.md @@ -0,0 +1,7 @@ +--- +changeKind: feature +packages: + - "@azure-tools/typespec-client-generator-core" +--- + +add validation on import of tcgc and remove duplicate validation warnings \ No newline at end of file diff --git a/packages/typespec-client-generator-core/src/decorators.ts b/packages/typespec-client-generator-core/src/decorators.ts index 6ab122392c..b1bb827a29 100644 --- a/packages/typespec-client-generator-core/src/decorators.ts +++ b/packages/typespec-client-generator-core/src/decorators.ts @@ -33,21 +33,21 @@ import { SdkEmitterOptions, SdkOperationGroup, } from "./interfaces.js"; -import { parseEmitterName } from "./internal-utils.js"; +import { TCGCContext, createTCGCContext, parseEmitterName } from "./internal-utils.js"; import { createStateSymbol, reportDiagnostic } from "./lib.js"; import { getAllModels, getSdkEnum, getSdkModel } from "./types.js"; export const namespace = "Azure.ClientGenerator.Core"; const AllScopes = Symbol.for("@azure-core/typespec-client-generator-core/all-scopes"); -function getScopedDecoratorData(context: SdkContext, key: symbol, target: Type): any { +function getScopedDecoratorData(context: TCGCContext, key: symbol, target: Type): any { const retval: Record = context.program.stateMap(key).get(target); if (retval === undefined) return retval; if (Object.keys(retval).includes(context.emitterName)) return retval[context.emitterName]; return retval[AllScopes]; // in this case it applies to all languages } -function listScopedDecoratorData(context: SdkContext, key: symbol): any[] { +function listScopedDecoratorData(context: TCGCContext, key: symbol): any[] { const retval = [...context.program.stateMap(key).values()]; return retval .filter((targetEntry) => { @@ -161,11 +161,14 @@ function findClientService( /** * Return the client object for the given namespace or interface, or undefined if the given namespace or interface is not a client. * - * @param context SdkContext + * @param context TCGCContext * @param type Type to check * @returns Client or undefined */ -export function getClient(context: SdkContext, type: Namespace | Interface): SdkClient | undefined { +export function getClient( + context: TCGCContext, + type: Namespace | Interface +): SdkClient | undefined { if (hasExplicitClientOrOperationGroup(context)) { return getScopedDecoratorData(context, clientKey, type); } @@ -183,7 +186,7 @@ export function getClient(context: SdkContext, type: Namespace | Interface): Sdk return undefined; } -function hasExplicitClientOrOperationGroup(context: SdkContext): boolean { +function hasExplicitClientOrOperationGroup(context: TCGCContext): boolean { return ( listScopedDecoratorData(context, clientKey).length > 0 || listScopedDecoratorData(context, operationGroupKey).length > 0 @@ -193,10 +196,10 @@ function hasExplicitClientOrOperationGroup(context: SdkContext): boolean { /** * List all the clients. * - * @param context SdkContext + * @param context TCGCContext * @returns Array of clients */ -export function listClients(context: SdkContext): SdkClient[] { +export function listClients(context: TCGCContext): SdkClient[] { const explicitClients = [...listScopedDecoratorData(context, clientKey)]; if (explicitClients.length > 0) { return explicitClients; @@ -255,11 +258,11 @@ export function $operationGroup( /** * Check a namespace or interface is an operation group. - * @param context SdkContext + * @param context TCGCContext * @param type Type to check * @returns boolean */ -export function isOperationGroup(context: SdkContext, type: Namespace | Interface): boolean { +export function isOperationGroup(context: TCGCContext, type: Namespace | Interface): boolean { if (hasExplicitClientOrOperationGroup(context)) { return getScopedDecoratorData(context, operationGroupKey, type) !== undefined; } @@ -274,12 +277,12 @@ export function isOperationGroup(context: SdkContext, type: Namespace | Interfac } /** * Check an operation is in an operation group. - * @param context SdkContext + * @param context TCGCContext * @param type Type to check * @returns boolean */ export function isInOperationGroup( - context: SdkContext, + context: TCGCContext, type: Namespace | Interface | Operation ): boolean { switch (type.kind) { @@ -298,7 +301,7 @@ export function isInOperationGroup( } } -function buildOperationGroupPath(context: SdkContext, type: Namespace | Interface): string { +function buildOperationGroupPath(context: TCGCContext, type: Namespace | Interface): string { const path = []; while (true) { const client = getClient(context, type); @@ -319,12 +322,12 @@ function buildOperationGroupPath(context: SdkContext, type: Namespace | Interfac } /** * Return the operation group object for the given namespace or interface or undefined is not an operation group. - * @param context SdkContext + * @param context TCGCContext * @param type Type to check * @returns Operation group or undefined. */ export function getOperationGroup( - context: SdkContext, + context: TCGCContext, type: Namespace | Interface ): SdkOperationGroup | undefined { let operationGroup: SdkOperationGroup | undefined; @@ -381,13 +384,13 @@ export function getOperationGroup( /** * List all the operation groups inside a client or an operation group. If ignoreHierarchy is true, the result will include all nested operation groups. * - * @param context SdkContext + * @param context TCGCContext * @param group Client or operation group to list operation groups * @param ignoreHierarchy Whether to get all nested operation groups * @returns */ export function listOperationGroups( - context: SdkContext, + context: TCGCContext, group: SdkClient | SdkOperationGroup, ignoreHierarchy = false ): SdkOperationGroup[] { @@ -421,13 +424,13 @@ export function listOperationGroups( /** * List operations inside a client or an operation group. If ignoreHierarchy is true, the result will include all nested operations. - * @param program SdkContext + * @param program TCGCContext * @param group Client or operation group to list operations * @param ignoreHierarchy Whether to get all nested operations * @returns */ export function listOperationsInOperationGroup( - context: SdkContext, + context: TCGCContext, group: SdkOperationGroup | SdkClient, ignoreHierarchy = false ): Operation[] { @@ -477,7 +480,7 @@ export function createSdkContext = SdkEmitt const generateConvenienceMethods = context.options["generate-convenience-methods"] ?? convenienceOptions; return { - program: context.program, + ...createTCGCContext(context.program), emitContext: context, emitterName: parseEmitterName(emitterName ?? context.program.emitters[0]?.metadata?.name), // eslint-disable-line deprecation/deprecation generateProtocolMethods: generateProtocolMethods, @@ -509,14 +512,14 @@ export function $convenientAPI( setScopedDecoratorData(context, $convenientAPI, convenientAPIKey, entity, value, scope); } -export function shouldGenerateProtocol(context: SdkContext, entity: Operation): boolean { +export function shouldGenerateProtocol(context: TCGCContext, entity: Operation): boolean { const value = getScopedDecoratorData(context, protocolAPIKey, entity); - return value ?? context.generateProtocolMethods; + return value ?? !!context.generateProtocolMethods; } -export function shouldGenerateConvenient(context: SdkContext, entity: Operation): boolean { +export function shouldGenerateConvenient(context: TCGCContext, entity: Operation): boolean { const value = getScopedDecoratorData(context, convenientAPIKey, entity); - return value ?? context.generateConvenienceMethods; + return value ?? !!context.generateConvenienceMethods; } const excludeKey = createStateSymbol("exclude"); @@ -540,14 +543,14 @@ export function $include(context: DecoratorContext, entity: Model, scope?: Langu /** * @deprecated This function is unused and will be removed in a future release. */ -export function isExclude(context: SdkContext, entity: Model): boolean { +export function isExclude(context: TCGCContext, entity: Model): boolean { return getScopedDecoratorData(context, excludeKey, entity) ?? false; } /** * @deprecated This function is unused and will be removed in a future release. */ -export function isInclude(context: SdkContext, entity: Model): boolean { +export function isInclude(context: TCGCContext, entity: Model): boolean { return getScopedDecoratorData(context, includeKey, entity) ?? false; } @@ -619,7 +622,7 @@ export function $clientFormat( * @deprecated This function is unused and will be removed in a future release. */ export function getClientFormat( - context: SdkContext, + context: TCGCContext, entity: ModelProperty ): ClientFormat | undefined { let retval: ClientFormat | undefined = getScopedDecoratorData(context, clientFormatKey, entity); @@ -654,12 +657,15 @@ export function $internal(context: DecoratorContext, target: Operation, scope?: * Whether a model / operation is internal or not. If it's internal, emitters * should not expose them to users * - * @param context SdkContext + * @param context TCGCContext * @param entity model / operation that we want to check is internal or not * @returns whether the entity is internal * @deprecated This function is unused and will be removed in a future release. */ -export function isInternal(context: SdkContext, entity: Model | Operation | Enum | Union): boolean { +export function isInternal( + context: TCGCContext, + entity: Model | Operation | Enum | Union +): boolean { const found = getScopedDecoratorData(context, internalKey, entity) ?? false; if (entity.kind === "Operation" || found) { return found; @@ -722,13 +728,13 @@ export function $usage( } export function getUsageOverride( - context: SdkContext, + context: TCGCContext, entity: Model | Enum ): UsageFlags | undefined { return getScopedDecoratorData(context, usageKey, entity); } -export function getUsage(context: SdkContext, entity: Model | Enum): UsageFlags { +export function getUsage(context: TCGCContext, entity: Model | Enum): UsageFlags { if (!context.modelsMap) { getAllModels(context); // this will populate modelsMap } @@ -756,14 +762,14 @@ export function $access( } export function getAccessOverride( - context: SdkContext, + context: TCGCContext, entity: Model | Enum | Operation ): AccessFlags | undefined { return getScopedDecoratorData(context, accessKey, entity); } export function getAccess( - context: SdkContext, + context: TCGCContext, entity: Model | Enum | Operation ): AccessFlags | undefined { const override = getScopedDecoratorData(context, accessKey, entity); @@ -800,11 +806,11 @@ export function $flattenProperty( /** * Whether a model property should be flattened or not. * - * @param context SdkContext + * @param context TCGCContext * @param target ModelProperty that we want to check whether it should be flattened or not * @returns whether the model property should be flattened or not */ -export function shouldFlattenProperty(context: SdkContext, target: ModelProperty): boolean { +export function shouldFlattenProperty(context: TCGCContext, target: ModelProperty): boolean { return getScopedDecoratorData(context, flattenPropertyKey, target) ?? false; } @@ -819,6 +825,6 @@ export function $clientName( setScopedDecoratorData(context, $clientName, clientNameKey, entity, value, scope); } -export function getClientNameOverride(context: SdkContext, entity: Type): string | undefined { +export function getClientNameOverride(context: TCGCContext, entity: Type): string | undefined { return getScopedDecoratorData(context, clientNameKey, entity); } diff --git a/packages/typespec-client-generator-core/src/interfaces.ts b/packages/typespec-client-generator-core/src/interfaces.ts index e80299d99c..047637f921 100644 --- a/packages/typespec-client-generator-core/src/interfaces.ts +++ b/packages/typespec-client-generator-core/src/interfaces.ts @@ -5,8 +5,6 @@ import { Interface, ModelProperty, Namespace, - Operation, - Program, Type, UsageFlags, } from "@typespec/compiler"; @@ -18,6 +16,7 @@ import { HttpVerb, Visibility, } from "@typespec/http"; +import { TCGCContext } from "./internal-utils.js"; export type SdkParameterLocation = | "endpointPath" @@ -28,18 +27,8 @@ export type SdkParameterLocation = | "unknown"; export type SdkParameterImplementation = "Client" | "Method"; -export interface SdkContext> { - program: Program; +export interface SdkContext> extends TCGCContext { emitContext: EmitContext; - emitterName: string; - generateProtocolMethods: boolean; - generateConvenienceMethods: boolean; - filterOutCoreModels?: boolean; - packageName?: string; - modelsMap?: Map; - operationModelsMap?: Map>; - generatedNames?: Set; - arm?: boolean; } export interface SdkEmitterOptions { diff --git a/packages/typespec-client-generator-core/src/internal-utils.ts b/packages/typespec-client-generator-core/src/internal-utils.ts index 2b3d1bcbd2..23c13d83f7 100644 --- a/packages/typespec-client-generator-core/src/internal-utils.ts +++ b/packages/typespec-client-generator-core/src/internal-utils.ts @@ -1,3 +1,6 @@ +import { Operation, Program, Type } from "@typespec/compiler"; +import { SdkEnumType, SdkModelType } from "./interfaces.js"; + export function parseEmitterName(emitterName?: string): string { if (!emitterName) { throw new Error("No emitter name found in program"); @@ -9,3 +12,23 @@ export function parseEmitterName(emitterName?: string): string { if (["typescript", "ts"].includes(language)) return "javascript"; return language; } + +export interface TCGCContext { + program: Program; + emitterName: string; + generateProtocolMethods?: boolean; + generateConvenienceMethods?: boolean; + filterOutCoreModels?: boolean; + packageName?: string; + arm?: boolean; + modelsMap?: Map; + operationModelsMap?: Map>; + generatedNames?: Set; +} + +export function createTCGCContext(program: Program): TCGCContext { + return { + program, + emitterName: "__TCGC_INTERNAL__", + }; +} diff --git a/packages/typespec-client-generator-core/src/public-utils.ts b/packages/typespec-client-generator-core/src/public-utils.ts index e4ae05314e..11976fcc97 100644 --- a/packages/typespec-client-generator-core/src/public-utils.ts +++ b/packages/typespec-client-generator-core/src/public-utils.ts @@ -34,8 +34,7 @@ import { listOperationGroups, listOperationsInOperationGroup, } from "./decorators.js"; -import { SdkContext } from "./interfaces.js"; -import { parseEmitterName } from "./internal-utils.js"; +import { parseEmitterName, TCGCContext } from "./internal-utils.js"; import { reportDiagnostic } from "./lib.js"; /** @@ -45,7 +44,7 @@ import { reportDiagnostic } from "./lib.js"; * @returns */ export function getDefaultApiVersion( - context: SdkContext, + context: TCGCContext, serviceNamespace: Namespace ): Version | undefined { try { @@ -63,7 +62,7 @@ export function getDefaultApiVersion( * @returns */ export function isApiVersion( - context: SdkContext, + context: TCGCContext, parameter: HttpOperationParameter | ModelProperty ): boolean { return ( @@ -78,7 +77,7 @@ export function isApiVersion( * @param context * @returns */ -export function getClientNamespaceString(context: SdkContext): string | undefined { +export function getClientNamespaceString(context: TCGCContext): string | undefined { let packageName = context.packageName; if (packageName) { packageName = packageName @@ -102,7 +101,7 @@ export function getClientNamespaceString(context: SdkContext): string | undefine * @param type * @returns */ -export function getEffectivePayloadType(context: SdkContext, type: Model): Model { +export function getEffectivePayloadType(context: TCGCContext, type: Model): Model { const program = context.program; // if a type has name, we should resolve the name @@ -148,7 +147,7 @@ export function isAzureCoreModel(t: Type): boolean { * * @deprecated This function is deprecated. Please pass in your emitter name as a parameter name to createSdkContext */ -export function getEmitterTargetName(context: SdkContext): string { +export function getEmitterTargetName(context: TCGCContext): string { return parseEmitterName(context.program.emitters[0]?.metadata?.name); // eslint-disable-line deprecation/deprecation } @@ -158,7 +157,7 @@ export function getEmitterTargetName(context: SdkContext): string { * @param property * @returns a tuple of the library and wire name for a model property */ -export function getPropertyNames(context: SdkContext, property: ModelProperty): [string, string] { +export function getPropertyNames(context: TCGCContext, property: ModelProperty): [string, string] { return [getLibraryName(context, property), getWireName(context, property)]; } @@ -176,7 +175,7 @@ export function getPropertyNames(context: SdkContext, property: ModelProperty): * @param type * @returns the library name for a typespec type */ -export function getLibraryName(context: SdkContext, type: Type & { name?: string }): string { +export function getLibraryName(context: TCGCContext, type: Type & { name?: string }): string { // 1. check if there's a client name let emitterSpecificName = getClientNameOverride(context, type); if (emitterSpecificName) return emitterSpecificName; @@ -197,7 +196,7 @@ export function capitalize(name: string): string { return name[0].toUpperCase() + name.slice(1); } -export function reportUnionUnsupported(context: SdkContext, type: Union): void { +export function reportUnionUnsupported(context: TCGCContext, type: Union): void { reportDiagnostic(context.program, { code: "union-unsupported", target: type }); } @@ -210,7 +209,7 @@ interface DocWrapper { details?: string; } -export function getDocHelper(context: SdkContext, type: Type): DocWrapper { +export function getDocHelper(context: TCGCContext, type: Type): DocWrapper { if (getSummary(context.program, type)) { return { description: getSummary(context.program, type), @@ -222,7 +221,7 @@ export function getDocHelper(context: SdkContext, type: Type): DocWrapper { }; } -export function getWireName(context: SdkContext, type: Type & { name: string }) { +export function getWireName(context: TCGCContext, type: Type & { name: string }) { // 1. Check if there's an encoded name const encodedName = resolveEncodedName(context.program, type, "application/json"); if (encodedName !== type.name) return encodedName; @@ -242,7 +241,7 @@ interface SdkTypeBaseHelper { * @param type */ export function getSdkTypeBaseHelper( - context: SdkContext, + context: TCGCContext, type: Type | string, kind: TKind ): SdkTypeBaseHelper { @@ -287,7 +286,7 @@ export function getCrossLanguageDefinitionId(type: { * @param type */ export function getGeneratedName( - context: SdkContext, + context: TCGCContext, type: Model | Union, operation?: Operation ): string { @@ -308,7 +307,7 @@ export function getGeneratedName( * @param type * @returns */ -function findContextPath(context: SdkContext, type: Model | Union): ContextNode[] { +function findContextPath(context: TCGCContext, type: Model | Union): ContextNode[] { for (const client of listClients(context)) { for (const operation of listOperationsInOperationGroup(context, client)) { const result = getContextPath(context, operation, type); @@ -350,7 +349,7 @@ interface ContextNode { * @returns */ function getContextPath( - context: SdkContext, + context: TCGCContext, root: Operation | Model, typeToFind: Model | Union ): ContextNode[] { @@ -490,7 +489,7 @@ function getContextPath( * @param contextPaths * @returns */ -function buildNameFromContextPaths(context: SdkContext, contextPath: ContextNode[]): string { +function buildNameFromContextPaths(context: TCGCContext, contextPath: ContextNode[]): string { // fallback to empty name for corner case if (contextPath.length === 0) { return ""; diff --git a/packages/typespec-client-generator-core/src/types.ts b/packages/typespec-client-generator-core/src/types.ts index b962eb7f95..4eed12aee0 100644 --- a/packages/typespec-client-generator-core/src/types.ts +++ b/packages/typespec-client-generator-core/src/types.ts @@ -61,7 +61,6 @@ import { SdkBuiltInKinds, SdkBuiltInType, SdkConstantType, - SdkContext, SdkDatetimeType, SdkDictionaryType, SdkDurationType, @@ -85,14 +84,16 @@ import { isAzureCoreModel, } from "./public-utils.js"; -function getAnyType(context: SdkContext, type: Type): SdkBuiltInType { +import { TCGCContext } from "./internal-utils.js"; + +function getAnyType(context: TCGCContext, type: Type): SdkBuiltInType { return { ...getSdkTypeBaseHelper(context, type, "any"), encode: getEncodeHelper(context, type, "any"), }; } -function getEncodeHelper(context: SdkContext, type: Type, kind: string): string { +function getEncodeHelper(context: TCGCContext, type: Type, kind: string): string { if (type.kind === "ModelProperty" || type.kind === "Scalar") { return getEncode(context.program, type)?.encoding || kind; } @@ -108,7 +109,7 @@ function getEncodeHelper(context: SdkContext, type: Type, kind: string): string * @param propertyType the type of the property, i.e. the internal type that we add the format info onto */ function addFormatInfo( - context: SdkContext, + context: TCGCContext, type: ModelProperty | Scalar, propertyType: SdkType ): void { @@ -149,7 +150,7 @@ function addFormatInfo( * @param propertyType the type of the property, i.e. the internal type that we add the encoding info onto */ function addEncodeInfo( - context: SdkContext, + context: TCGCContext, type: ModelProperty | Scalar, propertyType: SdkType ): [void, readonly Diagnostic[]] { @@ -224,7 +225,7 @@ function getScalarKind(scalar: Scalar): SdkBuiltInKinds { * @returns the corresponding sdk type */ export function getSdkBuiltInType( - context: SdkContext, + context: TCGCContext, type: Scalar | IntrinsicType | NumericLiteral | StringLiteral | BooleanLiteral ): SdkBuiltInType { if (context.program.checker.isStdType(type) || type.kind === "Intrinsic") { @@ -254,7 +255,7 @@ export function getSdkBuiltInType( throw Error(`Unknown kind ${type.kind}`); } -export function getSdkDurationType(context: SdkContext, type: Scalar): SdkDurationType { +export function getSdkDurationType(context: TCGCContext, type: Scalar): SdkDurationType { // we don't get encode info until we get to the property / parameter level // so we insert the default. Later in properties, we will check // for encoding info and override accordingly @@ -266,7 +267,7 @@ export function getSdkDurationType(context: SdkContext, type: Scalar): SdkDurati } export function getSdkArrayOrDict( - context: SdkContext, + context: TCGCContext, type: Model, operation?: Operation ): [(SdkDictionaryType | SdkArrayType) | undefined, readonly Diagnostic[]] { @@ -301,7 +302,7 @@ export function getSdkArrayOrDict( } export function getSdkTuple( - context: SdkContext, + context: TCGCContext, type: Tuple, operation?: Operation ): [SdkTupleType, readonly Diagnostic[]] { @@ -314,12 +315,12 @@ export function getSdkTuple( }); } -function getNonNullOptions(context: SdkContext, type: Union): Type[] { +function getNonNullOptions(context: TCGCContext, type: Union): Type[] { return [...type.variants.values()].map((x) => x.type).filter((t) => !isNullType(t)); } export function getSdkUnion( - context: SdkContext, + context: TCGCContext, type: Union, operation?: Operation ): [SdkType, readonly Diagnostic[]] { @@ -350,7 +351,7 @@ export function getSdkUnion( } export function getSdkConstant( - context: SdkContext, + context: TCGCContext, type: StringLiteral | NumericLiteral | BooleanLiteral ): SdkConstantType { switch (type.kind) { @@ -367,7 +368,7 @@ export function getSdkConstant( } function addDiscriminatorToModelType( - context: SdkContext, + context: TCGCContext, type: Model, model: SdkModelType, operation?: Operation @@ -455,7 +456,7 @@ function addDiscriminatorToModelType( } export function getSdkModel( - context: SdkContext, + context: TCGCContext, type: Model, operation?: Operation ): [SdkModelType, readonly Diagnostic[]] { @@ -531,7 +532,7 @@ export function getSdkModel( } function getSdkEnumValueType( - context: SdkContext, + context: TCGCContext, type: EnumMember | StringLiteral | NumericLiteral ): SdkBuiltInType { let kind: "string" | "int32" | "float32" = "string"; @@ -545,7 +546,7 @@ function getSdkEnumValueType( } export function getSdkEnumValue( - context: SdkContext, + context: TCGCContext, enumType: SdkEnumType, type: EnumMember ): SdkEnumValueType { @@ -561,7 +562,7 @@ export function getSdkEnumValue( }; } -export function getSdkEnum(context: SdkContext, type: Enum, operation?: Operation): SdkEnumType { +export function getSdkEnum(context: TCGCContext, type: Enum, operation?: Operation): SdkEnumType { let sdkType = context.modelsMap?.get(type) as SdkEnumType | undefined; if (!sdkType) { const docWrapper = getDocHelper(context, type); @@ -587,7 +588,7 @@ export function getSdkEnum(context: SdkContext, type: Enum, operation?: Operatio } function getSdkUnionEnumValues( - context: SdkContext, + context: TCGCContext, type: UnionEnum, enumType: SdkEnumType ): SdkEnumValueType[] { @@ -608,7 +609,7 @@ function getSdkUnionEnumValues( return values; } -function getSdkUnionEnum(context: SdkContext, type: UnionEnum, operation?: Operation) { +function getSdkUnionEnum(context: TCGCContext, type: UnionEnum, operation?: Operation) { let sdkType = context.modelsMap?.get(type.union) as SdkEnumType | undefined; if (!sdkType) { const union = type.union as Union & { name: string }; @@ -634,7 +635,7 @@ function getSdkUnionEnum(context: SdkContext, type: UnionEnum, operation?: Opera } function getKnownValuesEnum( - context: SdkContext, + context: TCGCContext, type: Scalar | ModelProperty, operation?: Operation ): SdkEnumType | undefined { @@ -672,7 +673,7 @@ function getKnownValuesEnum( } export function getClientTypeWithDiagnostics( - context: SdkContext, + context: TCGCContext, type: Type, operation?: Operation ): [SdkType, readonly Diagnostic[]] { @@ -759,7 +760,7 @@ export function getClientTypeWithDiagnostics( return diagnostics.wrap(retval); } -export function getClientType(context: SdkContext, type: Type, operation?: Operation): SdkType { +export function getClientType(context: TCGCContext, type: Type, operation?: Operation): SdkType { return ignoreDiagnostics(getClientTypeWithDiagnostics(context, type, operation)); } @@ -774,7 +775,7 @@ export function isReadOnly(property: SdkBodyModelPropertyType) { return false; } -function getSdkVisibility(context: SdkContext, type: ModelProperty): Visibility[] | undefined { +function getSdkVisibility(context: TCGCContext, type: ModelProperty): Visibility[] | undefined { const visibility = getVisibility(context.program, type); if (visibility) { const result = []; @@ -798,7 +799,7 @@ function getSdkVisibility(context: SdkContext, type: ModelProperty): Visibility[ return undefined; } -function getAvailableApiVersions(context: SdkContext, type: Type): string[] { +function getAvailableApiVersions(context: TCGCContext, type: Type): string[] { const allVersions = getVersions(context.program, type)[1]?.getVersions() ?? []; const addedOnVersions = getAddedOnVersions(context.program, type)?.map((x) => x.value) ?? []; const removedOnVersions = getRemovedOnVersions(context.program, type)?.map((x) => x.value) ?? []; @@ -824,7 +825,7 @@ function getAvailableApiVersions(context: SdkContext, type: Type): string[] { } function getSdkModelPropertyType( - context: SdkContext, + context: TCGCContext, type: ModelProperty, operation?: Operation ): [SdkModelPropertyTypeBase, readonly Diagnostic[]] { @@ -849,7 +850,7 @@ function getSdkModelPropertyType( } function getSdkBodyModelPropertyType( - context: SdkContext, + context: TCGCContext, type: ModelProperty, operation?: Operation ): [SdkBodyModelPropertyType, readonly Diagnostic[]] { @@ -887,7 +888,7 @@ function getSdkBodyModelPropertyType( } function addPropertiesToModelType( - context: SdkContext, + context: TCGCContext, type: Model, sdkType: SdkType, operation?: Operation @@ -916,7 +917,12 @@ function addPropertiesToModelType( return diagnostics.wrap(undefined); } -function updateModelsMap(context: SdkContext, type: Type, sdkType: SdkType, operation?: Operation) { +function updateModelsMap( + context: TCGCContext, + type: Type, + sdkType: SdkType, + operation?: Operation +) { if (sdkType.kind !== "model" && sdkType.kind !== "enum") { return; } @@ -983,7 +989,7 @@ function updateModelsMap(context: SdkContext, type: Type, sdkType: SdkType, oper } function checkAndGetClientType( - context: SdkContext, + context: TCGCContext, type: Type, operation?: Operation ): [SdkType | undefined, readonly Diagnostic[]] { @@ -999,7 +1005,7 @@ function checkAndGetClientType( } function updateUsageOfModel( - context: SdkContext, + context: TCGCContext, type: SdkType, usage: UsageFlags, seenModelNames?: Set @@ -1049,7 +1055,7 @@ function updateUsageOfModel( } function updateTypesFromOperation( - context: SdkContext, + context: TCGCContext, operation: Operation ): [void, readonly Diagnostic[]] { const diagnostics = createDiagnosticCollector(); @@ -1105,7 +1111,7 @@ function updateTypesFromOperation( return diagnostics.wrap(undefined); } -function updateAccessOfModel(context: SdkContext): void { +function updateAccessOfModel(context: TCGCContext): void { for (const [type, sdkType] of context.modelsMap?.entries() ?? []) { const internal = isInternal(context, type as any); // eslint-disable-line deprecation/deprecation if (internal) { @@ -1146,7 +1152,7 @@ interface GetAllModelsOptions { output?: boolean; } -function handleServiceOrphanType(context: SdkContext, type: Model | Enum) { +function handleServiceOrphanType(context: TCGCContext, type: Model | Enum) { const diagnostics = createDiagnosticCollector(); // eslint-disable-next-line deprecation/deprecation if (type.kind === "Model" && isInclude(context, type)) { @@ -1164,7 +1170,7 @@ function handleServiceOrphanType(context: SdkContext, type: Model | Enum) { } export function getAllModelsWithDiagnostics( - context: SdkContext, + context: TCGCContext, options: GetAllModelsOptions = {} ): [(SdkModelType | SdkEnumType)[], readonly Diagnostic[]] { const diagnostics = createDiagnosticCollector(); @@ -1228,11 +1234,11 @@ export function getAllModelsWithDiagnostics( } export function getAllModels( - context: SdkContext, + context: TCGCContext, options: GetAllModelsOptions = {} ): (SdkModelType | SdkEnumType)[] { // we currently don't return diagnostics even though we keep track of them // when we move to the new sdk type ecosystem completely, we'll expose - // diagnostics as a separate property on the sdkContext + // diagnostics as a separate property on the TCGCContext return ignoreDiagnostics(getAllModelsWithDiagnostics(context, options)); } diff --git a/packages/typespec-client-generator-core/src/validate.ts b/packages/typespec-client-generator-core/src/validate.ts new file mode 100644 index 0000000000..833c7fcdbb --- /dev/null +++ b/packages/typespec-client-generator-core/src/validate.ts @@ -0,0 +1,12 @@ +import { Program } from "@typespec/compiler"; +import { createTCGCContext } from "./internal-utils.js"; +import { getAllModelsWithDiagnostics } from "./types.js"; + +export function $onValidate(program: Program) { + // Pass along any diagnostics that might be returned from the HTTP library + const tcgcContext = createTCGCContext(program); + const [_, diagnostics] = getAllModelsWithDiagnostics(tcgcContext); + if (diagnostics.length > 0) { + program.reportDiagnostics(diagnostics); + } +}