UNPKG

flowgen

Version:

Generate flowtype definition files from TypeScript

295 lines (217 loc) 10.8 kB
"use strict"; exports.__esModule = true; exports.variableDeclaration = exports.typeReference = exports.typeDeclaration = exports.propertyDeclaration = exports.interfaceType = exports.interfaceDeclaration = exports.enumDeclaration = exports.classDeclaration = void 0; var ts = _interopRequireWildcard(require("typescript")); var _options = require("../options"); var _checker = require("../checker"); var _namespace = _interopRequireDefault(require("../nodes/namespace")); var printers = _interopRequireWildcard(require("./index")); var _env = require("../env"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } const propertyDeclaration = (node, keywordPrefix) => { let left = keywordPrefix; const symbol = _checker.checker.current.getSymbolAtLocation(node.name); const name = ts.isVariableDeclaration(node) ? printers.node.getFullyQualifiedName(symbol, node.name) : printers.node.printType(node.name); if (node.modifiers && node.modifiers.some(modifier => modifier.kind === ts.SyntaxKind.PrivateKeyword)) { return ""; } if (node.modifiers && node.modifiers.some(modifier => modifier.kind === ts.SyntaxKind.ReadonlyKeyword)) { left += "+"; } left += name; if (node.type) { let right = printers.node.printType(node.type); if (ts.isPropertyDeclaration(node) && node.questionToken) { if (node.name.kind !== ts.SyntaxKind.ComputedPropertyName) { left += "?"; } else { right = `(${right}) | void`; } } return left + ": " + right; } return left + `: ${printers.node.printType(node.initializer)}\n`; }; exports.propertyDeclaration = propertyDeclaration; const variableDeclaration = node => { const declarations = node.declarationList.declarations.map(printers.node.printType); return declarations.map(name => `declare ${printers.relationships.exporter(node)}var ${name};`).join("\n"); }; exports.variableDeclaration = variableDeclaration; const interfaceType = (node, nodeName, mergedNamespaceChildren, withSemicolons = false, isType = false) => { const isInexact = (0, _options.opts)().inexact; const members = node.members.map(member => { const printed = printers.node.printType(member); if (!printed) { return null; } return "\n" + printers.common.jsdoc(member) + printed; }); if (mergedNamespaceChildren.length > 0) { for (const child of _namespace.default.formatChildren(mergedNamespaceChildren, nodeName)) { members.push(`static ${child}\n`); } } const hasIndexSignature = node.members.some(member => ts.isIndexSignatureDeclaration(member)); // If an index signature is present in our type, we probably don't want to // provide the trailing "...", since it will be ignored by Flow anyway. if (isType && isInexact && !hasIndexSignature) { members.push("...\n"); } else if (members.length > 0) { members.push("\n"); } const inner = members.filter(Boolean) // Filter rows which didn't print properly (private fields et al) .join(withSemicolons ? ";" : ","); // we only want type literals to be exact. i.e. class Foo {} should not be class Foo {||} if (!ts.isTypeLiteralNode(node)) { return `{${inner}}`; } // If a type contains an index signature (e.g. `[key: string]: number`), then // we want to treat it as inexact no matter what, otherwise we may generate a // Flow type like `{|[k: string]: any|}` which is invalid in Flow prior to // v0.126.0 (source: https://github.com/facebook/flow/blob/main/Changelog.md#01260) // // It should also be safe to assume that if an index signature is present, the // equivalent human-authored Flow type would be inexact. if (hasIndexSignature) { return `{${inner}}`; } return isInexact ? `{${inner}}` : `{|${inner}|}`; }; exports.interfaceType = interfaceType; const interfaceRecordType = (node, heritage, withSemicolons = false) => { const isInexact = (0, _options.opts)().inexact; let members = node.members.map(member => { const printed = printers.node.printType(member); if (!printed) { return null; } return "\n" + printers.common.jsdoc(member) + printed; }).filter(Boolean) // Filter rows which didnt print propely (private fields et al) .join(withSemicolons ? ";" : ","); if (members.length > 0) { members += "\n"; } if (isInexact) { return `{${heritage}${members}}`; } else { return `{|${heritage}${members}|}`; } }; const classHeritageClause = (classMixins, classImplements) => (0, _env.withEnv)((env, type) => { env.classHeritage = true; // TODO: refactor this const symbol = _checker.checker.current.getSymbolAtLocation(type.expression); printers.node.fixDefaultTypeArguments(symbol, type); if (ts.isIdentifier(type.expression) && symbol) { const value = printers.node.getFullyQualifiedPropertyAccessExpression(symbol, type.expression) + printers.common.generics(type.typeArguments); if (symbol.declarations.some(declaration => declaration.kind === ts.SyntaxKind.InterfaceDeclaration)) { classImplements.push(value); } else { classMixins.push(value); } } else { classMixins.push(printers.node.printType(type)); } env.classHeritage = false; }); const interfaceHeritageClause = type => { // TODO: refactor this const symbol = _checker.checker.current.getSymbolAtLocation(type.expression); printers.node.fixDefaultTypeArguments(symbol, type); if (ts.isIdentifier(type.expression) && symbol) { const name = printers.node.getFullyQualifiedPropertyAccessExpression(symbol, type.expression); return name + printers.common.generics(type.typeArguments); } else if (ts.isIdentifier(type.expression)) { const name = printers.identifiers.print(type.expression.text); if (typeof name === "function") { return name(type.typeArguments); } else { return name; } } else { return printers.node.printType(type); } }; const interfaceRecordDeclaration = (nodeName, node, modifier) => { let heritage = ""; // If the class is extending something if (node.heritageClauses) { heritage = node.heritageClauses.map(clause => { return clause.types.map(interfaceHeritageClause).map(type => `...$Exact<${type}>`).join(",\n"); }).join(""); heritage = heritage.length > 0 ? `${heritage},\n` : ""; } const str = `${modifier}type ${nodeName}${printers.common.generics(node.typeParameters)} = ${interfaceRecordType(node, heritage)}\n`; return str; }; const interfaceDeclaration = (nodeName, node, modifier) => { const isRecord = (0, _options.opts)().interfaceRecords; if (isRecord) { return interfaceRecordDeclaration(nodeName, node, modifier); } let heritage = ""; // If the class is extending something if (node.heritageClauses) { heritage = node.heritageClauses.map(clause => { return clause.types.map(interfaceHeritageClause).join(" & "); }).join(""); heritage = heritage.length > 0 ? `& ${heritage}\n` : ""; } const type = node.heritageClauses ? "type" : "interface"; const str = `${modifier}${type} ${nodeName}${printers.common.generics(node.typeParameters)} ${type === "type" ? "= " : ""}${interfaceType(node, nodeName, [], false, type === "type")} ${heritage}`; return str; }; exports.interfaceDeclaration = interfaceDeclaration; const typeDeclaration = (nodeName, node, modifier) => { const str = `${modifier}type ${nodeName}${printers.common.generics(node.typeParameters)} = ${printers.node.printType(node.type)};`; return str; }; exports.typeDeclaration = typeDeclaration; const enumDeclaration = (nodeName, node) => { const exporter = printers.relationships.exporter(node); let members = ""; // @ts-expect-error iterating over an iterator for (const [index, member] of node.members.entries()) { let value; if (typeof member.initializer !== "undefined") { value = printers.node.printType(member.initializer); } else { value = index; } members += `+${member.name.text}: ${value},`; members += `// ${value}\n`; } return ` declare ${exporter} var ${nodeName}: {| ${members} |};\n`; }; exports.enumDeclaration = enumDeclaration; const typeReference = (node, identifier) => { if (ts.isQualifiedName(node.typeName)) { return printers.node.printType(node.typeName) + printers.common.generics(node.typeArguments); } let name = node.typeName.text; if (identifier) { const replaced = printers.identifiers.print(node.typeName.text); if (typeof replaced === "function") { return replaced(node.typeArguments); } name = replaced; } return printers.relationships.namespaceProp(name) + printers.common.generics(node.typeArguments); }; exports.typeReference = typeReference; const classDeclaration = (nodeName, node, mergedNamespaceChildren) => { let heritage = ""; // If the class is extending something if (node.heritageClauses) { const classMixins = []; const classImplements = []; node.heritageClauses.forEach(clause => { clause.types.forEach(classHeritageClause(classMixins, classImplements)); }); const mixinsMessage = classMixins.length > 0 ? `mixins ${classMixins.join(",")}` : ""; const classImplementsMessage = classImplements.length > 0 ? ` implements ${classImplements.join(",")}` : ""; heritage += mixinsMessage; heritage += classImplementsMessage; } const str = `declare ${printers.relationships.exporter(node)}class ${nodeName}${printers.common.generics(node.typeParameters)} ${heritage} ${interfaceType(node, nodeName, mergedNamespaceChildren, true)}`; return str; }; exports.classDeclaration = classDeclaration;