UNPKG

@nestjs/graphql

Version:

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

266 lines (265 loc) 9.97 kB
import * as ts from 'typescript'; import { getJSDocDeprecatedTag, getJSDocTags, getTextOfJSDocComment, ObjectFlags, SyntaxKind, TypeFlags, TypeFormatFlags, } from 'typescript'; import { isDynamicallyAdded } from './plugin-utils.js'; export function getDecorators(node) { return (ts.canHaveDecorators(node) && ts.getDecorators(node)) ?? []; } export function getModifiers(node) { return (ts.canHaveModifiers(node) && ts.getModifiers(node)) ?? []; } export function isArray(type) { const symbol = type.getSymbol(); if (!symbol) { return false; } return symbol.getName() === 'Array' && getTypeArguments(type).length === 1; } export function getTypeArguments(type) { return type.typeArguments || []; } export function isBoolean(type) { return hasFlag(type, TypeFlags.Boolean); } export function isString(type) { return hasFlag(type, TypeFlags.String); } export function isStringLiteral(type) { return (hasFlag(type, TypeFlags.StringLiteral) && !hasFlag(type, TypeFlags.EnumLiteral) && !type.isUnion()); } export function isBigInt(type) { return hasFlag(type, TypeFlags.BigInt); } export function isNumber(type) { return hasFlag(type, TypeFlags.Number); } export function isInterface(type) { return hasObjectFlag(type, ObjectFlags.Interface); } export function isEnum(type) { const hasEnumFlag = hasFlag(type, TypeFlags.Enum); if (hasEnumFlag) { return true; } if (isEnumLiteral(type)) { return false; } const symbol = type.getSymbol(); if (!symbol) { return false; } const valueDeclaration = symbol.valueDeclaration; if (!valueDeclaration) { return false; } return valueDeclaration.kind === SyntaxKind.EnumDeclaration; } export function isEnumLiteral(type) { return hasFlag(type, TypeFlags.EnumLiteral) && !type.isUnion(); } export function isNull(type) { if (type.isUnion()) { return Boolean(type.types.find((t) => hasFlag(t, TypeFlags.Null))); } else { return hasFlag(type, TypeFlags.Null); } } export function isUndefined(type) { if (type.isUnion()) { return Boolean(type.types.find((t) => hasFlag(t, TypeFlags.Undefined))); } else { return hasFlag(type, TypeFlags.Undefined); } } export function hasFlag(type, flag) { return (type.flags & flag) === flag; } export function hasObjectFlag(type, flag) { return (type.objectFlags & flag) === flag; } export function getText(type, typeChecker, enclosingNode, typeFormatFlags) { if (!typeFormatFlags) { typeFormatFlags = getDefaultTypeFormatFlags(enclosingNode); } const compilerNode = !enclosingNode ? undefined : enclosingNode; return typeChecker.typeToString(type, compilerNode, typeFormatFlags); } export function getDefaultTypeFormatFlags(enclosingNode) { let formatFlags = TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.NoTruncation | TypeFormatFlags.UseFullyQualifiedType | TypeFormatFlags.WriteTypeArgumentsOfSignature; if (enclosingNode && enclosingNode.kind === SyntaxKind.TypeAliasDeclaration) formatFlags |= TypeFormatFlags.InTypeAlias; return formatFlags; } export function getDecoratorArguments(decorator) { const callExpression = decorator.expression; return (callExpression && callExpression.arguments) || []; } export function getDecoratorName(decorator) { const isDecoratorFactory = decorator.expression.kind === SyntaxKind.CallExpression; if (isDecoratorFactory) { const callExpression = decorator.expression; if (callExpression.expression?.kind === ts.SyntaxKind.PropertyAccessExpression) { // When "import * as _" is used const propertyAccessExpression = callExpression.expression; return getIdentifierFromName(propertyAccessExpression.name).getText(); } if (callExpression.kind === ts.SyntaxKind.CallExpression) { const identifier = callExpression.expression; if (isDynamicallyAdded(identifier)) { return undefined; } return getIdentifierFromName(identifier).getText(); } } return getIdentifierFromName(decorator.expression).getText(); } function getIdentifierFromName(expression) { const identifier = getNameFromExpression(expression); if (expression && expression.kind !== SyntaxKind.Identifier) { throw new Error(); } return identifier; } function getNameFromExpression(expression) { if (expression && expression.kind === SyntaxKind.PropertyAccessExpression) { return expression.name; } return expression; } export function getJSDocDescription(node) { const jsDoc = node.jsDoc; if (!jsDoc || !jsDoc[0]) { return undefined; } return getTextOfJSDocComment(jsDoc[0].comment); } export function hasJSDocTags(node, tagName) { const tags = getJSDocTags(node); return tags.some((tag) => tagName.includes(tag.tagName.text)); // return jsDoc; } export function getJsDocDeprecation(node) { const deprecatedTag = getJSDocDeprecatedTag(node); if (!deprecatedTag) { return undefined; } return getTextOfJSDocComment(deprecatedTag.comment) || 'deprecated'; } export function findNullableTypeFromUnion(typeNode, typeChecker) { return typeNode.types.find((tNode) => hasFlag(typeChecker.getTypeAtLocation(tNode), TypeFlags.Null)); } export function hasModifiers(modifiers, toCheck) { if (!modifiers) { return false; } return modifiers.some((modifier) => toCheck.includes(modifier.kind)); } export function hasDecorators(decorators, toCheck) { if (!decorators) { return false; } return decorators.some((decorator) => { return toCheck.includes(getDecoratorName(decorator)); }); } export function hasImport(sf, what) { for (const statement of sf.statements) { if (ts.isImportDeclaration(statement) && ts.isNamedImports(statement.importClause.namedBindings)) { const bindings = statement.importClause.namedBindings.elements; for (const namedBinding of bindings) { if (namedBinding.name.text === what) { return true; } } } } return false; } export function createImportEquals(f, identifier, from) { return f.createImportEqualsDeclaration(undefined, false, identifier, f.createExternalModuleReference(f.createStringLiteral(from))); } export function createNamedImport(f, what, from) { const importClause = f.createImportClause(false, undefined, f.createNamedImports(what.map((name) => f.createImportSpecifier(false, undefined, f.createIdentifier(name))))); return f.createImportDeclaration(undefined, importClause, f.createStringLiteral(from)); } export function isCallExpressionOf(name, node) { return ts.isIdentifier(node.expression) && node.expression.text === name; } function isNode(value) { return typeof value === 'object' && value.constructor.name === 'NodeObject'; } export function serializePrimitiveObjectToAst(f, object) { const properties = []; Object.keys(object).forEach((key) => { const value = object[key]; if (value === undefined) { return; } let initializer; if (isNode(value)) { initializer = value; } else if (typeof value === 'string') { initializer = f.createStringLiteral(value); } else if (typeof value === 'boolean') { initializer = value ? f.createTrue() : f.createFalse(); } else if (typeof value === 'object') { initializer = serializePrimitiveObjectToAst(f, value); } properties.push(f.createPropertyAssignment(key, initializer)); }); return f.createObjectLiteralExpression(properties); } export function safelyMergeObjects(f, a, b) { // if both of objects are ObjectLiterals, so merge property by property in compile time // if one or both of expressions not an object literal, produce rest spread and merge in runtime if (ts.isObjectLiteralExpression(a) && ts.isObjectLiteralExpression(b)) { const aMap = a.properties.reduce((acc, prop) => { acc[prop.name.text] = prop; return acc; }, {}); b.properties.forEach((prop) => { aMap[prop.name.text] = prop; }, {}); return f.createObjectLiteralExpression(Object.values(aMap)); } else { return f.createObjectLiteralExpression([ f.createSpreadAssignment(a), f.createSpreadAssignment(b), ]); } } export function updateDecoratorArguments(f, node, decoratorName, replaceFn) { let updated = false; const nodeOriginalDecorators = getDecorators(node); const decorators = nodeOriginalDecorators.map((decorator) => { if (getDecoratorName(decorator) !== decoratorName) { return decorator; } const decoratorExpression = decorator.expression; updated = true; return f.updateDecorator(decorator, f.updateCallExpression(decoratorExpression, decoratorExpression.expression, decoratorExpression.typeArguments, replaceFn(decoratorExpression.arguments))); }); if (!updated) { return node; } if (ts.isClassDeclaration(node)) { return f.updateClassDeclaration(node, [...decorators, ...getModifiers(node)], node.name, node.typeParameters, node.heritageClauses, node.members); } if (ts.isPropertyDeclaration(node)) { return f.updatePropertyDeclaration(node, [...decorators, ...getModifiers(node)], node.name, node.questionToken, node.type, node.initializer); } if (ts.isGetAccessorDeclaration(node)) { return f.updateGetAccessorDeclaration(node, [...decorators, ...getModifiers(node)], node.name, node.parameters, node.type, node.body); } }