@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
@@ -1,12 +1,9 @@
1
1
  import { NameType, normalizeName } from "@azure-tools/rlc-common";
2
2
  import { Project, SourceFile } from "ts-morph";
3
- import {
4
- getClassicalClientName,
5
- getClientName
6
- } from "./helpers/namingHelpers.js";
3
+ import { getClassicalClientName } from "./helpers/namingHelpers.js";
7
4
  import { ModularEmitterOptions } from "./interfaces.js";
8
5
  import { resolveReference } from "../framework/reference.js";
9
- import { PagingHelpers } from "./static-helpers-metadata.js";
6
+ import { MultipartHelpers, PagingHelpers } from "./static-helpers-metadata.js";
10
7
  import {
11
8
  SdkClientType,
12
9
  SdkContext,
@@ -14,6 +11,7 @@ import {
14
11
  } from "@azure-tools/typespec-client-generator-core";
15
12
  import { getModularClientOptions } from "../utils/clientUtils.js";
16
13
  import { getMethodHierarchiesMap } from "../utils/operationUtil.js";
14
+ import { join } from "path/posix";
17
15
 
18
16
  export function buildRootIndex(
19
17
  context: SdkContext,
@@ -53,11 +51,12 @@ export function buildRootIndex(
53
51
  const modelsExportsIndex = rootIndexFile
54
52
  .getExportDeclarations()
55
53
  ?.find((i) => {
56
- return i.getModuleSpecifierValue() === `./models/index.js`;
54
+ return i.getModuleSpecifierValue()?.startsWith(`./models/`);
57
55
  });
58
56
  if (!modelsExportsIndex) {
59
57
  exportModules(rootIndexFile, project, srcPath, clientName, "models", {
60
- isTopLevel: true
58
+ isTopLevel: true,
59
+ recursive: true
61
60
  });
62
61
  }
63
62
  exportModules(rootIndexFile, project, srcPath, clientName, "api", {
@@ -71,6 +70,7 @@ export function buildRootIndex(
71
70
  });
72
71
 
73
72
  exportPagingTypes(context, rootIndexFile);
73
+ exportFileContentsType(context, rootIndexFile);
74
74
  }
75
75
 
76
76
  /**
@@ -109,6 +109,28 @@ function hasPaging(context: SdkContext): boolean {
109
109
  });
110
110
  }
111
111
 
112
+ function exportFileContentsType(
113
+ context: SdkContext,
114
+ rootIndexFile: SourceFile
115
+ ) {
116
+ if (
117
+ context.sdkPackage.models.some((x) =>
118
+ x.properties.some(
119
+ (y) => y.kind === "property" && y.multipartOptions?.isFilePart
120
+ )
121
+ )
122
+ ) {
123
+ const existingExports = getExistingExports(rootIndexFile);
124
+ const namedExports = [resolveReference(MultipartHelpers.FileContents)];
125
+
126
+ const newNamedExports = getNewNamedExports(namedExports, existingExports);
127
+
128
+ if (newNamedExports.length > 0) {
129
+ addExportsToRootIndexFile(rootIndexFile, newNamedExports);
130
+ }
131
+ }
132
+ }
133
+
112
134
  function getExistingExports(rootIndexFile: SourceFile): Set<string> {
113
135
  return new Set(
114
136
  rootIndexFile
@@ -177,7 +199,7 @@ function exportClassicalClient(
177
199
  subfolder: string,
178
200
  isSubClient: boolean = false
179
201
  ) {
180
- const clientName = `${getClientName(client)}Client`;
202
+ const clientName = client.name;
181
203
  indexFile.addExportDeclaration({
182
204
  namedExports: [clientName],
183
205
  moduleSpecifier: `./${
@@ -190,6 +212,7 @@ export interface ExportModulesOptions {
190
212
  interfaceOnly?: boolean;
191
213
  isTopLevel?: boolean;
192
214
  subfolder?: string;
215
+ recursive?: boolean;
193
216
  }
194
217
 
195
218
  function exportModules(
@@ -201,58 +224,80 @@ function exportModules(
201
224
  options: ExportModulesOptions = {
202
225
  interfaceOnly: false,
203
226
  isTopLevel: false,
204
- subfolder: ""
227
+ subfolder: "",
228
+ recursive: false
205
229
  }
206
230
  ) {
207
- const modelsFile = project.getSourceFile(
208
- `${srcPath}/${
209
- options.subfolder !== "" && options.subfolder
210
- ? options.subfolder + "/"
211
- : ""
212
- }${moduleName}/index.ts`
213
- );
214
- if (!modelsFile) {
215
- return;
231
+ const subfolder = options.subfolder ?? "";
232
+ let folders = [];
233
+ if (options.recursive) {
234
+ folders = project
235
+ .getDirectories()
236
+ .filter((dir) => {
237
+ const targetPath = join(srcPath, subfolder, moduleName);
238
+ return dir.getPath().replace(/\\/g, "/").startsWith(targetPath);
239
+ })
240
+ .map((dir) => {
241
+ return dir.getPath();
242
+ });
243
+ } else {
244
+ folders = [join(srcPath, subfolder, moduleName)];
216
245
  }
246
+ for (const folder of folders) {
247
+ const apiFilePattern = join(folder, "index.ts");
248
+ const modelsFile = project.getSourceFile(apiFilePattern);
249
+ if (!modelsFile) {
250
+ continue;
251
+ }
217
252
 
218
- const exported = [...indexFile.getExportedDeclarations().keys()];
219
- const namedExports = [...modelsFile.getExportedDeclarations().entries()]
220
- .filter((exDeclaration) => {
221
- if (exDeclaration[0].startsWith("_")) {
222
- return false;
223
- }
224
- return exDeclaration[1].some((ex) => {
225
- if (
226
- options.interfaceOnly &&
227
- ex.getKindName() !== "InterfaceDeclaration"
228
- ) {
253
+ const exported = [...indexFile.getExportedDeclarations().keys()];
254
+
255
+ const namedExports = [...modelsFile.getExportedDeclarations().entries()]
256
+ .filter((exDeclaration) => {
257
+ if (exDeclaration[0].startsWith("_")) {
229
258
  return false;
230
259
  }
231
- if (
232
- options.interfaceOnly &&
233
- options.isTopLevel &&
234
- exDeclaration[0].endsWith("Context")
235
- ) {
236
- return false;
260
+ return exDeclaration[1].some((ex) => {
261
+ if (
262
+ options.interfaceOnly &&
263
+ ex.getKindName() !== "InterfaceDeclaration"
264
+ ) {
265
+ return false;
266
+ }
267
+ if (
268
+ moduleName === "models" &&
269
+ ex.getKindName() === "FunctionDeclaration" &&
270
+ (exDeclaration[0].endsWith("Serializer") ||
271
+ exDeclaration[0].endsWith("Deserializer"))
272
+ ) {
273
+ return false;
274
+ }
275
+ if (
276
+ options.interfaceOnly &&
277
+ options.isTopLevel &&
278
+ exDeclaration[0].endsWith("Context")
279
+ ) {
280
+ return false;
281
+ }
282
+ return true;
283
+ });
284
+ })
285
+ .map((exDeclaration) => {
286
+ if (exported.indexOf(exDeclaration[0]) > -1) {
287
+ return `${exDeclaration[0]} as ${clientName}${exDeclaration[0]}`;
237
288
  }
238
- return true;
289
+ return exDeclaration[0];
239
290
  });
240
- })
241
- .map((exDeclaration) => {
242
- if (exported.indexOf(exDeclaration[0]) > -1) {
243
- return `${exDeclaration[0]} as ${clientName}${exDeclaration[0]}`;
244
- }
245
- return exDeclaration[0];
291
+ const moduleSpecifier = `.${modelsFile
292
+ .getFilePath()
293
+ .replace(indexFile.getDirectoryPath(), "")
294
+ .replace(/\\/g, "/")
295
+ .replace(".ts", "")}.js`;
296
+ indexFile.addExportDeclaration({
297
+ moduleSpecifier,
298
+ namedExports
246
299
  });
247
- const moduleSpecifier = `./${
248
- options.isTopLevel && options.subfolder !== "" && options.subfolder
249
- ? options.subfolder + "/"
250
- : ""
251
- }${moduleName}/index.js`;
252
- indexFile.addExportDeclaration({
253
- moduleSpecifier,
254
- namedExports
255
- });
300
+ }
256
301
  }
257
302
 
258
303
  export function buildSubClientIndexFile(
@@ -267,7 +312,7 @@ export function buildSubClientIndexFile(
267
312
  undefined,
268
313
  { overwrite: true }
269
314
  );
270
- const clientName = `${getClientName(client)}Client`;
315
+ const clientName = `${getClassicalClientName(client)}`;
271
316
  const clientFilePath = `${srcPath}/${
272
317
  subfolder && subfolder !== "" ? subfolder + "/" : ""
273
318
  }${normalizeName(clientName, NameType.File)}.ts`;
@@ -11,6 +11,7 @@ import { getModularClientOptions } from "../utils/clientUtils.js";
11
11
  export interface buildSubpathIndexFileOptions {
12
12
  exportIndex?: boolean;
13
13
  interfaceOnly?: boolean;
14
+ recursive?: boolean;
14
15
  }
15
16
 
16
17
  export function buildSubpathIndexFile(
@@ -26,73 +27,94 @@ export function buildSubpathIndexFile(
26
27
  const srcPath = emitterOptions.modularOptions.sourceRoot;
27
28
  // Skip to export these files because they are used internally.
28
29
  const skipFiles = ["pagingHelpers.ts", "pollingHelpers.ts"];
29
- const apiFilePattern = join(srcPath, subfolder, subpath);
30
- const apiFiles = emitterOptions.project.getSourceFiles().filter((file) => {
31
- return file
32
- .getFilePath()
33
- .replace(/\\/g, "/")
34
- .startsWith(apiFilePattern.replace(/\\/g, "/"));
35
- });
36
- if (apiFiles.length === 0) {
37
- return;
30
+ let folders = [];
31
+ if (options.recursive) {
32
+ folders = emitterOptions.project
33
+ .getDirectories()
34
+ .filter((dir) => {
35
+ const targetPath = join(srcPath, subfolder, subpath);
36
+ return (
37
+ dir.getPath().replace(/\\/g, "/").startsWith(targetPath) &&
38
+ !emitterOptions.project.getSourceFile(`${dir}/index.ts`)
39
+ );
40
+ })
41
+ .map((dir) => {
42
+ return dir.getPath();
43
+ });
44
+ } else {
45
+ folders = [join(srcPath, subfolder, subpath)];
38
46
  }
39
- const indexFile = emitterOptions.project.createSourceFile(
40
- `${srcPath}/${subfolder}/${subpath}/index.ts`
41
- );
42
- for (const file of apiFiles) {
43
- const filePath = file.getFilePath();
44
- if (!options.exportIndex && filePath.endsWith("index.ts")) {
45
- continue;
46
- }
47
- // Skip to export these files because they are used internally.
48
- if (skipFiles.some((skipFile) => filePath.endsWith(skipFile))) {
49
- continue;
50
- }
51
- if (filePath === indexFile.getFilePath()) {
47
+ for (const folder of folders) {
48
+ const apiFilePattern =
49
+ subpath === "models" ? join(folder, "models.ts") : folder;
50
+ const apiFiles = emitterOptions.project.getSourceFiles().filter((file) => {
51
+ return file
52
+ .getFilePath()
53
+ .replace(/\\/g, "/")
54
+ .startsWith(apiFilePattern.replace(/\\/g, "/"));
55
+ });
56
+ if (apiFiles.length === 0) {
52
57
  continue;
53
58
  }
59
+ const indexFile = emitterOptions.project.createSourceFile(
60
+ `${folder}/index.ts`
61
+ );
62
+ for (const file of apiFiles) {
63
+ const filePath = file.getFilePath();
64
+ if (!options.exportIndex && filePath.endsWith("index.ts")) {
65
+ continue;
66
+ }
67
+ // Skip to export these files because they are used internally.
68
+ if (skipFiles.some((skipFile) => filePath.endsWith(skipFile))) {
69
+ continue;
70
+ }
71
+ if (filePath === indexFile.getFilePath()) {
72
+ continue;
73
+ }
54
74
 
55
- let namedExports: string[] = [...file.getExportedDeclarations().entries()]
56
- .filter((exDeclaration) => {
57
- if (exDeclaration[0].startsWith("_")) {
58
- return false;
59
- }
60
- return exDeclaration[1].some((ex) => {
61
- if (
62
- options.interfaceOnly &&
63
- ex.getKindName() !== "InterfaceDeclaration"
64
- ) {
75
+ let namedExports: string[] = [...file.getExportedDeclarations().entries()]
76
+ .filter((exDeclaration) => {
77
+ if (exDeclaration[0].startsWith("_")) {
65
78
  return false;
66
79
  }
80
+ return exDeclaration[1].some((ex) => {
81
+ if (
82
+ options.interfaceOnly &&
83
+ ex.getKindName() !== "InterfaceDeclaration"
84
+ ) {
85
+ return false;
86
+ }
67
87
 
68
- // skip exporting serializers for models
69
- if (
70
- subpath === "models" &&
71
- ex.getKindName() === "FunctionDeclaration" &&
72
- (exDeclaration[0].endsWith("Serializer") ||
73
- exDeclaration[0].endsWith("Deserializer"))
74
- ) {
75
- return false;
76
- }
88
+ // skip exporting serializers for models
89
+ if (
90
+ subpath === "models" &&
91
+ ex.getKindName() === "FunctionDeclaration" &&
92
+ (exDeclaration[0].endsWith("Serializer") ||
93
+ exDeclaration[0].endsWith("Deserializer"))
94
+ ) {
95
+ return false;
96
+ }
77
97
 
78
- return true;
98
+ return true;
99
+ });
100
+ })
101
+ .map((exDeclaration) => {
102
+ return exDeclaration[0];
79
103
  });
80
- })
81
- .map((exDeclaration) => {
82
- return exDeclaration[0];
104
+ // Skip to export PagedResult and BuildPagedAsyncIteratorOptions
105
+ if (filePath.endsWith("pagingTypes.ts")) {
106
+ namedExports = namedExports.filter(
107
+ (ex) =>
108
+ !["PagedResult", "BuildPagedAsyncIteratorOptions"].includes(ex)
109
+ );
110
+ }
111
+ indexFile.addExportDeclaration({
112
+ moduleSpecifier: `.${filePath
113
+ .replace(indexFile.getDirectoryPath(), "")
114
+ .replace(/\\/g, "/")
115
+ .replace(".ts", "")}.js`,
116
+ namedExports
83
117
  });
84
- // Skip to export PagedResult and BuildPagedAsyncIteratorOptions
85
- if (filePath.endsWith("pagingTypes.ts")) {
86
- namedExports = namedExports.filter(
87
- (ex) => !["PagedResult", "BuildPagedAsyncIteratorOptions"].includes(ex)
88
- );
89
118
  }
90
- indexFile.addExportDeclaration({
91
- moduleSpecifier: `.${filePath
92
- .replace(indexFile.getDirectoryPath(), "")
93
- .replace(/\\/g, "/")
94
- .replace(".ts", "")}.js`,
95
- namedExports
96
- });
97
119
  }
98
120
  }
@@ -35,7 +35,6 @@ import {
35
35
 
36
36
  import { SdkContext } from "../utils/interfaces.js";
37
37
  import { addDeclaration } from "../framework/declaration.js";
38
- import { addImportBySymbol } from "../utils/importHelper.js";
39
38
  import { buildModelDeserializer } from "./serialization/buildDeserializerFunction.js";
40
39
  import { buildModelSerializer } from "./serialization/buildSerializerFunction.js";
41
40
  import { extractPagedMetadataNested } from "../utils/operationUtil.js";
@@ -46,7 +45,7 @@ import {
46
45
  import path from "path";
47
46
  import { refkey } from "../framework/refkey.js";
48
47
  import { useContext } from "../contextManager.js";
49
- import { isMetadata } from "@typespec/http";
48
+ import { isMetadata, isOrExtendsHttpFile } from "@typespec/http";
50
49
  import {
51
50
  isAzureCoreErrorType,
52
51
  isAzureCoreLroType
@@ -56,6 +55,8 @@ import { isDiscriminatedUnion } from "./serialization/serializeUtils.js";
56
55
  import { reportDiagnostic } from "../lib.js";
57
56
  import { NoTarget } from "@typespec/compiler";
58
57
  import { emitQueue } from "../framework/hooks/sdkTypes.js";
58
+ import { resolveReference } from "../framework/reference.js";
59
+ import { MultipartHelpers } from "./static-helpers-metadata.js";
59
60
 
60
61
  type InterfaceStructure = OptionalKind<InterfaceDeclarationStructure> & {
61
62
  extends?: string[];
@@ -84,22 +85,9 @@ export function emitTypes(
84
85
  context: SdkContext,
85
86
  { sourceRoot }: { sourceRoot: string }
86
87
  ) {
87
- const { sdkPackage } = context;
88
88
  const outputProject = useContext("outputProject");
89
89
 
90
- const modelsFilePath = getModelsPath(sourceRoot);
91
90
  let sourceFile;
92
- if (
93
- emitQueue.size > 0 &&
94
- (sdkPackage.models.length > 0 || sdkPackage.enums.length > 0)
95
- ) {
96
- sourceFile = outputProject.createSourceFile(modelsFilePath);
97
- if (!sourceFile) {
98
- throw new Error(`Failed to create source file at ${modelsFilePath}`);
99
- }
100
- } else {
101
- return;
102
- }
103
91
 
104
92
  for (const type of emitQueue) {
105
93
  if (!isGenerableType(type)) {
@@ -108,19 +96,31 @@ export function emitTypes(
108
96
  if (isAzureCoreLroType(type.__raw)) {
109
97
  continue;
110
98
  }
99
+
100
+ const namespaces = getModelNamespaces(context, type);
101
+ const filepath = getModelsPath(sourceRoot, namespaces);
102
+ sourceFile = outputProject.getSourceFile(filepath);
103
+ if (!sourceFile) {
104
+ sourceFile = outputProject.createSourceFile(filepath);
105
+ }
111
106
  emitType(context, type, sourceFile);
112
107
  }
113
108
 
114
- if (
115
- sourceFile.getInterfaces().length === 0 &&
116
- sourceFile.getTypeAliases().length === 0 &&
117
- sourceFile.getEnums().length === 0
118
- ) {
119
- sourceFile.delete();
120
- return;
109
+ const modelFiles = outputProject.getSourceFiles(
110
+ sourceRoot + "/models/**/*.ts"
111
+ );
112
+ for (const modelFile of modelFiles) {
113
+ if (
114
+ modelFile.getInterfaces().length === 0 &&
115
+ modelFile.getTypeAliases().length === 0 &&
116
+ modelFile.getEnums().length === 0
117
+ ) {
118
+ modelFile.delete();
119
+ return;
120
+ }
121
121
  }
122
- addImportBySymbol("serializeRecord", sourceFile);
123
- return sourceFile;
122
+
123
+ return modelFiles;
124
124
  }
125
125
 
126
126
  function emitType(context: SdkContext, type: SdkType, sourceFile: SourceFile) {
@@ -128,11 +128,15 @@ function emitType(context: SdkContext, type: SdkType, sourceFile: SourceFile) {
128
128
  if (isAzureCoreErrorType(context.program, type.__raw)) {
129
129
  return;
130
130
  }
131
+ if (isOrExtendsHttpFile(context.program, type.__raw!)) {
132
+ return;
133
+ }
131
134
  if (
132
135
  !type.usage ||
133
136
  (type.usage !== undefined &&
134
137
  (type.usage & UsageFlags.Output) !== UsageFlags.Output &&
135
- (type.usage & UsageFlags.Input) !== UsageFlags.Input)
138
+ (type.usage & UsageFlags.Input) !== UsageFlags.Input &&
139
+ (type.usage & UsageFlags.Exception) !== UsageFlags.Exception)
136
140
  ) {
137
141
  return;
138
142
  }
@@ -175,7 +179,9 @@ function emitType(context: SdkContext, type: SdkType, sourceFile: SourceFile) {
175
179
  const apiVersionEnumOnly = type.usage === UsageFlags.ApiVersionEnum;
176
180
  const inputUsage = (type.usage & UsageFlags.Input) === UsageFlags.Input;
177
181
  const outputUsage = (type.usage & UsageFlags.Output) === UsageFlags.Output;
178
- if (!(inputUsage || outputUsage || apiVersionEnumOnly)) {
182
+ const exceptionUsage =
183
+ (type.usage & UsageFlags.Exception) === UsageFlags.Exception;
184
+ if (!(inputUsage || outputUsage || apiVersionEnumOnly || exceptionUsage)) {
179
185
  return;
180
186
  }
181
187
  const [enumType, knownValuesEnum] = buildEnumTypes(context, type);
@@ -217,8 +223,49 @@ export function getApiVersionEnum(context: SdkContext) {
217
223
  return apiVersionEnum;
218
224
  }
219
225
 
220
- export function getModelsPath(sourceRoot: string): string {
221
- return path.join(...[sourceRoot, "models", `models.ts`]);
226
+ export function getModelsPath(
227
+ sourceRoot: string,
228
+ modelNamespace: string[] = []
229
+ ): string {
230
+ return path.join(
231
+ ...[
232
+ sourceRoot,
233
+ "models",
234
+ ...modelNamespace.map((n) => normalizeName(n, NameType.File)),
235
+ `models.ts`
236
+ ]
237
+ );
238
+ }
239
+
240
+ export function getModelNamespaces(
241
+ context: SdkContext,
242
+ model: SdkType
243
+ ): string[] {
244
+ const rootNamespace = context.sdkPackage.rootNamespace.split(".");
245
+ if (
246
+ model.kind === "model" ||
247
+ model.kind === "enum" ||
248
+ model.kind === "union"
249
+ ) {
250
+ if (
251
+ model.clientNamespace.startsWith("Azure.ResourceManager") ||
252
+ model.clientNamespace.startsWith("Azure.Core")
253
+ ) {
254
+ return [];
255
+ }
256
+ const segments = model.clientNamespace.split(".");
257
+ if (segments.length > rootNamespace.length) {
258
+ while (segments[0] === rootNamespace[0]) {
259
+ segments.shift();
260
+ rootNamespace.shift();
261
+ }
262
+ return segments;
263
+ }
264
+ return [];
265
+ } else if (model.kind === "array" || model.kind === "dict") {
266
+ return getModelNamespaces(context, model.valueType);
267
+ }
268
+ return [];
222
269
  }
223
270
 
224
271
  function addSerializationFunctions(
@@ -524,10 +571,52 @@ function buildModelProperty(
524
571
  target: NoTarget
525
572
  });
526
573
  }
574
+
575
+ let typeExpression: string;
576
+ if (property.kind === "property" && property.isMultipartFileInput) {
577
+ const multipartOptions = property.multipartOptions;
578
+ typeExpression = "{";
579
+ typeExpression += `contents: ${resolveReference(MultipartHelpers.FileContents)};`;
580
+
581
+ const isContentTypeOptional =
582
+ multipartOptions?.contentType === undefined ||
583
+ multipartOptions.contentType.optional ||
584
+ multipartOptions.defaultContentTypes.length > 0;
585
+ const isFilenameOptional =
586
+ multipartOptions?.filename === undefined ||
587
+ multipartOptions.filename.optional;
588
+
589
+ const contentTypeType = multipartOptions?.contentType
590
+ ? getTypeExpression(context, multipartOptions.contentType.type)
591
+ : "string";
592
+ const filenameType = multipartOptions?.filename
593
+ ? getTypeExpression(context, multipartOptions.filename.type)
594
+ : "string";
595
+
596
+ typeExpression += `contentType${isContentTypeOptional ? "?" : ""}: ${contentTypeType};`;
597
+ typeExpression += `filename${isFilenameOptional ? "?" : ""}: ${filenameType};`;
598
+
599
+ typeExpression += "}";
600
+
601
+ if (isContentTypeOptional && isFilenameOptional) {
602
+ // Allow passing content directly if both filename and content type are optional
603
+ typeExpression = `(${resolveReference(MultipartHelpers.FileContents)}) | ${typeExpression}`;
604
+ } else {
605
+ // If either one is required, still accept File at the top level since it requires a filename
606
+ typeExpression = `File | ${typeExpression}`;
607
+ }
608
+
609
+ if (property.type.kind === "array") {
610
+ typeExpression = `Array<${typeExpression}>`;
611
+ }
612
+ } else {
613
+ typeExpression = getTypeExpression(context, property.type);
614
+ }
615
+
527
616
  const propertyStructure: PropertySignatureStructure = {
528
617
  kind: StructureKind.PropertySignature,
529
618
  name: normalizedPropName,
530
- type: getTypeExpression(context, property.type),
619
+ type: typeExpression,
531
620
  hasQuestionToken: property.optional,
532
621
  isReadonly: isReadOnly(property as SdkBodyModelPropertyType)
533
622
  };