@azure-tools/typespec-ts 0.29.0-alpha.20240626.1 → 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/dist/src/contextManager.d.ts +70 -0
- package/dist/src/contextManager.d.ts.map +1 -0
- package/dist/src/contextManager.js +59 -0
- package/dist/src/contextManager.js.map +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +13 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/metaTree.d.ts +12 -0
- package/dist/src/metaTree.d.ts.map +1 -0
- package/dist/src/metaTree.js +2 -0
- package/dist/src/metaTree.js.map +1 -0
- package/dist/src/modular/buildClassicalClient.js +3 -1
- package/dist/src/modular/buildClassicalClient.js.map +1 -1
- package/dist/src/modular/buildCodeModel.d.ts.map +1 -1
- package/dist/src/modular/buildCodeModel.js +17 -7
- package/dist/src/modular/buildCodeModel.js.map +1 -1
- package/dist/src/modular/buildHelperSerializers.d.ts +3 -0
- package/dist/src/modular/buildHelperSerializers.d.ts.map +1 -0
- package/dist/src/modular/buildHelperSerializers.js +60 -0
- package/dist/src/modular/buildHelperSerializers.js.map +1 -0
- package/dist/src/modular/buildOperations.d.ts.map +1 -1
- package/dist/src/modular/buildOperations.js +5 -0
- package/dist/src/modular/buildOperations.js.map +1 -1
- package/dist/src/modular/buildSerializeUtils.d.ts.map +1 -1
- package/dist/src/modular/buildSerializeUtils.js +11 -4
- package/dist/src/modular/buildSerializeUtils.js.map +1 -1
- package/dist/src/modular/buildSubpathIndex.d.ts.map +1 -1
- package/dist/src/modular/buildSubpathIndex.js +6 -0
- package/dist/src/modular/buildSubpathIndex.js.map +1 -1
- package/dist/src/modular/emitModels.d.ts +5 -5
- package/dist/src/modular/emitModels.d.ts.map +1 -1
- package/dist/src/modular/emitModels.js +32 -4
- package/dist/src/modular/emitModels.js.map +1 -1
- package/dist/src/modular/helpers/operationHelpers.d.ts +10 -2
- package/dist/src/modular/helpers/operationHelpers.d.ts.map +1 -1
- package/dist/src/modular/helpers/operationHelpers.js +135 -56
- package/dist/src/modular/helpers/operationHelpers.js.map +1 -1
- package/dist/src/modular/helpers/typeHelpers.d.ts +1 -0
- package/dist/src/modular/helpers/typeHelpers.d.ts.map +1 -1
- package/dist/src/modular/helpers/typeHelpers.js +24 -19
- package/dist/src/modular/helpers/typeHelpers.js.map +1 -1
- package/dist/src/modular/modularCodeModel.d.ts +2 -2
- package/dist/src/modular/modularCodeModel.d.ts.map +1 -1
- package/dist/src/modular/serialization/buildSerializerFunction.d.ts +4 -0
- package/dist/src/modular/serialization/buildSerializerFunction.d.ts.map +1 -0
- package/dist/src/modular/serialization/buildSerializerFunction.js +160 -0
- package/dist/src/modular/serialization/buildSerializerFunction.js.map +1 -0
- package/dist/src/transform/transformSchemas.d.ts.map +1 -1
- package/dist/src/transform/transformSchemas.js +3 -0
- package/dist/src/transform/transformSchemas.js.map +1 -1
- package/dist/src/utils/importHelper.d.ts +10 -0
- package/dist/src/utils/importHelper.d.ts.map +1 -0
- package/dist/src/utils/importHelper.js +54 -0
- package/dist/src/utils/importHelper.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/src/contextManager.ts +99 -0
- package/src/index.ts +14 -2
- package/src/metaTree.ts +14 -0
- package/src/modular/buildClassicalClient.ts +5 -1
- package/src/modular/buildCodeModel.ts +22 -6
- package/src/modular/buildHelperSerializers.ts +71 -0
- package/src/modular/buildOperations.ts +7 -1
- package/src/modular/buildSerializeUtils.ts +19 -11
- package/src/modular/buildSubpathIndex.ts +10 -0
- package/src/modular/emitModels.ts +66 -16
- package/src/modular/helpers/operationHelpers.ts +198 -84
- package/src/modular/helpers/typeHelpers.ts +24 -17
- package/src/modular/modularCodeModel.ts +2 -2
- package/src/modular/serialization/buildSerializerFunction.ts +224 -0
- package/src/transform/transformSchemas.ts +3 -0
- package/src/utils/importHelper.ts +64 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@azure-tools/typespec-ts",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.0",
|
|
4
4
|
"description": "An experimental TypeSpec emitter for TypeScript RLC",
|
|
5
5
|
"main": "dist/src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"author": "Jose Heredia <joheredi@microsoft.com>",
|
|
18
18
|
"license": "MIT",
|
|
19
19
|
"devDependencies": {
|
|
20
|
-
"@azure-rest/core-client": "^2.
|
|
20
|
+
"@azure-rest/core-client": "^2.1.0",
|
|
21
21
|
"@azure-tools/cadl-ranch-expect": "^0.13.4",
|
|
22
22
|
"@azure-tools/cadl-ranch-specs": "^0.33.4",
|
|
23
23
|
"@azure-tools/cadl-ranch": "^0.12.8",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"@azure-tools/typespec-client-generator-core": ">=0.43.0 <1.0.0",
|
|
28
28
|
"@azure/abort-controller": "^2.1.2",
|
|
29
29
|
"@azure/core-auth": "^1.6.0",
|
|
30
|
-
"@azure/core-lro": "3.0.0
|
|
30
|
+
"@azure/core-lro": "3.0.0",
|
|
31
31
|
"@azure/core-paging": "^1.5.0",
|
|
32
32
|
"@azure/core-rest-pipeline": "^1.14.0",
|
|
33
33
|
"@azure/core-util": "^1.4.0",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"@typespec/versioning": ">=0.57.0 <1.0.0"
|
|
71
71
|
},
|
|
72
72
|
"dependencies": {
|
|
73
|
-
"@azure-tools/rlc-common": "^0.
|
|
73
|
+
"@azure-tools/rlc-common": "^0.30.0",
|
|
74
74
|
"fs-extra": "^11.1.0",
|
|
75
75
|
"lodash": "^4.17.21",
|
|
76
76
|
"prettier": "^3.1.0",
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { Project, SourceFile } from "ts-morph";
|
|
2
|
+
import { ModularMetaTree, RlcMetaTree } from "./metaTree.js";
|
|
3
|
+
import { EmitContext } from "@typespec/compiler";
|
|
4
|
+
import { SdkContext } from "@azure-tools/typespec-client-generator-core";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Contexts Object Guidelines
|
|
8
|
+
* --------------------------
|
|
9
|
+
* The `Contexts` object contains various application-wide data that needs to be accessible across different parts of the program without the need to pass down props deeply.
|
|
10
|
+
*
|
|
11
|
+
* When to add a new context:
|
|
12
|
+
* - **Cross-Cutting Concerns**: Add a new context when the data or functionality is a cross-cutting concern used by multiple unrelated components or modules.
|
|
13
|
+
* - **Global State**: If the state needs to be globally accessible and mutable by different parts of the application, and does not belong to a specific component or module.
|
|
14
|
+
* - **Performance Optimization**: To avoid unnecessary compute or prop drilling that may lead to performance bottlenecks, especially when passing down props through many levels.
|
|
15
|
+
* - **Shared Resources**: For resources that are shared across various parts of the application, such as settings, configurations, or metadata.
|
|
16
|
+
*
|
|
17
|
+
* Remember, adding too many contexts can lead to complex dependencies and harder-to-maintain code. Always evaluate if the context is truly necessary or if there are better alternatives such as localized state management or passing props for simpler scenarios.
|
|
18
|
+
*/
|
|
19
|
+
type Contexts = {
|
|
20
|
+
rlcMetaTree: RlcMetaTree; // Context for RLC types metadata.
|
|
21
|
+
modularMetaTree: ModularMetaTree; // Context for modular types metadata.
|
|
22
|
+
outputProject: Project; // The TS-Morph root project context for code generation.
|
|
23
|
+
symbolMap: Map<string, SourceFile>; // Mapping of symbols to their corresponding source files.
|
|
24
|
+
emitContext: {
|
|
25
|
+
compilerContext: EmitContext;
|
|
26
|
+
tcgcContext: SdkContext;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
type ContextKey = keyof Contexts;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Manages shared contexts across the application to minimize prop drilling and enhance modularity.
|
|
34
|
+
* Implements the Singleton pattern to ensure there is a single instance of ContextManager.
|
|
35
|
+
*/
|
|
36
|
+
class ContextManager {
|
|
37
|
+
private static instance: ContextManager;
|
|
38
|
+
private contexts: Map<ContextKey, any> = new Map();
|
|
39
|
+
|
|
40
|
+
private constructor() {}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Retrieves the singleton instance of the ContextManager.
|
|
44
|
+
* @returns {ContextManager} The singleton instance.
|
|
45
|
+
*/
|
|
46
|
+
public static getInstance(): ContextManager {
|
|
47
|
+
if (!ContextManager.instance) {
|
|
48
|
+
ContextManager.instance = new ContextManager();
|
|
49
|
+
}
|
|
50
|
+
return ContextManager.instance;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Sets a value for a specific context key.
|
|
55
|
+
* @param {ContextKey} key - The key of the context to set.
|
|
56
|
+
* @param {Contexts[K]} value - The value to set for the specified context.
|
|
57
|
+
*/
|
|
58
|
+
public setContext<K extends ContextKey>(key: K, value: Contexts[K]): void {
|
|
59
|
+
this.contexts.set(key, value);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Retrieves the context value for a specific key.
|
|
64
|
+
* @param {ContextKey} key - The key of the context to retrieve.
|
|
65
|
+
* @returns {Contexts[K] | undefined} The value of the context if found, otherwise undefined.
|
|
66
|
+
*/
|
|
67
|
+
public getContext<K extends ContextKey>(key: K): Contexts[K] | undefined {
|
|
68
|
+
return this.contexts.get(key) as Contexts[K] | undefined;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Expose the singleton instance of the context manager.
|
|
73
|
+
export const contextManager = ContextManager.getInstance();
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* A utility function to use a context by key.
|
|
77
|
+
* @param {ContextKey} key - The key of the context to retrieve.
|
|
78
|
+
* @throws Will throw an error if the context is not found.
|
|
79
|
+
* @returns {Contexts[K]} The context value.
|
|
80
|
+
*/
|
|
81
|
+
export function useContext<K extends ContextKey>(key: K): Contexts[K] {
|
|
82
|
+
const context = contextManager.getContext(key);
|
|
83
|
+
if (!context) {
|
|
84
|
+
throw new Error(`Context ${key} not found`);
|
|
85
|
+
}
|
|
86
|
+
return context;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Provides a context with a value to be accessible across the program.
|
|
91
|
+
* @param {ContextKey} key - The key of the context to provide.
|
|
92
|
+
* @param {Contexts[K]} value - The value to set for the specified context.
|
|
93
|
+
*/
|
|
94
|
+
export function provideContext<K extends ContextKey>(
|
|
95
|
+
key: K,
|
|
96
|
+
value: Contexts[K]
|
|
97
|
+
): void {
|
|
98
|
+
contextManager.setContext(key, value);
|
|
99
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -66,6 +66,8 @@ import { transformRLCOptions } from "./transform/transfromRLCOptions.js";
|
|
|
66
66
|
import { getRLCClients } from "./utils/clientUtils.js";
|
|
67
67
|
import { emitContentByBuilder, emitModels } from "./utils/emitUtil.js";
|
|
68
68
|
import { GenerationDirDetail, SdkContext } from "./utils/interfaces.js";
|
|
69
|
+
import { provideContext, useContext } from "./contextManager.js";
|
|
70
|
+
import { emitSerializerHelpersFile } from "./modular/buildHelperSerializers.js";
|
|
69
71
|
|
|
70
72
|
export * from "./lib.js";
|
|
71
73
|
|
|
@@ -79,6 +81,15 @@ export async function $onEmit(context: EmitContext) {
|
|
|
79
81
|
string,
|
|
80
82
|
RLCModel
|
|
81
83
|
>();
|
|
84
|
+
provideContext("rlcMetaTree", new Map());
|
|
85
|
+
provideContext("symbolMap", new Map());
|
|
86
|
+
provideContext("modularMetaTree", new Map());
|
|
87
|
+
provideContext("outputProject", new Project());
|
|
88
|
+
provideContext("emitContext", {
|
|
89
|
+
compilerContext: context,
|
|
90
|
+
tcgcContext: dpgContext
|
|
91
|
+
});
|
|
92
|
+
|
|
82
93
|
const rlcCodeModels: RLCModel[] = [];
|
|
83
94
|
let modularCodeModel: ModularCodeModel;
|
|
84
95
|
// 1. Enrich the dpg context with path detail and common options
|
|
@@ -173,7 +184,8 @@ export async function $onEmit(context: EmitContext) {
|
|
|
173
184
|
// TODO: Emit modular parts of the library
|
|
174
185
|
const modularSourcesRoot =
|
|
175
186
|
dpgContext.generationPathDetail?.modularSourcesDir ?? "src";
|
|
176
|
-
const project =
|
|
187
|
+
const project = useContext("outputProject");
|
|
188
|
+
emitSerializerHelpersFile(project, modularSourcesRoot);
|
|
177
189
|
modularCodeModel = emitCodeModel(
|
|
178
190
|
dpgContext,
|
|
179
191
|
serviceNameToRlcModelsMap,
|
|
@@ -302,7 +314,7 @@ export async function $onEmit(context: EmitContext) {
|
|
|
302
314
|
);
|
|
303
315
|
|
|
304
316
|
if (option.isModularLibrary) {
|
|
305
|
-
const project =
|
|
317
|
+
const project = useContext("outputProject");
|
|
306
318
|
for (const file of project.getSourceFiles()) {
|
|
307
319
|
await emitContentByBuilder(
|
|
308
320
|
program,
|
package/src/metaTree.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Schema as RlcType } from "@azure-tools/rlc-common";
|
|
2
|
+
import { Type as ModularType } from "./modular/modularCodeModel.js";
|
|
3
|
+
import { Type } from "@typespec/compiler";
|
|
4
|
+
|
|
5
|
+
export interface RlcTypeMetadata {
|
|
6
|
+
rlcType: RlcType;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface ModularTypeMetadata {
|
|
10
|
+
modularType: ModularType;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type RlcMetaTree = Map<Type, RlcTypeMetadata>;
|
|
14
|
+
export type ModularMetaTree = Map<Type, ModularTypeMetadata>;
|
|
@@ -142,7 +142,11 @@ function importAllModels(
|
|
|
142
142
|
return;
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
-
const exported = [...apiModels.getExportedDeclarations().keys()]
|
|
145
|
+
const exported = [...apiModels.getExportedDeclarations().keys()].filter(
|
|
146
|
+
(e) => {
|
|
147
|
+
return !e.startsWith("_");
|
|
148
|
+
}
|
|
149
|
+
);
|
|
146
150
|
|
|
147
151
|
if (exported.length > 0) {
|
|
148
152
|
clientFile.addImportDeclaration({
|
|
@@ -92,6 +92,7 @@ import {
|
|
|
92
92
|
} from "../utils/modelUtils.js";
|
|
93
93
|
import { getModelNamespaceName } from "../utils/namespaceUtils.js";
|
|
94
94
|
import {
|
|
95
|
+
extractPagedMetadataNested,
|
|
95
96
|
getOperationGroupName,
|
|
96
97
|
getOperationName,
|
|
97
98
|
isBinaryPayload,
|
|
@@ -113,6 +114,7 @@ import {
|
|
|
113
114
|
Response,
|
|
114
115
|
Type as HrlcType
|
|
115
116
|
} from "./modularCodeModel.js";
|
|
117
|
+
import { useContext } from "../contextManager.js";
|
|
116
118
|
|
|
117
119
|
interface HttpServerParameter {
|
|
118
120
|
type: "endpointPath";
|
|
@@ -308,6 +310,8 @@ function getType(
|
|
|
308
310
|
type: EmitterType,
|
|
309
311
|
options: { disableEffectiveModel?: boolean; usage?: UsageFlags } = {}
|
|
310
312
|
): any {
|
|
313
|
+
const modularMetatree = useContext("modularMetaTree");
|
|
314
|
+
|
|
311
315
|
// don't cache simple type(string, int, etc) since decorators may change the result
|
|
312
316
|
const enableCache = !isSimpleType(context.program, type);
|
|
313
317
|
const effectiveModel =
|
|
@@ -322,6 +326,7 @@ function getType(
|
|
|
322
326
|
}
|
|
323
327
|
}
|
|
324
328
|
let newValue: any;
|
|
329
|
+
|
|
325
330
|
if (isEmptyAnonymousModel(type)) {
|
|
326
331
|
// do not generate model for empty model, treat it as any
|
|
327
332
|
newValue = { type: "any" };
|
|
@@ -332,6 +337,12 @@ function getType(
|
|
|
332
337
|
newValue = applyEncoding(context.program, type, newValue);
|
|
333
338
|
}
|
|
334
339
|
|
|
340
|
+
if (isTypespecType(type)) {
|
|
341
|
+
newValue.tcgcType = getClientType(context, type);
|
|
342
|
+
newValue.__raw = type;
|
|
343
|
+
modularMetatree.set(type, newValue);
|
|
344
|
+
}
|
|
345
|
+
|
|
335
346
|
if (enableCache) {
|
|
336
347
|
if (!options.disableEffectiveModel) {
|
|
337
348
|
typesMap.set(effectiveModel, newValue);
|
|
@@ -352,7 +363,8 @@ function getType(
|
|
|
352
363
|
}
|
|
353
364
|
}
|
|
354
365
|
} else {
|
|
355
|
-
const
|
|
366
|
+
const { __raw, tcgcType, ...keyableValue } = newValue;
|
|
367
|
+
const key = JSON.stringify(keyableValue);
|
|
356
368
|
const value = simpleTypesMap.get(key);
|
|
357
369
|
if (value) {
|
|
358
370
|
newValue = value;
|
|
@@ -373,12 +385,14 @@ function getType(
|
|
|
373
385
|
target: type
|
|
374
386
|
});
|
|
375
387
|
}
|
|
376
|
-
|
|
377
|
-
newValue.tcgcType = getClientType(context, type as Type);
|
|
378
|
-
}
|
|
388
|
+
|
|
379
389
|
return newValue;
|
|
380
390
|
}
|
|
381
391
|
|
|
392
|
+
function isTypespecType(type: EmitterType): type is Type {
|
|
393
|
+
return type.kind !== "Credential" && type.kind !== "CredentialTypeUnion";
|
|
394
|
+
}
|
|
395
|
+
|
|
382
396
|
// To pass the yaml dump
|
|
383
397
|
function getAddedOnVersion(p: Program, t: Type): string | undefined {
|
|
384
398
|
return getAddedOnVersions(p, t)?.[0]?.value;
|
|
@@ -1022,9 +1036,11 @@ function emitModel(
|
|
|
1022
1036
|
.join("") + "List";
|
|
1023
1037
|
}
|
|
1024
1038
|
|
|
1039
|
+
const page = extractPagedMetadataNested(context.program, type);
|
|
1040
|
+
const isPaging = page && page.itemsSegments && page.itemsSegments.length > 0;
|
|
1025
1041
|
return {
|
|
1026
1042
|
type: "model",
|
|
1027
|
-
name: modelName
|
|
1043
|
+
name: `${isPaging ? "_" : ""}${modelName}`,
|
|
1028
1044
|
description: getDocStr(context.program, type),
|
|
1029
1045
|
parents: baseModel ? [baseModel] : [],
|
|
1030
1046
|
discriminatedSubtypes: [],
|
|
@@ -1342,7 +1358,7 @@ function emitUnion(
|
|
|
1342
1358
|
usage: UsageFlags
|
|
1343
1359
|
): Record<string, any> {
|
|
1344
1360
|
let sdkType = getSdkUnion(context, type);
|
|
1345
|
-
const isNull =
|
|
1361
|
+
const isNull = false;
|
|
1346
1362
|
if (sdkType.kind === "nullable") {
|
|
1347
1363
|
sdkType = sdkType.type;
|
|
1348
1364
|
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Project } from "ts-morph";
|
|
2
|
+
import { useContext } from "../contextManager.js";
|
|
3
|
+
const serializeRecordFunction = {
|
|
4
|
+
symbol: "serializeRecord",
|
|
5
|
+
content: `
|
|
6
|
+
export function serializeRecord<
|
|
7
|
+
T extends string | number | boolean | Date | null,
|
|
8
|
+
R
|
|
9
|
+
>(item: Record<string, T>): Record<string, R>;
|
|
10
|
+
export function serializeRecord<T, R>(
|
|
11
|
+
item: Record<string, T>,
|
|
12
|
+
serializer: (item: T) => R
|
|
13
|
+
): Record<string, R>;
|
|
14
|
+
export function serializeRecord<T, R>(
|
|
15
|
+
item: Record<string, T>,
|
|
16
|
+
serializer?: (item: T) => R
|
|
17
|
+
): Record<string, R> {
|
|
18
|
+
return Object.keys(item).reduce(
|
|
19
|
+
(acc, key) => {
|
|
20
|
+
if (isSupportedRecordType(item[key])) {
|
|
21
|
+
acc[key] = item[key] as any;
|
|
22
|
+
} else if (serializer) {
|
|
23
|
+
const value = item[key];
|
|
24
|
+
if (value !== undefined) {
|
|
25
|
+
acc[key] = serializer(value);
|
|
26
|
+
}
|
|
27
|
+
} else {
|
|
28
|
+
console.warn(\`Don't know how to serialize \${item[key]}\`);
|
|
29
|
+
acc[key] = item[key] as any;
|
|
30
|
+
}
|
|
31
|
+
return acc;
|
|
32
|
+
},
|
|
33
|
+
{} as Record<string, R>
|
|
34
|
+
);
|
|
35
|
+
}`
|
|
36
|
+
};
|
|
37
|
+
const isRecordElementSupportedFunction = {
|
|
38
|
+
symbol: "isSupportedRecordType",
|
|
39
|
+
content: `
|
|
40
|
+
function isSupportedRecordType(t: any) {
|
|
41
|
+
return ["number", "string", "boolean", "null"].includes(typeof t) || t instanceof Date;
|
|
42
|
+
}
|
|
43
|
+
`
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export function emitSerializerHelpersFile(
|
|
47
|
+
project: Project,
|
|
48
|
+
srcPath: string = "src"
|
|
49
|
+
) {
|
|
50
|
+
const symbolMap = useContext("symbolMap");
|
|
51
|
+
const sourceFile = project.createSourceFile(
|
|
52
|
+
`${srcPath}/helpers/serializerHelpers.ts`,
|
|
53
|
+
"",
|
|
54
|
+
{
|
|
55
|
+
overwrite: true
|
|
56
|
+
}
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
if (!symbolMap.has(serializeRecordFunction.symbol)) {
|
|
60
|
+
symbolMap.set(serializeRecordFunction.symbol, sourceFile);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (!symbolMap.has(isRecordElementSupportedFunction.symbol)) {
|
|
64
|
+
symbolMap.set(isRecordElementSupportedFunction.symbol, sourceFile);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
sourceFile.addStatements([
|
|
68
|
+
serializeRecordFunction.content,
|
|
69
|
+
isRecordElementSupportedFunction.content
|
|
70
|
+
]);
|
|
71
|
+
}
|
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
getSendPrivateFunction as experimentalGetSendPrivateFunction
|
|
28
28
|
} from "./serialization/operationHelpers.js";
|
|
29
29
|
import { SerializerMap } from "./serialization/util.js";
|
|
30
|
-
|
|
30
|
+
import { addImportBySymbol } from "../utils/importHelper.js";
|
|
31
31
|
/**
|
|
32
32
|
* This function creates a file under /api for each operation group.
|
|
33
33
|
* If there is no operation group in the TypeSpec program, we create a single
|
|
@@ -195,7 +195,10 @@ export function buildOperationFiles(
|
|
|
195
195
|
]
|
|
196
196
|
}
|
|
197
197
|
]);
|
|
198
|
+
|
|
198
199
|
addImportsToFiles(codeModel.runtimeImports, operationGroupFile);
|
|
200
|
+
addImportBySymbol("serializeRecord", operationGroupFile);
|
|
201
|
+
|
|
199
202
|
operationGroupFile.fixMissingImports();
|
|
200
203
|
// have to fixUnusedIdentifiers after everything get generated.
|
|
201
204
|
operationGroupFile.fixUnusedIdentifiers();
|
|
@@ -222,6 +225,9 @@ export function importModels(
|
|
|
222
225
|
const models: string[] = [];
|
|
223
226
|
|
|
224
227
|
for (const [name] of modelsFile?.getExportedDeclarations().entries() ?? []) {
|
|
228
|
+
if (name.startsWith("_")) {
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
225
231
|
models.push(name);
|
|
226
232
|
}
|
|
227
233
|
|
|
@@ -25,7 +25,7 @@ import { ModularCodeModel, Type } from "./modularCodeModel.js";
|
|
|
25
25
|
*/
|
|
26
26
|
export function buildSerializeUtils(model: ModularCodeModel) {
|
|
27
27
|
const serializeUtilFiles = [];
|
|
28
|
-
for (const serializeType of ["
|
|
28
|
+
for (const serializeType of ["deserialize"]) {
|
|
29
29
|
const usageCondition =
|
|
30
30
|
serializeType === "serialize" ? UsageFlags.Input : UsageFlags.Output;
|
|
31
31
|
const specialUnions = model.types.filter(
|
|
@@ -107,7 +107,8 @@ export function buildSerializeUtils(model: ModularCodeModel) {
|
|
|
107
107
|
});
|
|
108
108
|
addImportsToFiles(model.runtimeImports, utilsFile, {
|
|
109
109
|
rlcIndex: "../rest/index.js",
|
|
110
|
-
modularModel: "../models/models.js"
|
|
110
|
+
modularModel: "../models/models.js",
|
|
111
|
+
coreUtil: "@azure/core-util"
|
|
111
112
|
});
|
|
112
113
|
serializeUtilFiles.push(utilsFile);
|
|
113
114
|
}
|
|
@@ -517,11 +518,14 @@ function getTypeSerializeFunction(
|
|
|
517
518
|
returnType: typeName
|
|
518
519
|
};
|
|
519
520
|
if (type.properties) {
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
521
|
+
const { propertiesStr } = getRequestModelMapping(
|
|
522
|
+
type,
|
|
523
|
+
"obj",
|
|
524
|
+
runtimeImports
|
|
524
525
|
);
|
|
526
|
+
statements.push(`return {
|
|
527
|
+
${propertiesStr.join(", ")}
|
|
528
|
+
};`);
|
|
525
529
|
} else {
|
|
526
530
|
statements.push(`return {};`);
|
|
527
531
|
}
|
|
@@ -556,12 +560,16 @@ function getTypeSerializeFunction(
|
|
|
556
560
|
parameters: [{ name: "obj", type: type.elementType.name + "[]" }],
|
|
557
561
|
returnType: `${typeName}[]`
|
|
558
562
|
};
|
|
563
|
+
const { propertiesStr } = getRequestModelMapping(
|
|
564
|
+
type.elementType,
|
|
565
|
+
"item",
|
|
566
|
+
runtimeImports
|
|
567
|
+
);
|
|
568
|
+
|
|
559
569
|
statements.push(
|
|
560
|
-
`return (obj || []).map(item => {
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
runtimeImports
|
|
564
|
-
).join(", ")}}})`
|
|
570
|
+
`return (obj || []).map(item => {
|
|
571
|
+
return { ${propertiesStr.join(", ")} }
|
|
572
|
+
})`
|
|
565
573
|
);
|
|
566
574
|
functionStatement.statements = statements.join("\n");
|
|
567
575
|
if (!hasDuplicateFunction(sourceFile, functionStatement)) {
|
|
@@ -53,6 +53,16 @@ export function buildSubpathIndexFile(
|
|
|
53
53
|
) {
|
|
54
54
|
return false;
|
|
55
55
|
}
|
|
56
|
+
|
|
57
|
+
// skip exporting serializers for models
|
|
58
|
+
if (
|
|
59
|
+
subpath === "models" &&
|
|
60
|
+
ex.getKindName() === "FunctionDeclaration" &&
|
|
61
|
+
exDeclaration[0].endsWith("Serializer")
|
|
62
|
+
) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
|
|
56
66
|
return true;
|
|
57
67
|
});
|
|
58
68
|
})
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getImportSpecifier } from "@azure-tools/rlc-common";
|
|
1
|
+
import { addImportsToFiles, getImportSpecifier } from "@azure-tools/rlc-common";
|
|
2
2
|
import * as path from "path";
|
|
3
3
|
import {
|
|
4
4
|
InterfaceDeclarationStructure,
|
|
@@ -10,11 +10,18 @@ import { buildOperationOptions } from "./buildOperations.js";
|
|
|
10
10
|
import { getDocsFromDescription } from "./helpers/docsHelpers.js";
|
|
11
11
|
import { getModularModelFilePath } from "./helpers/namingHelpers.js";
|
|
12
12
|
import { getType } from "./helpers/typeHelpers.js";
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
Client,
|
|
15
|
+
ModularCodeModel,
|
|
16
|
+
Type as ModularType
|
|
17
|
+
} from "./modularCodeModel.js";
|
|
18
|
+
import { buildModelSerializer } from "./serialization/buildSerializerFunction.js";
|
|
19
|
+
import { toCamelCase } from "../utils/casingUtils.js";
|
|
20
|
+
import { addImportBySymbol } from "../utils/importHelper.js";
|
|
14
21
|
|
|
15
22
|
// ====== UTILITIES ======
|
|
16
23
|
|
|
17
|
-
function isAzureCoreErrorSdkType(t:
|
|
24
|
+
function isAzureCoreErrorSdkType(t: ModularType) {
|
|
18
25
|
return (
|
|
19
26
|
t.name &&
|
|
20
27
|
["error", "errormodel", "innererror", "errorresponse"].includes(
|
|
@@ -24,7 +31,7 @@ function isAzureCoreErrorSdkType(t: Type) {
|
|
|
24
31
|
);
|
|
25
32
|
}
|
|
26
33
|
|
|
27
|
-
function isAzureCoreLroSdkType(t:
|
|
34
|
+
function isAzureCoreLroSdkType(t: ModularType) {
|
|
28
35
|
return (
|
|
29
36
|
t.name &&
|
|
30
37
|
["operationstate"].includes(t.name.toLowerCase()) &&
|
|
@@ -32,11 +39,11 @@ function isAzureCoreLroSdkType(t: Type) {
|
|
|
32
39
|
);
|
|
33
40
|
}
|
|
34
41
|
|
|
35
|
-
function isAnonymousModel(t:
|
|
42
|
+
function isAnonymousModel(t: ModularType) {
|
|
36
43
|
return t.type === "model" && t.name === "";
|
|
37
44
|
}
|
|
38
45
|
|
|
39
|
-
export function isModelWithAdditionalProperties(t:
|
|
46
|
+
export function isModelWithAdditionalProperties(t: ModularType) {
|
|
40
47
|
return t.type === "dict" && t.name !== "Record";
|
|
41
48
|
}
|
|
42
49
|
|
|
@@ -54,7 +61,7 @@ function getCoreLroType(name: string, coreLroTypes: Set<string>) {
|
|
|
54
61
|
|
|
55
62
|
// ====== TYPE EXTRACTION ======
|
|
56
63
|
|
|
57
|
-
function extractModels(codeModel: ModularCodeModel):
|
|
64
|
+
function extractModels(codeModel: ModularCodeModel): ModularType[] {
|
|
58
65
|
const models = codeModel.types.filter(
|
|
59
66
|
(t) =>
|
|
60
67
|
((t.type === "model" || t.type === "enum") &&
|
|
@@ -65,7 +72,7 @@ function extractModels(codeModel: ModularCodeModel): Type[] {
|
|
|
65
72
|
);
|
|
66
73
|
|
|
67
74
|
for (const model of codeModel.types) {
|
|
68
|
-
if (model.type === "combined"
|
|
75
|
+
if (model.type === "combined") {
|
|
69
76
|
for (const unionModel of model.types ?? []) {
|
|
70
77
|
if (unionModel.type === "model") {
|
|
71
78
|
models.push(unionModel);
|
|
@@ -81,7 +88,7 @@ function extractModels(codeModel: ModularCodeModel): Type[] {
|
|
|
81
88
|
* 1. alias from polymorphic base model, where we need to use typescript union to combine all the sub models
|
|
82
89
|
* 2. alias from unions, where we also need to use typescript union to combine all the union variants
|
|
83
90
|
*/
|
|
84
|
-
export function extractAliases(codeModel: ModularCodeModel):
|
|
91
|
+
export function extractAliases(codeModel: ModularCodeModel): ModularType[] {
|
|
85
92
|
const models = codeModel.types.filter(
|
|
86
93
|
(t) =>
|
|
87
94
|
((t.type === "model" || t.type === "combined") &&
|
|
@@ -93,7 +100,7 @@ export function extractAliases(codeModel: ModularCodeModel): Type[] {
|
|
|
93
100
|
}
|
|
94
101
|
// ====== TYPE BUILDERS ======
|
|
95
102
|
function buildEnumModel(
|
|
96
|
-
model:
|
|
103
|
+
model: ModularType
|
|
97
104
|
): OptionalKind<TypeAliasDeclarationStructure> {
|
|
98
105
|
const valueType = model.valueType?.type === "string" ? "string" : "number";
|
|
99
106
|
return {
|
|
@@ -122,7 +129,7 @@ type InterfaceStructure = OptionalKind<InterfaceDeclarationStructure> & {
|
|
|
122
129
|
};
|
|
123
130
|
|
|
124
131
|
export function buildModelInterface(
|
|
125
|
-
model:
|
|
132
|
+
model: ModularType,
|
|
126
133
|
cache: { coreClientTypes: Set<string>; coreLroTypes: Set<string> }
|
|
127
134
|
): InterfaceStructure {
|
|
128
135
|
const modelProperties = model.properties ?? [];
|
|
@@ -178,7 +185,6 @@ export function buildModels(
|
|
|
178
185
|
const modelsFile = codeModel.project.createSourceFile(
|
|
179
186
|
getModularModelFilePath(codeModel, subClient)
|
|
180
187
|
);
|
|
181
|
-
|
|
182
188
|
for (const model of models) {
|
|
183
189
|
if (model.type === "enum") {
|
|
184
190
|
if (modelsFile.getTypeAlias(model.name!)) {
|
|
@@ -214,10 +220,38 @@ export function buildModels(
|
|
|
214
220
|
codeModel.modularOptions.compatibilityMode
|
|
215
221
|
);
|
|
216
222
|
}
|
|
217
|
-
|
|
223
|
+
|
|
224
|
+
if (!modelsFile.getInterface(modelInterface.name)) {
|
|
225
|
+
modelsFile.addInterface(modelInterface);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Generate a serializer function next to each model
|
|
229
|
+
const serializerFunction = buildModelSerializer(
|
|
230
|
+
model,
|
|
231
|
+
codeModel.runtimeImports
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
if (
|
|
235
|
+
serializerFunction &&
|
|
236
|
+
!modelsFile.getFunction(toCamelCase(modelInterface.name + "Serializer"))
|
|
237
|
+
) {
|
|
238
|
+
modelsFile.addStatements(serializerFunction);
|
|
239
|
+
}
|
|
240
|
+
addImportBySymbol("serializeRecord", modelsFile);
|
|
241
|
+
modelsFile.fixUnusedIdentifiers();
|
|
218
242
|
}
|
|
219
243
|
}
|
|
220
244
|
|
|
245
|
+
const projectRootFromModels = codeModel.clients.length > 1 ? "../.." : "../";
|
|
246
|
+
addImportsToFiles(codeModel.runtimeImports, modelsFile, {
|
|
247
|
+
rlcIndex: path.posix.join(projectRootFromModels, "rest", "index.js"),
|
|
248
|
+
serializerHelpers: path.posix.join(
|
|
249
|
+
projectRootFromModels,
|
|
250
|
+
"helpers",
|
|
251
|
+
"serializerHelpers.js"
|
|
252
|
+
)
|
|
253
|
+
});
|
|
254
|
+
|
|
221
255
|
if (coreClientTypes.size > 0) {
|
|
222
256
|
modelsFile.addImportDeclarations([
|
|
223
257
|
{
|
|
@@ -248,12 +282,22 @@ export function buildModels(
|
|
|
248
282
|
|
|
249
283
|
aliases.forEach((alias) => {
|
|
250
284
|
modelsFile.addTypeAlias(buildModelTypeAlias(alias));
|
|
285
|
+
if (!models.includes(alias)) {
|
|
286
|
+
// Generate a serializer function next to each model
|
|
287
|
+
const serializerFunction = buildModelSerializer(
|
|
288
|
+
alias,
|
|
289
|
+
codeModel.runtimeImports
|
|
290
|
+
);
|
|
291
|
+
if (serializerFunction) {
|
|
292
|
+
modelsFile.addStatements(serializerFunction);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
251
295
|
});
|
|
252
296
|
return modelsFile;
|
|
253
297
|
}
|
|
254
298
|
|
|
255
299
|
function addExtendedDictInfo(
|
|
256
|
-
model:
|
|
300
|
+
model: ModularType,
|
|
257
301
|
modelInterface: InterfaceStructure,
|
|
258
302
|
compatibilityMode: boolean = false
|
|
259
303
|
) {
|
|
@@ -282,7 +326,7 @@ function addExtendedDictInfo(
|
|
|
282
326
|
}
|
|
283
327
|
}
|
|
284
328
|
|
|
285
|
-
export function buildModelTypeAlias(model:
|
|
329
|
+
export function buildModelTypeAlias(model: ModularType) {
|
|
286
330
|
return {
|
|
287
331
|
name: model.name!,
|
|
288
332
|
isExported: true,
|
|
@@ -321,7 +365,13 @@ export function buildModelsOptions(
|
|
|
321
365
|
}
|
|
322
366
|
]);
|
|
323
367
|
|
|
324
|
-
modelOptionsFile.fixMissingImports(
|
|
368
|
+
modelOptionsFile.fixMissingImports(
|
|
369
|
+
{},
|
|
370
|
+
{
|
|
371
|
+
importModuleSpecifierPreference: "shortest",
|
|
372
|
+
importModuleSpecifierEnding: "js"
|
|
373
|
+
}
|
|
374
|
+
);
|
|
325
375
|
modelOptionsFile
|
|
326
376
|
.getImportDeclarations()
|
|
327
377
|
.filter((id) => {
|