From 9053cad4a8f96c040b73d2537af7a17321aa9d59 Mon Sep 17 00:00:00 2001 From: Kyle Zhang Date: Thu, 12 Feb 2026 14:42:05 +0800 Subject: [PATCH 1/4] feat: enhance package and README file updates with client context paths --- .../src/metadata/buildPackageFile.ts | 21 +++++++++++-- .../src/metadata/buildReadmeFile.ts | 30 +++++++++++++++---- packages/typespec-ts/src/index.ts | 19 +++++++++++- 3 files changed, 61 insertions(+), 9 deletions(-) diff --git a/packages/rlc-common/src/metadata/buildPackageFile.ts b/packages/rlc-common/src/metadata/buildPackageFile.ts index fe24339f22..8e9d6f7f4e 100644 --- a/packages/rlc-common/src/metadata/buildPackageFile.ts +++ b/packages/rlc-common/src/metadata/buildPackageFile.ts @@ -91,15 +91,17 @@ export function buildPackageFile( export function updatePackageFile( model: RLCModel, existingFilePathOrContent: string | Record, - { exports }: PackageFileOptions = {} + { exports, clientContextPaths }: PackageFileOptions = {} ) { const hasLro = hasPollingOperations(model); const isAzure = isAzurePackage(model); const needsLroUpdate = isAzure && hasLro; const needsExportsUpdate = exports; + const needsConstantPathsUpdate = + clientContextPaths && clientContextPaths.length > 0; // Early return if nothing needs to be updated - if (!needsLroUpdate && !needsExportsUpdate) { + if (!needsLroUpdate && !needsExportsUpdate && !needsConstantPathsUpdate) { return; } @@ -136,6 +138,21 @@ export function updatePackageFile( }; } + // Update constantPaths metadata for Azure packages + if (needsConstantPathsUpdate && isAzure && packageInfo["//metadata"]) { + const metadata = packageInfo["//metadata"]; + // Filter out existing userAgentInfo entries + const nonUserAgentPaths = (metadata.constantPaths || []).filter( + (item: any) => item.prefix !== "userAgentInfo" + ); + // Add new userAgentInfo entries from clientContextPaths + const newUserAgentPaths = clientContextPaths!.map((path) => ({ + path: path, + prefix: "userAgentInfo" + })); + metadata.constantPaths = [...nonUserAgentPaths, ...newUserAgentPaths]; + } + return { path: "package.json", content: JSON.stringify(packageInfo, null, 2) diff --git a/packages/rlc-common/src/metadata/buildReadmeFile.ts b/packages/rlc-common/src/metadata/buildReadmeFile.ts index f0dfb5c506..e0ffcf1c49 100644 --- a/packages/rlc-common/src/metadata/buildReadmeFile.ts +++ b/packages/rlc-common/src/metadata/buildReadmeFile.ts @@ -370,20 +370,38 @@ export function updateReadmeFile( try { const existingContent = readFileSync(existingReadmeFilePath, "utf8"); const metadata = createMetadata(model) ?? {}; + const newClientName = getClientName(model); + let updatedContent = existingContent; + + // Update API reference link const newApiRefLink = hbs .compile(apiReferenceTemplate, { noEscape: true })(metadata) .trim(); - if (!newApiRefLink) { - return { path: "README.md", content: existingContent }; + if (newApiRefLink) { + const apiRefRegex = + /^- \[API reference documentation\]\(https:\/\/learn\.microsoft\.com\/javascript\/api\/[^)]+\)$/m; + updatedContent = updatedContent.replace(apiRefRegex, (match) => + match ? newApiRefLink : match + ); } - const apiRefRegex = - /^- \[API reference documentation\]\(https:\/\/learn\.microsoft\.com\/javascript\/api\/[^)]+\)$/m; - const updatedContent = existingContent.replace(apiRefRegex, (match) => - match ? newApiRefLink : match + // Extract old client name from existing README import statements + const importMatch = existingContent.match( + /import\s*\{\s*([A-Za-z0-9_]+)\s*\}\s*from\s*["'][^"']*["']/ ); + const oldClientName = importMatch?.[1]; + + // Only update client name if we found an old name and it's different from the new one + if (oldClientName && newClientName && oldClientName !== newClientName) { + // Create a regex that matches the old client name as a whole word + const oldClientNameRegex = new RegExp(`\\b${oldClientName}\\b`, "g"); + updatedContent = updatedContent.replace( + oldClientNameRegex, + newClientName + ); + } return { path: "README.md", content: updatedContent }; } catch { diff --git a/packages/typespec-ts/src/index.ts b/packages/typespec-ts/src/index.ts index 145c5dcb9c..53f3ce8b6d 100644 --- a/packages/typespec-ts/src/index.ts +++ b/packages/typespec-ts/src/index.ts @@ -552,7 +552,11 @@ export async function $onEmit(context: EmitContext) { let modularPackageInfo = {}; if (option.isModularLibrary) { modularPackageInfo = { - exports: getModuleExports(context, modularEmitterOptions) + exports: getModuleExports(context, modularEmitterOptions), + clientContextPaths: getRelativeContextPaths( + context, + modularEmitterOptions + ) }; } await emitContentByBuilder( @@ -572,6 +576,19 @@ export async function $onEmit(context: EmitContext) { dpgContext.generationPathDetail?.metadataDir ); } + + // Regenerate snippets.spec.ts if test folder exists + if (option.generateTest && isAzureFlavor && hasTestFolder) { + for (const subClient of dpgContext.sdkPackage.clients) { + await emitContentByBuilder( + program, + (model) => + buildSnippets(model, subClient.name, option.azureSdkForJs), + rlcClient, + dpgContext.generationPathDetail?.metadataDir + ); + } + } } if (isAzureFlavor) { await emitContentByBuilder( From 7785d9762b75a810b4e976ccf53298823277ec1b Mon Sep 17 00:00:00 2001 From: Kyle Zhang Date: Fri, 13 Feb 2026 11:28:35 +0800 Subject: [PATCH 2/4] add cases for constantPaths in package.json --- .../test/integration/packageJson.spec.ts | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/packages/rlc-common/test/integration/packageJson.spec.ts b/packages/rlc-common/test/integration/packageJson.spec.ts index b18bd0baed..6369d12e26 100644 --- a/packages/rlc-common/test/integration/packageJson.spec.ts +++ b/packages/rlc-common/test/integration/packageJson.spec.ts @@ -815,6 +815,114 @@ describe("Package file generation", () => { ); expect(packageFile).to.not.have.property("tshy"); }); + + it("should update constantPaths when clientContextPaths option is provided for Azure packages", () => { + const model = createMockModel({ + moduleKind: "esm", + flavor: "azure", + isMonorepo: true, + hasLro: false + }); + + const initialPackageInfo = { + name: "@azure/test-package", + version: "1.0.0", + dependencies: { + "@azure/core-client": "^1.0.0" + }, + "//metadata": { + constantPaths: [ + { path: "src/old-path.ts", prefix: "userAgentInfo" }, + { path: "src/other-file.ts", prefix: "packageDetails" } + ] + } + }; + + const packageFileContent = updatePackageFile(model, initialPackageInfo, { + clientContextPaths: [ + "src/api/newContext.ts", + "src/api/anotherContext.ts" + ] + }); + const packageFile = JSON.parse(packageFileContent?.content ?? "{}"); + + expect(packageFile["//metadata"]).to.have.property("constantPaths"); + const constantPaths = packageFile["//metadata"]["constantPaths"]; + + // Should keep non-userAgentInfo entries + expect(constantPaths).to.deep.include({ + path: "src/other-file.ts", + prefix: "packageDetails" + }); + + // Should replace old userAgentInfo entries with new ones + expect(constantPaths).to.deep.include({ + path: "src/api/newContext.ts", + prefix: "userAgentInfo" + }); + expect(constantPaths).to.deep.include({ + path: "src/api/anotherContext.ts", + prefix: "userAgentInfo" + }); + + // Should not include old userAgentInfo entry + expect(constantPaths).to.not.deep.include({ + path: "src/old-path.ts", + prefix: "userAgentInfo" + }); + }); + + it("should not update constantPaths when clientContextPaths is empty", () => { + const model = createMockModel({ + moduleKind: "esm", + flavor: "azure", + isMonorepo: true, + hasLro: false + }); + + const initialPackageInfo = { + name: "@azure/test-package", + version: "1.0.0", + "//metadata": { + constantPaths: [{ path: "src/old-path.ts", prefix: "userAgentInfo" }] + } + }; + + const packageFileContent = updatePackageFile(model, initialPackageInfo, { + clientContextPaths: [] + }); + + // Should return undefined when nothing needs to be updated + expect(packageFileContent).to.be.undefined; + }); + + it("should not update constantPaths for non-Azure packages", () => { + const model = createMockModel({ + moduleKind: "esm", + flavor: undefined, + isMonorepo: true, + hasLro: false + }); + + const initialPackageInfo = { + name: "@test/test-package", + version: "1.0.0", + "//metadata": { + constantPaths: [{ path: "src/old-path.ts", prefix: "userAgentInfo" }] + } + }; + + const packageFileContent = updatePackageFile(model, initialPackageInfo, { + clientContextPaths: ["src/api/newContext.ts"] + }); + + // Should return package.json but without updating constantPaths for non-Azure packages + expect(packageFileContent).to.not.be.undefined; + const packageFile = JSON.parse(packageFileContent?.content ?? "{}"); + expect(packageFile["//metadata"]["constantPaths"]).to.deep.equal([ + { path: "src/old-path.ts", prefix: "userAgentInfo" } + ]); + }); }); describe("Flavorless lib", () => { From 45f1097aca646b45af77544588a7c5667243745a Mon Sep 17 00:00:00 2001 From: Kyle Zhang Date: Fri, 13 Feb 2026 11:31:46 +0800 Subject: [PATCH 3/4] update for comment --- packages/typespec-ts/src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/typespec-ts/src/index.ts b/packages/typespec-ts/src/index.ts index 53f3ce8b6d..8723ceb5f9 100644 --- a/packages/typespec-ts/src/index.ts +++ b/packages/typespec-ts/src/index.ts @@ -577,8 +577,8 @@ export async function $onEmit(context: EmitContext) { ); } - // Regenerate snippets.spec.ts if test folder exists - if (option.generateTest && isAzureFlavor && hasTestFolder) { + // Regenerate snippets.spec.ts for test generation + if (option.generateTest && isAzureFlavor) { for (const subClient of dpgContext.sdkPackage.clients) { await emitContentByBuilder( program, From bce6c656796ba19d530738c9d5b96e1ba89268e2 Mon Sep 17 00:00:00 2001 From: Kyle Zhang Date: Tue, 24 Feb 2026 14:33:30 +0800 Subject: [PATCH 4/4] Update snippet generation options for Azure SDK support --- packages/typespec-ts/src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/typespec-ts/src/index.ts b/packages/typespec-ts/src/index.ts index 1be03c3dc8..7c35608572 100644 --- a/packages/typespec-ts/src/index.ts +++ b/packages/typespec-ts/src/index.ts @@ -582,8 +582,8 @@ export async function $onEmit(context: EmitContext) { ); } - // Regenerate snippets.spec.ts for test generation - if (option.generateTest && isAzureFlavor) { + // Regenerate snippets.spec.ts for documentation + if (option.azureSdkForJs) { for (const subClient of dpgContext.sdkPackage.clients) { await emitContentByBuilder( program,