Skip to content

Commit 48945fc

Browse files
add the MTG's emitter source code into this project (#5383)
* add the emitter source code into this project * format * fix linter issues
1 parent e21427e commit 48945fc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+4157
-31
lines changed

package-lock.json

Lines changed: 1 addition & 18 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/TypeSpec.Extension/Emitter.Csharp/package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,7 @@
3434
"dist/src/**"
3535
],
3636
"dependencies": {
37-
"@autorest/csharp": "3.0.0-beta.20240625.4",
38-
"@typespec/http-client-csharp": "1.0.0-alpha.20250715.1"
37+
"@autorest/csharp": "3.0.0-beta.20240625.4"
3938
},
4039
"peerDependencies": {
4140
"@azure-tools/typespec-azure-core": ">=0.57.0 <0.58.0 || ~0.58.0-0",

src/TypeSpec.Extension/Emitter.Csharp/src/backward-compatibility.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import {
77
SdkBuiltInKinds,
88
UsageFlags
99
} from "@azure-tools/typespec-client-generator-core";
10-
import { CodeModel, InputClient } from "@typespec/http-client-csharp";
10+
import { CodeModel } from "./mtgEmitter/type/code-model.js";
11+
import { InputClient } from "./mtgEmitter/type/input-type.js";
1112

1213
/*
1314
* This function transforms the code model for backward compatibility to avoid massive code on autorest.csharp's csharp part.

src/TypeSpec.Extension/Emitter.Csharp/src/emitter.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,21 @@ import { EmitContext, Program, resolvePath } from "@typespec/compiler";
66
import { execSync } from "child_process";
77
import fs, { existsSync } from "fs";
88
import path from "node:path";
9-
import {
10-
Logger,
11-
configurationFileName,
12-
createCSharpEmitterContext,
13-
createModel,
14-
tspOutputFileName,
15-
writeCodeModel
16-
} from "@typespec/http-client-csharp";
179
import { createSdkContext } from "@azure-tools/typespec-client-generator-core";
1810
import {
1911
AzureCSharpEmitterOptions,
2012
resolveAzureEmitterOptions
2113
} from "./options.js";
2214
import { azureSDKContextOptions } from "./sdk-context-options.js";
2315
import { transformCodeModel } from "./backward-compatibility.js";
16+
import { Logger } from "./mtgEmitter/lib/logger.js";
17+
import { createCSharpEmitterContext } from "./mtgEmitter/sdk-context.js";
18+
import { createModel } from "./mtgEmitter/lib/client-model-builder.js";
19+
import { writeCodeModel } from "./mtgEmitter/code-model-writer.js";
20+
import {
21+
configurationFileName,
22+
tspOutputFileName
23+
} from "./mtgEmitter/constants.js";
2424

2525
export async function $onEmit(context: EmitContext<AzureCSharpEmitterOptions>) {
2626
const program: Program = context.program;
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
import { UsageFlags } from "@azure-tools/typespec-client-generator-core";
5+
import { resolvePath } from "@typespec/compiler";
6+
import { configurationFileName, tspOutputFileName } from "./constants.js";
7+
import { CSharpEmitterContext } from "./sdk-context.js";
8+
import { CodeModel } from "./type/code-model.js";
9+
import { Configuration } from "./type/configuration.js";
10+
11+
/**
12+
* Writes the code model to the output folder. Should only be used by autorest.csharp.
13+
* @param context - The CSharp emitter context
14+
* @param codeModel - The code model to write
15+
* @param outputFolder - The output folder to write the code model to
16+
* @beta
17+
*/
18+
export async function writeCodeModel(
19+
context: CSharpEmitterContext,
20+
codeModel: CodeModel,
21+
outputFolder: string
22+
) {
23+
await context.program.host.writeFile(
24+
resolvePath(outputFolder, tspOutputFileName),
25+
prettierOutput(
26+
JSON.stringify(
27+
buildJson(context, codeModel),
28+
transformJSONProperties,
29+
2
30+
)
31+
)
32+
);
33+
}
34+
35+
/**
36+
* This function builds a json from code model with refs and ids in it.
37+
* @param context - The CSharp emitter context
38+
* @param codeModel - The code model to build
39+
*/
40+
function buildJson(context: CSharpEmitterContext, codeModel: CodeModel): any {
41+
const objectsIds = new Map<any, string>();
42+
const stack: any[] = [];
43+
44+
return doBuildJson(codeModel, stack);
45+
46+
function doBuildJson(obj: any, stack: any[]): any {
47+
// check if this is a primitive type or null or undefined
48+
if (!obj || typeof obj !== "object") {
49+
return obj;
50+
}
51+
// we switch here for object, arrays and primitives
52+
if (Array.isArray(obj)) {
53+
// array types
54+
return obj.map((item) => doBuildJson(item, stack));
55+
} else {
56+
// this is an object
57+
if (shouldHaveRef(obj)) {
58+
// we will add the $id property to the object if this is the first time we see it
59+
// or returns a $ref if we have seen it before
60+
let id = objectsIds.get(obj);
61+
if (id) {
62+
// we have seen this object before
63+
return {
64+
$ref: id
65+
};
66+
} else {
67+
// this is the first time we see this object
68+
id = (objectsIds.size + 1).toString();
69+
objectsIds.set(obj, id);
70+
return handleObject(obj, id, stack);
71+
}
72+
} else {
73+
// this is not an object to ref
74+
return handleObject(obj, undefined, stack);
75+
}
76+
}
77+
}
78+
79+
function handleObject(obj: any, id: string | undefined, stack: any[]): any {
80+
if (stack.includes(obj)) {
81+
// we have a cyclical reference, we should not continue
82+
context.logger.warn(
83+
`Cyclical reference detected in the code model (id: ${id}).`
84+
);
85+
return undefined;
86+
}
87+
88+
const result: any = id === undefined ? {} : { $id: id };
89+
stack.push(obj);
90+
91+
for (const property in obj) {
92+
if (property === "__raw") {
93+
continue; // skip __raw property
94+
}
95+
const v = obj[property];
96+
result[property] = doBuildJson(v, stack);
97+
}
98+
99+
stack.pop();
100+
return result;
101+
}
102+
103+
function shouldHaveRef(obj: any): boolean {
104+
// we only add reference to those types with a crossLanguageDefinitionId or a kind property.
105+
// TODO -- crossLanguageDefinitionId should be enough but there is something that should be referenced but does not have it.
106+
return "crossLanguageDefinitionId" in obj || "kind" in obj;
107+
}
108+
}
109+
110+
export async function writeConfiguration(
111+
context: CSharpEmitterContext,
112+
configurations: Configuration,
113+
outputFolder: string
114+
) {
115+
await context.program.host.writeFile(
116+
resolvePath(outputFolder, configurationFileName),
117+
prettierOutput(JSON.stringify(configurations, null, 2))
118+
);
119+
}
120+
121+
function transformJSONProperties(this: any, key: string, value: any): any {
122+
// convertUsageNumbersToStrings
123+
if (key === "usage" && typeof value === "number") {
124+
if (value === 0) {
125+
return "None";
126+
}
127+
const result: string[] = [];
128+
for (const prop in UsageFlags) {
129+
if (!isNaN(Number(prop))) {
130+
if ((value & Number(prop)) !== 0) {
131+
result.push(UsageFlags[prop]);
132+
}
133+
}
134+
}
135+
return result.join(",");
136+
}
137+
138+
// skip __raw if there is one
139+
if (key === "__raw") {
140+
return undefined;
141+
}
142+
143+
return value;
144+
}
145+
146+
function prettierOutput(output: string) {
147+
return output + "\n";
148+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
/**
5+
* The name of the file that contains the generated code model.
6+
* @beta
7+
*/
8+
export const tspOutputFileName = "tspCodeModel.json";
9+
/**
10+
* The name of the file that contains the generated configuration file.
11+
* @beta
12+
*/
13+
export const configurationFileName = "Configuration.json";
14+
15+
/**
16+
* The minimum supported major version of the .NET SDK for invoking the generator.
17+
* @internal
18+
*/
19+
export const _minSupportedDotNetSdkVersion = 8;
20+
21+
/**
22+
* The default name of the code model generator.
23+
* @internal
24+
*/
25+
export const _defaultGeneratorName = "ScmCodeModelGenerator";

0 commit comments

Comments
 (0)