@azure-tools/typespec-ts 0.29.0-alpha.20240626.1 → 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/dist/src/contextManager.d.ts +70 -0
- package/dist/src/contextManager.d.ts.map +1 -0
- package/dist/src/contextManager.js +59 -0
- package/dist/src/contextManager.js.map +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +13 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/metaTree.d.ts +12 -0
- package/dist/src/metaTree.d.ts.map +1 -0
- package/dist/src/metaTree.js +2 -0
- package/dist/src/metaTree.js.map +1 -0
- package/dist/src/modular/buildClassicalClient.js +3 -1
- package/dist/src/modular/buildClassicalClient.js.map +1 -1
- package/dist/src/modular/buildCodeModel.d.ts.map +1 -1
- package/dist/src/modular/buildCodeModel.js +17 -7
- package/dist/src/modular/buildCodeModel.js.map +1 -1
- package/dist/src/modular/buildHelperSerializers.d.ts +3 -0
- package/dist/src/modular/buildHelperSerializers.d.ts.map +1 -0
- package/dist/src/modular/buildHelperSerializers.js +60 -0
- package/dist/src/modular/buildHelperSerializers.js.map +1 -0
- package/dist/src/modular/buildOperations.d.ts.map +1 -1
- package/dist/src/modular/buildOperations.js +5 -0
- package/dist/src/modular/buildOperations.js.map +1 -1
- package/dist/src/modular/buildSerializeUtils.d.ts.map +1 -1
- package/dist/src/modular/buildSerializeUtils.js +11 -4
- package/dist/src/modular/buildSerializeUtils.js.map +1 -1
- package/dist/src/modular/buildSubpathIndex.d.ts.map +1 -1
- package/dist/src/modular/buildSubpathIndex.js +6 -0
- package/dist/src/modular/buildSubpathIndex.js.map +1 -1
- package/dist/src/modular/emitModels.d.ts +5 -5
- package/dist/src/modular/emitModels.d.ts.map +1 -1
- package/dist/src/modular/emitModels.js +32 -4
- package/dist/src/modular/emitModels.js.map +1 -1
- package/dist/src/modular/helpers/operationHelpers.d.ts +10 -2
- package/dist/src/modular/helpers/operationHelpers.d.ts.map +1 -1
- package/dist/src/modular/helpers/operationHelpers.js +135 -56
- package/dist/src/modular/helpers/operationHelpers.js.map +1 -1
- package/dist/src/modular/helpers/typeHelpers.d.ts +1 -0
- package/dist/src/modular/helpers/typeHelpers.d.ts.map +1 -1
- package/dist/src/modular/helpers/typeHelpers.js +24 -19
- package/dist/src/modular/helpers/typeHelpers.js.map +1 -1
- package/dist/src/modular/modularCodeModel.d.ts +2 -2
- package/dist/src/modular/modularCodeModel.d.ts.map +1 -1
- package/dist/src/modular/serialization/buildSerializerFunction.d.ts +4 -0
- package/dist/src/modular/serialization/buildSerializerFunction.d.ts.map +1 -0
- package/dist/src/modular/serialization/buildSerializerFunction.js +160 -0
- package/dist/src/modular/serialization/buildSerializerFunction.js.map +1 -0
- package/dist/src/transform/transformSchemas.d.ts.map +1 -1
- package/dist/src/transform/transformSchemas.js +3 -0
- package/dist/src/transform/transformSchemas.js.map +1 -1
- package/dist/src/utils/importHelper.d.ts +10 -0
- package/dist/src/utils/importHelper.d.ts.map +1 -0
- package/dist/src/utils/importHelper.js +54 -0
- package/dist/src/utils/importHelper.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/src/contextManager.ts +99 -0
- package/src/index.ts +14 -2
- package/src/metaTree.ts +14 -0
- package/src/modular/buildClassicalClient.ts +5 -1
- package/src/modular/buildCodeModel.ts +22 -6
- package/src/modular/buildHelperSerializers.ts +71 -0
- package/src/modular/buildOperations.ts +7 -1
- package/src/modular/buildSerializeUtils.ts +19 -11
- package/src/modular/buildSubpathIndex.ts +10 -0
- package/src/modular/emitModels.ts +66 -16
- package/src/modular/helpers/operationHelpers.ts +198 -84
- package/src/modular/helpers/typeHelpers.ts +24 -17
- package/src/modular/modularCodeModel.ts +2 -2
- package/src/modular/serialization/buildSerializerFunction.ts +224 -0
- package/src/transform/transformSchemas.ts +3 -0
- package/src/utils/importHelper.ts +64 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { toCamelCase, toPascalCase } from "../../utils/casingUtils.js";
|
|
2
|
+
import { Type as ModularType } from "../modularCodeModel.js";
|
|
3
|
+
import { getRequestModelMapping } from "../helpers/operationHelpers.js";
|
|
4
|
+
import { useContext } from "../../contextManager.js";
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
Imports as RuntimeImports,
|
|
8
|
+
SchemaContext,
|
|
9
|
+
addImportToSpecifier
|
|
10
|
+
} from "@azure-tools/rlc-common";
|
|
11
|
+
import { UsageFlags } from "@typespec/compiler";
|
|
12
|
+
import {
|
|
13
|
+
SdkModelType,
|
|
14
|
+
SdkType
|
|
15
|
+
} from "@azure-tools/typespec-client-generator-core";
|
|
16
|
+
|
|
17
|
+
function getTcgcType(type: ModularType): SdkType {
|
|
18
|
+
if (type.tcgcType?.kind === "nullable") {
|
|
19
|
+
return type.tcgcType.type!;
|
|
20
|
+
}
|
|
21
|
+
return type.tcgcType!;
|
|
22
|
+
}
|
|
23
|
+
export function buildModelSerializer(
|
|
24
|
+
type: ModularType,
|
|
25
|
+
runtimeImports: RuntimeImports
|
|
26
|
+
): string | undefined {
|
|
27
|
+
const rlcMetadata = useContext("rlcMetaTree");
|
|
28
|
+
const modelTcgcType = getTcgcType(type) as SdkModelType;
|
|
29
|
+
if (
|
|
30
|
+
modelTcgcType.usage !== undefined &&
|
|
31
|
+
(modelTcgcType.usage & UsageFlags.Input) !== UsageFlags.Input
|
|
32
|
+
) {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!type.name) {
|
|
37
|
+
throw new Error(`NYI Serialization of anonymous types`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
let serializerName = `${toCamelCase(type.name)}Serializer`;
|
|
41
|
+
|
|
42
|
+
const restModel = rlcMetadata.get(type.__raw!);
|
|
43
|
+
const restModelName = restModel?.rlcType.name;
|
|
44
|
+
|
|
45
|
+
const output: string[] = [];
|
|
46
|
+
|
|
47
|
+
if (
|
|
48
|
+
!isDiscriminatedUnion(type) &&
|
|
49
|
+
type.type === "combined" &&
|
|
50
|
+
type.discriminator
|
|
51
|
+
) {
|
|
52
|
+
return buildPolymorphicSerializer(type);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (isDiscriminatedUnion(type)) {
|
|
56
|
+
const discriminatedTgcType = getTcgcType(type) as SdkModelType;
|
|
57
|
+
const cases: string[] = [];
|
|
58
|
+
const baseSerializerName = `${toCamelCase(
|
|
59
|
+
discriminatedTgcType.name
|
|
60
|
+
)}Serializer`;
|
|
61
|
+
for (const key in discriminatedTgcType.discriminatedSubtypes) {
|
|
62
|
+
const subType = discriminatedTgcType.discriminatedSubtypes[key]!;
|
|
63
|
+
const discriminatedValue = subType.discriminatorValue!;
|
|
64
|
+
const union = subType.discriminatedSubtypes ? "Union" : "";
|
|
65
|
+
const subTypeName = `${toPascalCase(subType.name)}${union}`;
|
|
66
|
+
const subtypeSerializerName = toCamelCase(`${subTypeName}Serializer`);
|
|
67
|
+
|
|
68
|
+
cases.push(`
|
|
69
|
+
case "${discriminatedValue}":
|
|
70
|
+
return ${subtypeSerializerName}(item as ${subTypeName});
|
|
71
|
+
`);
|
|
72
|
+
}
|
|
73
|
+
output.push(`
|
|
74
|
+
export function ${toCamelCase(type.name)}Serializer(item: ${toPascalCase(
|
|
75
|
+
type.name
|
|
76
|
+
)}) {
|
|
77
|
+
switch (item.${type.discriminator}) {
|
|
78
|
+
${cases.join("\n")}
|
|
79
|
+
default:
|
|
80
|
+
return ${baseSerializerName}(item);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
`);
|
|
84
|
+
|
|
85
|
+
serializerName = baseSerializerName;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
let serializerReturnType = "";
|
|
89
|
+
|
|
90
|
+
// There are some situations where the type is not present in the REST API,
|
|
91
|
+
// this can happen when client.tsp overrides visibility to public on an orphan model
|
|
92
|
+
// In this case we just let TypeScript infer the return type.
|
|
93
|
+
let restModelNameAlias = "";
|
|
94
|
+
if (restModelName && restModel.rlcType.usage?.includes(SchemaContext.Input)) {
|
|
95
|
+
restModelNameAlias = `${restModelName}Rest`;
|
|
96
|
+
serializerReturnType = `: ${restModelNameAlias}`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (type.type === "model" || type.type === "dict") {
|
|
100
|
+
const nullabilityPrefix = "";
|
|
101
|
+
// getPropertySerializationPrefix({
|
|
102
|
+
// clientName: "item",
|
|
103
|
+
// type
|
|
104
|
+
// });
|
|
105
|
+
|
|
106
|
+
// This is only handling the compatibility mode, will need to update when we handle additionalProperties property.
|
|
107
|
+
const additionalPropertiesSpread = hasAdditionalProperties(type.tcgcType)
|
|
108
|
+
? "...item,"
|
|
109
|
+
: "";
|
|
110
|
+
|
|
111
|
+
const { propertiesStr, directAssignment } = getRequestModelMapping(
|
|
112
|
+
type,
|
|
113
|
+
"item",
|
|
114
|
+
runtimeImports
|
|
115
|
+
);
|
|
116
|
+
const propertiesSerialization = propertiesStr.filter((p) => p.trim());
|
|
117
|
+
|
|
118
|
+
// don't emit a serializer if there is nothing to serialize
|
|
119
|
+
if (propertiesSerialization.length || additionalPropertiesSpread) {
|
|
120
|
+
const spreadSerialized = directAssignment ? "..." : "";
|
|
121
|
+
const fnBody = `{
|
|
122
|
+
${additionalPropertiesSpread}
|
|
123
|
+
${nullabilityPrefix} ${spreadSerialized} ${propertiesSerialization.join(
|
|
124
|
+
",\n"
|
|
125
|
+
)}
|
|
126
|
+
}`;
|
|
127
|
+
output.push(`
|
|
128
|
+
export function ${serializerName}(item: ${toPascalCase(
|
|
129
|
+
type.name
|
|
130
|
+
)})${serializerReturnType} {
|
|
131
|
+
return ${fnBody}
|
|
132
|
+
}
|
|
133
|
+
`);
|
|
134
|
+
|
|
135
|
+
if (serializerReturnType) {
|
|
136
|
+
addImportToSpecifier(
|
|
137
|
+
"rlcIndex",
|
|
138
|
+
runtimeImports,
|
|
139
|
+
`${restModelName} as ${restModelNameAlias}`
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
} else {
|
|
143
|
+
output.push(`
|
|
144
|
+
export function ${serializerName}(item: ${toPascalCase(type.name)}) {
|
|
145
|
+
return item as any;
|
|
146
|
+
}
|
|
147
|
+
`);
|
|
148
|
+
}
|
|
149
|
+
} else if (type.type === "enum") {
|
|
150
|
+
output.push(`
|
|
151
|
+
export function ${serializerName}(item: ${toPascalCase(
|
|
152
|
+
type.name
|
|
153
|
+
)})${serializerReturnType} {
|
|
154
|
+
return item;
|
|
155
|
+
}
|
|
156
|
+
`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return output.join("\n");
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function isDiscriminatedUnion(
|
|
163
|
+
type: ModularType
|
|
164
|
+
): type is ModularType & { tcgcType: SdkModelType } {
|
|
165
|
+
const { tcgcType } = type;
|
|
166
|
+
|
|
167
|
+
return Boolean(
|
|
168
|
+
tcgcType?.kind === "model" &&
|
|
169
|
+
tcgcType.discriminatorProperty &&
|
|
170
|
+
tcgcType.discriminatedSubtypes
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function hasAdditionalProperties(type: SdkType | undefined) {
|
|
175
|
+
if (!type || !("additionalProperties" in type)) {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (type.additionalProperties) {
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (type.baseModel) {
|
|
184
|
+
return hasAdditionalProperties(type.baseModel);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function buildPolymorphicSerializer(type: ModularType) {
|
|
191
|
+
if (!type.discriminator) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
const output: string[] = [];
|
|
195
|
+
|
|
196
|
+
const subTypes = type.types ?? [];
|
|
197
|
+
|
|
198
|
+
const cases: string[] = [];
|
|
199
|
+
for (const subType of subTypes) {
|
|
200
|
+
const discriminatedValue = subType.discriminatorValue!;
|
|
201
|
+
const union = subType.types ? "Union" : "";
|
|
202
|
+
const subTypeName = `${toPascalCase(subType.name!)}${union}`;
|
|
203
|
+
const subtypeSerializerName = toCamelCase(`${subTypeName}Serializer`);
|
|
204
|
+
|
|
205
|
+
cases.push(`
|
|
206
|
+
case "${discriminatedValue}":
|
|
207
|
+
return ${subtypeSerializerName}(item as ${subTypeName});
|
|
208
|
+
`);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
output.push(`
|
|
212
|
+
export function ${toCamelCase(type.name!)}Serializer(item: ${toPascalCase(
|
|
213
|
+
type.name!
|
|
214
|
+
)}) {
|
|
215
|
+
switch (item.${type.discriminator}) {
|
|
216
|
+
${cases.join("\n")}
|
|
217
|
+
default:
|
|
218
|
+
return item;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
`);
|
|
222
|
+
|
|
223
|
+
return output.join("\n");
|
|
224
|
+
}
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
isAzureCoreErrorType,
|
|
20
20
|
trimUsage
|
|
21
21
|
} from "../utils/modelUtils.js";
|
|
22
|
+
import { useContext } from "../contextManager.js";
|
|
22
23
|
|
|
23
24
|
export function transformSchemas(client: SdkClient, dpgContext: SdkContext) {
|
|
24
25
|
const program = dpgContext.program;
|
|
@@ -118,6 +119,7 @@ export function transformSchemas(client: SdkClient, dpgContext: SdkContext) {
|
|
|
118
119
|
}
|
|
119
120
|
transformHostParameters();
|
|
120
121
|
usageMap.forEach((context, tspModel) => {
|
|
122
|
+
const metatree = useContext("rlcMetaTree");
|
|
121
123
|
const model = getSchemaForType(dpgContext, tspModel, {
|
|
122
124
|
usage: context,
|
|
123
125
|
isRequestBody: requestBodySet.has(tspModel),
|
|
@@ -127,6 +129,7 @@ export function transformSchemas(client: SdkClient, dpgContext: SdkContext) {
|
|
|
127
129
|
if (model) {
|
|
128
130
|
model.usage = context;
|
|
129
131
|
}
|
|
132
|
+
metatree.set(tspModel, { rlcType: model });
|
|
130
133
|
if (model.name === "") {
|
|
131
134
|
return;
|
|
132
135
|
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { dirname, relative, resolve } from "path";
|
|
2
|
+
import { useContext } from "../contextManager.js";
|
|
3
|
+
import { SourceFile } from "ts-morph";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Adds a named import to a TypeScript source file if it does not already exist.
|
|
7
|
+
* Utilizes a global symbol map context to resolve module paths and ensure the import is correctly referenced.
|
|
8
|
+
*
|
|
9
|
+
* @param {string} symbol The name of the export to import.
|
|
10
|
+
* @param {SourceFile} currentFile The ts-morph SourceFile object where the import will be added.
|
|
11
|
+
*/
|
|
12
|
+
export function addImportBySymbol(symbol: string, currentFile: SourceFile) {
|
|
13
|
+
// Retrieve the global map of symbols to their source files.
|
|
14
|
+
const symbolMap = useContext("symbolMap");
|
|
15
|
+
|
|
16
|
+
// Attempt to resolve the file path for the given symbol.
|
|
17
|
+
const modulePath = symbolMap.get(symbol)?.getFilePath();
|
|
18
|
+
if (!modulePath) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Resolve absolute path and compute a relative path from the current file to the module.
|
|
23
|
+
const moduleAbsolutePath = resolve(modulePath);
|
|
24
|
+
const relativeImportPath = getRelativeImportPath(
|
|
25
|
+
currentFile.getFilePath(),
|
|
26
|
+
moduleAbsolutePath
|
|
27
|
+
)
|
|
28
|
+
.replace(/\\/g, "/")
|
|
29
|
+
.replace(/\.ts$/, ".js");
|
|
30
|
+
|
|
31
|
+
// Check if the import declaration already exists and if it includes the symbol.
|
|
32
|
+
const existing = currentFile.getImportDeclaration(
|
|
33
|
+
(i) => i.getModuleSpecifierValue() === relativeImportPath
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
if (existing) {
|
|
37
|
+
const sameImport = existing
|
|
38
|
+
.getNamedImports()
|
|
39
|
+
.some((i) => i.getName() === symbol);
|
|
40
|
+
|
|
41
|
+
if (!sameImport) {
|
|
42
|
+
existing.addNamedImport(symbol);
|
|
43
|
+
}
|
|
44
|
+
} else {
|
|
45
|
+
currentFile.addImportDeclaration({
|
|
46
|
+
moduleSpecifier: relativeImportPath,
|
|
47
|
+
namedImports: [symbol]
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Computes a relative import path suitable for TypeScript imports given two file paths.
|
|
54
|
+
* Ensures the returned path starts with './' if necessary to be recognized as a relative module.
|
|
55
|
+
*
|
|
56
|
+
* @param {string} from The file path to import from.
|
|
57
|
+
* @param {string} to The file path to import to.
|
|
58
|
+
* @returns {string} The relative path adjusted for import syntax.
|
|
59
|
+
*/
|
|
60
|
+
function getRelativeImportPath(from: string, to: string) {
|
|
61
|
+
const relativePath = relative(dirname(from), to);
|
|
62
|
+
// Adjust the path format to TypeScript module syntax
|
|
63
|
+
return relativePath.startsWith(".") ? relativePath : "./" + relativePath;
|
|
64
|
+
}
|