-
-
Notifications
You must be signed in to change notification settings - Fork 40
Expand file tree
/
Copy pathcommon.mts
More file actions
172 lines (152 loc) · 4.71 KB
/
common.mts
File metadata and controls
172 lines (152 loc) · 4.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
import { type PathLike } from "fs";
import { stat } from "fs/promises";
import ts from "typescript";
import path from "path";
import {
MethodDeclaration,
SourceFile,
ParameterDeclaration,
ClassDeclaration,
} from "ts-morph";
import { LimitedUserConfig } from "./cli.mjs";
import { queriesOutputPath, requestsOutputPath } from "./constants.mjs";
export const TData = ts.factory.createIdentifier("TData");
export const TError = ts.factory.createIdentifier("TError");
export const TContext = ts.factory.createIdentifier("TContext");
export const queryKeyGenericType =
ts.factory.createTypeReferenceNode("TQueryKey");
export const queryKeyConstraint = ts.factory.createTypeReferenceNode("Array", [
ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword),
]);
export const capitalizeFirstLetter = (str: string) => {
return str.charAt(0).toUpperCase() + str.slice(1);
};
export const lowercaseFirstLetter = (str: string) => {
return str.charAt(0).toLowerCase() + str.slice(1);
};
export const getNameFromMethod = (method: MethodDeclaration) => {
const methodName = method.getName();
if (!methodName) {
throw new Error("Method name not found");
}
return methodName;
};
export type MethodDescription = {
className: string;
node: SourceFile;
method: MethodDeclaration;
methodBlock: ts.Block;
httpMethodName: string;
jsDoc: string;
isDeprecated: boolean;
};
export async function exists(f: PathLike) {
try {
await stat(f);
return true;
} catch {
return false;
}
}
const Common = "Common";
/**
* Build a common type name by prepending the Common namespace.
*/
export function BuildCommonTypeName(name: string | ts.Identifier) {
if (typeof name === "string") {
return ts.factory.createIdentifier(`${Common}.${name}`);
}
return ts.factory.createIdentifier(`${Common}.${name.text}`);
}
/**
* Safely parse a value into a number. Checks for NaN and Infinity.
* Returns NaN if the string is not a valid number.
* @param value The value to parse.
* @returns The parsed number or NaN if the value is not a valid number.
*/
export function safeParseNumber(value: unknown): number {
const parsed = Number(value);
if (!isNaN(parsed) && isFinite(parsed)) {
return parsed;
}
return NaN;
}
export function extractPropertiesFromObjectParam(param: ParameterDeclaration) {
const referenced = param.findReferences()[0];
const def = referenced.getDefinition();
const paramNodes = def
.getNode()
.getType()
.getProperties()
.map((prop) => ({
name: prop.getName(),
optional: prop.isOptional(),
type: prop.getValueDeclaration()?.getType()!,
}));
return paramNodes;
}
/**
* Replace the import("...") surrounding the type if there is one.
* This can happen when the type is imported from another file, but
* we are already importing all the types from that file.
*
* https://regex101.com/r/3DyHaQ/1
*
* TODO: Replace with a more robust solution.
*/
export function getShortType(type: string) {
return type.replaceAll(/import\(".*"\)\./g, "");
}
export function getClassesFromService(node: SourceFile) {
const klasses = node.getClasses();
if (!klasses.length) {
throw new Error("No classes found");
}
return klasses.map((klass) => {
const className = klass.getName();
if (!className) {
throw new Error("Class name not found");
}
return {
className,
klass,
};
});
}
export function getClassNameFromClassNode(klass: ClassDeclaration) {
const className = klass.getName();
if (!className) {
throw new Error("Class name not found");
}
return className;
}
export function formatOptions(options: LimitedUserConfig) {
// loop through properties on the options object
// if the property is a string of number then convert it to a number
// if the property is a string of boolean then convert it to a boolean
const formattedOptions = Object.entries(options).reduce(
(acc, [key, value]) => {
const typedKey = key as keyof LimitedUserConfig;
const typedValue = value as (typeof options)[keyof LimitedUserConfig];
const parsedNumber = safeParseNumber(typedValue);
if (value === "true" || value === true) {
(acc as any)[typedKey] = true;
} else if (value === "false" || value === false) {
(acc as any)[typedKey] = false;
} else if (!isNaN(parsedNumber)) {
(acc as any)[typedKey] = parsedNumber;
} else {
(acc as any)[typedKey] = typedValue;
}
return acc;
},
options
);
return formattedOptions;
}
export function buildRequestsOutputPath(outputPath: string) {
return path.join(outputPath, requestsOutputPath);
}
export function buildQueriesOutputPath(outputPath: string) {
return path.join(outputPath, queriesOutputPath);
}