@nestjs/graphql
Version:
Nest - modern, fast, powerful node.js web framework (@graphql)
266 lines (265 loc) • 9.97 kB
JavaScript
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);
}
}