flowgen
Version:
Generate flowtype definition files from TypeScript
240 lines (191 loc) • 7.78 kB
JavaScript
exports.__esModule = true;
exports.default = void 0;
var _path = _interopRequireDefault(require("path"));
var _fs = _interopRequireDefault(require("fs"));
var _util = require("./util");
var _meta = _interopRequireDefault(require("./meta"));
var _beautifier = _interopRequireDefault(require("./beautifier"));
var _compiler = _interopRequireDefault(require("./compiler"));
var _defaultExporter = _interopRequireDefault(require("./default-exporter"));
var _flowTypedExporter = require("./flow-typed-exporter");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const readDir = (0, _util.promisify)(_fs.default.readdir);
const readFile = (0, _util.promisify)(_fs.default.readFile);
async function outputFile(flowDefinitions, {
intro,
index,
file,
moduleName,
outputFile
}, options, writeFile) {
// Produce the flow library content
try {
// Write the output to disk
let code = intro + flowDefinitions;
try {
code = (0, _beautifier.default)(code);
} catch (e) {
console.error(e);
}
const absoluteOutputFilePath = await writeFile(outputFile, code, index); // Check if we should compile tests as well
if (options.compileTests) {
// Assume tests file is in same dir, named <filename>-tests.ts
// Based on DD conventions
const testFileName = _path.default.dirname(file) + "/" + moduleName + "-tests.ts";
const testFileOutput = _path.default.dirname(absoluteOutputFilePath) + "/test_" + moduleName + ".js"; // Try to compile the test file. Will fail silently if not present.
_compiler.default.compileTest(testFileName, testFileOutput);
}
} catch (e) {
console.error("Parsing", file, "failed");
console.error(e);
}
}
function getFile(file, options, rawFile, isInDir, index, pkg) {
// Get the module name from the file name
const moduleName = getModuleNameFromFile(file, pkg);
const mode = getMode(options, file, isInDir);
const mainTypes = pkg ? pkg.typings || pkg.types || "" : "";
const isMain = _path.default.join(rawFile, mainTypes) === file; // The format of the output argument varies a bit based on which
// exporting format we're using. For flow-typed, only the module name
// is required, otherwise we use the cli arg.
const outputFile = getOutputFile(options, file, rawFile, mode, moduleName); // Get the intro text
let intro = (0, _meta.default)(moduleName, options.version, options.addFlowHeader || mode === "directory");
if (mode === "directory-flow-typed") intro = "";
return {
file,
index,
isMain,
outputFile,
moduleName,
intro,
mode
};
}
async function bfs(rootDir, options) {
const queue = [];
const files = [];
let pkg;
try {
pkg = JSON.parse((await readFile(_path.default.join(rootDir, "package.json"))).toString());
} catch (err) {// ignored
}
queue.push(rootDir);
let current;
while (queue.length) {
current = queue.shift();
try {
const dir = await readDir(current, {
withFileTypes: true
});
for (const file of dir) {
if (file.isDirectory()) {
if (file.name === "node_modules") continue;
queue.push(_path.default.join(current, file.name));
} else {
if (!file.name.endsWith(".d.ts")) continue;
files.push(getFile(_path.default.join(current, file.name), options, rootDir, true, files.length, pkg));
}
}
} catch {
files.push(getFile(current, options, rootDir, false, files.length, pkg));
}
}
return files;
}
var _default = options => {
const fileOptions = {
jsdoc: options.jsdoc,
quiet: options.quiet,
interfaceRecords: options.interfaceRecords,
moduleExports: options.moduleExports,
inexact: options.inexact,
asModule: options.asModule
}; // No real reason to return an object here instead of combining
// the compile function into the wrapper, but I like the API it produces.
return {
compile: async rawFiles => {
const files = []; // Iterate all the files the user has passed in
for (const rawFile of rawFiles) {
files.push(...(await bfs(rawFile, options)));
}
const filesByPath = files.reduce((acc, file) => {
acc.set(file.file, file);
return acc;
}, new Map());
const fileMapper = (sourceCode, fileName) => {
if (!sourceCode) return;
const file = filesByPath.get(fileName);
if (!file) return sourceCode;
if (file.mode !== "directory-flow-typed") return sourceCode;
if (file.isMain) {
return `declare module "${file.outputFile.moduleName}" {${sourceCode}}
declare module "${file.outputFile.rootModuleName}" {
declare export * from "${file.outputFile.moduleName}";
}
`;
}
return `declare module "${file.outputFile.moduleName}" {${sourceCode}}`;
};
if (files.length > 1) {
const sources = _compiler.default.compileDefinitionFiles(files.map(v => v.file), fileOptions, fileMapper);
for (let index = 0; index < sources.length; index++) {
const [, flowDefinitions] = sources[index];
const file = files[index];
let writeFile = _defaultExporter.default;
if (file.mode === "flow-typed") writeFile = _flowTypedExporter.flowTypedExporter;
if (file.mode === "directory-flow-typed") writeFile = _flowTypedExporter.flowTypedDirectoryExporter; // Let the user know what's going on
if (files.length >= 3) {
// If we're compiling a lot of files, show more stats
const progress = Math.round((index + 1) / files.length * 100);
process.stdout.write("\r\x1b[K");
process.stdout.write(progress + "% | " + file.moduleName);
} else {
console.log("Parsing", file.moduleName);
}
outputFile(flowDefinitions, file, options, writeFile);
}
} else {
const file = files[0];
const flowDefinitions = _compiler.default.compileDefinitionFile(file.file, fileOptions, fileMapper);
let writeFile = _defaultExporter.default;
if (file.mode === "flow-typed") writeFile = _flowTypedExporter.flowTypedExporter;
if (file.mode === "directory-flow-typed") writeFile = _flowTypedExporter.flowTypedDirectoryExporter; // Let the user know what's going on
console.log("Parsing", file.moduleName);
outputFile(flowDefinitions, file, options, writeFile);
}
if (files.length >= 3) process.stdout.write("\n");
}
};
};
exports.default = _default;
function getModuleNameFromFile(fileName, pkg) {
if (pkg) return pkg.name;
return _path.default.basename(fileName).replace(".d.ts", "");
}
function getMode(options, file, isDir) {
if (isDir && options.flowTypedFormat) return "directory-flow-typed";
if (isDir) return "directory";
if (options.flowTypedFormat) return "flow-typed";
return "file";
}
function getOutputFile(options, file, prefix, mode, moduleName) {
switch (mode) {
case "directory-flow-typed":
return {
rootModuleName: moduleName,
moduleName: _path.default.join(moduleName, _path.default.relative(prefix, file).replace(".d.ts", "")),
filename: _path.default.normalize(file.replace(prefix, "").replace(".d.ts", ""))
};
case "directory":
{
var _options$out;
const basedir = (_options$out = options.out) != null ? _options$out : "exports";
return _path.default.normalize(file.replace(prefix, `${basedir}${_path.default.sep}`).replace(".d.ts", ".js.flow"));
}
case "flow-typed":
return moduleName;
default:
return options.out;
}
}
;