@azure-tools/typespec-ts 0.37.0 → 0.38.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.
Files changed (85) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +12 -2
  3. package/dist/src/index.d.ts +3 -0
  4. package/dist/src/index.d.ts.map +1 -1
  5. package/dist/src/index.js +23 -19
  6. package/dist/src/index.js.map +1 -1
  7. package/dist/src/lib.d.ts.map +1 -1
  8. package/dist/src/lib.js +10 -2
  9. package/dist/src/lib.js.map +1 -1
  10. package/dist/src/modular/buildClassicalClient.js +2 -2
  11. package/dist/src/modular/buildClassicalClient.js.map +1 -1
  12. package/dist/src/modular/buildClientContext.d.ts.map +1 -1
  13. package/dist/src/modular/buildClientContext.js +2 -2
  14. package/dist/src/modular/buildClientContext.js.map +1 -1
  15. package/dist/src/modular/buildOperations.d.ts.map +1 -1
  16. package/dist/src/modular/buildOperations.js +0 -2
  17. package/dist/src/modular/buildOperations.js.map +1 -1
  18. package/dist/src/modular/buildProjectFiles.d.ts.map +1 -1
  19. package/dist/src/modular/buildProjectFiles.js +21 -3
  20. package/dist/src/modular/buildProjectFiles.js.map +1 -1
  21. package/dist/src/modular/buildRestorePoller.js +2 -2
  22. package/dist/src/modular/buildRestorePoller.js.map +1 -1
  23. package/dist/src/modular/buildRootIndex.d.ts +1 -0
  24. package/dist/src/modular/buildRootIndex.d.ts.map +1 -1
  25. package/dist/src/modular/buildRootIndex.js +80 -39
  26. package/dist/src/modular/buildRootIndex.js.map +1 -1
  27. package/dist/src/modular/buildSubpathIndex.d.ts +1 -0
  28. package/dist/src/modular/buildSubpathIndex.d.ts.map +1 -1
  29. package/dist/src/modular/buildSubpathIndex.js +67 -49
  30. package/dist/src/modular/buildSubpathIndex.js.map +1 -1
  31. package/dist/src/modular/emitModels.d.ts +4 -3
  32. package/dist/src/modular/emitModels.d.ts.map +1 -1
  33. package/dist/src/modular/emitModels.js +91 -26
  34. package/dist/src/modular/emitModels.js.map +1 -1
  35. package/dist/src/modular/helpers/classicalOperationHelpers.d.ts.map +1 -1
  36. package/dist/src/modular/helpers/classicalOperationHelpers.js +30 -18
  37. package/dist/src/modular/helpers/classicalOperationHelpers.js.map +1 -1
  38. package/dist/src/modular/helpers/clientHelpers.js +3 -3
  39. package/dist/src/modular/helpers/clientHelpers.js.map +1 -1
  40. package/dist/src/modular/helpers/operationHelpers.d.ts +5 -6
  41. package/dist/src/modular/helpers/operationHelpers.d.ts.map +1 -1
  42. package/dist/src/modular/helpers/operationHelpers.js +108 -27
  43. package/dist/src/modular/helpers/operationHelpers.js.map +1 -1
  44. package/dist/src/modular/serialization/buildDeserializerFunction.d.ts.map +1 -1
  45. package/dist/src/modular/serialization/buildDeserializerFunction.js +2 -1
  46. package/dist/src/modular/serialization/buildDeserializerFunction.js.map +1 -1
  47. package/dist/src/modular/serialization/buildSerializerFunction.d.ts.map +1 -1
  48. package/dist/src/modular/serialization/buildSerializerFunction.js +72 -20
  49. package/dist/src/modular/serialization/buildSerializerFunction.js.map +1 -1
  50. package/dist/src/modular/static-helpers-metadata.d.ts +12 -0
  51. package/dist/src/modular/static-helpers-metadata.d.ts.map +1 -1
  52. package/dist/src/modular/static-helpers-metadata.js +12 -0
  53. package/dist/src/modular/static-helpers-metadata.js.map +1 -1
  54. package/dist/src/transform/transfromRLCOptions.d.ts.map +1 -1
  55. package/dist/src/transform/transfromRLCOptions.js +8 -1
  56. package/dist/src/transform/transfromRLCOptions.js.map +1 -1
  57. package/dist/src/utils/modelUtils.d.ts.map +1 -1
  58. package/dist/src/utils/modelUtils.js +67 -44
  59. package/dist/src/utils/modelUtils.js.map +1 -1
  60. package/dist/tsconfig.tsbuildinfo +1 -1
  61. package/package.json +45 -42
  62. package/src/index.ts +40 -21
  63. package/src/lib.ts +10 -2
  64. package/src/modular/buildClassicalClient.ts +2 -2
  65. package/src/modular/buildClientContext.ts +5 -2
  66. package/src/modular/buildOperations.ts +0 -2
  67. package/src/modular/buildProjectFiles.ts +27 -3
  68. package/src/modular/buildRestorePoller.ts +2 -2
  69. package/src/modular/buildRootIndex.ts +97 -52
  70. package/src/modular/buildSubpathIndex.ts +79 -57
  71. package/src/modular/emitModels.ts +118 -29
  72. package/src/modular/helpers/classicalOperationHelpers.ts +53 -34
  73. package/src/modular/helpers/clientHelpers.ts +3 -3
  74. package/src/modular/helpers/operationHelpers.ts +167 -59
  75. package/src/modular/serialization/buildDeserializerFunction.ts +2 -1
  76. package/src/modular/serialization/buildSerializerFunction.ts +90 -25
  77. package/src/modular/static-helpers-metadata.ts +13 -0
  78. package/src/transform/transfromRLCOptions.ts +11 -1
  79. package/src/utils/modelUtils.ts +67 -43
  80. package/static/static-helpers/multipartHelpers.ts +22 -0
  81. package/dist/src/modular/buildHelperSerializers.d.ts +0 -3
  82. package/dist/src/modular/buildHelperSerializers.d.ts.map +0 -1
  83. package/dist/src/modular/buildHelperSerializers.js +0 -60
  84. package/dist/src/modular/buildHelperSerializers.js.map +0 -1
  85. package/src/modular/buildHelperSerializers.ts +0 -72
