flowgen
Version:
Generate flowtype definition files from TypeScript
295 lines (217 loc) • 10.8 kB
JavaScript
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;
;