diff --git a/.chronus/changes/decorator-post-validator-2025-10-26-18-27-31.md b/.chronus/changes/decorator-post-validator-2025-10-26-18-27-31.md new file mode 100644 index 00000000000..90099e0719c --- /dev/null +++ b/.chronus/changes/decorator-post-validator-2025-10-26-18-27-31.md @@ -0,0 +1,21 @@ +--- +# Change versionKind to one of: internal, fix, dependencies, feature, deprecation, breaking +changeKind: internal +packages: + - "@typespec/events" + - "@typespec/http-client" + - "@typespec/http" + - "@typespec/json-schema" + - "@typespec/openapi" + - "@typespec/openapi3" + - "@typespec/protobuf" + - "@typespec/rest" + - "@typespec/spector" + - "@typespec/sse" + - "@typespec/streams" + - "@typespec/tspd" + - "@typespec/versioning" + - "@typespec/xml" +--- + +Regenerate signature diff --git a/packages/compiler/generated-defs/TypeSpec.Prototypes.ts b/packages/compiler/generated-defs/TypeSpec.Prototypes.ts index 0819a6017d2..97a473e2868 100644 --- a/packages/compiler/generated-defs/TypeSpec.Prototypes.ts +++ b/packages/compiler/generated-defs/TypeSpec.Prototypes.ts @@ -1,6 +1,9 @@ -import type { DecoratorContext, Type } from "../src/index.js"; +import type { DecoratorContext, DecoratorValidatorCallback, Type } from "../src/index.js"; -export type GetterDecorator = (context: DecoratorContext, target: Type) => void; +export type GetterDecorator = ( + context: DecoratorContext, + target: Type, +) => DecoratorValidatorCallback | void; export type TypeSpecPrototypesDecorators = { getter: GetterDecorator; diff --git a/packages/compiler/generated-defs/TypeSpec.ts b/packages/compiler/generated-defs/TypeSpec.ts index 3325db0772a..3804f8e730a 100644 --- a/packages/compiler/generated-defs/TypeSpec.ts +++ b/packages/compiler/generated-defs/TypeSpec.ts @@ -1,5 +1,6 @@ import type { DecoratorContext, + DecoratorValidatorCallback, Enum, EnumValue, Interface, @@ -70,7 +71,7 @@ export type MediaTypeHintDecorator = ( context: DecoratorContext, target: Model | Scalar | Enum | Union, mediaType: string, -) => void; +) => DecoratorValidatorCallback | void; /** * Specify how to encode the target type. @@ -102,7 +103,7 @@ export type EncodeDecorator = ( target: Scalar | ModelProperty, encodingOrEncodeAs: Scalar | string | EnumValue, encodedAs?: Scalar, -) => void; +) => DecoratorValidatorCallback | void; /** * Attach a documentation string. Content support CommonMark markdown formatting. @@ -120,17 +121,23 @@ export type DocDecorator = ( target: Type, doc: string, formatArgs?: Type, -) => void; +) => DecoratorValidatorCallback | void; /** * Returns the model with required properties removed. */ -export type WithOptionalPropertiesDecorator = (context: DecoratorContext, target: Model) => void; +export type WithOptionalPropertiesDecorator = ( + context: DecoratorContext, + target: Model, +) => DecoratorValidatorCallback | void; /** * Returns the model with non-updateable properties removed. */ -export type WithUpdateablePropertiesDecorator = (context: DecoratorContext, target: Model) => void; +export type WithUpdateablePropertiesDecorator = ( + context: DecoratorContext, + target: Model, +) => DecoratorValidatorCallback | void; /** * Returns the model with the given properties omitted. @@ -141,7 +148,7 @@ export type WithoutOmittedPropertiesDecorator = ( context: DecoratorContext, target: Model, omit: Type, -) => void; +) => DecoratorValidatorCallback | void; /** * Returns the model with only the given properties included. @@ -152,12 +159,15 @@ export type WithPickedPropertiesDecorator = ( context: DecoratorContext, target: Model, pick: Type, -) => void; +) => DecoratorValidatorCallback | void; /** * Returns the model with any default values removed. */ -export type WithoutDefaultValuesDecorator = (context: DecoratorContext, target: Model) => void; +export type WithoutDefaultValuesDecorator = ( + context: DecoratorContext, + target: Model, +) => DecoratorValidatorCallback | void; /** * Set the visibility of key properties in a model if not already set. @@ -178,7 +188,7 @@ export type WithDefaultKeyVisibilityDecorator = ( context: DecoratorContext, target: Model, visibility: EnumValue, -) => void; +) => DecoratorValidatorCallback | void; /** * Typically a short, single-line description. @@ -190,7 +200,11 @@ export type WithDefaultKeyVisibilityDecorator = ( * model Pet {} * ``` */ -export type SummaryDecorator = (context: DecoratorContext, target: Type, summary: string) => void; +export type SummaryDecorator = ( + context: DecoratorContext, + target: Type, + summary: string, +) => DecoratorValidatorCallback | void; /** * Attach a documentation string to describe the successful return types of an operation. @@ -207,7 +221,7 @@ export type ReturnsDocDecorator = ( context: DecoratorContext, target: Operation, doc: string, -) => void; +) => DecoratorValidatorCallback | void; /** * Attach a documentation string to describe the error return types of an operation. @@ -224,7 +238,7 @@ export type ErrorsDocDecorator = ( context: DecoratorContext, target: Operation, doc: string, -) => void; +) => DecoratorValidatorCallback | void; /** * Mark this namespace as describing a service and configure service properties. @@ -245,7 +259,7 @@ export type ServiceDecorator = ( context: DecoratorContext, target: Namespace, options?: ServiceOptions, -) => void; +) => DecoratorValidatorCallback | void; /** * Specify that this model is an error type. Operations return error types when the operation has failed. @@ -259,7 +273,10 @@ export type ServiceDecorator = ( * } * ``` */ -export type ErrorDecorator = (context: DecoratorContext, target: Model) => void; +export type ErrorDecorator = ( + context: DecoratorContext, + target: Model, +) => DecoratorValidatorCallback | void; /** * Specify a known data format hint for this string type. For example `uuid`, `uri`, etc. @@ -277,7 +294,7 @@ export type FormatDecorator = ( context: DecoratorContext, target: Scalar | ModelProperty, format: string, -) => void; +) => DecoratorValidatorCallback | void; /** * Specify the the pattern this string should respect using simple regular expression syntax. @@ -302,7 +319,7 @@ export type PatternDecorator = ( target: Scalar | ModelProperty, pattern: string, validationMessage?: string, -) => void; +) => DecoratorValidatorCallback | void; /** * Specify the minimum length this string type should be. @@ -318,7 +335,7 @@ export type MinLengthDecorator = ( context: DecoratorContext, target: Scalar | ModelProperty, value: Numeric, -) => void; +) => DecoratorValidatorCallback | void; /** * Specify the maximum length this string type should be. @@ -334,7 +351,7 @@ export type MaxLengthDecorator = ( context: DecoratorContext, target: Scalar | ModelProperty, value: Numeric, -) => void; +) => DecoratorValidatorCallback | void; /** * Specify the minimum number of items this array should have. @@ -350,7 +367,7 @@ export type MinItemsDecorator = ( context: DecoratorContext, target: Type | ModelProperty, value: Numeric, -) => void; +) => DecoratorValidatorCallback | void; /** * Specify the maximum number of items this array should have. @@ -366,7 +383,7 @@ export type MaxItemsDecorator = ( context: DecoratorContext, target: Type | ModelProperty, value: Numeric, -) => void; +) => DecoratorValidatorCallback | void; /** * Specify the minimum value this numeric type should be. @@ -382,7 +399,7 @@ export type MinValueDecorator = ( context: DecoratorContext, target: Scalar | ModelProperty, value: Numeric | ScalarValue | ScalarValue | ScalarValue | ScalarValue | ScalarValue, -) => void; +) => DecoratorValidatorCallback | void; /** * Specify the maximum value this numeric type should be. @@ -398,7 +415,7 @@ export type MaxValueDecorator = ( context: DecoratorContext, target: Scalar | ModelProperty, value: Numeric | ScalarValue | ScalarValue | ScalarValue | ScalarValue | ScalarValue, -) => void; +) => DecoratorValidatorCallback | void; /** * Specify the minimum value this numeric type should be, exclusive of the given @@ -415,7 +432,7 @@ export type MinValueExclusiveDecorator = ( context: DecoratorContext, target: Scalar | ModelProperty, value: Numeric | ScalarValue | ScalarValue | ScalarValue | ScalarValue | ScalarValue, -) => void; +) => DecoratorValidatorCallback | void; /** * Specify the maximum value this numeric type should be, exclusive of the given @@ -432,7 +449,7 @@ export type MaxValueExclusiveDecorator = ( context: DecoratorContext, target: Scalar | ModelProperty, value: Numeric | ScalarValue | ScalarValue | ScalarValue | ScalarValue | ScalarValue, -) => void; +) => DecoratorValidatorCallback | void; /** * Mark this value as a secret value that should be treated carefully to avoid exposure @@ -446,7 +463,7 @@ export type MaxValueExclusiveDecorator = ( export type SecretDecorator = ( context: DecoratorContext, target: Scalar | ModelProperty | Model | Union | Enum, -) => void; +) => DecoratorValidatorCallback | void; /** * Attaches a tag to an operation, interface, or namespace. Multiple `@tag` decorators can be specified to attach multiple tags to a TypeSpec element. @@ -457,7 +474,7 @@ export type TagDecorator = ( context: DecoratorContext, target: Namespace | Interface | Operation, tag: string, -) => void; +) => DecoratorValidatorCallback | void; /** * Specifies how a templated type should name their instances. @@ -478,7 +495,7 @@ export type FriendlyNameDecorator = ( target: Type, name: string, formatArgs?: Type, -) => void; +) => DecoratorValidatorCallback | void; /** * Mark a model property as the key to identify instances of that type @@ -495,7 +512,7 @@ export type KeyDecorator = ( context: DecoratorContext, target: ModelProperty, altName?: string, -) => void; +) => DecoratorValidatorCallback | void; /** * Specify this operation is an overload of the given operation. @@ -514,7 +531,7 @@ export type OverloadDecorator = ( context: DecoratorContext, target: Operation, overloadbase: Operation, -) => void; +) => DecoratorValidatorCallback | void; /** * Provide an alternative name for this type when serialized to the given mime type. @@ -541,7 +558,7 @@ export type EncodedNameDecorator = ( target: Type, mimeType: string, name: string, -) => void; +) => DecoratorValidatorCallback | void; /** * Specify that this union is discriminated. @@ -603,7 +620,7 @@ export type DiscriminatedDecorator = ( context: DecoratorContext, target: Union, options?: DiscriminatedOptions, -) => void; +) => DecoratorValidatorCallback | void; /** * Specify the property to be used to discriminate this type. @@ -622,7 +639,7 @@ export type DiscriminatorDecorator = ( context: DecoratorContext, target: Model, propertyName: string, -) => void; +) => DecoratorValidatorCallback | void; /** * Provide an example value for a data type. @@ -643,7 +660,7 @@ export type ExampleDecorator = ( target: Model | Enum | Scalar | Union | ModelProperty | UnionVariant, example: unknown, options?: ExampleOptions, -) => void; +) => DecoratorValidatorCallback | void; /** * Provide example values for an operation's parameters and corresponding return type. @@ -661,12 +678,15 @@ export type OpExampleDecorator = ( target: Operation, example: OperationExample, options?: ExampleOptions, -) => void; +) => DecoratorValidatorCallback | void; /** * Mark this operation as a `list` operation that returns a paginated list of items. */ -export type ListDecorator = (context: DecoratorContext, target: Operation) => void; +export type ListDecorator = ( + context: DecoratorContext, + target: Operation, +) => DecoratorValidatorCallback | void; /** * Pagination property defining the number of items to skip. @@ -679,7 +699,10 @@ export type ListDecorator = (context: DecoratorContext, target: Operation) => vo * @list op listPets(@offset skip: int32, @pageSize pageSize: int8): Page; * ``` */ -export type OffsetDecorator = (context: DecoratorContext, target: ModelProperty) => void; +export type OffsetDecorator = ( + context: DecoratorContext, + target: ModelProperty, +) => DecoratorValidatorCallback | void; /** * Pagination property defining the page index. @@ -692,7 +715,10 @@ export type OffsetDecorator = (context: DecoratorContext, target: ModelProperty) * @list op listPets(@pageIndex page: int32, @pageSize pageSize: int8): Page; * ``` */ -export type PageIndexDecorator = (context: DecoratorContext, target: ModelProperty) => void; +export type PageIndexDecorator = ( + context: DecoratorContext, + target: ModelProperty, +) => DecoratorValidatorCallback | void; /** * Specify the pagination parameter that controls the maximum number of items to include in a page. @@ -705,7 +731,10 @@ export type PageIndexDecorator = (context: DecoratorContext, target: ModelProper * @list op listPets(@pageIndex page: int32, @pageSize pageSize: int8): Page; * ``` */ -export type PageSizeDecorator = (context: DecoratorContext, target: ModelProperty) => void; +export type PageSizeDecorator = ( + context: DecoratorContext, + target: ModelProperty, +) => DecoratorValidatorCallback | void; /** * Specify the the property that contains the array of page items. @@ -718,7 +747,10 @@ export type PageSizeDecorator = (context: DecoratorContext, target: ModelPropert * @list op listPets(@pageIndex page: int32, @pageSize pageSize: int8): Page; * ``` */ -export type PageItemsDecorator = (context: DecoratorContext, target: ModelProperty) => void; +export type PageItemsDecorator = ( + context: DecoratorContext, + target: ModelProperty, +) => DecoratorValidatorCallback | void; /** * Pagination property defining the token to get to the next page. @@ -733,7 +765,10 @@ export type PageItemsDecorator = (context: DecoratorContext, target: ModelProper * @list op listPets(@continuationToken continuationToken: string): Page; * ``` */ -export type ContinuationTokenDecorator = (context: DecoratorContext, target: ModelProperty) => void; +export type ContinuationTokenDecorator = ( + context: DecoratorContext, + target: ModelProperty, +) => DecoratorValidatorCallback | void; /** * Pagination property defining a link to the next page. @@ -752,7 +787,10 @@ export type ContinuationTokenDecorator = (context: DecoratorContext, target: Mod * @list op listPets(): Page; * ``` */ -export type NextLinkDecorator = (context: DecoratorContext, target: ModelProperty) => void; +export type NextLinkDecorator = ( + context: DecoratorContext, + target: ModelProperty, +) => DecoratorValidatorCallback | void; /** * Pagination property defining a link to the previous page. @@ -771,7 +809,10 @@ export type NextLinkDecorator = (context: DecoratorContext, target: ModelPropert * @list op listPets(): Page; * ``` */ -export type PrevLinkDecorator = (context: DecoratorContext, target: ModelProperty) => void; +export type PrevLinkDecorator = ( + context: DecoratorContext, + target: ModelProperty, +) => DecoratorValidatorCallback | void; /** * Pagination property defining a link to the first page. @@ -790,7 +831,10 @@ export type PrevLinkDecorator = (context: DecoratorContext, target: ModelPropert * @list op listPets(): Page; * ``` */ -export type FirstLinkDecorator = (context: DecoratorContext, target: ModelProperty) => void; +export type FirstLinkDecorator = ( + context: DecoratorContext, + target: ModelProperty, +) => DecoratorValidatorCallback | void; /** * Pagination property defining a link to the last page. @@ -809,14 +853,21 @@ export type FirstLinkDecorator = (context: DecoratorContext, target: ModelProper * @list op listPets(): Page; * ``` */ -export type LastLinkDecorator = (context: DecoratorContext, target: ModelProperty) => void; +export type LastLinkDecorator = ( + context: DecoratorContext, + target: ModelProperty, +) => DecoratorValidatorCallback | void; /** * A debugging decorator used to inspect a type. * * @param text Custom text to log */ -export type InspectTypeDecorator = (context: DecoratorContext, target: Type, text: string) => void; +export type InspectTypeDecorator = ( + context: DecoratorContext, + target: Type, + text: string, +) => DecoratorValidatorCallback | void; /** * A debugging decorator used to inspect a type name. @@ -827,7 +878,7 @@ export type InspectTypeNameDecorator = ( context: DecoratorContext, target: Type, text: string, -) => void; +) => DecoratorValidatorCallback | void; /** * Sets the visibility modifiers that are active on a property, indicating that it is only considered to be present @@ -879,7 +930,7 @@ export type VisibilityDecorator = ( context: DecoratorContext, target: ModelProperty, ...visibilities: EnumValue[] -) => void; +) => DecoratorValidatorCallback | void; /** * Indicates that a property is not visible in the given visibility class. @@ -901,7 +952,7 @@ export type InvisibleDecorator = ( context: DecoratorContext, target: ModelProperty, visibilityClass: Enum, -) => void; +) => DecoratorValidatorCallback | void; /** * Removes visibility modifiers from a property. @@ -926,7 +977,7 @@ export type RemoveVisibilityDecorator = ( context: DecoratorContext, target: ModelProperty, ...visibilities: EnumValue[] -) => void; +) => DecoratorValidatorCallback | void; /** * Removes properties that do not have at least one of the given visibility modifiers @@ -975,7 +1026,7 @@ export type WithVisibilityDecorator = ( context: DecoratorContext, target: Model, ...visibilities: EnumValue[] -) => void; +) => DecoratorValidatorCallback | void; /** * Declares the visibility constraint of the parameters of a given operation. @@ -991,7 +1042,7 @@ export type ParameterVisibilityDecorator = ( context: DecoratorContext, target: Operation, ...visibilities: EnumValue[] -) => void; +) => DecoratorValidatorCallback | void; /** * Declares the visibility constraint of the return type of a given operation. @@ -1007,7 +1058,7 @@ export type ReturnTypeVisibilityDecorator = ( context: DecoratorContext, target: Operation, ...visibilities: EnumValue[] -) => void; +) => DecoratorValidatorCallback | void; /** * Declares the default visibility modifiers for a visibility class. @@ -1023,7 +1074,7 @@ export type DefaultVisibilityDecorator = ( context: DecoratorContext, target: Enum, ...visibilities: EnumValue[] -) => void; +) => DecoratorValidatorCallback | void; /** * Applies the given visibility filter to the properties of the target model. @@ -1058,7 +1109,7 @@ export type WithVisibilityFilterDecorator = ( target: Model, filter: VisibilityFilter, nameTemplate?: string, -) => void; +) => DecoratorValidatorCallback | void; /** * Transforms the `target` model to include only properties that are visible during the @@ -1096,7 +1147,7 @@ export type WithLifecycleUpdateDecorator = ( context: DecoratorContext, target: Model, nameTemplate?: string, -) => void; +) => DecoratorValidatorCallback | void; export type TypeSpecDecorators = { mediaTypeHint: MediaTypeHintDecorator; diff --git a/packages/compiler/src/core/checker.ts b/packages/compiler/src/core/checker.ts index b39e55cd85f..de5359e3daa 100644 --- a/packages/compiler/src/core/checker.ts +++ b/packages/compiler/src/core/checker.ts @@ -56,6 +56,7 @@ import { DecoratorContext, DecoratorDeclarationStatementNode, DecoratorExpressionNode, + DecoratorValidatorCallback, Diagnostic, DiagnosticTarget, DocContent, @@ -368,6 +369,7 @@ export function createChecker(program: Program, resolver: NameResolver): Checker * Key is the SymId of a node. It can be retrieved with getNodeSymId(node) */ const pendingResolutions = new PendingResolutions(); + const postCheckValidators: DecoratorValidatorCallback[] = []; const typespecNamespaceBinding = resolver.symbols.global.exports!.get("TypeSpec"); if (typespecNamespaceBinding) { @@ -3485,6 +3487,7 @@ export function createChecker(program: Program, resolver: NameResolver): Checker internalDecoratorValidation(); assertNoPendingResolutions(); + runPostValidators(postCheckValidators); } function assertNoPendingResolutions() { @@ -5956,9 +5959,7 @@ export function createChecker(program: Program, resolver: NameResolver): Checker if (!options.skipDecorators) { if ("decorators" in typeDef) { - for (const decApp of typeDef.decorators) { - applyDecoratorToType(program, decApp, typeDef); - } + applyDecoratorsToType(typeDef); } typeDef.isFinished = true; Object.setPrototypeOf(typeDef, typePrototype); @@ -5968,6 +5969,31 @@ export function createChecker(program: Program, resolver: NameResolver): Checker return typeDef; } + function applyDecoratorsToType(typeDef: Type & { decorators: DecoratorApplication[] }) { + const postSelfValidators: DecoratorValidatorCallback[] = []; + for (const decApp of typeDef.decorators) { + const validator = applyDecoratorToType(program, decApp, typeDef); + if (validator) { + switch (validator.when) { + case "onFinish": + postSelfValidators.push(validator); + break; + case "onGraphFinish": + postCheckValidators.push(validator); + break; + } + } + } + runPostValidators(postSelfValidators); + } + + /** Run a list of post validator */ + function runPostValidators(validators: DecoratorValidatorCallback[]) { + for (const validator of validators) { + program.reportDiagnostics(validator.validator()); + } + } + function markAsChecked(type: T) { if (!type.creating) return; delete type.creating; @@ -6669,7 +6695,11 @@ function reportDeprecation( } } -function applyDecoratorToType(program: Program, decApp: DecoratorApplication, target: Type) { +function applyDecoratorToType( + program: Program, + decApp: DecoratorApplication, + target: Type, +): DecoratorValidatorCallback | void { compilerAssert("decorators" in target, "Cannot apply decorator to non-decoratable type", target); for (const arg of decApp.args) { @@ -6697,7 +6727,7 @@ function applyDecoratorToType(program: Program, decApp: DecoratorApplication, ta const args = decApp.args.map((x) => x.jsValue); const fn = decApp.decorator; const context = createDecoratorContext(program, decApp); - fn(context, target, ...args); + return fn(context, target, ...args); } catch (error: any) { // do not fail the language server for exceptions in decorators if (program.compilerOptions.designTimeBuild) { diff --git a/packages/compiler/src/core/types.ts b/packages/compiler/src/core/types.ts index 7dd56a806a2..6fa70b4ee07 100644 --- a/packages/compiler/src/core/types.ts +++ b/packages/compiler/src/core/types.ts @@ -48,11 +48,27 @@ export interface DecoratorApplication { node?: DecoratorExpressionNode | AugmentDecoratorStatementNode; } +/** + * Signature for a decorator JS implementation function. + * Use `@typespec/tspd` to generate an accurate signature from the `extern dec` + */ export interface DecoratorFunction { - (program: DecoratorContext, target: any, ...customArgs: any[]): void; + (program: DecoratorContext, target: any, ...customArgs: any[]): DecoratorValidatorCallback | void; namespace?: string; } +export interface DecoratorValidatorCallback { + /** + * When should this validator run. + * "onFinish": After all decorators are run on the same type. Useful if trying to validate this decorator is compatible with other decorators without relying on the order they are applied. + * "onGraphFinish": After everything is checked in the type graph. Useful when trying to get an overall view of the program. + */ + readonly when: "onFinish" | "onGraphFinish"; + + /** Validator implementation. This function will be run according to the kind defined above. */ + readonly validator: () => readonly Diagnostic[]; +} + export interface BaseType { readonly entityKind: "Type"; kind: string; diff --git a/packages/compiler/src/index.ts b/packages/compiler/src/index.ts index 55ae15c153d..61e9a06012e 100644 --- a/packages/compiler/src/index.ts +++ b/packages/compiler/src/index.ts @@ -322,6 +322,7 @@ export type { DecoratorContext, DecoratorFunction, DecoratorImplementations, + DecoratorValidatorCallback, DeprecatedDirective, Diagnostic, DiagnosticCreator, diff --git a/packages/compiler/test/checker/decorators.test.ts b/packages/compiler/test/checker/decorators.test.ts index 0841d5ec379..be266e24917 100644 --- a/packages/compiler/test/checker/decorators.test.ts +++ b/packages/compiler/test/checker/decorators.test.ts @@ -3,7 +3,9 @@ import { beforeEach, describe, it } from "vitest"; import { numericRanges } from "../../src/core/numeric-ranges.js"; import { Numeric } from "../../src/core/numeric.js"; import { + DecoratorContext, DecoratorFunction, + Model, Namespace, PackageFlags, setTypeSpecNamespace, @@ -14,7 +16,9 @@ import { createTestHost, createTestWrapper, expectDiagnostics, + mockFile, } from "../../src/testing/index.js"; +import { Tester } from "../tester.js"; describe("compiler: checker: decorators", () => { let testHost: TestHost; @@ -546,3 +550,88 @@ describe("compiler: checker: decorators", () => { ok(result, "expected Foo to be blue in isBlue decorator"); }); }); + +describe("validators", () => { + async function testerForDecorator(fn: DecoratorFunction) { + return await Tester.files({ + "dec.tsp": ` + import "./dec.js"; + namespace MyLibrary; + extern dec myDecorator(target: unknown); + `, + "dec.js": mockFile.js({ + $decorators: { + MyLibrary: { + myDecorator: fn, + }, + }, + }), + }) + .import("./dec.tsp") + .using("MyLibrary"); + } + + it("postSelf apply validator after checking the type", async () => { + const order: string[] = []; + const tester = await testerForDecorator((_: DecoratorContext, target: Model) => { + order.push(`apply(${target.name})`); + return { + when: "onFinish", + validator: () => { + order.push(`validate(${target.name})`); + return []; + }, + }; + }); + await tester.compile(` + @myDecorator + @myDecorator + model A {} + @myDecorator + @myDecorator + model B {} + `); + deepStrictEqual(order, [ + `apply(A)`, + `apply(A)`, + `validate(A)`, + `validate(A)`, + `apply(B)`, + `apply(B)`, + `validate(B)`, + `validate(B)`, + ]); + }); + + it("post apply validator after checking every type", async () => { + const order: string[] = []; + const tester = await testerForDecorator((_: DecoratorContext, target: Model) => { + order.push(`apply(${target.name})`); + return { + when: "onGraphFinish", + validator: () => { + order.push(`validate(${target.name})`); + return []; + }, + }; + }); + await tester.compile(` + @myDecorator + @myDecorator + model A {} + @myDecorator + @myDecorator + model B {} + `); + deepStrictEqual(order, [ + `apply(A)`, + `apply(A)`, + `apply(B)`, + `apply(B)`, + `validate(A)`, + `validate(A)`, + `validate(B)`, + `validate(B)`, + ]); + }); +}); diff --git a/packages/events/generated-defs/TypeSpec.Events.ts b/packages/events/generated-defs/TypeSpec.Events.ts index 00f7e0c222c..1765080ca03 100644 --- a/packages/events/generated-defs/TypeSpec.Events.ts +++ b/packages/events/generated-defs/TypeSpec.Events.ts @@ -1,4 +1,10 @@ -import type { DecoratorContext, ModelProperty, Union, UnionVariant } from "@typespec/compiler"; +import type { + DecoratorContext, + DecoratorValidatorCallback, + ModelProperty, + Union, + UnionVariant, +} from "@typespec/compiler"; /** * Specify that this union describes a set of events. @@ -13,7 +19,10 @@ import type { DecoratorContext, ModelProperty, Union, UnionVariant } from "@type * } * ``` */ -export type EventsDecorator = (context: DecoratorContext, target: Union) => void; +export type EventsDecorator = ( + context: DecoratorContext, + target: Union, +) => DecoratorValidatorCallback | void; /** * Specifies the content type of the event envelope, event body, or event payload. @@ -41,7 +50,7 @@ export type ContentTypeDecorator = ( context: DecoratorContext, target: UnionVariant | ModelProperty, contentType: string, -) => void; +) => DecoratorValidatorCallback | void; /** * Identifies the payload of an event. @@ -54,7 +63,10 @@ export type ContentTypeDecorator = ( * } * ``` */ -export type DataDecorator = (context: DecoratorContext, target: ModelProperty) => void; +export type DataDecorator = ( + context: DecoratorContext, + target: ModelProperty, +) => DecoratorValidatorCallback | void; export type TypeSpecEventsDecorators = { events: EventsDecorator; diff --git a/packages/http-client/generated-defs/TypeSpec.HttpClient.ts b/packages/http-client/generated-defs/TypeSpec.HttpClient.ts index d6001ffcbe3..76e97d71714 100644 --- a/packages/http-client/generated-defs/TypeSpec.HttpClient.ts +++ b/packages/http-client/generated-defs/TypeSpec.HttpClient.ts @@ -1,4 +1,4 @@ -import type { DecoratorContext, Type } from "@typespec/compiler"; +import type { DecoratorContext, DecoratorValidatorCallback, Type } from "@typespec/compiler"; export interface FeatureLifecycleOptions { readonly emitterScope?: string; @@ -8,7 +8,7 @@ export type ExperimentalDecorator = ( context: DecoratorContext, target: Type, options?: FeatureLifecycleOptions, -) => void; +) => DecoratorValidatorCallback | void; export type TypeSpecHttpClientDecorators = { experimental: ExperimentalDecorator; diff --git a/packages/http/generated-defs/TypeSpec.Http.Private.ts b/packages/http/generated-defs/TypeSpec.Http.Private.ts index ec331e57121..6ba1d811f91 100644 --- a/packages/http/generated-defs/TypeSpec.Http.Private.ts +++ b/packages/http/generated-defs/TypeSpec.Http.Private.ts @@ -1,4 +1,10 @@ -import type { DecoratorContext, Model, ModelProperty, Type } from "@typespec/compiler"; +import type { + DecoratorContext, + DecoratorValidatorCallback, + Model, + ModelProperty, + Type, +} from "@typespec/compiler"; export interface HttpPartOptions { readonly name?: string; @@ -8,16 +14,22 @@ export interface ApplyMergePatchOptions { readonly visibilityMode: unknown; } -export type PlainDataDecorator = (context: DecoratorContext, target: Model) => void; +export type PlainDataDecorator = ( + context: DecoratorContext, + target: Model, +) => DecoratorValidatorCallback | void; -export type HttpFileDecorator = (context: DecoratorContext, target: Model) => void; +export type HttpFileDecorator = ( + context: DecoratorContext, + target: Model, +) => DecoratorValidatorCallback | void; export type HttpPartDecorator = ( context: DecoratorContext, target: Model, type: Type, options: HttpPartOptions, -) => void; +) => DecoratorValidatorCallback | void; /** * Performs the canonical merge-patch transformation on the given model and injects its @@ -29,7 +41,7 @@ export type ApplyMergePatchDecorator = ( source: Model, nameTemplate: string, options: ApplyMergePatchOptions, -) => void; +) => DecoratorValidatorCallback | void; /** * Specify if inapplicable metadata should be included in the payload for the given entity. @@ -40,7 +52,7 @@ export type IncludeInapplicableMetadataInPayloadDecorator = ( context: DecoratorContext, target: Type, value: boolean, -) => void; +) => DecoratorValidatorCallback | void; /** * Marks a model that was generated by applying the MergePatch @@ -50,7 +62,7 @@ export type MergePatchModelDecorator = ( context: DecoratorContext, target: Model, source: Model, -) => void; +) => DecoratorValidatorCallback | void; /** * Links a modelProperty mutated as part of a mergePatch transform to @@ -60,7 +72,7 @@ export type MergePatchPropertyDecorator = ( context: DecoratorContext, target: ModelProperty, source: ModelProperty, -) => void; +) => DecoratorValidatorCallback | void; export type TypeSpecHttpPrivateDecorators = { plainData: PlainDataDecorator; diff --git a/packages/http/generated-defs/TypeSpec.Http.ts b/packages/http/generated-defs/TypeSpec.Http.ts index 7e0a318f61d..a78efa32fc5 100644 --- a/packages/http/generated-defs/TypeSpec.Http.ts +++ b/packages/http/generated-defs/TypeSpec.Http.ts @@ -1,5 +1,6 @@ import type { DecoratorContext, + DecoratorValidatorCallback, Interface, ModelProperty, Namespace, @@ -46,7 +47,10 @@ export interface PatchOptions { * }; * ``` */ -export type StatusCodeDecorator = (context: DecoratorContext, target: ModelProperty) => void; +export type StatusCodeDecorator = ( + context: DecoratorContext, + target: ModelProperty, +) => DecoratorValidatorCallback | void; /** * Explicitly specify that this property type will be exactly the HTTP body. @@ -60,7 +64,10 @@ export type StatusCodeDecorator = (context: DecoratorContext, target: ModelPrope * op download(): {@body image: bytes}; * ``` */ -export type BodyDecorator = (context: DecoratorContext, target: ModelProperty) => void; +export type BodyDecorator = ( + context: DecoratorContext, + target: ModelProperty, +) => DecoratorValidatorCallback | void; /** * Specify this property is to be sent or received as an HTTP header. @@ -83,7 +90,7 @@ export type HeaderDecorator = ( context: DecoratorContext, target: ModelProperty, headerNameOrOptions?: string | HeaderOptions, -) => void; +) => DecoratorValidatorCallback | void; /** * Specify this property is to be sent or received in the cookie. @@ -106,7 +113,7 @@ export type CookieDecorator = ( context: DecoratorContext, target: ModelProperty, cookieNameOrOptions?: string | CookieOptions, -) => void; +) => DecoratorValidatorCallback | void; /** * Specify this property is to be sent as a query parameter. @@ -122,7 +129,7 @@ export type QueryDecorator = ( context: DecoratorContext, target: ModelProperty, queryNameOrOptions?: string | QueryOptions, -) => void; +) => DecoratorValidatorCallback | void; /** * Explicitly specify that this property is to be interpolated as a path parameter. @@ -138,7 +145,7 @@ export type PathDecorator = ( context: DecoratorContext, target: ModelProperty, paramNameOrOptions?: string | PathOptions, -) => void; +) => DecoratorValidatorCallback | void; /** * Specify that the body resolution should be resolved from that property. @@ -151,7 +158,10 @@ export type PathDecorator = ( * op download(): {@bodyRoot user: {name: string, @header id: string}}; * ``` */ -export type BodyRootDecorator = (context: DecoratorContext, target: ModelProperty) => void; +export type BodyRootDecorator = ( + context: DecoratorContext, + target: ModelProperty, +) => DecoratorValidatorCallback | void; /** * Specify that this property shouldn't be included in the HTTP body. @@ -162,7 +172,10 @@ export type BodyRootDecorator = (context: DecoratorContext, target: ModelPropert * op upload(name: string, @bodyIgnore headers: {@header id: string}): void; * ``` */ -export type BodyIgnoreDecorator = (context: DecoratorContext, target: ModelProperty) => void; +export type BodyIgnoreDecorator = ( + context: DecoratorContext, + target: ModelProperty, +) => DecoratorValidatorCallback | void; /** * @@ -179,7 +192,10 @@ export type BodyIgnoreDecorator = (context: DecoratorContext, target: ModelPrope * ): void; * ``` */ -export type MultipartBodyDecorator = (context: DecoratorContext, target: ModelProperty) => void; +export type MultipartBodyDecorator = ( + context: DecoratorContext, + target: ModelProperty, +) => DecoratorValidatorCallback | void; /** * Specify the HTTP verb for the target operation to be `GET`. @@ -189,7 +205,10 @@ export type MultipartBodyDecorator = (context: DecoratorContext, target: ModelPr * @get op read(): string * ``` */ -export type GetDecorator = (context: DecoratorContext, target: Operation) => void; +export type GetDecorator = ( + context: DecoratorContext, + target: Operation, +) => DecoratorValidatorCallback | void; /** * Specify the HTTP verb for the target operation to be `PUT`. @@ -199,7 +218,10 @@ export type GetDecorator = (context: DecoratorContext, target: Operation) => voi * @put op set(pet: Pet): void * ``` */ -export type PutDecorator = (context: DecoratorContext, target: Operation) => void; +export type PutDecorator = ( + context: DecoratorContext, + target: Operation, +) => DecoratorValidatorCallback | void; /** * Specify the HTTP verb for the target operation to be `POST`. @@ -209,7 +231,10 @@ export type PutDecorator = (context: DecoratorContext, target: Operation) => voi * @post op create(pet: Pet): void * ``` */ -export type PostDecorator = (context: DecoratorContext, target: Operation) => void; +export type PostDecorator = ( + context: DecoratorContext, + target: Operation, +) => DecoratorValidatorCallback | void; /** * Specify the HTTP verb for the target operation to be `PATCH`. @@ -231,7 +256,7 @@ export type PatchDecorator = ( context: DecoratorContext, target: Operation, options?: PatchOptions, -) => void; +) => DecoratorValidatorCallback | void; /** * Specify the HTTP verb for the target operation to be `DELETE`. @@ -241,7 +266,10 @@ export type PatchDecorator = ( * @delete op set(petId: string): void * ``` */ -export type DeleteDecorator = (context: DecoratorContext, target: Operation) => void; +export type DeleteDecorator = ( + context: DecoratorContext, + target: Operation, +) => DecoratorValidatorCallback | void; /** * Specify the HTTP verb for the target operation to be `HEAD`. @@ -251,7 +279,10 @@ export type DeleteDecorator = (context: DecoratorContext, target: Operation) => * @head op ping(petId: string): void * ``` */ -export type HeadDecorator = (context: DecoratorContext, target: Operation) => void; +export type HeadDecorator = ( + context: DecoratorContext, + target: Operation, +) => DecoratorValidatorCallback | void; /** * Specify an endpoint for this service. Multiple `@server` decorators can be used to specify multiple endpoints. @@ -296,7 +327,7 @@ export type ServerDecorator = ( url: string, description?: string, parameters?: Type, -) => void; +) => DecoratorValidatorCallback | void; /** * Specify authentication for a whole service or specific methods. See the [documentation in the Http library](https://typespec.io/docs/libraries/http/authentication) for full details. @@ -313,7 +344,7 @@ export type UseAuthDecorator = ( context: DecoratorContext, target: Namespace | Interface | Operation, auth: Type, -) => void; +) => DecoratorValidatorCallback | void; /** * Defines the relative route URI template for the target operation as defined by [RFC 6570](https://datatracker.ietf.org/doc/html/rfc6570#section-3.2.3) @@ -340,7 +371,7 @@ export type RouteDecorator = ( context: DecoratorContext, target: Namespace | Interface | Operation, path: string, -) => void; +) => DecoratorValidatorCallback | void; /** * `@sharedRoute` marks the operation as sharing a route path with other operations. @@ -356,7 +387,10 @@ export type RouteDecorator = ( * op getWidget(@path id: string): Widget; * ``` */ -export type SharedRouteDecorator = (context: DecoratorContext, target: Operation) => void; +export type SharedRouteDecorator = ( + context: DecoratorContext, + target: Operation, +) => DecoratorValidatorCallback | void; export type TypeSpecHttpDecorators = { statusCode: StatusCodeDecorator; diff --git a/packages/http/src/private.decorators.ts b/packages/http/src/private.decorators.ts index aa0bb3aa9ed..f6d26823e42 100644 --- a/packages/http/src/private.decorators.ts +++ b/packages/http/src/private.decorators.ts @@ -20,6 +20,16 @@ import { HttpPartOptions, PlainDataDecorator, } from "../generated-defs/TypeSpec.Http.Private.js"; +import { + getCookieParamOptions, + getHeaderFieldOptions, + getPathOptions, + getQueryOptions, + isBody, + isBodyRoot, + isMultipartBodyProperty, + isStatusCode, +} from "./decorators.js"; import { HttpStateKeys, reportDiagnostic } from "./lib.js"; import { HttpOperationFileBody } from "./types.js"; @@ -151,8 +161,73 @@ export const $httpFile: HttpFileDecorator = (context: DecoratorContext, target: return undefined; } + + return { + when: "onGraphFinish", + validator: () => { + validateHttpFileModel(context.program, target); + return []; + }, + }; }; +function validateHttpFileModel(program: Program, model: Model) { + for (const prop of model.properties.values()) { + switch (prop.name) { + case "contentType": + case "contents": { + // Check if these properties have any HTTP metadata and if so, report an error + const annotations = { + header: getHeaderFieldOptions(program, prop), + cookie: getCookieParamOptions(program, prop), + query: getQueryOptions(program, prop), + path: getPathOptions(program, prop), + body: isBody(program, prop), + bodyRoot: isBodyRoot(program, prop), + multipartBody: isMultipartBodyProperty(program, prop), + statusCode: isStatusCode(program, prop), + }; + + reportDisallowed(prop, annotations); + break; + } + case "filename": { + const annotations = { + body: isBody(program, prop), + bodyRoot: isBodyRoot(program, prop), + multipartBody: isMultipartBodyProperty(program, prop), + statusCode: isStatusCode(program, prop), + cookie: getCookieParamOptions(program, prop), + }; + + reportDisallowed(prop, annotations); + break; + } + default: + reportDiagnostic(program, { + code: "http-file-extra-property", + format: { propName: prop.name }, + target: prop, + }); + } + } + for (const child of model.derivedModels) { + validateHttpFileModel(program, child); + } + + function reportDisallowed(target: ModelProperty, annotations: Record) { + const metadataEntries = Object.entries(annotations).filter((e) => !!e[1]); + + for (const [metadataType] of metadataEntries) { + reportDiagnostic(program, { + code: "http-file-disallowed-metadata", + format: { propName: target.name, metadataType }, + target: target, + }); + } + } +} + /** * Check if the given type is an `HttpFile` */ diff --git a/packages/http/src/validate.ts b/packages/http/src/validate.ts index 340356a2594..ea9d2c76500 100644 --- a/packages/http/src/validate.ts +++ b/packages/http/src/validate.ts @@ -1,16 +1,6 @@ -import type { Model, ModelProperty, Program } from "@typespec/compiler"; -import { - getCookieParamOptions, - getHeaderFieldOptions, - getPathOptions, - getQueryOptions, - isBody, - isBodyRoot, - isMultipartBodyProperty, - isStatusCode, -} from "./decorators.js"; +import type { Program } from "@typespec/compiler"; import { isSharedRoute } from "./decorators/shared-route.js"; -import { HttpStateKeys, reportDiagnostic } from "./lib.js"; +import { reportDiagnostic } from "./lib.js"; import { getAllHttpServices } from "./operations.js"; import { HttpOperation, HttpService } from "./types.js"; @@ -21,74 +11,6 @@ export function $onValidate(program: Program) { program.reportDiagnostics(diagnostics); } validateSharedRouteConsistency(program, services); - validateHttpFiles(program); -} - -function validateHttpFiles(program: Program) { - const httpFiles = [...program.stateSet(HttpStateKeys.file)]; - - for (const model of httpFiles) { - if (model.kind === "Model") { - validateHttpFileModel(program, model); - } - } -} - -function validateHttpFileModel(program: Program, model: Model) { - for (const prop of model.properties.values()) { - switch (prop.name) { - case "contentType": - case "contents": { - // Check if these properties have any HTTP metadata and if so, report an error - const annotations = { - header: getHeaderFieldOptions(program, prop), - cookie: getCookieParamOptions(program, prop), - query: getQueryOptions(program, prop), - path: getPathOptions(program, prop), - body: isBody(program, prop), - bodyRoot: isBodyRoot(program, prop), - multipartBody: isMultipartBodyProperty(program, prop), - statusCode: isStatusCode(program, prop), - }; - - reportDisallowed(prop, annotations); - break; - } - case "filename": { - const annotations = { - body: isBody(program, prop), - bodyRoot: isBodyRoot(program, prop), - multipartBody: isMultipartBodyProperty(program, prop), - statusCode: isStatusCode(program, prop), - cookie: getCookieParamOptions(program, prop), - }; - - reportDisallowed(prop, annotations); - break; - } - default: - reportDiagnostic(program, { - code: "http-file-extra-property", - format: { propName: prop.name }, - target: prop, - }); - } - } - for (const child of model.derivedModels) { - validateHttpFileModel(program, child); - } - - function reportDisallowed(target: ModelProperty, annotations: Record) { - const metadataEntries = Object.entries(annotations).filter((e) => !!e[1]); - - for (const [metadataType] of metadataEntries) { - reportDiagnostic(program, { - code: "http-file-disallowed-metadata", - format: { propName: target.name, metadataType }, - target: target, - }); - } - } } function groupHttpOperations( diff --git a/packages/json-schema/generated-defs/TypeSpec.JsonSchema.Private.ts b/packages/json-schema/generated-defs/TypeSpec.JsonSchema.Private.ts index 2c4f69bbc62..a0613346f47 100644 --- a/packages/json-schema/generated-defs/TypeSpec.JsonSchema.Private.ts +++ b/packages/json-schema/generated-defs/TypeSpec.JsonSchema.Private.ts @@ -1,10 +1,10 @@ -import type { DecoratorContext, Model, Type } from "@typespec/compiler"; +import type { DecoratorContext, DecoratorValidatorCallback, Model, Type } from "@typespec/compiler"; export type ValidatesRawJsonDecorator = ( context: DecoratorContext, target: Model, value: Type, -) => void; +) => DecoratorValidatorCallback | void; export type TypeSpecJsonSchemaPrivateDecorators = { validatesRawJson: ValidatesRawJsonDecorator; diff --git a/packages/json-schema/generated-defs/TypeSpec.JsonSchema.ts b/packages/json-schema/generated-defs/TypeSpec.JsonSchema.ts index ee71c6f53a6..8b9b4530a40 100644 --- a/packages/json-schema/generated-defs/TypeSpec.JsonSchema.ts +++ b/packages/json-schema/generated-defs/TypeSpec.JsonSchema.ts @@ -1,5 +1,6 @@ import type { DecoratorContext, + DecoratorValidatorCallback, ModelProperty, Namespace, Numeric, @@ -21,7 +22,7 @@ export type JsonSchemaDecorator = ( context: DecoratorContext, target: Type, baseUri?: string, -) => void; +) => DecoratorValidatorCallback | void; /** * Set the base URI for any schemas emitted from types within this namespace. @@ -32,7 +33,7 @@ export type BaseUriDecorator = ( context: DecoratorContext, target: Namespace, baseUri: string, -) => void; +) => DecoratorValidatorCallback | void; /** * Specify the JSON Schema id. If this model or a parent namespace has a base URI, @@ -42,12 +43,19 @@ export type BaseUriDecorator = ( * * @param id The id of the JSON schema for this declaration. */ -export type IdDecorator = (context: DecoratorContext, target: Type, id: string) => void; +export type IdDecorator = ( + context: DecoratorContext, + target: Type, + id: string, +) => DecoratorValidatorCallback | void; /** * Specify that `oneOf` should be used instead of `anyOf` for that union. */ -export type OneOfDecorator = (context: DecoratorContext, target: Union | ModelProperty) => void; +export type OneOfDecorator = ( + context: DecoratorContext, + target: Union | ModelProperty, +) => DecoratorValidatorCallback | void; /** * Specify that the numeric type must be a multiple of some numeric value. @@ -58,7 +66,7 @@ export type MultipleOfDecorator = ( context: DecoratorContext, target: Scalar | ModelProperty, value: Numeric, -) => void; +) => DecoratorValidatorCallback | void; /** * Specify that the array must contain at least one instance of the provided type. @@ -70,7 +78,7 @@ export type ContainsDecorator = ( context: DecoratorContext, target: Type | ModelProperty, value: Type, -) => void; +) => DecoratorValidatorCallback | void; /** * Used in conjunction with the `@contains` decorator, @@ -82,7 +90,7 @@ export type MinContainsDecorator = ( context: DecoratorContext, target: Type | ModelProperty, value: number, -) => void; +) => DecoratorValidatorCallback | void; /** * Used in conjunction with the `@contains` decorator, @@ -94,7 +102,7 @@ export type MaxContainsDecorator = ( context: DecoratorContext, target: Type | ModelProperty, value: number, -) => void; +) => DecoratorValidatorCallback | void; /** * Specify that every item in the array must be unique. @@ -102,7 +110,7 @@ export type MaxContainsDecorator = ( export type UniqueItemsDecorator = ( context: DecoratorContext, target: Type | ModelProperty, -) => void; +) => DecoratorValidatorCallback | void; /** * Specify the minimum number of properties this object can have. @@ -113,7 +121,7 @@ export type MinPropertiesDecorator = ( context: DecoratorContext, target: Type | ModelProperty, value: number, -) => void; +) => DecoratorValidatorCallback | void; /** * Specify the maximum number of properties this object can have. @@ -124,7 +132,7 @@ export type MaxPropertiesDecorator = ( context: DecoratorContext, target: Type | ModelProperty, value: number, -) => void; +) => DecoratorValidatorCallback | void; /** * Specify the encoding used for the contents of a string. @@ -137,7 +145,7 @@ export type ContentEncodingDecorator = ( context: DecoratorContext, target: Scalar | ModelProperty, value: string, -) => void; +) => DecoratorValidatorCallback | void; /** * Specify that the target array must begin with the provided types. @@ -148,7 +156,7 @@ export type PrefixItemsDecorator = ( context: DecoratorContext, target: Type | ModelProperty, value: Type, -) => void; +) => DecoratorValidatorCallback | void; /** * Specify the content type of content stored in a string. @@ -159,7 +167,7 @@ export type ContentMediaTypeDecorator = ( context: DecoratorContext, target: Scalar | ModelProperty, value: string, -) => void; +) => DecoratorValidatorCallback | void; /** * Specify the schema for the contents of a string when interpreted according to the content's @@ -171,7 +179,7 @@ export type ContentSchemaDecorator = ( context: DecoratorContext, target: Scalar | ModelProperty, value: Type, -) => void; +) => DecoratorValidatorCallback | void; /** * Specify a custom property to add to the emitted schema. This is useful for adding custom keywords @@ -197,7 +205,7 @@ export type ExtensionDecorator = ( target: Type, key: string, value: Type | unknown, -) => void; +) => DecoratorValidatorCallback | void; export type TypeSpecJsonSchemaDecorators = { jsonSchema: JsonSchemaDecorator; diff --git a/packages/openapi/generated-defs/TypeSpec.OpenAPI.ts b/packages/openapi/generated-defs/TypeSpec.OpenAPI.ts index b032c2f6889..9d82d0a0a42 100644 --- a/packages/openapi/generated-defs/TypeSpec.OpenAPI.ts +++ b/packages/openapi/generated-defs/TypeSpec.OpenAPI.ts @@ -1,4 +1,11 @@ -import type { DecoratorContext, Model, Namespace, Operation, Type } from "@typespec/compiler"; +import type { + DecoratorContext, + DecoratorValidatorCallback, + Model, + Namespace, + Operation, + Type, +} from "@typespec/compiler"; export interface AdditionalInfo { readonly [key: string]: unknown; @@ -49,7 +56,7 @@ export type OperationIdDecorator = ( context: DecoratorContext, target: Operation, operationId: string, -) => void; +) => DecoratorValidatorCallback | void; /** * Attach some custom data to the OpenAPI element generated from this type. @@ -68,7 +75,7 @@ export type ExtensionDecorator = ( target: Type, key: string, value: unknown, -) => void; +) => DecoratorValidatorCallback | void; /** * Specify that this model is to be treated as the OpenAPI `default` response. @@ -82,7 +89,10 @@ export type ExtensionDecorator = ( * op listPets(): Pet[] | PetStoreResponse; * ``` */ -export type DefaultResponseDecorator = (context: DecoratorContext, target: Model) => void; +export type DefaultResponseDecorator = ( + context: DecoratorContext, + target: Model, +) => DecoratorValidatorCallback | void; /** * Specify the OpenAPI `externalDocs` property for this type. @@ -100,7 +110,7 @@ export type ExternalDocsDecorator = ( target: Type, url: string, description?: string, -) => void; +) => DecoratorValidatorCallback | void; /** * Specify OpenAPI additional information. @@ -112,7 +122,7 @@ export type InfoDecorator = ( context: DecoratorContext, target: Namespace, additionalInfo: AdditionalInfo, -) => void; +) => DecoratorValidatorCallback | void; /** * Specify OpenAPI additional information. @@ -131,7 +141,7 @@ export type TagMetadataDecorator = ( target: Namespace, name: string, tagMetadata: TagMetadata, -) => void; +) => DecoratorValidatorCallback | void; export type TypeSpecOpenAPIDecorators = { operationId: OperationIdDecorator; diff --git a/packages/openapi3/generated-defs/TypeSpec.OpenAPI.ts b/packages/openapi3/generated-defs/TypeSpec.OpenAPI.ts index 1a9d7dc6755..385683874ef 100644 --- a/packages/openapi3/generated-defs/TypeSpec.OpenAPI.ts +++ b/packages/openapi3/generated-defs/TypeSpec.OpenAPI.ts @@ -1,9 +1,18 @@ -import type { DecoratorContext, Model, ModelProperty, Union } from "@typespec/compiler"; +import type { + DecoratorContext, + DecoratorValidatorCallback, + Model, + ModelProperty, + Union, +} from "@typespec/compiler"; /** * Specify that `oneOf` should be used instead of `anyOf` for that union. */ -export type OneOfDecorator = (context: DecoratorContext, target: Union | ModelProperty) => void; +export type OneOfDecorator = ( + context: DecoratorContext, + target: Union | ModelProperty, +) => DecoratorValidatorCallback | void; /** * Specify an external reference that should be used inside of emitting this type. @@ -14,7 +23,7 @@ export type UseRefDecorator = ( context: DecoratorContext, target: Model | ModelProperty, ref: string, -) => void; +) => DecoratorValidatorCallback | void; export type TypeSpecOpenAPIDecorators = { oneOf: OneOfDecorator; diff --git a/packages/protobuf/generated-defs/TypeSpec.Protobuf.Private.ts b/packages/protobuf/generated-defs/TypeSpec.Protobuf.Private.ts index 9e0312b7668..7da472fe47c 100644 --- a/packages/protobuf/generated-defs/TypeSpec.Protobuf.Private.ts +++ b/packages/protobuf/generated-defs/TypeSpec.Protobuf.Private.ts @@ -1,13 +1,16 @@ -import type { DecoratorContext, Model, Type } from "@typespec/compiler"; +import type { DecoratorContext, DecoratorValidatorCallback, Model, Type } from "@typespec/compiler"; export type ExternRefDecorator = ( context: DecoratorContext, target: Model, path: Type, name: Type, -) => void; +) => DecoratorValidatorCallback | void; -export type _mapDecorator = (context: DecoratorContext, target: Model) => void; +export type _mapDecorator = ( + context: DecoratorContext, + target: Model, +) => DecoratorValidatorCallback | void; export type TypeSpecProtobufPrivateDecorators = { externRef: ExternRefDecorator; diff --git a/packages/protobuf/generated-defs/TypeSpec.Protobuf.ts b/packages/protobuf/generated-defs/TypeSpec.Protobuf.ts index 0e59e7977b2..adfa8411457 100644 --- a/packages/protobuf/generated-defs/TypeSpec.Protobuf.ts +++ b/packages/protobuf/generated-defs/TypeSpec.Protobuf.ts @@ -1,5 +1,6 @@ import type { DecoratorContext, + DecoratorValidatorCallback, Interface, ModelProperty, Namespace, @@ -17,7 +18,10 @@ import type { * * This decorator will force the emitter to check and emit a model. */ -export type MessageDecorator = (context: DecoratorContext, target: Type) => void; +export type MessageDecorator = ( + context: DecoratorContext, + target: Type, +) => DecoratorValidatorCallback | void; /** * Defines the field index of a model property for conversion to a Protobuf @@ -52,7 +56,7 @@ export type FieldDecorator = ( context: DecoratorContext, target: ModelProperty, index: number, -) => void; +) => DecoratorValidatorCallback | void; /** * Reserve a field index, range, or name. If a field definition collides with a reservation, the emitter will produce @@ -90,13 +94,16 @@ export type ReserveDecorator = ( context: DecoratorContext, target: Type, ...reservations: (string | unknown | number)[] -) => void; +) => DecoratorValidatorCallback | void; /** * Declares that a TypeSpec interface constitutes a Protobuf service. The contents of the interface will be converted to * a `service` declaration in the resulting Protobuf file. */ -export type ServiceDecorator = (context: DecoratorContext, target: Interface) => void; +export type ServiceDecorator = ( + context: DecoratorContext, + target: Interface, +) => DecoratorValidatorCallback | void; /** * Declares that a TypeSpec namespace constitutes a Protobuf package. The contents of the namespace will be emitted to a @@ -108,7 +115,7 @@ export type PackageDecorator = ( context: DecoratorContext, target: Namespace, details?: Type, -) => void; +) => DecoratorValidatorCallback | void; /** * Set the streaming mode of an operation. See [StreamMode](./data-types#TypeSpec.Protobuf.StreamMode) for more information. @@ -125,7 +132,11 @@ export type PackageDecorator = ( * op connectToMessageService(...Message): Message; * ``` */ -export type StreamDecorator = (context: DecoratorContext, target: Operation, mode: Type) => void; +export type StreamDecorator = ( + context: DecoratorContext, + target: Operation, + mode: Type, +) => DecoratorValidatorCallback | void; export type TypeSpecProtobufDecorators = { message: MessageDecorator; diff --git a/packages/rest/generated-defs/TypeSpec.Rest.Private.ts b/packages/rest/generated-defs/TypeSpec.Rest.Private.ts index 0c6a8327b19..5e4185d2b27 100644 --- a/packages/rest/generated-defs/TypeSpec.Rest.Private.ts +++ b/packages/rest/generated-defs/TypeSpec.Rest.Private.ts @@ -1,5 +1,6 @@ import type { DecoratorContext, + DecoratorValidatorCallback, Model, ModelProperty, Operation, @@ -11,31 +12,31 @@ export type ResourceLocationDecorator = ( context: DecoratorContext, target: Scalar, resourceType: Model, -) => void; +) => DecoratorValidatorCallback | void; export type ValidateHasKeyDecorator = ( context: DecoratorContext, target: Type, value: Type, -) => void; +) => DecoratorValidatorCallback | void; export type ValidateIsErrorDecorator = ( context: DecoratorContext, target: Type, value: Type, -) => void; +) => DecoratorValidatorCallback | void; export type ActionSegmentDecorator = ( context: DecoratorContext, target: Operation, value: string, -) => void; +) => DecoratorValidatorCallback | void; export type ResourceTypeForKeyParamDecorator = ( context: DecoratorContext, entity: ModelProperty, resourceType: Model, -) => void; +) => DecoratorValidatorCallback | void; export type TypeSpecRestPrivateDecorators = { resourceLocation: ResourceLocationDecorator; diff --git a/packages/rest/generated-defs/TypeSpec.Rest.ts b/packages/rest/generated-defs/TypeSpec.Rest.ts index 90f68ad386e..2d2d987bad7 100644 --- a/packages/rest/generated-defs/TypeSpec.Rest.ts +++ b/packages/rest/generated-defs/TypeSpec.Rest.ts @@ -1,5 +1,6 @@ import type { DecoratorContext, + DecoratorValidatorCallback, Interface, Model, ModelProperty, @@ -17,7 +18,10 @@ import type { * } * ``` */ -export type AutoRouteDecorator = (context: DecoratorContext, target: Interface | Operation) => void; +export type AutoRouteDecorator = ( + context: DecoratorContext, + target: Interface | Operation, +) => DecoratorValidatorCallback | void; /** * Defines the preceding path segment for a @@ -38,7 +42,7 @@ export type SegmentDecorator = ( context: DecoratorContext, target: Model | ModelProperty | Operation, name: string, -) => void; +) => DecoratorValidatorCallback | void; /** * Returns the URL segment of a given model if it has `@segment` and `@key` decorator. @@ -49,7 +53,7 @@ export type SegmentOfDecorator = ( context: DecoratorContext, target: Operation, type: Model, -) => void; +) => DecoratorValidatorCallback | void; /** * Defines the separator string that is inserted before the action name in auto-generated routes for actions. @@ -60,7 +64,7 @@ export type ActionSeparatorDecorator = ( context: DecoratorContext, target: Model | ModelProperty | Operation, seperator: "/" | ":" | "/:", -) => void; +) => DecoratorValidatorCallback | void; /** * Mark this model as a resource type with a name. @@ -71,7 +75,7 @@ export type ResourceDecorator = ( context: DecoratorContext, target: Model, collectionName: string, -) => void; +) => DecoratorValidatorCallback | void; /** * Mark model as a child of the given parent resource. @@ -82,7 +86,7 @@ export type ParentResourceDecorator = ( context: DecoratorContext, target: Model, parent: Model, -) => void; +) => DecoratorValidatorCallback | void; /** * Specify that this is a Read operation for a given resource. @@ -96,7 +100,7 @@ export type ReadsResourceDecorator = ( context: DecoratorContext, target: Operation, resourceType: Model, -) => void; +) => DecoratorValidatorCallback | void; /** * Specify that this is a Create operation for a given resource. @@ -110,7 +114,7 @@ export type CreatesResourceDecorator = ( context: DecoratorContext, target: Operation, resourceType: Model, -) => void; +) => DecoratorValidatorCallback | void; /** * Specify that this is a CreateOrReplace operation for a given resource. @@ -124,7 +128,7 @@ export type CreatesOrReplacesResourceDecorator = ( context: DecoratorContext, target: Operation, resourceType: Model, -) => void; +) => DecoratorValidatorCallback | void; /** * Specify that this is a CreatesOrUpdate operation for a given resource. @@ -138,7 +142,7 @@ export type CreatesOrUpdatesResourceDecorator = ( context: DecoratorContext, target: Operation, resourceType: Model, -) => void; +) => DecoratorValidatorCallback | void; /** * Specify that this is a Update operation for a given resource. @@ -152,7 +156,7 @@ export type UpdatesResourceDecorator = ( context: DecoratorContext, target: Operation, resourceType: Model, -) => void; +) => DecoratorValidatorCallback | void; /** * Specify that this is a Delete operation for a given resource. @@ -166,7 +170,7 @@ export type DeletesResourceDecorator = ( context: DecoratorContext, target: Operation, resourceType: Model, -) => void; +) => DecoratorValidatorCallback | void; /** * Specify that this is a List operation for a given resource. @@ -180,14 +184,18 @@ export type ListsResourceDecorator = ( context: DecoratorContext, target: Operation, resourceType: Model, -) => void; +) => DecoratorValidatorCallback | void; /** * Specify this operation is an action. (Scoped to a resource item /pets/{petId}/my-action) * * @param name Name of the action. If not specified, the name of the operation will be used. */ -export type ActionDecorator = (context: DecoratorContext, target: Operation, name?: string) => void; +export type ActionDecorator = ( + context: DecoratorContext, + target: Operation, + name?: string, +) => DecoratorValidatorCallback | void; /** * Specify this operation is a collection action. (Scopped to a resource, /pets/my-action) @@ -203,7 +211,7 @@ export type CollectionActionDecorator = ( target: Operation, resourceType: Model, name?: string, -) => void; +) => DecoratorValidatorCallback | void; /** * Copy the resource key parameters on the model @@ -214,7 +222,7 @@ export type CopyResourceKeyParametersDecorator = ( context: DecoratorContext, target: Model, filter?: string, -) => void; +) => DecoratorValidatorCallback | void; export type TypeSpecRestDecorators = { autoRoute: AutoRouteDecorator; diff --git a/packages/spector/generated-defs/TypeSpec.Spector.ts b/packages/spector/generated-defs/TypeSpec.Spector.ts index 6003f5b7864..44a7f4fbf96 100644 --- a/packages/spector/generated-defs/TypeSpec.Spector.ts +++ b/packages/spector/generated-defs/TypeSpec.Spector.ts @@ -1,5 +1,6 @@ import type { DecoratorContext, + DecoratorValidatorCallback, Interface, Model, Namespace, @@ -15,7 +16,7 @@ export type ScenarioServiceDecorator = ( target: Namespace, route: string, options?: Type, -) => void; +) => DecoratorValidatorCallback | void; /** * Mark an operation, interface or namespace as a scenario. All containing operations will be part of the same scenario. @@ -24,7 +25,7 @@ export type ScenarioDecorator = ( context: DecoratorContext, target: Namespace | Interface | Operation, name?: string, -) => void; +) => DecoratorValidatorCallback | void; /** * Specify documentation on how to implement this scenario. @@ -37,7 +38,7 @@ export type ScenarioDocDecorator = ( target: Namespace | Interface | Operation, doc: string, formatArgs?: Model, -) => void; +) => DecoratorValidatorCallback | void; export type TypeSpecSpectorDecorators = { scenarioService: ScenarioServiceDecorator; diff --git a/packages/sse/generated-defs/TypeSpec.SSE.ts b/packages/sse/generated-defs/TypeSpec.SSE.ts index 387c44b7bda..f6ed4ad4a3d 100644 --- a/packages/sse/generated-defs/TypeSpec.SSE.ts +++ b/packages/sse/generated-defs/TypeSpec.SSE.ts @@ -1,10 +1,17 @@ -import type { DecoratorContext, UnionVariant } from "@typespec/compiler"; +import type { + DecoratorContext, + DecoratorValidatorCallback, + UnionVariant, +} from "@typespec/compiler"; /** * Indicates that the presence of this event is a terminal event, * and the client should disconnect from the server. */ -export type TerminalEventDecorator = (context: DecoratorContext, target: UnionVariant) => void; +export type TerminalEventDecorator = ( + context: DecoratorContext, + target: UnionVariant, +) => DecoratorValidatorCallback | void; export type TypeSpecSSEDecorators = { terminalEvent: TerminalEventDecorator; diff --git a/packages/streams/generated-defs/TypeSpec.Streams.ts b/packages/streams/generated-defs/TypeSpec.Streams.ts index 92e5538c695..717dce87e1e 100644 --- a/packages/streams/generated-defs/TypeSpec.Streams.ts +++ b/packages/streams/generated-defs/TypeSpec.Streams.ts @@ -1,4 +1,4 @@ -import type { DecoratorContext, Model, Type } from "@typespec/compiler"; +import type { DecoratorContext, DecoratorValidatorCallback, Model, Type } from "@typespec/compiler"; /** * Specify that a model represents a stream protocol type whose data is described @@ -18,7 +18,11 @@ import type { DecoratorContext, Model, Type } from "@typespec/compiler"; * } * ``` */ -export type StreamOfDecorator = (context: DecoratorContext, target: Model, type: Type) => void; +export type StreamOfDecorator = ( + context: DecoratorContext, + target: Model, + type: Type, +) => DecoratorValidatorCallback | void; export type TypeSpecStreamsDecorators = { streamOf: StreamOfDecorator; diff --git a/packages/tspd/src/gen-extern-signatures/components/decorator-signature-type.tsx b/packages/tspd/src/gen-extern-signatures/components/decorator-signature-type.tsx index 9a9211dbf80..3ca06304371 100644 --- a/packages/tspd/src/gen-extern-signatures/components/decorator-signature-type.tsx +++ b/packages/tspd/src/gen-extern-signatures/components/decorator-signature-type.tsx @@ -1,4 +1,4 @@ -import { For, join, List, Refkey, refkey } from "@alloy-js/core"; +import { code, For, join, List, Refkey, refkey } from "@alloy-js/core"; import * as ts from "@alloy-js/typescript"; import { getSourceLocation, @@ -58,7 +58,10 @@ export function DecoratorSignatureType(props: Readonly) name={props.signature.typeName} doc={getDocComment(props.signature.decorator)} > - + ); } diff --git a/packages/tspd/src/gen-extern-signatures/external-packages/compiler.ts b/packages/tspd/src/gen-extern-signatures/external-packages/compiler.ts index b734fb02f17..bba2cd1ca25 100644 --- a/packages/tspd/src/gen-extern-signatures/external-packages/compiler.ts +++ b/packages/tspd/src/gen-extern-signatures/external-packages/compiler.ts @@ -21,6 +21,7 @@ export const typespecCompiler = createPackage({ "EnumValue", "Numeric", "ScalarValue", + "DecoratorValidatorCallback", ], }, }, diff --git a/packages/tspd/test/gen-extern-signature/decorators-signatures.test.ts b/packages/tspd/test/gen-extern-signature/decorators-signatures.test.ts index 4f0be902852..42a32053438 100644 --- a/packages/tspd/test/gen-extern-signature/decorators-signatures.test.ts +++ b/packages/tspd/test/gen-extern-signature/decorators-signatures.test.ts @@ -44,7 +44,7 @@ it("generate simple decorator with no parameters", async () => { expected: ` ${importLine(["Type"])} -export type SimpleDecorator = (context: DecoratorContext, target: Type) => void; +export type SimpleDecorator = (context: DecoratorContext, target: Type) => DecoratorValidatorCallback | void; export type Decorators = { simple: SimpleDecorator; @@ -72,7 +72,7 @@ describe("generate target type", () => { expected: ` ${importLine([expected])} -export type SimpleDecorator = (context: DecoratorContext, target: ${expected}) => void; +export type SimpleDecorator = (context: DecoratorContext, target: ${expected}) => DecoratorValidatorCallback | void; export type Decorators = { simple: SimpleDecorator; @@ -92,7 +92,7 @@ export type Decorators = { expected: ` ${importLine([...expected])} -export type SimpleDecorator = (context: DecoratorContext, target: ${expected.join(" | ")}) => void; +export type SimpleDecorator = (context: DecoratorContext, target: ${expected.join(" | ")}) => DecoratorValidatorCallback | void; export type Decorators = { simple: SimpleDecorator; @@ -113,7 +113,7 @@ export type Decorators = { expected: ` ${importLine([expected])} -export type SimpleDecorator = (context: DecoratorContext, target: ${expected}) => void; +export type SimpleDecorator = (context: DecoratorContext, target: ${expected}) => DecoratorValidatorCallback | void; export type Decorators = { simple: SimpleDecorator; @@ -129,7 +129,7 @@ export type Decorators = { expected: ` ${importLine(["Model", "Scalar"])} -export type SimpleDecorator = (context: DecoratorContext, target: Scalar | Model) => void; +export type SimpleDecorator = (context: DecoratorContext, target: Scalar | Model) => DecoratorValidatorCallback | void; export type Decorators = { simple: SimpleDecorator; @@ -158,7 +158,7 @@ describe("generate parameter type", () => { expected: ` ${importLine(["Type", expected])} -export type SimpleDecorator = (context: DecoratorContext, target: Type, arg1: ${expected}) => void; +export type SimpleDecorator = (context: DecoratorContext, target: Type, arg1: ${expected}) => DecoratorValidatorCallback | void; export type Decorators = { simple: SimpleDecorator; @@ -178,7 +178,7 @@ export type Decorators = { expected: ` ${importLine(["Type", ...expected])} -export type SimpleDecorator = (context: DecoratorContext, target: Type, arg1: ${expected.join(" | ")}) => void; +export type SimpleDecorator = (context: DecoratorContext, target: Type, arg1: ${expected.join(" | ")}) => DecoratorValidatorCallback | void; export type Decorators = { simple: SimpleDecorator; @@ -210,7 +210,7 @@ export type Decorators = { expected: ` ${importLine(["Type", ...(expected === "Numeric" ? ["Numeric"] : [])])} -export type SimpleDecorator = (context: DecoratorContext, target: Type, arg1: ${expected}) => void; +export type SimpleDecorator = (context: DecoratorContext, target: Type, arg1: ${expected}) => DecoratorValidatorCallback | void; export type Decorators = { simple: SimpleDecorator; @@ -232,7 +232,7 @@ export type SimpleDecorator = ( readonly [key: string]: number; readonly other: string; }, -) => void; +) => DecoratorValidatorCallback | void; export type Decorators = { simple: SimpleDecorator; @@ -254,7 +254,7 @@ export type SimpleDecorator = ( readonly name: string; readonly age?: number; }, -) => void; +) => DecoratorValidatorCallback | void; export type Decorators = { simple: SimpleDecorator; @@ -269,7 +269,7 @@ export type Decorators = { expected: ` ${importLine(["ScalarValue", "Type"])} -export type SimpleDecorator = (context: DecoratorContext, target: Type, arg1: number | string | ScalarValue) => void; +export type SimpleDecorator = (context: DecoratorContext, target: Type, arg1: number | string | ScalarValue) => DecoratorValidatorCallback | void; export type Decorators = { simple: SimpleDecorator; @@ -291,7 +291,7 @@ export interface Info { readonly age?: number; } -export type SimpleDecorator = (context: DecoratorContext, target: Type, arg1: Info) => void; +export type SimpleDecorator = (context: DecoratorContext, target: Type, arg1: Info) => DecoratorValidatorCallback | void; export type Decorators = { simple: SimpleDecorator; @@ -311,7 +311,7 @@ export type Decorators = { expected: ` ${importLine(["Type", expected])} -export type SimpleDecorator = (context: DecoratorContext, target: Type, arg1: ${expected}) => void; +export type SimpleDecorator = (context: DecoratorContext, target: Type, arg1: ${expected}) => DecoratorValidatorCallback | void; export type Decorators = { simple: SimpleDecorator; @@ -334,7 +334,7 @@ ${importLine(["Type", "Model"])} /** * Some doc comment */ -export type SimpleDecorator = (context: DecoratorContext, target: Type, ...args: Model[]) => void; +export type SimpleDecorator = (context: DecoratorContext, target: Type, ...args: Model[]) => DecoratorValidatorCallback | void; export type Decorators = { simple: SimpleDecorator; @@ -362,7 +362,7 @@ export type Decorators = { expected: ` ${importLine(["Type", ...(expected === "Numeric[]" ? ["Numeric"] : [])])} -export type SimpleDecorator = (context: DecoratorContext, target: Type, ...args: ${expected}) => void; +export type SimpleDecorator = (context: DecoratorContext, target: Type, ...args: ${expected}) => DecoratorValidatorCallback | void; export type Decorators = { simple: SimpleDecorator; @@ -385,7 +385,7 @@ ${importLine(["Type"])} /** * Some doc comment */ -export type SimpleDecorator = (context: DecoratorContext, target: Type) => void; +export type SimpleDecorator = (context: DecoratorContext, target: Type) => DecoratorValidatorCallback | void; export type Decorators = { simple: SimpleDecorator; @@ -413,7 +413,7 @@ ${importLine(["Type"])} * @param arg1 This is the first argument * @param arg2 This is the second argument */ -export type SimpleDecorator = (context: DecoratorContext, target: Type, arg1: Type, arg2: Type) => void; +export type SimpleDecorator = (context: DecoratorContext, target: Type, arg1: Type, arg2: Type) => DecoratorValidatorCallback | void; export type Decorators = { simple: SimpleDecorator; @@ -424,6 +424,6 @@ export type Decorators = { }); function importLine(imports: string[]) { - const all = new Set(["DecoratorContext", ...imports]); + const all = new Set(["DecoratorContext", "DecoratorValidatorCallback", ...imports]); return `import type { ${[...all].sort().join(", ")} } from "@typespec/compiler";`; } diff --git a/packages/versioning/generated-defs/TypeSpec.Versioning.ts b/packages/versioning/generated-defs/TypeSpec.Versioning.ts index 03e4b22cf1f..e74c13d1e62 100644 --- a/packages/versioning/generated-defs/TypeSpec.Versioning.ts +++ b/packages/versioning/generated-defs/TypeSpec.Versioning.ts @@ -1,5 +1,6 @@ import type { DecoratorContext, + DecoratorValidatorCallback, Enum, EnumMember, Interface, @@ -32,7 +33,7 @@ export type VersionedDecorator = ( context: DecoratorContext, target: Namespace, versions: Enum, -) => void; +) => DecoratorValidatorCallback | void; /** * Identifies that a namespace or a given versioning enum member relies upon a versioned package. @@ -63,7 +64,7 @@ export type UseDependencyDecorator = ( context: DecoratorContext, target: EnumMember | Namespace, ...versionRecords: EnumMember[] -) => void; +) => DecoratorValidatorCallback | void; /** * Identifies when the target was added. @@ -98,7 +99,7 @@ export type AddedDecorator = ( | Scalar | Interface, version: EnumMember, -) => void; +) => DecoratorValidatorCallback | void; /** * Identifies when the target was removed. @@ -133,7 +134,7 @@ export type RemovedDecorator = ( | Scalar | Interface, version: EnumMember, -) => void; +) => DecoratorValidatorCallback | void; /** * Identifies when the target has been renamed. @@ -160,7 +161,7 @@ export type RenamedFromDecorator = ( | Interface, version: EnumMember, oldName: string, -) => void; +) => DecoratorValidatorCallback | void; /** * Identifies when a target was made optional. @@ -179,7 +180,7 @@ export type MadeOptionalDecorator = ( context: DecoratorContext, target: ModelProperty, version: EnumMember, -) => void; +) => DecoratorValidatorCallback | void; /** * Identifies when a target was made required. @@ -198,7 +199,7 @@ export type MadeRequiredDecorator = ( context: DecoratorContext, target: ModelProperty, version: EnumMember, -) => void; +) => DecoratorValidatorCallback | void; /** * Identifies when the target type changed. @@ -211,7 +212,7 @@ export type TypeChangedFromDecorator = ( target: ModelProperty, version: EnumMember, oldType: Type, -) => void; +) => DecoratorValidatorCallback | void; /** * Identifies when the target type changed. @@ -224,7 +225,7 @@ export type ReturnTypeChangedFromDecorator = ( target: Operation, version: EnumMember, oldType: Type, -) => void; +) => DecoratorValidatorCallback | void; export type TypeSpecVersioningDecorators = { versioned: VersionedDecorator; diff --git a/packages/xml/generated-defs/TypeSpec.Xml.ts b/packages/xml/generated-defs/TypeSpec.Xml.ts index 99ebfb9c9e5..4c2a5d5167d 100644 --- a/packages/xml/generated-defs/TypeSpec.Xml.ts +++ b/packages/xml/generated-defs/TypeSpec.Xml.ts @@ -1,4 +1,10 @@ -import type { DecoratorContext, Enum, ModelProperty, Type } from "@typespec/compiler"; +import type { + DecoratorContext, + DecoratorValidatorCallback, + Enum, + ModelProperty, + Type, +} from "@typespec/compiler"; /** * Provide the name of the XML element or attribute. This means the same thing as @@ -23,7 +29,11 @@ import type { DecoratorContext, Enum, ModelProperty, Type } from "@typespec/comp * * ``` */ -export type NameDecorator = (context: DecoratorContext, target: Type, name: string) => void; +export type NameDecorator = ( + context: DecoratorContext, + target: Type, + name: string, +) => DecoratorValidatorCallback | void; /** * Specify that the target property should be encoded as an XML attribute instead of node. @@ -54,7 +64,10 @@ export type NameDecorator = (context: DecoratorContext, target: Type, name: stri * * ``` */ -export type AttributeDecorator = (context: DecoratorContext, target: ModelProperty) => void; +export type AttributeDecorator = ( + context: DecoratorContext, + target: ModelProperty, +) => DecoratorValidatorCallback | void; /** * Specify that the target property shouldn't create a wrapper node. This can be used to flatten list nodes into the model node or to include raw text in the model node. @@ -121,7 +134,10 @@ export type AttributeDecorator = (context: DecoratorContext, target: ModelProper * * ``` */ -export type UnwrappedDecorator = (context: DecoratorContext, target: ModelProperty) => void; +export type UnwrappedDecorator = ( + context: DecoratorContext, + target: ModelProperty, +) => DecoratorValidatorCallback | void; /** * Specify the XML namespace for this element. It can be used in 2 different ways: @@ -164,12 +180,15 @@ export type NsDecorator = ( target: Type, ns: Type, prefix?: string, -) => void; +) => DecoratorValidatorCallback | void; /** * Mark an enum as declaring XML namespaces. See `@ns` */ -export type NsDeclarationsDecorator = (context: DecoratorContext, target: Enum) => void; +export type NsDeclarationsDecorator = ( + context: DecoratorContext, + target: Enum, +) => DecoratorValidatorCallback | void; export type TypeSpecXmlDecorators = { name: NameDecorator;