@azure-tools/typespec-ts 0.50.2 → 0.50.3
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 +6 -0
- package/dist/src/framework/hooks/binder.d.ts +1 -1
- package/dist/src/framework/hooks/binder.d.ts.map +1 -1
- package/dist/src/framework/hooks/binder.js +11 -3
- package/dist/src/framework/hooks/binder.js.map +1 -1
- package/dist/src/framework/load-static-helpers.d.ts +3 -0
- package/dist/src/framework/load-static-helpers.d.ts.map +1 -1
- package/dist/src/framework/load-static-helpers.js +49 -38
- package/dist/src/framework/load-static-helpers.js.map +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +19 -10
- package/dist/src/index.js.map +1 -1
- package/dist/src/lib.d.ts +7 -0
- package/dist/src/lib.d.ts.map +1 -1
- package/dist/src/lib.js +5 -0
- package/dist/src/lib.js.map +1 -1
- package/dist/src/modular/buildOperations.d.ts.map +1 -1
- package/dist/src/modular/buildOperations.js +1 -1
- package/dist/src/modular/buildOperations.js.map +1 -1
- package/dist/src/modular/emitModels.d.ts +8 -0
- package/dist/src/modular/emitModels.d.ts.map +1 -1
- package/dist/src/modular/emitModels.js +32 -2
- package/dist/src/modular/emitModels.js.map +1 -1
- package/dist/src/modular/emitSamples.js +9 -4
- package/dist/src/modular/emitSamples.js.map +1 -1
- package/dist/src/modular/emitTests.d.ts +7 -0
- package/dist/src/modular/emitTests.d.ts.map +1 -0
- package/dist/src/modular/emitTests.js +160 -0
- package/dist/src/modular/emitTests.js.map +1 -0
- package/dist/src/modular/external-dependencies.d.ts +42 -0
- package/dist/src/modular/external-dependencies.d.ts.map +1 -1
- package/dist/src/modular/external-dependencies.js +42 -0
- package/dist/src/modular/external-dependencies.js.map +1 -1
- package/dist/src/modular/helpers/exampleValueHelpers.d.ts +83 -0
- package/dist/src/modular/helpers/exampleValueHelpers.d.ts.map +1 -0
- package/dist/src/modular/helpers/exampleValueHelpers.js +631 -0
- package/dist/src/modular/helpers/exampleValueHelpers.js.map +1 -0
- package/dist/src/modular/helpers/operationHelpers.d.ts +22 -2
- package/dist/src/modular/helpers/operationHelpers.d.ts.map +1 -1
- package/dist/src/modular/helpers/operationHelpers.js +178 -9
- package/dist/src/modular/helpers/operationHelpers.js.map +1 -1
- package/dist/src/modular/static-helpers-metadata.d.ts +12 -0
- package/dist/src/modular/static-helpers-metadata.d.ts.map +1 -1
- package/dist/src/modular/static-helpers-metadata.js +12 -0
- package/dist/src/modular/static-helpers-metadata.js.map +1 -1
- package/dist/src/transform/transfromRLCOptions.d.ts.map +1 -1
- package/dist/src/transform/transfromRLCOptions.js +10 -0
- package/dist/src/transform/transfromRLCOptions.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/framework/hooks/binder.ts +15 -5
- package/src/framework/load-static-helpers.ts +79 -51
- package/src/index.ts +22 -7
- package/src/lib.ts +13 -0
- package/src/modular/buildOperations.ts +2 -1
- package/src/modular/emitModels.ts +47 -2
- package/src/modular/emitSamples.ts +7 -1
- package/src/modular/emitTests.ts +227 -0
- package/src/modular/external-dependencies.ts +43 -0
- package/src/modular/helpers/exampleValueHelpers.ts +940 -0
- package/src/modular/helpers/operationHelpers.ts +229 -17
- package/src/modular/static-helpers-metadata.ts +13 -0
- package/src/transform/transfromRLCOptions.ts +14 -0
- package/static/static-helpers/serialization/get-binary-response-body-browser.mts +22 -0
- package/static/static-helpers/serialization/get-binary-response-body.ts +24 -0
- package/static/test-helpers/recordedClient.ts +30 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@azure-tools/typespec-ts",
|
|
3
|
-
"version": "0.50.
|
|
3
|
+
"version": "0.50.3",
|
|
4
4
|
"description": "An experimental TypeSpec emitter for TypeScript RLC",
|
|
5
5
|
"main": "dist/src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
"@typespec/xml": "^0.79.0"
|
|
74
74
|
},
|
|
75
75
|
"dependencies": {
|
|
76
|
-
"@azure-tools/rlc-common": "^0.50.
|
|
76
|
+
"@azure-tools/rlc-common": "^0.50.3",
|
|
77
77
|
"fast-xml-parser": "^4.5.0",
|
|
78
78
|
"fs-extra": "^11.1.0",
|
|
79
79
|
"lodash": "^4.17.21",
|
|
@@ -42,7 +42,7 @@ export interface Binder {
|
|
|
42
42
|
sourceFile: SourceFile
|
|
43
43
|
): string;
|
|
44
44
|
resolveReference(refkey: unknown): string;
|
|
45
|
-
resolveAllReferences(sourceRoot: string): void;
|
|
45
|
+
resolveAllReferences(sourceRoot: string, testRoot?: string): void;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
const PLACEHOLDER_PREFIX = "__PLACEHOLDER_";
|
|
@@ -216,7 +216,7 @@ class BinderImp implements Binder {
|
|
|
216
216
|
/**
|
|
217
217
|
* Applies all tracked imports to their respective source files.
|
|
218
218
|
*/
|
|
219
|
-
resolveAllReferences(sourceRoot: string): void {
|
|
219
|
+
resolveAllReferences(sourceRoot: string, testRoot?: string): void {
|
|
220
220
|
this.project.getSourceFiles().map((file) => {
|
|
221
221
|
this.resolveDeclarationReferences(file);
|
|
222
222
|
this.resolveDependencyReferences(file);
|
|
@@ -232,7 +232,7 @@ class BinderImp implements Binder {
|
|
|
232
232
|
}
|
|
233
233
|
});
|
|
234
234
|
|
|
235
|
-
this.cleanUnreferencedHelpers(sourceRoot);
|
|
235
|
+
this.cleanUnreferencedHelpers(sourceRoot, testRoot);
|
|
236
236
|
}
|
|
237
237
|
|
|
238
238
|
private resolveDependencyReferences(file: SourceFile) {
|
|
@@ -292,7 +292,7 @@ class BinderImp implements Binder {
|
|
|
292
292
|
this.references.get(refkey)!.add(sourceFile);
|
|
293
293
|
}
|
|
294
294
|
|
|
295
|
-
private cleanUnreferencedHelpers(sourceRoot: string) {
|
|
295
|
+
private cleanUnreferencedHelpers(sourceRoot: string, testRoot?: string) {
|
|
296
296
|
const usedHelperNames: string[] = [];
|
|
297
297
|
for (const helper of this.staticHelpers.values()) {
|
|
298
298
|
const sourceFile = helper[SourceFileSymbol];
|
|
@@ -311,7 +311,6 @@ class BinderImp implements Binder {
|
|
|
311
311
|
|
|
312
312
|
function isFileUnused(file: SourceFile) {
|
|
313
313
|
const name = file.getBaseNameWithoutExtension();
|
|
314
|
-
|
|
315
314
|
// If one of the used helpers' name is a prefix of this file, the file likely represents a platform-specific implementation of the helper
|
|
316
315
|
// so it should be marked as used even if the file has no direct references.
|
|
317
316
|
return !usedHelperNames.some((s) => name.startsWith(s));
|
|
@@ -324,6 +323,17 @@ class BinderImp implements Binder {
|
|
|
324
323
|
)
|
|
325
324
|
.filter(isFileUnused)
|
|
326
325
|
.forEach((helperFile) => helperFile.delete());
|
|
326
|
+
|
|
327
|
+
if (!testRoot) {
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
this.project
|
|
331
|
+
//normalizae the final path to adapt to different systems
|
|
332
|
+
.getSourceFiles(
|
|
333
|
+
normalizePath(path.join(testRoot, "test/generated/util/**/*.*ts"))
|
|
334
|
+
)
|
|
335
|
+
.filter(isFileUnused)
|
|
336
|
+
.forEach((helperFile) => helperFile.delete());
|
|
327
337
|
}
|
|
328
338
|
}
|
|
329
339
|
|
|
@@ -37,12 +37,21 @@ export function isStaticHelperMetadata(
|
|
|
37
37
|
|
|
38
38
|
export type StaticHelpers = Record<string, StaticHelperMetadata>;
|
|
39
39
|
|
|
40
|
-
const
|
|
40
|
+
const DEFAULT_SOURCES_STATIC_HELPERS_PATH = "static/static-helpers";
|
|
41
|
+
const DEFAULT_SOURCES_TESTING_HELPERS_PATH = "static/test-helpers";
|
|
41
42
|
|
|
42
43
|
export interface LoadStaticHelpersOptions extends Partial<ModularEmitterOptions> {
|
|
43
44
|
helpersAssetDirectory?: string;
|
|
44
45
|
sourcesDir?: string;
|
|
46
|
+
rootDir?: string;
|
|
45
47
|
program?: Program;
|
|
48
|
+
/** When true, also load test helpers from static/test-helpers/ into test/generated/util/ */
|
|
49
|
+
loadTestHelpers?: boolean;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
interface FileMetadata {
|
|
53
|
+
source: string;
|
|
54
|
+
target: string;
|
|
46
55
|
}
|
|
47
56
|
|
|
48
57
|
export async function loadStaticHelpers(
|
|
@@ -50,64 +59,85 @@ export async function loadStaticHelpers(
|
|
|
50
59
|
helpers: StaticHelpers,
|
|
51
60
|
options: LoadStaticHelpersOptions = {}
|
|
52
61
|
): Promise<Map<string, StaticHelperMetadata>> {
|
|
53
|
-
const sourcesDir = options.sourcesDir ?? "";
|
|
54
62
|
const helpersMap = new Map<string, StaticHelperMetadata>();
|
|
63
|
+
// Load static helpers used in sources code
|
|
55
64
|
const defaultStaticHelpersPath = path.join(
|
|
56
65
|
resolveProjectRoot(),
|
|
57
|
-
|
|
66
|
+
DEFAULT_SOURCES_STATIC_HELPERS_PATH
|
|
58
67
|
);
|
|
59
|
-
const
|
|
68
|
+
const filesInSources = await traverseDirectory(
|
|
60
69
|
options.helpersAssetDirectory ?? defaultStaticHelpersPath,
|
|
61
70
|
options.program
|
|
62
71
|
);
|
|
72
|
+
await loadFiles(filesInSources, options.sourcesDir ?? "");
|
|
73
|
+
// Load static helpers used in testing code (only when loadTestHelpers is enabled)
|
|
74
|
+
if (
|
|
75
|
+
options.loadTestHelpers ??
|
|
76
|
+
(options.options?.generateTest &&
|
|
77
|
+
isAzurePackage({ options: options.options }))
|
|
78
|
+
) {
|
|
79
|
+
const defaultTestingHelpersPath = path.join(
|
|
80
|
+
resolveProjectRoot(),
|
|
81
|
+
DEFAULT_SOURCES_TESTING_HELPERS_PATH
|
|
82
|
+
);
|
|
83
|
+
const filesInTestings = await traverseDirectory(
|
|
84
|
+
defaultTestingHelpersPath,
|
|
85
|
+
options.program,
|
|
86
|
+
[],
|
|
87
|
+
"",
|
|
88
|
+
"test/generated/util"
|
|
89
|
+
);
|
|
90
|
+
await loadFiles(filesInTestings, options.rootDir ?? "");
|
|
91
|
+
}
|
|
92
|
+
return assertAllHelpersLoadedPresent(helpersMap);
|
|
63
93
|
|
|
64
|
-
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
if (
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
94
|
+
async function loadFiles(files: FileMetadata[], generateDir: string) {
|
|
95
|
+
for (const file of files) {
|
|
96
|
+
const targetPath = path.join(generateDir, file.target);
|
|
97
|
+
const contents = await readFile(file.source, "utf-8");
|
|
98
|
+
const addedFile = project.createSourceFile(targetPath, contents, {
|
|
99
|
+
overwrite: true
|
|
100
|
+
});
|
|
101
|
+
addedFile.getImportDeclarations().map((i) => {
|
|
102
|
+
if (!isAzurePackage({ options: options.options })) {
|
|
103
|
+
if (
|
|
104
|
+
i
|
|
105
|
+
.getModuleSpecifier()
|
|
106
|
+
.getFullText()
|
|
107
|
+
.includes("@azure/core-rest-pipeline")
|
|
108
|
+
) {
|
|
109
|
+
i.setModuleSpecifier("@typespec/ts-http-runtime");
|
|
110
|
+
}
|
|
111
|
+
if (
|
|
112
|
+
i
|
|
113
|
+
.getModuleSpecifier()
|
|
114
|
+
.getFullText()
|
|
115
|
+
.includes("@azure-rest/core-client")
|
|
116
|
+
) {
|
|
117
|
+
i.setModuleSpecifier("@typespec/ts-http-runtime");
|
|
118
|
+
}
|
|
79
119
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
) {
|
|
86
|
-
i.setModuleSpecifier("@typespec/ts-http-runtime");
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
for (const entry of Object.values(helpers)) {
|
|
123
|
+
if (!addedFile.getFilePath().endsWith(entry.location)) {
|
|
124
|
+
continue;
|
|
87
125
|
}
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
126
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
127
|
+
const declaration = getDeclarationByMetadata(addedFile, entry);
|
|
128
|
+
if (!declaration) {
|
|
129
|
+
throw new Error(
|
|
130
|
+
`Declaration ${
|
|
131
|
+
entry.name
|
|
132
|
+
} not found in file ${addedFile.getFilePath()}\n This is an Emitter bug, make sure that the map of static helpers passed to loadStaticHelpers matches what is in the file.`
|
|
133
|
+
);
|
|
134
|
+
}
|
|
95
135
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
throw new Error(
|
|
99
|
-
`Declaration ${
|
|
100
|
-
entry.name
|
|
101
|
-
} not found in file ${addedFile.getFilePath()}\n This is an Emitter bug, make sure that the map of static helpers passed to loadStaticHelpers matches what is in the file.`
|
|
102
|
-
);
|
|
136
|
+
entry[SourceFileSymbol] = addedFile;
|
|
137
|
+
helpersMap.set(refkey(entry), entry);
|
|
103
138
|
}
|
|
104
|
-
|
|
105
|
-
entry[SourceFileSymbol] = addedFile;
|
|
106
|
-
helpersMap.set(refkey(entry), entry);
|
|
107
139
|
}
|
|
108
140
|
}
|
|
109
|
-
|
|
110
|
-
return assertAllHelpersLoadedPresent(helpersMap);
|
|
111
141
|
}
|
|
112
142
|
|
|
113
143
|
function assertAllHelpersLoadedPresent(
|
|
@@ -166,7 +196,8 @@ async function traverseDirectory(
|
|
|
166
196
|
directory: string,
|
|
167
197
|
program?: Program,
|
|
168
198
|
result: { source: string; target: string }[] = [],
|
|
169
|
-
relativePath: string = ""
|
|
199
|
+
relativePath: string = "",
|
|
200
|
+
targetBaseDir: string = _targetStaticHelpersBaseDir
|
|
170
201
|
): Promise<{ source: string; target: string }[]> {
|
|
171
202
|
try {
|
|
172
203
|
const files = await readdir(directory);
|
|
@@ -181,18 +212,15 @@ async function traverseDirectory(
|
|
|
181
212
|
filePath,
|
|
182
213
|
program,
|
|
183
214
|
result,
|
|
184
|
-
path.join(relativePath, file)
|
|
215
|
+
path.join(relativePath, file),
|
|
216
|
+
targetBaseDir
|
|
185
217
|
);
|
|
186
218
|
} else if (
|
|
187
219
|
fileStat.isFile() &&
|
|
188
220
|
!file.endsWith(".d.ts") &&
|
|
189
221
|
/.*\..?ts$/.test(file)
|
|
190
222
|
) {
|
|
191
|
-
const target = path.join(
|
|
192
|
-
_targetStaticHelpersBaseDir,
|
|
193
|
-
relativePath,
|
|
194
|
-
file
|
|
195
|
-
);
|
|
223
|
+
const target = path.join(targetBaseDir, relativePath, file);
|
|
196
224
|
result.push({ source: filePath, target });
|
|
197
225
|
}
|
|
198
226
|
})
|
package/src/index.ts
CHANGED
|
@@ -6,13 +6,15 @@ import {
|
|
|
6
6
|
AzureCoreDependencies,
|
|
7
7
|
AzureIdentityDependencies,
|
|
8
8
|
AzurePollingDependencies,
|
|
9
|
-
DefaultCoreDependencies
|
|
9
|
+
DefaultCoreDependencies,
|
|
10
|
+
AzureTestDependencies
|
|
10
11
|
} from "./modular/external-dependencies.js";
|
|
11
12
|
import { clearDirectory } from "./utils/fileSystemUtils.js";
|
|
12
13
|
import { EmitContext, Program } from "@typespec/compiler";
|
|
13
14
|
import { GenerationDirDetail, SdkContext } from "./utils/interfaces.js";
|
|
14
15
|
import {
|
|
15
16
|
CloudSettingHelpers,
|
|
17
|
+
CreateRecorderHelpers,
|
|
16
18
|
MultipartHelpers,
|
|
17
19
|
PagingHelpers,
|
|
18
20
|
PollingHelpers,
|
|
@@ -91,7 +93,7 @@ import {
|
|
|
91
93
|
} from "@azure-tools/typespec-client-generator-core";
|
|
92
94
|
import { transformModularEmitterOptions } from "./modular/buildModularOptions.js";
|
|
93
95
|
import { emitLoggerFile } from "./modular/emitLoggerFile.js";
|
|
94
|
-
import { emitTypes } from "./modular/emitModels.js";
|
|
96
|
+
import { emitTypes, emitNonModelResponseTypes } from "./modular/emitModels.js";
|
|
95
97
|
import { existsSync } from "fs";
|
|
96
98
|
import { getModuleExports } from "./modular/buildProjectFiles.js";
|
|
97
99
|
import {
|
|
@@ -107,6 +109,7 @@ import { provideSdkTypes } from "./framework/hooks/sdkTypes.js";
|
|
|
107
109
|
import { transformRLCModel } from "./transform/transform.js";
|
|
108
110
|
import { transformRLCOptions } from "./transform/transfromRLCOptions.js";
|
|
109
111
|
import { emitSamples } from "./modular/emitSamples.js";
|
|
112
|
+
import { emitTests } from "./modular/emitTests.js";
|
|
110
113
|
import { generateCrossLanguageDefinitionFile } from "./utils/crossLanguageDef.js";
|
|
111
114
|
import { getClassicalClientName } from "./modular/helpers/namingHelpers.js";
|
|
112
115
|
|
|
@@ -148,10 +151,12 @@ export async function $onEmit(context: EmitContext) {
|
|
|
148
151
|
...MultipartHelpers,
|
|
149
152
|
...CloudSettingHelpers,
|
|
150
153
|
...XmlHelpers,
|
|
154
|
+
...(rlcOptions.generateTest ? CreateRecorderHelpers : {}),
|
|
151
155
|
...(rlcOptions.enableStorageCompat ? StorageCompatHelpers : {})
|
|
152
156
|
},
|
|
153
157
|
{
|
|
154
158
|
sourcesDir: dpgContext.generationPathDetail?.modularSourcesDir,
|
|
159
|
+
rootDir: dpgContext.generationPathDetail?.rootDir,
|
|
155
160
|
options: rlcOptions,
|
|
156
161
|
program
|
|
157
162
|
}
|
|
@@ -160,7 +165,8 @@ export async function $onEmit(context: EmitContext) {
|
|
|
160
165
|
? {
|
|
161
166
|
...AzurePollingDependencies,
|
|
162
167
|
...AzureCoreDependencies,
|
|
163
|
-
...AzureIdentityDependencies
|
|
168
|
+
...AzureIdentityDependencies,
|
|
169
|
+
...AzureTestDependencies
|
|
164
170
|
}
|
|
165
171
|
: { ...DefaultCoreDependencies };
|
|
166
172
|
const binder = provideBinder(outputProject, {
|
|
@@ -327,6 +333,7 @@ export async function $onEmit(context: EmitContext) {
|
|
|
327
333
|
);
|
|
328
334
|
|
|
329
335
|
emitTypes(dpgContext, { sourceRoot: modularSourcesRoot });
|
|
336
|
+
emitNonModelResponseTypes(dpgContext, { sourceRoot: modularSourcesRoot });
|
|
330
337
|
buildSubpathIndexFile(modularEmitterOptions, "models", undefined, {
|
|
331
338
|
recursive: true
|
|
332
339
|
});
|
|
@@ -381,7 +388,15 @@ export async function $onEmit(context: EmitContext) {
|
|
|
381
388
|
}
|
|
382
389
|
}
|
|
383
390
|
|
|
384
|
-
|
|
391
|
+
// Enable modular test generation when generateTest is true
|
|
392
|
+
if (dpgContext.rlcOptions?.generateTest === true) {
|
|
393
|
+
await emitTests(dpgContext);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
binder.resolveAllReferences(
|
|
397
|
+
modularSourcesRoot,
|
|
398
|
+
dpgContext.generationPathDetail?.rootDir
|
|
399
|
+
);
|
|
385
400
|
if (program.compilerOptions.noEmit || program.hasError()) {
|
|
386
401
|
return;
|
|
387
402
|
}
|
|
@@ -453,7 +468,7 @@ export async function $onEmit(context: EmitContext) {
|
|
|
453
468
|
"test"
|
|
454
469
|
);
|
|
455
470
|
const hasTestFolder = await existsSync(existingTestFolderPath);
|
|
456
|
-
if (option.
|
|
471
|
+
if (option.generateTest === undefined) {
|
|
457
472
|
if (hasTestFolder) {
|
|
458
473
|
option.generateTest = false;
|
|
459
474
|
} else {
|
|
@@ -535,7 +550,7 @@ export async function $onEmit(context: EmitContext) {
|
|
|
535
550
|
}
|
|
536
551
|
|
|
537
552
|
// TODO: need support snippets generation for multi-client cases. https://github.com/Azure/autorest.typescript/issues/3048
|
|
538
|
-
if (option.generateTest
|
|
553
|
+
if (option.generateTest) {
|
|
539
554
|
for (const subClient of dpgContext.sdkPackage.clients) {
|
|
540
555
|
commonBuilders.push((model) =>
|
|
541
556
|
buildSnippets(model, subClient.name, option.azureSdkForJs)
|
|
@@ -632,7 +647,7 @@ export async function $onEmit(context: EmitContext) {
|
|
|
632
647
|
}
|
|
633
648
|
|
|
634
649
|
// Generate test relevant files
|
|
635
|
-
if (option.generateTest &&
|
|
650
|
+
if (option.generateTest && !hasTestFolder) {
|
|
636
651
|
await emitContentByBuilder(
|
|
637
652
|
program,
|
|
638
653
|
[buildRecordedClientFile, buildSampleTest],
|
package/src/lib.ts
CHANGED
|
@@ -79,6 +79,13 @@ export interface EmitterOptions {
|
|
|
79
79
|
//TODO should remove this after finish the release tool test
|
|
80
80
|
"should-use-pnpm-dep"?: boolean;
|
|
81
81
|
"ignore-nullable-on-optional"?: boolean;
|
|
82
|
+
/**
|
|
83
|
+
* When set to true (default for Azure services), non-model return types (arrays, scalars, enums,
|
|
84
|
+
* bytes with binary content type) will be wrapped in an XxxResponse type to maintain backward
|
|
85
|
+
* compatibility with HLC-generated code during TypeSpec migration.
|
|
86
|
+
* Set to false to return the non-model types directly.
|
|
87
|
+
*/
|
|
88
|
+
"wrap-non-model-return"?: boolean;
|
|
82
89
|
/**
|
|
83
90
|
* When enabled, every regular (non-LRO, non-paging) operation return type is augmented with a
|
|
84
91
|
* `_response` property containing `rawResponse`, `parsedBody`, and `parsedHeaders`.
|
|
@@ -370,6 +377,12 @@ export const RLCOptionsSchema: JSONSchemaType<EmitterOptions> = {
|
|
|
370
377
|
description:
|
|
371
378
|
"If an optional property is also marked as nullable, it will be treated as just optional. Defaults to `true` for Azure services."
|
|
372
379
|
},
|
|
380
|
+
"wrap-non-model-return": {
|
|
381
|
+
type: "boolean",
|
|
382
|
+
nullable: true,
|
|
383
|
+
description:
|
|
384
|
+
"When set to true (default for Azure services), non-model return types (arrays, scalars, enums, bytes with binary content type) will be wrapped in an XxxResponse type for HLC backward compatibility during TypeSpec migration."
|
|
385
|
+
},
|
|
373
386
|
"enable-storage-compat": {
|
|
374
387
|
type: "boolean",
|
|
375
388
|
nullable: true,
|
|
@@ -92,7 +92,7 @@ export function buildOperationFiles(
|
|
|
92
92
|
);
|
|
93
93
|
const deserializeOperationDeclaration = getDeserializePrivateFunction(
|
|
94
94
|
dpgContext,
|
|
95
|
-
op
|
|
95
|
+
[prefixes, op]
|
|
96
96
|
);
|
|
97
97
|
const deserializeHeadersDeclaration =
|
|
98
98
|
getDeserializeHeadersPrivateFunction(dpgContext, op);
|
|
@@ -108,6 +108,7 @@ export function buildOperationFiles(
|
|
|
108
108
|
if (deserializeExceptionHeadersDeclaration) {
|
|
109
109
|
functionsToAdd.push(deserializeExceptionHeadersDeclaration);
|
|
110
110
|
}
|
|
111
|
+
|
|
111
112
|
operationGroupFile.addFunctions(functionsToAdd);
|
|
112
113
|
addDeclaration(
|
|
113
114
|
operationGroupFile,
|
|
@@ -79,9 +79,15 @@ import {
|
|
|
79
79
|
flattenPropertyModelMap,
|
|
80
80
|
getAllOperationsFromClient
|
|
81
81
|
} from "../framework/hooks/sdkTypes.js";
|
|
82
|
-
import {
|
|
83
|
-
|
|
82
|
+
import {
|
|
83
|
+
getAllAncestors,
|
|
84
|
+
getAllProperties,
|
|
85
|
+
buildNonModelResponseTypeDeclaration,
|
|
86
|
+
checkWrapNonModelReturn
|
|
87
|
+
} from "./helpers/operationHelpers.js";
|
|
84
88
|
import { getDirectSubtypes } from "./helpers/typeHelpers.js";
|
|
89
|
+
import { getClientHierarchyMap } from "../utils/clientUtils.js";
|
|
90
|
+
import { getMethodHierarchiesMap } from "../utils/operationUtil.js";
|
|
85
91
|
|
|
86
92
|
type InterfaceStructure = OptionalKind<InterfaceDeclarationStructure> & {
|
|
87
93
|
extends?: string[];
|
|
@@ -164,6 +170,45 @@ export function emitTypes(
|
|
|
164
170
|
return result;
|
|
165
171
|
}
|
|
166
172
|
|
|
173
|
+
/**
|
|
174
|
+
* Emits the XxxResponse wrapper type aliases for non-model return operations
|
|
175
|
+
* into the models.ts file, so they are exported publicly as part of the models API surface.
|
|
176
|
+
* This must be called after emitTypes() and before buildSubpathIndexFile("models").
|
|
177
|
+
*/
|
|
178
|
+
export function emitNonModelResponseTypes(
|
|
179
|
+
context: SdkContext,
|
|
180
|
+
{ sourceRoot }: { sourceRoot: string }
|
|
181
|
+
) {
|
|
182
|
+
const outputProject = useContext("outputProject");
|
|
183
|
+
const clientMap = getClientHierarchyMap(context);
|
|
184
|
+
|
|
185
|
+
const filepath = getModelsPath(sourceRoot);
|
|
186
|
+
let modelsFile = outputProject.getSourceFile(filepath);
|
|
187
|
+
|
|
188
|
+
for (const subClient of clientMap) {
|
|
189
|
+
const methodHierarchies = getMethodHierarchiesMap(context, subClient[1]);
|
|
190
|
+
for (const [prefixKey, operations] of methodHierarchies) {
|
|
191
|
+
const prefixes = prefixKey.split("/");
|
|
192
|
+
for (const op of operations) {
|
|
193
|
+
const { shouldWrap, isBinary } = checkWrapNonModelReturn(context, op);
|
|
194
|
+
if (!shouldWrap) {
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
if (!modelsFile) {
|
|
198
|
+
modelsFile = outputProject.createSourceFile(filepath);
|
|
199
|
+
}
|
|
200
|
+
const method: [string[], typeof op] = [prefixes, op];
|
|
201
|
+
const typeAlias = buildNonModelResponseTypeDeclaration(
|
|
202
|
+
context,
|
|
203
|
+
method,
|
|
204
|
+
isBinary
|
|
205
|
+
);
|
|
206
|
+
addDeclaration(modelsFile, typeAlias, refkey(op, "response"));
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
167
212
|
function emitType(context: SdkContext, type: SdkType, sourceFile: SourceFile) {
|
|
168
213
|
if (type.kind === "model") {
|
|
169
214
|
if (isAzureCoreErrorType(context.program, type.__raw)) {
|
|
@@ -627,9 +627,15 @@ function getParameterValue(
|
|
|
627
627
|
propRetValue =
|
|
628
628
|
paramValue.length > 2 ? paramValue.slice(1, -1) : undefined;
|
|
629
629
|
} else {
|
|
630
|
+
// Don't propagate enableFlatten:false to deeper levels — it's only
|
|
631
|
+
// meant to block consecutive (transition) flatten at the direct child
|
|
632
|
+
// level. Non-flatten properties should recurse with default behavior
|
|
633
|
+
// so that independent inner flattens at deeper levels still work.
|
|
634
|
+
const childOptions =
|
|
635
|
+
options?.overrides?.enableFlatten === false ? undefined : options;
|
|
630
636
|
propRetValue =
|
|
631
637
|
`"${mapper.get(propName) ?? propName}": ` +
|
|
632
|
-
getParameterValue(context, propValue,
|
|
638
|
+
getParameterValue(context, propValue, childOptions);
|
|
633
639
|
}
|
|
634
640
|
if (propRetValue) values.push(propRetValue);
|
|
635
641
|
}
|