@azure-tools/typespec-ts 0.45.1 → 0.46.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 +24 -0
- package/dist/src/framework/hooks/binder.d.ts.map +1 -1
- package/dist/src/framework/hooks/binder.js +10 -4
- package/dist/src/framework/hooks/binder.js.map +1 -1
- package/dist/src/framework/load-static-helpers.d.ts +2 -0
- package/dist/src/framework/load-static-helpers.d.ts.map +1 -1
- package/dist/src/framework/load-static-helpers.js +13 -5
- package/dist/src/framework/load-static-helpers.js.map +1 -1
- package/dist/src/framework/sample.js +0 -5
- package/dist/src/framework/sample.js.map +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +9 -29
- package/dist/src/index.js.map +1 -1
- package/dist/src/lib.d.ts +10 -1
- package/dist/src/lib.d.ts.map +1 -1
- package/dist/src/lib.js +6 -0
- package/dist/src/lib.js.map +1 -1
- package/dist/src/modular/buildRestorePoller.d.ts.map +1 -1
- package/dist/src/modular/buildRestorePoller.js +1 -2
- package/dist/src/modular/buildRestorePoller.js.map +1 -1
- package/dist/src/modular/emitModels.d.ts.map +1 -1
- package/dist/src/modular/emitModels.js +5 -0
- package/dist/src/modular/emitModels.js.map +1 -1
- package/dist/src/modular/emitSamples.d.ts.map +1 -1
- package/dist/src/modular/emitSamples.js +59 -9
- package/dist/src/modular/emitSamples.js.map +1 -1
- package/dist/src/modular/external-dependencies.d.ts.map +1 -1
- package/dist/src/modular/external-dependencies.js +10 -0
- package/dist/src/modular/external-dependencies.js.map +1 -1
- package/dist/src/modular/helpers/clientHelpers.js +6 -4
- package/dist/src/modular/helpers/clientHelpers.js.map +1 -1
- package/dist/src/modular/helpers/operationHelpers.d.ts.map +1 -1
- package/dist/src/modular/helpers/operationHelpers.js +21 -10
- package/dist/src/modular/helpers/operationHelpers.js.map +1 -1
- package/dist/src/modular/serialization/buildSerializerFunction.js +1 -1
- package/dist/src/modular/serialization/buildSerializerFunction.js.map +1 -1
- package/dist/src/modular/static-helpers-metadata.d.ts +5 -0
- package/dist/src/modular/static-helpers-metadata.d.ts.map +1 -1
- package/dist/src/modular/static-helpers-metadata.js +5 -0
- package/dist/src/modular/static-helpers-metadata.js.map +1 -1
- package/dist/src/modular/type-expressions/get-model-expression.d.ts.map +1 -1
- package/dist/src/modular/type-expressions/get-model-expression.js +2 -1
- package/dist/src/modular/type-expressions/get-model-expression.js.map +1 -1
- package/dist/src/utils/emitUtil.js +0 -1
- package/dist/src/utils/emitUtil.js.map +1 -1
- package/dist/src/utils/fileSystemUtils.d.ts +3 -0
- package/dist/src/utils/fileSystemUtils.d.ts.map +1 -0
- package/dist/src/utils/fileSystemUtils.js +40 -0
- package/dist/src/utils/fileSystemUtils.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/src/framework/hooks/binder.ts +12 -4
- package/src/framework/load-static-helpers.ts +15 -3
- package/src/framework/sample.ts +0 -6
- package/src/index.ts +16 -32
- package/src/lib.ts +6 -0
- package/src/modular/buildRestorePoller.ts +1 -2
- package/src/modular/emitModels.ts +7 -0
- package/src/modular/emitSamples.ts +87 -6
- package/src/modular/external-dependencies.ts +10 -0
- package/src/modular/helpers/clientHelpers.ts +5 -5
- package/src/modular/helpers/operationHelpers.ts +24 -10
- package/src/modular/serialization/buildSerializerFunction.ts +1 -1
- package/src/modular/static-helpers-metadata.ts +5 -0
- package/src/modular/type-expressions/get-model-expression.ts +2 -1
- package/src/utils/emitUtil.ts +0 -1
- package/src/utils/fileSystemUtils.ts +47 -0
- package/static/static-helpers/serialization/get-binary-response-browser.mts +20 -0
- package/static/static-helpers/serialization/get-binary-response.ts +25 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@azure-tools/typespec-ts",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.46.0",
|
|
4
4
|
"description": "An experimental TypeSpec emitter for TypeScript RLC",
|
|
5
5
|
"main": "dist/src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -18,11 +18,11 @@
|
|
|
18
18
|
"license": "MIT",
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"@azure-rest/core-client": "^2.3.1",
|
|
21
|
-
"@typespec/http-specs": "0.1.0-alpha.28-dev.
|
|
21
|
+
"@typespec/http-specs": "0.1.0-alpha.28-dev.1",
|
|
22
22
|
"@typespec/spector": "0.1.0-alpha.20-dev.0",
|
|
23
23
|
"@typespec/spec-api": "0.1.0-alpha.10-dev.0",
|
|
24
24
|
"@typespec/tspd": "0.73.0",
|
|
25
|
-
"@azure-tools/azure-http-specs": "0.1.0-alpha.
|
|
25
|
+
"@azure-tools/azure-http-specs": "0.1.0-alpha.32-dev.2",
|
|
26
26
|
"@azure-tools/typespec-autorest": "^0.61.0",
|
|
27
27
|
"@azure-tools/typespec-azure-core": "^0.61.0",
|
|
28
28
|
"@azure-tools/typespec-azure-resource-manager": "^0.61.0",
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
"@typespec/xml": "^0.75.0"
|
|
78
78
|
},
|
|
79
79
|
"dependencies": {
|
|
80
|
-
"@azure-tools/rlc-common": "^0.
|
|
80
|
+
"@azure-tools/rlc-common": "^0.46.0",
|
|
81
81
|
"fs-extra": "^11.1.0",
|
|
82
82
|
"lodash": "^4.17.21",
|
|
83
83
|
"prettier": "^3.3.3",
|
|
@@ -303,7 +303,7 @@ class BinderImp implements Binder {
|
|
|
303
303
|
}
|
|
304
304
|
|
|
305
305
|
private cleanUnreferencedHelpers(sourceRoot: string) {
|
|
306
|
-
const
|
|
306
|
+
const usedHelperNames: string[] = [];
|
|
307
307
|
for (const helper of this.staticHelpers.values()) {
|
|
308
308
|
const sourceFile = helper[SourceFileSymbol];
|
|
309
309
|
if (!sourceFile) {
|
|
@@ -315,16 +315,24 @@ class BinderImp implements Binder {
|
|
|
315
315
|
const referencedHelper = this.references.get(refkey(helper));
|
|
316
316
|
|
|
317
317
|
if (referencedHelper?.size) {
|
|
318
|
-
|
|
318
|
+
usedHelperNames.push(sourceFile.getBaseNameWithoutExtension());
|
|
319
319
|
}
|
|
320
320
|
}
|
|
321
321
|
|
|
322
|
+
function isFileUnused(file: SourceFile) {
|
|
323
|
+
const name = file.getBaseNameWithoutExtension();
|
|
324
|
+
|
|
325
|
+
// If one of the used helpers' name is a prefix of this file, the file likely represents a platform-specific implementation of the helper
|
|
326
|
+
// so it should be marked as used even if the file has no direct references.
|
|
327
|
+
return !usedHelperNames.some((s) => name.startsWith(s));
|
|
328
|
+
}
|
|
329
|
+
|
|
322
330
|
this.project
|
|
323
331
|
//normalizae the final path to adapt to different systems
|
|
324
332
|
.getSourceFiles(
|
|
325
|
-
normalizePath(path.join(sourceRoot, "static-helpers
|
|
333
|
+
normalizePath(path.join(sourceRoot, "static-helpers/**/*.*ts"))
|
|
326
334
|
)
|
|
327
|
-
.filter(
|
|
335
|
+
.filter(isFileUnused)
|
|
328
336
|
.forEach((helperFile) => helperFile.delete());
|
|
329
337
|
}
|
|
330
338
|
}
|
|
@@ -13,6 +13,8 @@ import { refkey } from "./refkey.js";
|
|
|
13
13
|
import { resolveProjectRoot } from "../utils/resolve-project-root.js";
|
|
14
14
|
import { isAzurePackage } from "@azure-tools/rlc-common";
|
|
15
15
|
import { ModularEmitterOptions } from "../modular/interfaces.js";
|
|
16
|
+
import { NoTarget, Program } from "@typespec/compiler";
|
|
17
|
+
import { reportDiagnostic } from "../lib.js";
|
|
16
18
|
export const SourceFileSymbol = Symbol("SourceFile");
|
|
17
19
|
export interface StaticHelperMetadata {
|
|
18
20
|
name: string;
|
|
@@ -41,6 +43,7 @@ export interface LoadStaticHelpersOptions
|
|
|
41
43
|
extends Partial<ModularEmitterOptions> {
|
|
42
44
|
helpersAssetDirectory?: string;
|
|
43
45
|
sourcesDir?: string;
|
|
46
|
+
program?: Program;
|
|
44
47
|
}
|
|
45
48
|
|
|
46
49
|
export async function loadStaticHelpers(
|
|
@@ -55,7 +58,8 @@ export async function loadStaticHelpers(
|
|
|
55
58
|
DEFAULT_STATIC_HELPERS_PATH
|
|
56
59
|
);
|
|
57
60
|
const files = await traverseDirectory(
|
|
58
|
-
options.helpersAssetDirectory ?? defaultStaticHelpersPath
|
|
61
|
+
options.helpersAssetDirectory ?? defaultStaticHelpersPath,
|
|
62
|
+
options.program
|
|
59
63
|
);
|
|
60
64
|
|
|
61
65
|
for (const file of files) {
|
|
@@ -161,6 +165,7 @@ function getDeclarationByMetadata(
|
|
|
161
165
|
const _targetStaticHelpersBaseDir = "static-helpers";
|
|
162
166
|
async function traverseDirectory(
|
|
163
167
|
directory: string,
|
|
168
|
+
program?: Program,
|
|
164
169
|
result: { source: string; target: string }[] = [],
|
|
165
170
|
relativePath: string = ""
|
|
166
171
|
): Promise<{ source: string; target: string }[]> {
|
|
@@ -175,13 +180,14 @@ async function traverseDirectory(
|
|
|
175
180
|
if (fileStat.isDirectory()) {
|
|
176
181
|
await traverseDirectory(
|
|
177
182
|
filePath,
|
|
183
|
+
program,
|
|
178
184
|
result,
|
|
179
185
|
path.join(relativePath, file)
|
|
180
186
|
);
|
|
181
187
|
} else if (
|
|
182
188
|
fileStat.isFile() &&
|
|
183
189
|
!file.endsWith(".d.ts") &&
|
|
184
|
-
file
|
|
190
|
+
/.*\..?ts$/.test(file)
|
|
185
191
|
) {
|
|
186
192
|
const target = path.join(
|
|
187
193
|
_targetStaticHelpersBaseDir,
|
|
@@ -195,7 +201,13 @@ async function traverseDirectory(
|
|
|
195
201
|
|
|
196
202
|
return result;
|
|
197
203
|
} catch (error) {
|
|
198
|
-
|
|
204
|
+
if (program) {
|
|
205
|
+
reportDiagnostic(program, {
|
|
206
|
+
code: "directory-traversal-error",
|
|
207
|
+
format: { directory, error: String(error) },
|
|
208
|
+
target: NoTarget
|
|
209
|
+
});
|
|
210
|
+
}
|
|
199
211
|
throw error;
|
|
200
212
|
}
|
|
201
213
|
}
|
package/src/framework/sample.ts
CHANGED
|
@@ -69,12 +69,6 @@ sourceFile2.addStatements(`let obj: ${modelReference} = { id: 1 };`);
|
|
|
69
69
|
// Apply imports to ensure correct references
|
|
70
70
|
binder.resolveAllReferences("/modularPackageFolder/src");
|
|
71
71
|
|
|
72
|
-
// Output the generated files
|
|
73
|
-
console.log("// test.ts");
|
|
74
|
-
console.log(sourceFile.getFullText());
|
|
75
|
-
console.log("// test2.ts");
|
|
76
|
-
console.log(sourceFile2.getFullText());
|
|
77
|
-
|
|
78
72
|
// Output
|
|
79
73
|
// test.ts
|
|
80
74
|
// interface MyInterface {
|
package/src/index.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
AzurePollingDependencies,
|
|
9
9
|
DefaultCoreDependencies
|
|
10
10
|
} from "./modular/external-dependencies.js";
|
|
11
|
+
import { clearDirectory } from "./utils/fileSystemUtils.js";
|
|
11
12
|
import { EmitContext, Program } from "@typespec/compiler";
|
|
12
13
|
import { GenerationDirDetail, SdkContext } from "./utils/interfaces.js";
|
|
13
14
|
import {
|
|
@@ -43,7 +44,9 @@ import {
|
|
|
43
44
|
buildTopLevelIndex,
|
|
44
45
|
buildTsConfig,
|
|
45
46
|
buildTsSnippetsConfig,
|
|
46
|
-
|
|
47
|
+
buildTestBrowserTsConfig,
|
|
48
|
+
buildTestNodeTsConfig,
|
|
49
|
+
buildTestMainTsConfig,
|
|
47
50
|
buildVitestConfig,
|
|
48
51
|
getClientName,
|
|
49
52
|
hasUnexpectedHelper,
|
|
@@ -52,8 +55,7 @@ import {
|
|
|
52
55
|
buildSampleEnvFile,
|
|
53
56
|
buildSnippets,
|
|
54
57
|
buildTsSrcConfig,
|
|
55
|
-
buildTsSampleConfig
|
|
56
|
-
buildTsTestConfig
|
|
58
|
+
buildTsSampleConfig
|
|
57
59
|
} from "@azure-tools/rlc-common";
|
|
58
60
|
import {
|
|
59
61
|
buildRootIndex,
|
|
@@ -98,7 +100,6 @@ import { generateCrossLanguageDefinitionFile } from "./utils/crossLanguageDef.js
|
|
|
98
100
|
export * from "./lib.js";
|
|
99
101
|
|
|
100
102
|
export async function $onEmit(context: EmitContext) {
|
|
101
|
-
console.time("onEmit");
|
|
102
103
|
if (context.program.compilerOptions.noEmit || context.program.hasError()) {
|
|
103
104
|
return;
|
|
104
105
|
}
|
|
@@ -106,9 +107,7 @@ export async function $onEmit(context: EmitContext) {
|
|
|
106
107
|
const outputProject = new Project();
|
|
107
108
|
const program: Program = context.program;
|
|
108
109
|
const emitterOptions: EmitterOptions = context.options;
|
|
109
|
-
console.time("onEmit: create context");
|
|
110
110
|
const dpgContext = await createContextWithDefaultOptions(context);
|
|
111
|
-
console.timeEnd("onEmit: create context");
|
|
112
111
|
// Enrich the dpg context with path detail and common options
|
|
113
112
|
await enrichDpgContext();
|
|
114
113
|
const rlcOptions = dpgContext.rlcOptions ?? {};
|
|
@@ -125,7 +124,6 @@ export async function $onEmit(context: EmitContext) {
|
|
|
125
124
|
compilerContext: context,
|
|
126
125
|
tcgcContext: dpgContext
|
|
127
126
|
});
|
|
128
|
-
console.time("onEmit: load static helpers");
|
|
129
127
|
const staticHelpers = await loadStaticHelpers(
|
|
130
128
|
outputProject,
|
|
131
129
|
{
|
|
@@ -138,10 +136,10 @@ export async function $onEmit(context: EmitContext) {
|
|
|
138
136
|
},
|
|
139
137
|
{
|
|
140
138
|
sourcesDir: dpgContext.generationPathDetail?.modularSourcesDir,
|
|
141
|
-
options: rlcOptions
|
|
139
|
+
options: rlcOptions,
|
|
140
|
+
program
|
|
142
141
|
}
|
|
143
142
|
);
|
|
144
|
-
console.timeEnd("onEmit: load static helpers");
|
|
145
143
|
const extraDependencies = isAzurePackage({ options: rlcOptions })
|
|
146
144
|
? {
|
|
147
145
|
...AzurePollingDependencies,
|
|
@@ -149,7 +147,6 @@ export async function $onEmit(context: EmitContext) {
|
|
|
149
147
|
...AzureIdentityDependencies
|
|
150
148
|
}
|
|
151
149
|
: { ...DefaultCoreDependencies };
|
|
152
|
-
console.time("onEmit: provide binder");
|
|
153
150
|
const binder = provideBinder(outputProject, {
|
|
154
151
|
staticHelpers,
|
|
155
152
|
dependencies: {
|
|
@@ -157,7 +154,6 @@ export async function $onEmit(context: EmitContext) {
|
|
|
157
154
|
}
|
|
158
155
|
});
|
|
159
156
|
provideSdkTypes(dpgContext);
|
|
160
|
-
console.timeEnd("onEmit: provide binder");
|
|
161
157
|
|
|
162
158
|
const rlcCodeModels: RLCModel[] = [];
|
|
163
159
|
let modularEmitterOptions: ModularEmitterOptions;
|
|
@@ -165,9 +161,7 @@ export async function $onEmit(context: EmitContext) {
|
|
|
165
161
|
await clearSrcFolder();
|
|
166
162
|
// 2. Generate RLC code model
|
|
167
163
|
// TODO: skip this step in modular once modular generator is sufficiently decoupled
|
|
168
|
-
console.time("onEmit: build RLC code models");
|
|
169
164
|
await buildRLCCodeModels();
|
|
170
|
-
console.timeEnd("onEmit: build RLC code models");
|
|
171
165
|
|
|
172
166
|
// 4. Generate sources
|
|
173
167
|
if (emitterOptions["is-modular-library"]) {
|
|
@@ -195,7 +189,12 @@ export async function $onEmit(context: EmitContext) {
|
|
|
195
189
|
emitterOptions["generate-sample"] = options.generateSample;
|
|
196
190
|
// clear output folder if needed
|
|
197
191
|
if (options.clearOutputFolder) {
|
|
198
|
-
|
|
192
|
+
// Clear output directory while preserving TempTypeSpecFiles
|
|
193
|
+
await clearDirectory(
|
|
194
|
+
context.emitterOutputDir,
|
|
195
|
+
["TempTypeSpecFiles"],
|
|
196
|
+
program
|
|
197
|
+
);
|
|
199
198
|
}
|
|
200
199
|
const hasTestFolder = await fsextra.pathExists(
|
|
201
200
|
join(dpgContext.generationPathDetail?.metadataDir ?? "", "test")
|
|
@@ -274,7 +273,6 @@ export async function $onEmit(context: EmitContext) {
|
|
|
274
273
|
}
|
|
275
274
|
|
|
276
275
|
async function generateModularSources() {
|
|
277
|
-
console.time("onEmit: generate modular sources");
|
|
278
276
|
const modularSourcesRoot =
|
|
279
277
|
dpgContext.generationPathDetail?.modularSourcesDir ?? "src";
|
|
280
278
|
const project = useContext("outputProject");
|
|
@@ -297,13 +295,10 @@ export async function $onEmit(context: EmitContext) {
|
|
|
297
295
|
);
|
|
298
296
|
|
|
299
297
|
const isMultiClients = dpgContext.sdkPackage.clients.length > 1;
|
|
300
|
-
console.time("onEmit: emit models");
|
|
301
298
|
emitTypes(dpgContext, { sourceRoot: modularSourcesRoot });
|
|
302
|
-
console.timeEnd("onEmit: emit models");
|
|
303
299
|
buildSubpathIndexFile(modularEmitterOptions, "models", undefined, {
|
|
304
300
|
recursive: true
|
|
305
301
|
});
|
|
306
|
-
console.time("onEmit: emit source files");
|
|
307
302
|
const clientMap = getClientHierarchyMap(dpgContext);
|
|
308
303
|
if (clientMap.length === 0) {
|
|
309
304
|
// If no clients, we still need to build the root index file
|
|
@@ -343,12 +338,9 @@ export async function $onEmit(context: EmitContext) {
|
|
|
343
338
|
subClient
|
|
344
339
|
);
|
|
345
340
|
}
|
|
346
|
-
console.timeEnd("onEmit: emit source files");
|
|
347
341
|
// Enable modular sample generation when explicitly set to true or MPG
|
|
348
342
|
if (emitterOptions["generate-sample"] === true) {
|
|
349
|
-
console.time("onEmit: emit samples");
|
|
350
343
|
const samples = emitSamples(dpgContext);
|
|
351
|
-
console.timeEnd("onEmit: emit samples");
|
|
352
344
|
// Refine the rlc sample generation logic
|
|
353
345
|
// TODO: remember to remove this out when RLC is splitted from Modular
|
|
354
346
|
if (samples.length > 0) {
|
|
@@ -356,14 +348,10 @@ export async function $onEmit(context: EmitContext) {
|
|
|
356
348
|
}
|
|
357
349
|
}
|
|
358
350
|
|
|
359
|
-
console.time("onEmit: resolve references");
|
|
360
351
|
binder.resolveAllReferences(modularSourcesRoot);
|
|
361
352
|
if (program.compilerOptions.noEmit || program.hasError()) {
|
|
362
353
|
return;
|
|
363
354
|
}
|
|
364
|
-
console.timeEnd("onEmit: resolve references");
|
|
365
|
-
|
|
366
|
-
console.time("onEmit: generate files");
|
|
367
355
|
|
|
368
356
|
for (const file of project.getSourceFiles()) {
|
|
369
357
|
await emitContentByBuilder(
|
|
@@ -372,8 +360,6 @@ export async function $onEmit(context: EmitContext) {
|
|
|
372
360
|
modularEmitterOptions as any
|
|
373
361
|
);
|
|
374
362
|
}
|
|
375
|
-
console.timeEnd("onEmit: generate files");
|
|
376
|
-
console.timeEnd("onEmit: generate modular sources");
|
|
377
363
|
}
|
|
378
364
|
|
|
379
365
|
interface Metadata {
|
|
@@ -456,7 +442,9 @@ export async function $onEmit(context: EmitContext) {
|
|
|
456
442
|
commonBuilders.push((model) => buildVitestConfig(model, "node"));
|
|
457
443
|
commonBuilders.push((model) => buildVitestConfig(model, "esm"));
|
|
458
444
|
commonBuilders.push((model) => buildVitestConfig(model, "browser"));
|
|
459
|
-
commonBuilders.push((model) =>
|
|
445
|
+
commonBuilders.push((model) => buildTestBrowserTsConfig(model));
|
|
446
|
+
commonBuilders.push((model) => buildTestNodeTsConfig(model));
|
|
447
|
+
commonBuilders.push((model) => buildTestMainTsConfig(model));
|
|
460
448
|
}
|
|
461
449
|
if (isAzureFlavor) {
|
|
462
450
|
commonBuilders.push(buildEsLintConfig);
|
|
@@ -488,9 +476,6 @@ export async function $onEmit(context: EmitContext) {
|
|
|
488
476
|
if (option.generateSample) {
|
|
489
477
|
commonBuilders.push(buildTsSampleConfig);
|
|
490
478
|
}
|
|
491
|
-
if (option.generateTest) {
|
|
492
|
-
commonBuilders.push(buildTsTestConfig);
|
|
493
|
-
}
|
|
494
479
|
}
|
|
495
480
|
|
|
496
481
|
// TODO: need support snippets generation for multi-client cases. https://github.com/Azure/autorest.typescript/issues/3048
|
|
@@ -558,7 +543,6 @@ export async function $onEmit(context: EmitContext) {
|
|
|
558
543
|
.map((subClient) => getClientContextPath(subClient, options))
|
|
559
544
|
.map((path) => path.substring(path.indexOf("src")));
|
|
560
545
|
}
|
|
561
|
-
console.timeEnd("onEmit");
|
|
562
546
|
}
|
|
563
547
|
|
|
564
548
|
export async function createContextWithDefaultOptions(
|
package/src/lib.ts
CHANGED
|
@@ -582,6 +582,12 @@ const libDef = {
|
|
|
582
582
|
messages: {
|
|
583
583
|
default: paramMessage`Failed to format file: ${"filePath"}. Error: ${"error"}.`
|
|
584
584
|
}
|
|
585
|
+
},
|
|
586
|
+
"directory-traversal-error": {
|
|
587
|
+
severity: "error",
|
|
588
|
+
messages: {
|
|
589
|
+
default: paramMessage`Error traversing directory ${"directory"}: ${"error"}`
|
|
590
|
+
}
|
|
585
591
|
}
|
|
586
592
|
},
|
|
587
593
|
emitter: {
|
|
@@ -126,8 +126,7 @@ export function buildRestorePoller(
|
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
interface DeserializationHelper {
|
|
129
|
-
|
|
130
|
-
deserializer: Function;
|
|
129
|
+
deserializer: (result: PathUncheckedResponse) => Promise<any>;
|
|
131
130
|
expectedStatuses: string[];
|
|
132
131
|
}
|
|
133
132
|
|
|
@@ -112,6 +112,13 @@ export function emitTypes(
|
|
|
112
112
|
sourceFile = outputProject.getSourceFile(filepath);
|
|
113
113
|
if (!sourceFile) {
|
|
114
114
|
sourceFile = outputProject.createSourceFile(filepath);
|
|
115
|
+
sourceFile.addStatements(
|
|
116
|
+
`/**
|
|
117
|
+
* This file contains only generated model types and (de)serializers.
|
|
118
|
+
* Disable this rule for deserializer functions which require 'any' for raw JSON input.
|
|
119
|
+
*/
|
|
120
|
+
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */`
|
|
121
|
+
);
|
|
115
122
|
}
|
|
116
123
|
emitType(context, type, sourceFile);
|
|
117
124
|
}
|
|
@@ -37,6 +37,8 @@ import {
|
|
|
37
37
|
ServiceOperation
|
|
38
38
|
} from "../utils/operationUtil.js";
|
|
39
39
|
import { getSubscriptionId } from "../transform/transfromRLCOptions.js";
|
|
40
|
+
import { getClientParametersDeclaration } from "./helpers/clientHelpers.js";
|
|
41
|
+
import { getOperationFunction } from "./helpers/operationHelpers.js";
|
|
40
42
|
|
|
41
43
|
/**
|
|
42
44
|
* Interfaces for samples generations
|
|
@@ -53,6 +55,7 @@ interface EmitSampleOptions {
|
|
|
53
55
|
generatedFiles: SourceFile[];
|
|
54
56
|
classicalMethodPrefix?: string;
|
|
55
57
|
subFolder?: string;
|
|
58
|
+
hierarchies?: string[]; // Add hierarchies to track operation path
|
|
56
59
|
}
|
|
57
60
|
/**
|
|
58
61
|
* Helpers to emit samples
|
|
@@ -80,8 +83,8 @@ function emitClientSamples(
|
|
|
80
83
|
) {
|
|
81
84
|
const methodMap = getMethodHierarchiesMap(dpgContext, client);
|
|
82
85
|
for (const [prefixKey, operations] of methodMap) {
|
|
83
|
-
const
|
|
84
|
-
|
|
86
|
+
const hierarchies = prefixKey ? prefixKey.split("/") : [];
|
|
87
|
+
const prefix = hierarchies
|
|
85
88
|
.map((name) => {
|
|
86
89
|
return normalizeName(name, NameType.Property);
|
|
87
90
|
})
|
|
@@ -89,7 +92,8 @@ function emitClientSamples(
|
|
|
89
92
|
for (const op of operations) {
|
|
90
93
|
emitMethodSamples(dpgContext, op, {
|
|
91
94
|
...options,
|
|
92
|
-
classicalMethodPrefix: prefix
|
|
95
|
+
classicalMethodPrefix: prefix,
|
|
96
|
+
hierarchies: hierarchies
|
|
93
97
|
});
|
|
94
98
|
}
|
|
95
99
|
}
|
|
@@ -173,10 +177,35 @@ function emitMethodSamples(
|
|
|
173
177
|
);
|
|
174
178
|
|
|
175
179
|
// prepare operation-level parameters
|
|
180
|
+
// Get the actual function signature parameter order
|
|
181
|
+
const operationFunction = getOperationFunction(
|
|
182
|
+
dpgContext,
|
|
183
|
+
[options.hierarchies ?? [], method],
|
|
184
|
+
"Client"
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
// Extract parameter names from the function signature (excluding context and options)
|
|
188
|
+
const signatureParamNames =
|
|
189
|
+
operationFunction.parameters
|
|
190
|
+
?.filter(
|
|
191
|
+
(p) =>
|
|
192
|
+
p.name !== "context" &&
|
|
193
|
+
!p.type?.toString().includes("OptionalParams")
|
|
194
|
+
)
|
|
195
|
+
.map((p) => p.name) ?? [];
|
|
196
|
+
|
|
176
197
|
const methodParamValues = parameters.filter((p) => !p.onClient);
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
198
|
+
|
|
199
|
+
// Create a map for quick lookup of parameter values by name
|
|
200
|
+
const paramValueMap = new Map(methodParamValues.map((p) => [p.name, p]));
|
|
201
|
+
|
|
202
|
+
// Reorder methodParamValues according to the signature order
|
|
203
|
+
const orderedRequiredParams = signatureParamNames
|
|
204
|
+
.map((name) => paramValueMap.get(name))
|
|
205
|
+
.filter((p): p is ExampleValue => p !== undefined && !p.isOptional);
|
|
206
|
+
|
|
207
|
+
const methodParams = orderedRequiredParams.map((p) => `${p.value}`);
|
|
208
|
+
|
|
180
209
|
const optionalParams = methodParamValues
|
|
181
210
|
.filter((p) => p.isOptional)
|
|
182
211
|
.map((param) => `${param.name}: ${param.value}`);
|
|
@@ -265,6 +294,31 @@ function prepareExampleParameters(
|
|
|
265
294
|
// TODO: blocked by TCGC issue: https://github.com/Azure/typespec-azure/issues/1419
|
|
266
295
|
// refine this to support generic client-level parameters once resolved
|
|
267
296
|
const result: ExampleValue[] = [];
|
|
297
|
+
const clientParams = getClientParametersDeclaration(
|
|
298
|
+
topLevelClient,
|
|
299
|
+
dpgContext,
|
|
300
|
+
{
|
|
301
|
+
onClientOnly: true
|
|
302
|
+
}
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
for (const param of clientParams) {
|
|
306
|
+
if (param.name === "options" || param.name === "credential") {
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const exampleValue: ExampleValue = {
|
|
311
|
+
name: param.name === "endpointParam" ? "endpoint" : param.name,
|
|
312
|
+
value: getEnvironmentVariableName(
|
|
313
|
+
param.name,
|
|
314
|
+
getClassicalClientName(topLevelClient)
|
|
315
|
+
),
|
|
316
|
+
isOptional: Boolean(param.hasQuestionToken),
|
|
317
|
+
onClient: true
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
result.push(exampleValue);
|
|
321
|
+
}
|
|
268
322
|
const credentialExampleValue = getCredentialExampleValue(
|
|
269
323
|
dpgContext,
|
|
270
324
|
topLevelClient.clientInitialization
|
|
@@ -513,3 +567,30 @@ function escapeSpecialCharToSpace(str: string) {
|
|
|
513
567
|
}
|
|
514
568
|
return str.replace(/_|,|\.|\(|\)|'s |\[|\]/g, " ").replace(/\//g, " Or ");
|
|
515
569
|
}
|
|
570
|
+
|
|
571
|
+
function getEnvironmentVariableName(
|
|
572
|
+
paramName: string,
|
|
573
|
+
clientName?: string
|
|
574
|
+
): string {
|
|
575
|
+
// Remove "Param" suffix if present
|
|
576
|
+
const cleanName = paramName.replace(/Param$/, "");
|
|
577
|
+
|
|
578
|
+
// Remove "Client" suffix from client name if present and convert to UPPER_SNAKE_CASE
|
|
579
|
+
let prefix = "";
|
|
580
|
+
if (clientName) {
|
|
581
|
+
const cleanClientName = clientName.replace(/Client$/, "");
|
|
582
|
+
prefix =
|
|
583
|
+
cleanClientName
|
|
584
|
+
.replace(/([A-Z])/g, "_$1")
|
|
585
|
+
.toUpperCase()
|
|
586
|
+
.replace(/^_/, "") + "_";
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// Convert camelCase to UPPER_SNAKE_CASE
|
|
590
|
+
const envVarName = cleanName
|
|
591
|
+
.replace(/([A-Z])/g, "_$1")
|
|
592
|
+
.toUpperCase()
|
|
593
|
+
.replace(/^_/, "");
|
|
594
|
+
|
|
595
|
+
return `process.env.${prefix}${envVarName} || ""`;
|
|
596
|
+
}
|
|
@@ -85,6 +85,11 @@ export const DefaultCoreDependencies: CoreDependencies = {
|
|
|
85
85
|
kind: "externalDependency",
|
|
86
86
|
name: "ErrorModel",
|
|
87
87
|
module: "@typespec/ts-http-runtime"
|
|
88
|
+
},
|
|
89
|
+
AzureCoreErrorResponse: {
|
|
90
|
+
kind: "externalDependency",
|
|
91
|
+
name: "ErrorResponse",
|
|
92
|
+
module: "@azure-rest/core-client"
|
|
88
93
|
}
|
|
89
94
|
} as const;
|
|
90
95
|
|
|
@@ -196,6 +201,11 @@ export const AzureCoreDependencies: CoreDependencies = {
|
|
|
196
201
|
kind: "externalDependency",
|
|
197
202
|
name: "ErrorModel",
|
|
198
203
|
module: "@azure-rest/core-client"
|
|
204
|
+
},
|
|
205
|
+
AzureCoreErrorResponse: {
|
|
206
|
+
kind: "externalDependency",
|
|
207
|
+
name: "ErrorResponse",
|
|
208
|
+
module: "@azure-rest/core-client"
|
|
199
209
|
}
|
|
200
210
|
};
|
|
201
211
|
|
|
@@ -73,14 +73,15 @@ export function getClientParameters(
|
|
|
73
73
|
const hasDefaultValue = (p: SdkParameter) =>
|
|
74
74
|
p.clientDefaultValue || p.__raw?.defaultValue || p.type.kind === "constant";
|
|
75
75
|
const isRequired = (p: SdkParameter) =>
|
|
76
|
-
|
|
77
|
-
(
|
|
76
|
+
// Special case: when apiVersionAsRequired is true, apiVersion should always be considered required
|
|
77
|
+
(options.apiVersionAsRequired && p.isApiVersionParam) ||
|
|
78
|
+
(!p.optional &&
|
|
79
|
+
!hasDefaultValue(p) &&
|
|
78
80
|
!(
|
|
79
81
|
p.type.kind === "endpoint" &&
|
|
80
82
|
p.type.templateArguments[0] &&
|
|
81
83
|
hasDefaultValue(p.type.templateArguments[0])
|
|
82
|
-
))
|
|
83
|
-
(options.apiVersionAsRequired && p.isApiVersionParam));
|
|
84
|
+
));
|
|
84
85
|
const isOptional = (p: SdkParameter) => p.optional || hasDefaultValue(p);
|
|
85
86
|
const skipCredentials = (p: SdkParameter) => p.kind !== "credential";
|
|
86
87
|
const skipMethodParam = (p: SdkParameter) => p.kind !== "method";
|
|
@@ -98,7 +99,6 @@ export function getClientParameters(
|
|
|
98
99
|
const params = clientParams.filter((p) =>
|
|
99
100
|
filters.every((filter) => !filter || filter(p))
|
|
100
101
|
);
|
|
101
|
-
|
|
102
102
|
return params;
|
|
103
103
|
}
|
|
104
104
|
|
|
@@ -8,6 +8,7 @@ import { NoTarget, Program } from "@typespec/compiler";
|
|
|
8
8
|
import {
|
|
9
9
|
PagingHelpers,
|
|
10
10
|
PollingHelpers,
|
|
11
|
+
SerializationHelpers,
|
|
11
12
|
UrlTemplateHelpers
|
|
12
13
|
} from "../static-helpers-metadata.js";
|
|
13
14
|
import {
|
|
@@ -472,11 +473,18 @@ export function getOperationFunction(
|
|
|
472
473
|
};
|
|
473
474
|
|
|
474
475
|
const statements: string[] = [];
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
)
|
|
476
|
+
|
|
477
|
+
const parameterList = parameters.map((p) => p.name).join(", ");
|
|
478
|
+
// Special case for binary-only bodies: use helper to call streaming methods so that Core doesn't poison the response body by
|
|
479
|
+
// doing a UTF-8 decode on the raw bytes.
|
|
480
|
+
if (response?.type?.kind === "bytes" && response.type.encode === "bytes") {
|
|
481
|
+
statements.push(`const streamableMethod = _${name}Send(${parameterList});`);
|
|
482
|
+
statements.push(
|
|
483
|
+
`const result = await ${resolveReference(SerializationHelpers.getBinaryResponse)}(streamableMethod);`
|
|
484
|
+
);
|
|
485
|
+
} else {
|
|
486
|
+
statements.push(`const result = await _${name}Send(${parameterList});`);
|
|
487
|
+
}
|
|
480
488
|
statements.push(`return _${name}Deserialize(result);`);
|
|
481
489
|
|
|
482
490
|
return {
|
|
@@ -703,10 +711,16 @@ function getHeaderAndBodyParameters(
|
|
|
703
711
|
) {
|
|
704
712
|
continue;
|
|
705
713
|
}
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
param
|
|
709
|
-
|
|
714
|
+
// Check if this parameter still exists in the corresponding method params (after override)
|
|
715
|
+
if (
|
|
716
|
+
param.correspondingMethodParams &&
|
|
717
|
+
param.correspondingMethodParams.length > 0
|
|
718
|
+
) {
|
|
719
|
+
parametersImplementation[param.kind].push({
|
|
720
|
+
paramMap: getParameterMap(dpgContext, param, optionalParamName),
|
|
721
|
+
param
|
|
722
|
+
});
|
|
723
|
+
}
|
|
710
724
|
}
|
|
711
725
|
}
|
|
712
726
|
|
|
@@ -1571,7 +1585,7 @@ export function getExpectedStatuses(operation: ServiceOperation): string {
|
|
|
1571
1585
|
let statusCodes = operation.operation.responses.map((x) => x.statusCodes);
|
|
1572
1586
|
// LROs may call the same path but with GET to get the operation status.
|
|
1573
1587
|
if (isLroOnlyOperation(operation) && operation.operation.verb !== "get") {
|
|
1574
|
-
statusCodes = Array.from(new Set([...statusCodes, 200, 202]));
|
|
1588
|
+
statusCodes = Array.from(new Set([...statusCodes, 200, 201, 202]));
|
|
1575
1589
|
}
|
|
1576
1590
|
|
|
1577
1591
|
return `[${statusCodes.map((x) => `"${x}"`).join(", ")}]`;
|
|
@@ -473,7 +473,7 @@ function getAdditionalPropertiesStatement(
|
|
|
473
473
|
false,
|
|
474
474
|
true
|
|
475
475
|
);
|
|
476
|
-
const params = [`item.${getAdditionalPropertiesName(context, type)}`];
|
|
476
|
+
const params = [`item.${getAdditionalPropertiesName(context, type)} ?? {}`];
|
|
477
477
|
if (typeof deserializerFunction === "string") {
|
|
478
478
|
params.push("undefined");
|
|
479
479
|
params.push(deserializerFunction);
|
|
@@ -28,6 +28,11 @@ export const SerializationHelpers = {
|
|
|
28
28
|
kind: "function",
|
|
29
29
|
name: "serializeRecord",
|
|
30
30
|
location: "serialization/serialize-record.ts"
|
|
31
|
+
},
|
|
32
|
+
getBinaryResponse: {
|
|
33
|
+
kind: "function",
|
|
34
|
+
name: "getBinaryResponse",
|
|
35
|
+
location: "serialization/get-binary-response.ts"
|
|
31
36
|
}
|
|
32
37
|
} as const;
|
|
33
38
|
|
|
@@ -48,7 +48,8 @@ export function getModelExpression(
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
const externalModels: Record<string, string> = {
|
|
51
|
-
"Azure.Core.Foundations.Error": "ErrorModel"
|
|
51
|
+
"Azure.Core.Foundations.Error": "ErrorModel",
|
|
52
|
+
"Azure.Core.Foundations.ErrorResponse": "AzureCoreErrorResponse"
|
|
52
53
|
};
|
|
53
54
|
|
|
54
55
|
export function getExternalModel(type: SdkModelType) {
|