@@ -74,21 +74,26 @@ export function getClassicalOperation(
74
74
  .filter((i) => i.getName() === interfaceName)[0];
75
75
  const properties: OptionalKind<PropertySignatureStructure>[] = [];
76
76
  if (layer !== prefixes.length - 1) {
77
- properties.push({
78
- kind: StructureKind.PropertySignature,
79
- name: normalizeName(
80
- (layer === prefixes.length - 1
81
- ? prefixes[layer]
82
- : prefixes[layer + 1]) ?? "",
83
- NameType.Property
84
- ),
85
- type: `${getClassicalLayerPrefix(
86
- prefixes,
87
- NameType.Interface,
77
+ const name = normalizeName(
78
+ (layer === prefixes.length - 1 ? prefixes[layer] : prefixes[layer + 1]) ??
88
79
  "",
89
- layer + 1
90
- )}Operations`
91
- });
80
+ NameType.Property
81
+ );
82
+ if (
83
+ !properties.some((x) => x.name === name) &&
84
+ !(existInterface && existInterface.getProperty(name))
85
+ ) {
86
+ properties.push({
87
+ kind: StructureKind.PropertySignature,
88
+ name,
89
+ type: `${getClassicalLayerPrefix(
90
+ prefixes,
91
+ NameType.Interface,
92
+ "",
93
+ layer + 1
94
+ )}Operations`
95
+ });
96
+ }
92
97
  } else {
93
98
  operationDeclarations.forEach((d) => {
94
99
  properties.push({
@@ -124,13 +129,13 @@ export function getClassicalOperation(
124
129
 
125
130
  if (layer === prefixes.length - 1) {
126
131
  classicFile.addFunction({
127
- name: `get${getClassicalLayerPrefix(
132
+ name: `_get${getClassicalLayerPrefix(
128
133
  prefixes,
129
134
  NameType.Interface,
130
135
  "",
131
136
  layer
132
137
  )}`,
133
- isExported: true,
138
+ isExported: false,
134
139
  parameters: [
135
140
  {
136
141
  name: "context",
@@ -164,7 +169,7 @@ export function getClassicalOperation(
164
169
  });
165
170
  }
166
171
 
167
- const operationFunctionName = `get${getClassicalLayerPrefix(
172
+ const operationFunctionName = `_get${getClassicalLayerPrefix(
168
173
  prefixes,
169
174
  NameType.Interface,
170
175
  "",
@@ -176,27 +181,41 @@ export function getClassicalOperation(
176
181
  if (existFunction) {
177
182
  const returnStatement = existFunction.getBodyText();
178
183
  if (returnStatement) {
179
- let statement = `,
180
- ...get${getClassicalLayerPrefix(
184
+ let statement: string | undefined = undefined;
185
+ if (layer !== prefixes.length - 1) {
186
+ const name = normalizeName(
187
+ prefixes[layer + 1] ?? "FIXME",
188
+ NameType.Property
189
+ );
190
+
191
+ // HACK: check if the statement includes a group of this name already to prevent an operation group appearing multiple times
192
+ // TODO: would be good to refactor so that we have an intermediate data structure before generating the return statement
193
+ if (!returnStatement.includes(`${name}:`)) {
194
+ statement = `,
195
+ ${normalizeName(
196
+ prefixes[layer + 1] ?? "FIXME",
197
+ NameType.Property
198
+ )}: _get${getClassicalLayerPrefix(
199
+ prefixes,
200
+ NameType.Interface,
201
+ "",
202
+ layer + 1
203
+ )}Operations(context)}`;
204
+ }
205
+ } else {
206
+ statement = `,
207
+ ..._get${getClassicalLayerPrefix(
181
208
  prefixes,
182
209
  NameType.Interface,
183
210
  "",
184
211
  layer + 1
185
212
  )}Operations(context)}`;
186
- if (layer !== prefixes.length - 1) {
187
- statement = `,
188
- ${normalizeName(
189
- prefixes[layer + 1] ?? "FIXME",
190
- NameType.Property
191
- )}: get${getClassicalLayerPrefix(
192
- prefixes,
193
- NameType.Interface,
194
- "",
195
- layer + 1
196
- )}Operations(context)}`;
197
213
  }
198
- const newReturnStatement = returnStatement.replace(/}$/, statement);
199
- existFunction.setBodyText(newReturnStatement);
214
+
215
+ if (statement) {
216
+ const newReturnStatement = returnStatement.replace(/}$/, statement);
217
+ existFunction.setBodyText(newReturnStatement);
218
+ }
200
219
  }
201
220
  } else {
202
221
  const functions = {
@@ -220,7 +239,7 @@ export function getClassicalOperation(
220
239
  ${normalizeName(
221
240
  prefixes[layer + 1] ?? "FIXME",
222
241
  NameType.Property
223
- )}: get${getClassicalLayerPrefix(
242
+ )}: _get${getClassicalLayerPrefix(
224
243
  prefixes,
225
244
  NameType.Interface,
226
245
  "",
@@ -228,7 +247,7 @@ export function getClassicalOperation(
228
247
  )}Operations(context)
229
248
  }`
230
249
  : `return {
231
- ...get${getClassicalLayerPrefix(
250
+ ..._get${getClassicalLayerPrefix(
232
251
  prefixes,
233
252
  NameType.Interface,
234
253
  "",
@@ -17,7 +17,7 @@ import {
17
17
  PackageFlavor
18
18
  } from "@azure-tools/rlc-common";
19
19
  import { SdkContext } from "../../utils/interfaces.js";
20
- import { getClientName } from "./namingHelpers.js";
20
+ import { getClassicalClientName } from "./namingHelpers.js";
21
21
  import { getTypeExpression } from "../type-expressions/get-type-expression.js";
22
22
  import { isCredentialType } from "./typeHelpers.js";
23
23
 
@@ -106,10 +106,10 @@ export function getClientParametersDeclaration(
106
106
  apiVersionAsRequired: false
107
107
  }
108
108
  ): OptionalKind<ParameterDeclarationStructure>[] {
109
- const name = getClientName(client);
109
+ const name = getClassicalClientName(client);
110
110
  const optionsParam = {
111
111
  name: "options",
112
- type: `${name}ClientOptionalParams`,
112
+ type: `${name}OptionalParams`,
113
113
  initializer: "{}"
114
114
  };
115
115
 
@@ -168,7 +168,7 @@ export function getDeserializePrivateFunction(
168
168
  );
169
169
  statements.push(
170
170
  `if(!expectedStatuses.includes(result.status)){`,
171
- `throw ${createRestErrorReference}(result);`,
171
+ `${getExceptionThrowStatement(context, operation)}`,
172
172
  "}"
173
173
  );
174
174
  const deserializedType = isLroOnly
@@ -186,7 +186,7 @@ export function getDeserializePrivateFunction(
186
186
  if (isLroOnly && lroSubPath) {
187
187
  statements.push(
188
188
  `if(${deserializedRoot.split(".").join("?.")} === undefined) {
189
- throw createRestError(\`Expected a result in the response at position "${deserializedRoot}"\`, result);
189
+ throw ${createRestErrorReference}(\`Expected a result in the response at position "${deserializedRoot}"\`, result);
190
190
  }
191
191
  `
192
192
  );
@@ -228,6 +228,103 @@ export function getDeserializePrivateFunction(
228
228
  };
229
229
  }
230
230
 
231
+ interface ExceptionThrowDetail {
232
+ start: number;
233
+ end?: number;
234
+ deserializer: string;
235
+ }
236
+
237
+ interface OperationExceptionDetails {
238
+ customized: ExceptionThrowDetail[];
239
+ defaultDeserializer?: string;
240
+ }
241
+
242
+ function getExceptionDetails(
243
+ context: SdkContext,
244
+ operation: ServiceOperation
245
+ ): OperationExceptionDetails {
246
+ const customized: ExceptionThrowDetail[] = [];
247
+ let defaultDeserializer: string | undefined;
248
+ for (const exception of operation.operation.exceptions) {
249
+ if (!exception.type) {
250
+ continue;
251
+ }
252
+ const statusCode = exception.statusCodes;
253
+ const deserializeFunctionName = buildModelDeserializer(
254
+ context,
255
+ exception.type,
256
+ false,
257
+ true
258
+ );
259
+ if (
260
+ !deserializeFunctionName ||
261
+ typeof deserializeFunctionName !== "string"
262
+ ) {
263
+ continue;
264
+ }
265
+ if (statusCode === "*") {
266
+ defaultDeserializer = deserializeFunctionName;
267
+ } else if (typeof statusCode === "number") {
268
+ customized.push({
269
+ start: statusCode,
270
+ deserializer: deserializeFunctionName
271
+ });
272
+ } else {
273
+ customized.push({
274
+ start: statusCode.start,
275
+ end: statusCode.end,
276
+ deserializer: deserializeFunctionName
277
+ });
278
+ }
279
+ }
280
+ return { customized, defaultDeserializer };
281
+ }
282
+
283
+ function getExceptionThrowStatement(
284
+ context: SdkContext,
285
+ operation: ServiceOperation
286
+ ) {
287
+ const statements = [];
288
+ const createRestErrorReference = resolveReference(
289
+ useDependencies().createRestError
290
+ );
291
+ const { customized, defaultDeserializer } = getExceptionDetails(
292
+ context,
293
+ operation
294
+ );
295
+ if (customized.length > 0) {
296
+ statements.push(`const error = ${createRestErrorReference}(result);`);
297
+ statements.push(`const statusCode = Number.parseInt(result.status);`);
298
+ const stats: string[] = customized.map((exception) => {
299
+ if (exception.end) {
300
+ return `if(statusCode >= ${exception.start} && statusCode <= ${exception.end}) {
301
+ error.details = ${exception.deserializer}(result.body);
302
+ }`;
303
+ } else {
304
+ return `if(statusCode === ${exception.start}) {
305
+ error.details = ${exception.deserializer}(result.body);
306
+ }`;
307
+ }
308
+ });
309
+ statements.push(stats.join("\nelse "));
310
+ if (defaultDeserializer) {
311
+ statements.push(`else {
312
+ error.details = ${defaultDeserializer}(result.body);
313
+ }`);
314
+ }
315
+ statements.push("throw error;");
316
+ } else {
317
+ if (defaultDeserializer) {
318
+ statements.push(`const error = ${createRestErrorReference}(result);
319
+ error.details = ${defaultDeserializer}(result.body);`);
320
+ statements.push("throw error;");
321
+ } else {
322
+ statements.push(`throw ${createRestErrorReference}(result);`);
323
+ }
324
+ }
325
+ return statements.join("\n");
326
+ }
327
+
231
328
  function getOptionalParamsName(
232
329
  parameters: OptionalKind<ParameterDeclarationStructure>[]
233
330
  ) {
@@ -795,7 +892,7 @@ function getRequired(context: SdkContext, param: SdkModelPropertyType) {
795
892
  const serializedName = getPropertySerializedName(param);
796
893
  const clientValue = `${param.onClient ? "context." : ""}${param.name}`;
797
894
  if (param.type.kind === "model") {
798
- const { propertiesStr } = getRequestModelMapping(
895
+ const propertiesStr = getRequestModelMapping(
799
896
  context,
800
897
  { ...param.type, optional: param.optional },
801
898
  clientValue
@@ -834,15 +931,12 @@ function getOptional(
834
931
  const serializedName = getPropertySerializedName(param);
835
932
  const paramName = `${param.onClient ? "context." : `${optionalParamName}?.`}${param.name}`;
836
933
  if (param.type.kind === "model") {
837
- const { propertiesStr, directAssignment } = getRequestModelMapping(
934
+ const propertiesStr = getRequestModelMapping(
838
935
  context,
839
936
  { ...param.type, optional: param.optional },
840
937
  paramName + "?."
841
938
  );
842
- const serializeContent =
843
- directAssignment === true
844
- ? propertiesStr.join(",")
845
- : `{${propertiesStr.join(",")}}`;
939
+ const serializeContent = `{${propertiesStr.join(",")}}`;
846
940
  return `"${serializedName}": ${serializeContent}`;
847
941
  }
848
942
  return `"${serializedName}": ${serializeRequestValue(
@@ -948,72 +1042,86 @@ function getNullableCheck(name: string, type: SdkType) {
948
1042
  return `${name} === null ? null :`;
949
1043
  }
950
1044
 
951
- /**
952
- *
953
- * This function helps translating an HLC request to RLC request,
954
- * extracting properties from body and headers and building the RLC response object
955
- */
956
- interface RequestModelMappingResult {
957
- propertiesStr: string[];
958
- directAssignment?: boolean;
1045
+ export function getSerializationExpression(
1046
+ context: SdkContext,
1047
+ property: SdkModelPropertyType,
1048
+ propertyPath: string
1049
+ ): string {
1050
+ const dot = propertyPath.endsWith("?") ? "." : "";
1051
+ const propertyPathWithDot = `${propertyPath ? `${propertyPath}${dot}` : `${dot}`}`;
1052
+ const nullOrUndefinedPrefix = getPropertySerializationPrefix(
1053
+ context,
1054
+ property,
1055
+ propertyPath
1056
+ );
1057
+
1058
+ const propertyFullName = getPropertyFullName(
1059
+ context,
1060
+ property,
1061
+ propertyPathWithDot
1062
+ );
1063
+ const serializeFunctionName = buildModelSerializer(
1064
+ context,
1065
+ getNullableValidType(property.type),
1066
+ false,
1067
+ true
1068
+ );
1069
+ if (serializeFunctionName) {
1070
+ return `${nullOrUndefinedPrefix}${serializeFunctionName}(${propertyFullName})`;
1071
+ } else if (isAzureCoreErrorType(context.program, property.type.__raw)) {
1072
+ return `${nullOrUndefinedPrefix}${propertyFullName}`;
1073
+ } else {
1074
+ return serializeRequestValue(
1075
+ context,
1076
+ property.type,
1077
+ propertyFullName,
1078
+ !property.optional,
1079
+ getEncodeForType(property.type)
1080
+ );
1081
+ }
959
1082
  }
960
- export function getRequestModelMapping(
1083
+
1084
+ export function getRequestModelProperties(
961
1085
  context: SdkContext,
962
1086
  modelPropertyType: SdkModelType & { optional?: boolean },
963
1087
  propertyPath: string = "body"
964
- ): RequestModelMappingResult {
965
- const props: string[] = [];
1088
+ ): Array<[string, string]> {
1089
+ const props: [string, string][] = [];
966
1090
  const allParents = getAllAncestors(modelPropertyType);
967
1091
  const properties: SdkModelPropertyType[] =
968
1092
  getAllProperties(modelPropertyType, allParents) ?? [];
969
1093
  if (properties.length <= 0) {
970
- return { propertiesStr: [] };
1094
+ return [];
971
1095
  }
972
1096
  for (const property of properties) {
973
1097
  if (property.kind === "property" && isReadOnly(property)) {
974
1098
  continue;
975
1099
  }
976
- const dot = propertyPath.endsWith("?") ? "." : "";
977
- const serializedName = getPropertySerializedName(property);
978
- const propertyPathWithDot = `${propertyPath ? `${propertyPath}${dot}` : `${dot}`}`;
979
- const nullOrUndefinedPrefix = getPropertySerializationPrefix(
980
- context,
981
- property,
982
- propertyPath
983
- );
984
1100
 
985
- const propertyFullName = getPropertyFullName(
986
- context,
987
- property,
988
- propertyPathWithDot
989
- );
990
- const serializeFunctionName = buildModelSerializer(
991
- context,
992
- getNullableValidType(property.type),
993
- false,
994
- true
995
- );
996
- if (serializeFunctionName) {
997
- props.push(
998
- `"${serializedName}": ${nullOrUndefinedPrefix}${serializeFunctionName}(${propertyFullName})`
999
- );
1000
- } else if (isAzureCoreErrorType(context.program, property.type.__raw)) {
1001
- props.push(
1002
- `"${serializedName}": ${nullOrUndefinedPrefix}${propertyFullName}`
1003
- );
1004
- } else {
1005
- const serializedValue = serializeRequestValue(
1006
- context,
1007
- property.type,
1008
- propertyFullName,
1009
- !property.optional,
1010
- getEncodeForType(property.type)
1011
- );
1012
- props.push(`"${serializedName}": ${serializedValue}`);
1013
- }
1101
+ props.push([
1102
+ getPropertySerializedName(property)!,
1103
+ getSerializationExpression(context, property, propertyPath)
1104
+ ]);
1014
1105
  }
1015
1106
 
1016
- return { propertiesStr: props };
1107
+ return props;
1108
+ }
1109
+
1110
+ /**
1111
+ *
1112
+ * This function helps translating an HLC request to RLC request,
1113
+ * extracting properties from body and headers and building the RLC response object
1114
+ */
1115
+ export function getRequestModelMapping(
1116
+ context: SdkContext,
1117
+ modelPropertyType: SdkModelType & { optional?: boolean },
1118
+ propertyPath: string = "body"
1119
+ ): string[] {
1120
+ return getRequestModelProperties(
1121
+ context,
1122
+ modelPropertyType,
1123
+ propertyPath
1124
+ ).map(([name, value]) => `"${name}": ${value}`);
1017
1125
  }
1018
1126
 
1019
1127
  function getPropertySerializedName(property: SdkModelPropertyType) {
@@ -1164,7 +1272,7 @@ export function serializeRequestValue(
1164
1272
  return `${clientValue} as any`;
1165
1273
  }
1166
1274
  case "model": // this is to build serialization logic for spread model types
1167
- return `{${getRequestModelMapping(context, type, "").propertiesStr.join(",")}}`;
1275
+ return `{${getRequestModelMapping(context, type, "").join(",")}}`;
1168
1276
  case "nullable":
1169
1277
  return serializeRequestValue(
1170
1278
  context,
@@ -34,7 +34,8 @@ export function buildModelDeserializer(
34
34
  if (
35
35
  !type.usage ||
36
36
  (type.usage !== undefined &&
37
- (type.usage & UsageFlags.Output) !== UsageFlags.Output)
37
+ (type.usage & UsageFlags.Output) !== UsageFlags.Output &&
38
+ (type.usage & UsageFlags.Exception) !== UsageFlags.Exception)
38
39
  ) {
39
40
  return undefined;
40
41
  }
@@ -10,7 +10,13 @@ import {
10
10
  import { toCamelCase, toPascalCase } from "../../utils/casingUtils.js";
11
11
 
12
12
  import { SdkContext } from "../../utils/interfaces.js";
13
- import { getRequestModelMapping } from "../helpers/operationHelpers.js";
13
+ import {
14
+ getAllAncestors,
15
+ getAllProperties,
16
+ getPropertyFullName,
17
+ getRequestModelMapping,
18
+ getSerializationExpression
19
+ } from "../helpers/operationHelpers.js";
14
20
  import { normalizeModelName } from "../emitModels.js";
15
21
  import { NameType } from "@azure-tools/rlc-common";
16
22
  import { isAzureCoreErrorType } from "../../utils/modelUtils.js";
@@ -19,6 +25,9 @@ import {
19
25
  isSupportedSerializeType,
20
26
  ModelSerializeOptions
21
27
  } from "./serializeUtils.js";
28
+ import { MultipartHelpers } from "../static-helpers-metadata.js";
29
+ import { resolveReference } from "../../framework/reference.js";
30
+ import { isOrExtendsHttpFile } from "@typespec/http";
22
31
 
23
32
  export function buildModelSerializer(
24
33
  context: SdkContext,
@@ -43,7 +52,10 @@ export function buildModelSerializer(
43
52
  // throw new Error(`NYI Serialization of anonymous types`);
44
53
  return undefined;
45
54
  }
46
- if (isAzureCoreErrorType(context.program, type.__raw!)) {
55
+ if (
56
+ isAzureCoreErrorType(context.program, type.__raw!) ||
57
+ isOrExtendsHttpFile(context.program, type.__raw!)
58
+ ) {
47
59
  return undefined;
48
60
  }
49
61
  }
@@ -331,37 +343,90 @@ function buildModelTypeSerializer(
331
343
  statements: ["return item;"]
332
344
  };
333
345
 
334
- // This is only handling the compatibility mode, will need to update when we handle additionalProperties property.
335
- const additionalPropertiesSpread = hasAdditionalProperties(type)
336
- ? "...item"
337
- : "";
346
+ if (
347
+ (type.usage & UsageFlags.Input) === UsageFlags.Input &&
348
+ (type.usage & UsageFlags.MultipartFormData) === UsageFlags.MultipartFormData
349
+ ) {
350
+ // For MFD models, serialize into an array of parts
351
+ // TODO: cleaner abstraction, quite a bit of duplication with the non-MFD stuff here
352
+ const parts: string[] = [];
338
353
 
339
- const { directAssignment, propertiesStr } = getRequestModelMapping(
340
- context,
341
- type,
342
- "item"
343
- );
344
- if (additionalPropertiesSpread) {
345
- propertiesStr.unshift(additionalPropertiesSpread);
346
- }
347
- const serializeContent =
348
- directAssignment === true
349
- ? propertiesStr.join(",")
350
- : `{${propertiesStr.join(",")}}`;
354
+ const properties = getAllProperties(type, getAllAncestors(type));
355
+ for (const property of properties) {
356
+ if (property.kind !== "property") {
357
+ continue;
358
+ }
359
+ const expr = getSerializationExpression(context, property, "item");
360
+
361
+ let partDefinition: string;
362
+ if (property.isMultipartFileInput) {
363
+ const createFilePartDescriptorDefinition = resolveReference(
364
+ MultipartHelpers.createFilePartDescriptor
365
+ );
366
+
367
+ const itemPath = property.multipartOptions?.isMulti
368
+ ? "x"
369
+ : getPropertyFullName(context, property, "item");
370
+ partDefinition = `${createFilePartDescriptorDefinition}("${property.serializedName}", ${itemPath}, )`;
371
+
372
+ // If the TypeSpec doesn't specify a default content type, TCGC will infer a default of "*/*".
373
+ // In this case, we actually want the content type to be left unset so that Core will take care of
374
+ // setting the content type correctly.
375
+ const contentType =
376
+ property.multipartOptions?.defaultContentTypes?.[0] === "*/*"
377
+ ? undefined
378
+ : property.multipartOptions?.defaultContentTypes?.[0];
351
379
 
352
- const output = [];
380
+ if (property.multipartOptions?.isMulti) {
381
+ partDefinition = `...(item["${property.serializedName}"].map((x: unknown) => ${createFilePartDescriptorDefinition}("${property.serializedName}", x${contentType ? `", ${contentType}"` : ""})))`;
382
+ } else {
383
+ partDefinition = `${createFilePartDescriptorDefinition}("${property.serializedName}", item["${property.serializedName}"]${contentType ? `, "${contentType}"` : ""})`;
384
+ }
385
+ } else if (property.multipartOptions?.isMulti) {
386
+ partDefinition = `...((${expr}).map((x: unknown) => ({ name: "${property.serializedName}", body: x })))`;
387
+ } else {
388
+ partDefinition = `{ name: "${property.serializedName}", body: (${expr}) }`;
389
+ }
390
+
391
+ if (property.optional) {
392
+ parts.push(
393
+ `...(${getPropertyFullName(context, property, "item")} === undefined ? [] : [${partDefinition}])`
394
+ );
395
+ } else {
396
+ parts.push(partDefinition);
397
+ }
398
+
399
+ // TODO: How to handle additionalProperties for MFD?
400
+ }
401
+
402
+ serializerFunction.statements = [`return [${parts.join(",")}]`];
403
+ } else {
404
+ // This is only handling the compatibility mode, will need to update when we handle additionalProperties property.
405
+ const additionalPropertiesSpread = hasAdditionalProperties(type)
406
+ ? "...item"
407
+ : "";
353
408
 
354
- // don't emit a serializer if there is nothing to serialize
355
- if (propertiesStr.length) {
356
- output.push(`
409
+ const propertiesStr = getRequestModelMapping(context, type, "item");
410
+
411
+ if (additionalPropertiesSpread) {
412
+ propertiesStr.unshift(additionalPropertiesSpread);
413
+ }
414
+ const serializeContent = `{${propertiesStr.join(",")}}`;
415
+
416
+ const output = [];
417
+
418
+ // don't emit a serializer if there is nothing to serialize
419
+ if (propertiesStr.length) {
420
+ output.push(`
357
421
  return ${serializeContent}
358
422
  `);
359
- } else {
360
- output.push(`
423
+ } else {
424
+ output.push(`
361
425
  return item;
362
426
  `);
427
+ }
428
+ serializerFunction.statements = output;
363
429
  }
364
- serializerFunction.statements = output;
365
430
  return serializerFunction;
366
431
  }
367
432
 
@@ -71,3 +71,16 @@ export const PollingHelpers = {
71
71
  location: "pollingHelpers.ts"
72
72
  }
73
73
  } as const;
74
+
75
+ export const MultipartHelpers = {
76
+ FileContents: {
77
+ kind: "typeAlias",
78
+ name: "FileContents",
79
+ location: "multipartHelpers.ts"
80
+ },
81
+ createFilePartDescriptor: {
82
+ kind: "function",
83
+ name: "createFilePartDescriptor",
84
+ location: "multipartHelpers.ts"
85
+ }
86
+ } as const;
@@ -5,7 +5,8 @@ import {
5
5
  PackageDetails,
6
6
  PackageFlavor,
7
7
  RLCOptions,
8
- ServiceInfo
8
+ ServiceInfo,
9
+ isAzurePackage
9
10
  } from "@azure-tools/rlc-common";
10
11
  import {
11
12
  getHttpOperationWithCache,
@@ -32,6 +33,15 @@ export function transformRLCOptions(
32
33
  emitterOptions,
33
34
  dpgContext.generationPathDetail?.rootDir ?? ""
34
35
  );
36
+ if (
37
+ !isAzurePackage({ options }) &&
38
+ emitterOptions.isModularLibrary !== false
39
+ ) {
40
+ options.isModularLibrary = true;
41
+ }
42
+ if (dpgContext.arm && emitterOptions.isModularLibrary !== false) {
43
+ options.isModularLibrary = true;
44
+ }
35
45
  const batch = getRLCClients(dpgContext);
36
46
  options.batch = batch;
37
47
  return options;