UNPKG

8.96 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.convertPath = exports.isAutoGeneratedTypeUnion = exports.isAutoGeneratedEnumUnion = exports.isDynamicallyAdded = exports.insertAt = exports.replaceImportPath = exports.isPromiseOrObservable = exports.getTypeReferenceAsString = void 0;
4const lodash_1 = require("lodash");
5const path_1 = require("path");
6const ts = require("typescript");
7const ast_utils_1 = require("./ast-utils");
8function getTypeReferenceAsString(type, typeChecker, arrayDepth = 0) {
9 if ((0, ast_utils_1.isArray)(type)) {
10 const arrayType = (0, ast_utils_1.getTypeArguments)(type)[0];
11 const { typeName, arrayDepth: depth } = getTypeReferenceAsString(arrayType, typeChecker, arrayDepth + 1);
12 if (!typeName) {
13 return { typeName: undefined };
14 }
15 return {
16 typeName: `${typeName}`,
17 isArray: true,
18 arrayDepth: depth,
19 };
20 }
21 if ((0, ast_utils_1.isBoolean)(type)) {
22 return { typeName: Boolean.name, arrayDepth };
23 }
24 if ((0, ast_utils_1.isNumber)(type) || (0, ast_utils_1.isBigInt)(type)) {
25 return { typeName: Number.name, arrayDepth };
26 }
27 if ((0, ast_utils_1.isString)(type) || (0, ast_utils_1.isStringLiteral)(type)) {
28 return { typeName: String.name, arrayDepth };
29 }
30 if (isPromiseOrObservable((0, ast_utils_1.getText)(type, typeChecker))) {
31 const typeArguments = (0, ast_utils_1.getTypeArguments)(type);
32 const elementType = getTypeReferenceAsString((0, lodash_1.head)(typeArguments), typeChecker, arrayDepth);
33 return elementType;
34 }
35 if (type.isClass()) {
36 return { typeName: (0, ast_utils_1.getText)(type, typeChecker), arrayDepth };
37 }
38 try {
39 const text = (0, ast_utils_1.getText)(type, typeChecker);
40 if (text === Date.name) {
41 return { typeName: text, arrayDepth };
42 }
43 if (isOptionalBoolean(text)) {
44 return { typeName: Boolean.name, arrayDepth };
45 }
46 if ((0, ast_utils_1.isEnum)(type)) {
47 return { typeName: text, arrayDepth };
48 }
49 const isEnumMember = type.symbol && type.symbol.flags === ts.SymbolFlags.EnumMember;
50 if (isEnumMember) {
51 type = typeChecker.getDeclaredTypeOfSymbol(type.symbol.parent);
52 if (!type) {
53 return undefined;
54 }
55 return { typeName: text, arrayDepth };
56 }
57 if (isAutoGeneratedTypeUnion(type) ||
58 isAutoGeneratedEnumUnion(type, typeChecker)) {
59 const types = type.types;
60 return getTypeReferenceAsString(types[types.length - 1], typeChecker);
61 }
62 if (text === 'any' ||
63 text === 'unknown' ||
64 text === 'object' ||
65 (0, ast_utils_1.isInterface)(type) ||
66 (type.isUnionOrIntersection() && !(0, ast_utils_1.isEnum)(type))) {
67 return { typeName: 'Object', arrayDepth };
68 }
69 if (type.aliasSymbol) {
70 return { typeName: 'Object', arrayDepth };
71 }
72 return { typeName: 'Object', arrayDepth };
73 }
74 catch {
75 return { typeName: 'Object', arrayDepth };
76 }
77}
78exports.getTypeReferenceAsString = getTypeReferenceAsString;
79function isPromiseOrObservable(type) {
80 return type.includes('Promise') || type.includes('Observable');
81}
82exports.isPromiseOrObservable = isPromiseOrObservable;
83function replaceImportPath(typeReference, fileName, options) {
84 if (!typeReference.includes('import')) {
85 return { typeReference, importPath: null };
86 }
87 let importPath = /\("([^)]).+(")/.exec(typeReference)[0];
88 if (!importPath) {
89 return { typeReference: undefined, importPath: null };
90 }
91 importPath = convertPath(importPath);
92 importPath = importPath.slice(2, importPath.length - 1);
93 const from = options?.readonly
94 ? convertPath(options.pathToSource)
95 : path_1.posix.dirname(convertPath(fileName));
96 let relativePath = path_1.posix.relative(from, importPath);
97 relativePath =
98 !(0, path_1.isAbsolute)(relativePath) && relativePath[0] !== '.'
99 ? './' + relativePath
100 : relativePath;
101 const nodeModulesText = 'node_modules';
102 const nodeModulePos = relativePath.indexOf(nodeModulesText);
103 if (nodeModulePos >= 0) {
104 relativePath = relativePath.slice(nodeModulePos + nodeModulesText.length + 1);
105 const typesText = '@types';
106 const typesPos = relativePath.indexOf(typesText);
107 if (typesPos >= 0) {
108 relativePath = relativePath.slice(typesPos + typesText.length + 1);
109 }
110 const indexText = '/index';
111 const indexPos = relativePath.indexOf(indexText);
112 if (indexPos >= 0) {
113 relativePath = relativePath.slice(0, indexPos);
114 }
115 }
116 typeReference = typeReference.replace(importPath, relativePath);
117 if (options.readonly) {
118 const { typeName, typeImportStatement } = convertToAsyncImport(typeReference);
119 return {
120 typeReference: typeImportStatement,
121 typeName,
122 importPath: relativePath,
123 };
124 }
125 return {
126 typeReference: typeReference.replace('import', 'require'),
127 importPath: relativePath,
128 };
129}
130exports.replaceImportPath = replaceImportPath;
131function convertToAsyncImport(typeReference) {
132 const regexp = /import\(.+\).([^\]]+)(\])?/;
133 const match = regexp.exec(typeReference);
134 if (match?.length >= 2) {
135 const importPos = typeReference.indexOf(match[0]);
136 typeReference = typeReference.replace(`.${match[1]}`, '');
137 return {
138 typeImportStatement: insertAt(typeReference, importPos, 'await '),
139 typeName: match[1],
140 };
141 }
142 return { typeImportStatement: typeReference };
143}
144function insertAt(string, index, substring) {
145 return string.slice(0, index) + substring + string.slice(index);
146}
147exports.insertAt = insertAt;
148function isDynamicallyAdded(identifier) {
149 return identifier && !identifier.parent && identifier.pos === -1;
150}
151exports.isDynamicallyAdded = isDynamicallyAdded;
152/**
153 * when "strict" mode enabled, TypeScript transform the enum type to a union composed of
154 * the enum values and the undefined type. Hence, we have to lookup all the union types to get the original type
155 * @param type
156 * @param typeChecker
157 */
158function isAutoGeneratedEnumUnion(type, typeChecker) {
159 if (type.isUnionOrIntersection() && !(0, ast_utils_1.isEnum)(type)) {
160 if (!type.types) {
161 return undefined;
162 }
163 const undefinedTypeIndex = type.types.findIndex((type) => type.intrinsicName === 'undefined');
164 if (undefinedTypeIndex < 0) {
165 return undefined;
166 }
167 // "strict" mode for enums
168 let parentType = undefined;
169 const isParentSymbolEqual = type.types.every((item, index) => {
170 if (index === undefinedTypeIndex) {
171 return true;
172 }
173 if (!item.symbol) {
174 return false;
175 }
176 if (!item.symbol.parent ||
177 item.symbol.flags !== ts.SymbolFlags.EnumMember) {
178 return false;
179 }
180 const symbolType = typeChecker.getDeclaredTypeOfSymbol(item.symbol.parent);
181 if (symbolType === parentType || !parentType) {
182 parentType = symbolType;
183 return true;
184 }
185 return false;
186 });
187 if (isParentSymbolEqual) {
188 return parentType;
189 }
190 }
191 return undefined;
192}
193exports.isAutoGeneratedEnumUnion = isAutoGeneratedEnumUnion;
194/**
195 * when "strict" mode enabled, TypeScript transform the type signature of optional properties to
196 * the {undefined | T} where T is the original type. Hence, we have to extract the last type of type union
197 * @param type
198 */
199function isAutoGeneratedTypeUnion(type) {
200 if (type.isUnionOrIntersection() && !(0, ast_utils_1.isEnum)(type)) {
201 if (!type.types) {
202 return false;
203 }
204 const undefinedTypeIndex = type.types.findIndex((type) => type.intrinsicName === 'undefined');
205 // "strict" mode for non-enum properties
206 if (type.types.length === 2 && undefinedTypeIndex >= 0) {
207 return true;
208 }
209 }
210 return false;
211}
212exports.isAutoGeneratedTypeUnion = isAutoGeneratedTypeUnion;
213/**
214 * when "strict" mode enabled, TypeScript transform optional boolean properties to "boolean | undefined"
215 * @param text
216 */
217function isOptionalBoolean(text) {
218 return typeof text === 'string' && text === 'boolean | undefined';
219}
220/**
221 * Converts Windows specific file paths to posix
222 * @param windowsPath
223 */
224function convertPath(windowsPath) {
225 return windowsPath
226 .replace(/^\\\\\?\\/, '')
227 .replace(/\\/g, '/')
228 .replace(/\/\/+/g, '/');
229}
230exports.convertPath = convertPath;