UNPKG

@nestjs/graphql

Version:

Nest - modern, fast, powerful node.js web framework (@graphql)

231 lines (230 loc) 8.96 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.convertPath = exports.isAutoGeneratedTypeUnion = exports.isAutoGeneratedEnumUnion = exports.isDynamicallyAdded = exports.insertAt = exports.replaceImportPath = exports.isPromiseOrObservable = exports.getTypeReferenceAsString = void 0; const lodash_1 = require("lodash"); const path_1 = require("path"); const ts = require("typescript"); const ast_utils_1 = require("./ast-utils"); function getTypeReferenceAsString(type, typeChecker, arrayDepth = 0) { if ((0, ast_utils_1.isArray)(type)) { const arrayType = (0, ast_utils_1.getTypeArguments)(type)[0]; const { typeName, arrayDepth: depth } = getTypeReferenceAsString(arrayType, typeChecker, arrayDepth + 1); if (!typeName) { return { typeName: undefined }; } return { typeName: `${typeName}`, isArray: true, arrayDepth: depth, }; } if ((0, ast_utils_1.isBoolean)(type)) { return { typeName: Boolean.name, arrayDepth }; } if ((0, ast_utils_1.isNumber)(type) || (0, ast_utils_1.isBigInt)(type)) { return { typeName: Number.name, arrayDepth }; } if ((0, ast_utils_1.isString)(type) || (0, ast_utils_1.isStringLiteral)(type)) { return { typeName: String.name, arrayDepth }; } if (isPromiseOrObservable((0, ast_utils_1.getText)(type, typeChecker))) { const typeArguments = (0, ast_utils_1.getTypeArguments)(type); const elementType = getTypeReferenceAsString((0, lodash_1.head)(typeArguments), typeChecker, arrayDepth); return elementType; } if (type.isClass()) { return { typeName: (0, ast_utils_1.getText)(type, typeChecker), arrayDepth }; } try { const text = (0, ast_utils_1.getText)(type, typeChecker); if (text === Date.name) { return { typeName: text, arrayDepth }; } if (isOptionalBoolean(text)) { return { typeName: Boolean.name, arrayDepth }; } if ((0, ast_utils_1.isEnum)(type)) { return { typeName: text, arrayDepth }; } const isEnumMember = type.symbol && type.symbol.flags === ts.SymbolFlags.EnumMember; if (isEnumMember) { type = typeChecker.getDeclaredTypeOfSymbol(type.symbol.parent); if (!type) { return undefined; } return { typeName: text, arrayDepth }; } if (isAutoGeneratedTypeUnion(type) || isAutoGeneratedEnumUnion(type, typeChecker)) { const types = type.types; return getTypeReferenceAsString(types[types.length - 1], typeChecker); } if (text === 'any' || text === 'unknown' || text === 'object' || (0, ast_utils_1.isInterface)(type) || (type.isUnionOrIntersection() && !(0, ast_utils_1.isEnum)(type))) { return { typeName: 'Object', arrayDepth }; } if (type.aliasSymbol) { return { typeName: 'Object', arrayDepth }; } return { typeName: 'Object', arrayDepth }; } catch { return { typeName: 'Object', arrayDepth }; } } exports.getTypeReferenceAsString = getTypeReferenceAsString; function isPromiseOrObservable(type) { return type.includes('Promise') || type.includes('Observable'); } exports.isPromiseOrObservable = isPromiseOrObservable; function replaceImportPath(typeReference, fileName, options) { if (!typeReference.includes('import')) { return { typeReference, importPath: null }; } let importPath = /\("([^)]).+(")/.exec(typeReference)[0]; if (!importPath) { return { typeReference: undefined, importPath: null }; } importPath = convertPath(importPath); importPath = importPath.slice(2, importPath.length - 1); const from = options?.readonly ? convertPath(options.pathToSource) : path_1.posix.dirname(convertPath(fileName)); let relativePath = path_1.posix.relative(from, importPath); relativePath = !(0, path_1.isAbsolute)(relativePath) && relativePath[0] !== '.' ? './' + relativePath : relativePath; const nodeModulesText = 'node_modules'; const nodeModulePos = relativePath.indexOf(nodeModulesText); if (nodeModulePos >= 0) { relativePath = relativePath.slice(nodeModulePos + nodeModulesText.length + 1); const typesText = '@types'; const typesPos = relativePath.indexOf(typesText); if (typesPos >= 0) { relativePath = relativePath.slice(typesPos + typesText.length + 1); } const indexText = '/index'; const indexPos = relativePath.indexOf(indexText); if (indexPos >= 0) { relativePath = relativePath.slice(0, indexPos); } } typeReference = typeReference.replace(importPath, relativePath); if (options.readonly) { const { typeName, typeImportStatement } = convertToAsyncImport(typeReference); return { typeReference: typeImportStatement, typeName, importPath: relativePath, }; } return { typeReference: typeReference.replace('import', 'require'), importPath: relativePath, }; } exports.replaceImportPath = replaceImportPath; function convertToAsyncImport(typeReference) { const regexp = /import\(.+\).([^\]]+)(\])?/; const match = regexp.exec(typeReference); if (match?.length >= 2) { const importPos = typeReference.indexOf(match[0]); typeReference = typeReference.replace(`.${match[1]}`, ''); return { typeImportStatement: insertAt(typeReference, importPos, 'await '), typeName: match[1], }; } return { typeImportStatement: typeReference }; } function insertAt(string, index, substring) { return string.slice(0, index) + substring + string.slice(index); } exports.insertAt = insertAt; function isDynamicallyAdded(identifier) { return identifier && !identifier.parent && identifier.pos === -1; } exports.isDynamicallyAdded = isDynamicallyAdded; /** * when "strict" mode enabled, TypeScript transform the enum type to a union composed of * the enum values and the undefined type. Hence, we have to lookup all the union types to get the original type * @param type * @param typeChecker */ function isAutoGeneratedEnumUnion(type, typeChecker) { if (type.isUnionOrIntersection() && !(0, ast_utils_1.isEnum)(type)) { if (!type.types) { return undefined; } const undefinedTypeIndex = type.types.findIndex((type) => type.intrinsicName === 'undefined'); if (undefinedTypeIndex < 0) { return undefined; } // "strict" mode for enums let parentType = undefined; const isParentSymbolEqual = type.types.every((item, index) => { if (index === undefinedTypeIndex) { return true; } if (!item.symbol) { return false; } if (!item.symbol.parent || item.symbol.flags !== ts.SymbolFlags.EnumMember) { return false; } const symbolType = typeChecker.getDeclaredTypeOfSymbol(item.symbol.parent); if (symbolType === parentType || !parentType) { parentType = symbolType; return true; } return false; }); if (isParentSymbolEqual) { return parentType; } } return undefined; } exports.isAutoGeneratedEnumUnion = isAutoGeneratedEnumUnion; /** * when "strict" mode enabled, TypeScript transform the type signature of optional properties to * the {undefined | T} where T is the original type. Hence, we have to extract the last type of type union * @param type */ function isAutoGeneratedTypeUnion(type) { if (type.isUnionOrIntersection() && !(0, ast_utils_1.isEnum)(type)) { if (!type.types) { return false; } const undefinedTypeIndex = type.types.findIndex((type) => type.intrinsicName === 'undefined'); // "strict" mode for non-enum properties if (type.types.length === 2 && undefinedTypeIndex >= 0) { return true; } } return false; } exports.isAutoGeneratedTypeUnion = isAutoGeneratedTypeUnion; /** * when "strict" mode enabled, TypeScript transform optional boolean properties to "boolean | undefined" * @param text */ function isOptionalBoolean(text) { return typeof text === 'string' && text === 'boolean | undefined'; } /** * Converts Windows specific file paths to posix * @param windowsPath */ function convertPath(windowsPath) { return windowsPath .replace(/^\\\\\?\\/, '') .replace(/\\/g, '/') .replace(/\/\/+/g, '/'); } exports.convertPath = convertPath;