diff --git a/.chronus/changes/getCrossLanguagePackageId-2024-2-1-14-13-36.md b/.chronus/changes/getCrossLanguagePackageId-2024-2-1-14-13-36.md new file mode 100644 index 0000000000..4c846768fb --- /dev/null +++ b/.chronus/changes/getCrossLanguagePackageId-2024-2-1-14-13-36.md @@ -0,0 +1,7 @@ +--- +changeKind: feature +packages: + - "@azure-tools/typespec-client-generator-core" +--- + +add helper function getCrossLanguagePackageId. getCrossLanguagePackageId returns a package id that is consistent across languages, allowing emitters to identify that they are generating from the same service tsp \ No newline at end of file diff --git a/packages/typespec-client-generator-core/src/lib.ts b/packages/typespec-client-generator-core/src/lib.ts index 40145e062d..8ada1f4a8c 100644 --- a/packages/typespec-client-generator-core/src/lib.ts +++ b/packages/typespec-client-generator-core/src/lib.ts @@ -105,6 +105,12 @@ export const $lib = createTypeSpecLibrary({ default: paramMessage`Unsupported kind ${"kind"}`, }, }, + "multiple-services": { + severity: "warning", + messages: { + default: paramMessage`Multiple services found in definition. Only one service is supported, so we will choose the first one ${"service"}`, + }, + }, }, }); diff --git a/packages/typespec-client-generator-core/src/public-utils.ts b/packages/typespec-client-generator-core/src/public-utils.ts index 29f72b390d..6d90267815 100644 --- a/packages/typespec-client-generator-core/src/public-utils.ts +++ b/packages/typespec-client-generator-core/src/public-utils.ts @@ -1,4 +1,6 @@ import { + createDiagnosticCollector, + Diagnostic, getDeprecationDetails, getDoc, getEffectiveModelType, @@ -36,7 +38,7 @@ import { listOperationsInOperationGroup, } from "./decorators.js"; import { parseEmitterName, TCGCContext } from "./internal-utils.js"; -import { reportDiagnostic } from "./lib.js"; +import { createDiagnostic, reportDiagnostic } from "./lib.js"; /** * Return the default api version for a versioned service. Will return undefined if one does not exist @@ -284,6 +286,28 @@ export function getCrossLanguageDefinitionId(type: { return retval; } +/** + * Helper function return the cross langauge package id for a package + */ +export function getCrossLanguagePackageId(context: TCGCContext): [string, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + const services = listServices(context.program); + if (services.length === 0) return diagnostics.wrap(""); + const serviceNamespace = getNamespaceFullName(services[0].type); + if (services.length > 1) { + diagnostics.add( + createDiagnostic({ + code: "multiple-services", + target: services[0].type, + format: { + service: serviceNamespace, + }, + }) + ); + } + return diagnostics.wrap(serviceNamespace); +} + /** * Create a name for anonymous model * @param context diff --git a/packages/typespec-client-generator-core/test/decorators.test.ts b/packages/typespec-client-generator-core/test/decorators.test.ts index 5f708d6911..238a7baf89 100644 --- a/packages/typespec-client-generator-core/test/decorators.test.ts +++ b/packages/typespec-client-generator-core/test/decorators.test.ts @@ -1,4 +1,12 @@ -import { Enum, Interface, Model, Namespace, Operation, UsageFlags } from "@typespec/compiler"; +import { + Enum, + Interface, + Model, + Namespace, + Operation, + UsageFlags, + ignoreDiagnostics, +} from "@typespec/compiler"; import { expectDiagnostics } from "@typespec/compiler/testing"; import { deepStrictEqual, ok, strictEqual } from "assert"; import { beforeEach, describe, it } from "vitest"; @@ -14,7 +22,7 @@ import { shouldGenerateProtocol, } from "../src/decorators.js"; import { SdkOperationGroup } from "../src/interfaces.js"; -import { getCrossLanguageDefinitionId } from "../src/public-utils.js"; +import { getCrossLanguageDefinitionId, getCrossLanguagePackageId } from "../src/public-utils.js"; import { getAllModels } from "../src/types.js"; import { SdkTestRunner, createSdkContextTestHelper, createSdkTestRunner } from "./test-host.js"; @@ -307,6 +315,24 @@ describe("typespec-client-generator-core: decorators", () => { strictEqual(getCrossLanguageDefinitionId(one), "MyClient.SubNamespace.Widgets.one"); }); + it("crossLanguagePackageId", async () => { + await runner.compile(` + @client({name: "MyPackageClient"}) + @service({}) + namespace My.Package.Namespace; + + namespace SubNamespace { + interface Widgets { + @test op one(): void; + } + } + `); + strictEqual( + ignoreDiagnostics(getCrossLanguagePackageId(runner.context)), + "My.Package.Namespace" + ); + }); + it("@operationGroup with scope", async () => { const testCode = ` @service({