Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions packages/rlc-common/src/metadata/buildPackageFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,15 +91,17 @@ export function buildPackageFile(
export function updatePackageFile(
model: RLCModel,
existingFilePathOrContent: string | Record<string, any>,
{ 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;
}

Expand Down Expand Up @@ -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)
Expand Down
30 changes: 24 additions & 6 deletions packages/rlc-common/src/metadata/buildReadmeFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
108 changes: 108 additions & 0 deletions packages/rlc-common/test/integration/packageJson.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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", () => {
Expand Down
19 changes: 18 additions & 1 deletion packages/typespec-ts/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -557,7 +557,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(
Expand All @@ -577,6 +581,19 @@ export async function $onEmit(context: EmitContext) {
dpgContext.generationPathDetail?.metadataDir
);
}

// Regenerate snippets.spec.ts for documentation
if (option.azureSdkForJs) {
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(
Expand Down
Loading