UNPKG

@itrocks/property-type

Version:

Runtime type reflection from TypeScript declaration files for properties

218 lines 8.23 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.UnknownType = exports.UnionType = exports.TypeType = exports.LiteralType = exports.IntersectionType = exports.RecordType = exports.CompositeType = exports.CollectionType = exports.CanonicalType = exports.PropertyType = void 0; exports.isCanonical = isCanonical; exports.isLiteral = isLiteral; exports.isType = isType; exports.propertyTypesFromFile = propertyTypesFromFile; const node_fs_1 = require("node:fs"); const node_path_1 = require("node:path"); const typescript_1 = __importDefault(require("typescript")); class PropertyType { type; optional; constructor(type, optional = false) { this.type = type; this.optional = optional; } get lead() { return this.type; } } exports.PropertyType = PropertyType; class CanonicalType extends PropertyType { constructor(type) { super(type); } } exports.CanonicalType = CanonicalType; class CollectionType extends PropertyType { elementType; constructor(type, elementType) { super(type); this.elementType = elementType; } } exports.CollectionType = CollectionType; class CompositeType extends PropertyType { types; constructor(types) { super(types[0].type); this.types = types; } get lead() { return this.types[0].lead; } } exports.CompositeType = CompositeType; class RecordType extends PropertyType { keyType; elementType; constructor(keyType, elementType) { super(Object); this.keyType = keyType; this.elementType = elementType; } } exports.RecordType = RecordType; class IntersectionType extends CompositeType { } exports.IntersectionType = IntersectionType; class LiteralType extends PropertyType { value; constructor(value) { super(literalValueType(value)); this.value = value; } } exports.LiteralType = LiteralType; class TypeType extends PropertyType { args; constructor(type, args) { super(type); this.args = args; } } exports.TypeType = TypeType; class UnionType extends CompositeType { } exports.UnionType = UnionType; class UnknownType extends PropertyType { raw; constructor(raw) { super(undefined); this.raw = raw; } } exports.UnknownType = UnknownType; function isCanonical(propertyType, type) { return (propertyType instanceof CanonicalType) && ((arguments.length === 1) || (propertyType.type === type)); } function isLiteral(propertyType, literal) { return (propertyType instanceof LiteralType) && ((arguments.length === 1) || (propertyType.value === literal)); } function isType(propertyType, type) { return (propertyType instanceof TypeType) && ((arguments.length === 1) || (propertyType.type === type)); } function literalValueType(literal) { switch (typeof literal) { case 'bigint': return BigInt; case 'boolean': return Boolean; case 'number': return Number; case 'string': return String; case 'symbol': return Symbol; } } function nodeToCanonicalType(node) { const kind = node.kind; const kinds = typescript_1.default.SyntaxKind; switch (kind) { case kinds.BigIntKeyword: return new CanonicalType(BigInt); case kinds.BooleanKeyword: return new CanonicalType(Boolean); case kinds.NumberKeyword: return new CanonicalType(Number); case kinds.ObjectKeyword: return new CanonicalType(Object); case kinds.StringKeyword: return new CanonicalType(String); case kinds.SymbolKeyword: return new CanonicalType(Symbol); } } function nodeToLiteralType(node) { if (!typescript_1.default.isLiteralTypeNode(node)) return; const kinds = typescript_1.default.SyntaxKind; const literal = node.literal; switch (literal.kind) { case kinds.FalseKeyword: return new LiteralType(false); case kinds.NullKeyword: return new LiteralType(null); case kinds.TrueKeyword: return new LiteralType(true); case kinds.UndefinedKeyword: return new LiteralType(undefined); } if (typescript_1.default.isNumericLiteral(literal)) { return new LiteralType(+literal.text); } if (typescript_1.default.isStringLiteral(literal)) { return new LiteralType(literal.text); } } function nodeToType(node, typeImports) { if (typescript_1.default.isArrayTypeNode(node)) { return new CollectionType(Array, nodeToType(node.elementType, typeImports)); } if (typescript_1.default.isIntersectionTypeNode(node)) { return new IntersectionType(node.types.map(node => nodeToType(node, typeImports))); } if (typescript_1.default.isUnionTypeNode(node)) { return new UnionType(node.types.map(node => nodeToType(node, typeImports))); } return nodeToCanonicalType(node) ?? nodeToLiteralType(node) ?? nodeToTypeType(node, typeImports) ?? new UnknownType(node.getText()); } function nodeToTypeType(node, typeImports) { if (!typescript_1.default.isTypeReferenceNode(node)) return; const name = node.typeName.getText(); const args = node.typeArguments?.map(node => nodeToType(node, typeImports)); return ((name === 'Record') && (args?.length === 2)) ? new RecordType(args[0], args[1]) : new TypeType(strToType(name, typeImports), args); } function propertyTypesFromFile(file) { const content = readFile(file); const filePath = (0, node_path_1.dirname)(file); const sourceFile = typescript_1.default.createSourceFile(file, content, typescript_1.default.ScriptTarget.Latest, true); const propertyTypes = {}; const typeImports = {}; function parseNode(node) { if (typescript_1.default.isImportDeclaration(node) && node.importClause) { let importPath = node.moduleSpecifier.text; if ((importPath[0] === '.') && !importPath.endsWith('.js')) { importPath += '.js'; } const importFile = (importPath[0] === '.') ? (0, node_path_1.normalize)(filePath + '/' + importPath) : importPath; if (node.importClause.name) { typeImports[node.importClause.name.getText()] = { import: importFile, name: 'default' }; } const namedBindings = node.importClause.namedBindings; if (namedBindings && typescript_1.default.isNamedImports(namedBindings)) { for (const importSpecifier of namedBindings.elements) { const name = importSpecifier.name.getText(); const alias = importSpecifier.propertyName?.getText() ?? name; typeImports[alias] = { import: importFile, name }; } } } if (typescript_1.default.isClassDeclaration(node) && node.name && node.modifiers?.some(modifier => modifier.kind === typescript_1.default.SyntaxKind.ExportKeyword)) { const className = node.name.getText(); typeImports[className] = { import: file, name: className }; for (const member of node.members) { if (typescript_1.default.isPropertyDeclaration(member) && member.type) { const type = nodeToType(member.type, typeImports); type.optional = !!member.questionToken; propertyTypes[member.name.text] = type; } } return; } typescript_1.default.forEachChild(node, parseNode); } parseNode(sourceFile); return propertyTypes; } function readFile(file) { try { return (0, node_fs_1.readFileSync)(file.substring(0, file.lastIndexOf('.')) + '.d.ts', 'utf8'); } catch (exception) { console.error('file', file); throw exception; } } function strToType(type, typeImports) { const typeImport = typeImports[type]; return typeImport ? require(typeImport.import)[typeImport.name] : globalThis[type]; } //# sourceMappingURL=property-type.js.map