@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
211 lines • 8.17 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getAllFiles = getAllFiles;
exports.getAllFilesSync = getAllFilesSync;
exports.allRFiles = allRFiles;
exports.allRFilesFrom = allRFilesFrom;
exports.writeTableAsCsv = writeTableAsCsv;
exports.readLineByLine = readLineByLine;
exports.readLineByLineSync = readLineByLineSync;
exports.getParentDirectory = getParentDirectory;
exports.isFilePath = isFilePath;
const fs_1 = __importStar(require("fs"));
const path_1 = __importDefault(require("path"));
const log_1 = require("./log");
const n_readlines_1 = __importDefault(require("n-readlines"));
/**
* Retrieves all files in the given directory recursively
* @param dir - Directory path to start the search from
* @param suffix - Suffix of the files to be retrieved
* Based on {@link https://stackoverflow.com/a/45130990}
* @see {@link getAllFilesSync} for a synchronous version.
*/
async function* getAllFiles(dir, suffix = /.*/) {
const entries = await fs_1.promises.readdir(dir, { withFileTypes: true, recursive: false });
for (const subEntries of entries) {
const res = path_1.default.resolve(dir, subEntries.name);
if (subEntries.isDirectory()) {
yield* getAllFiles(res, suffix);
}
else if (suffix.test(subEntries.name)) {
yield res;
}
}
}
/**
* Retrieves all files in the given directory recursively (synchronously)
* @see {@link getAllFiles} - for an asynchronous version.
*/
function* getAllFilesSync(dir, suffix = /.*/, ignoreDirs = undefined) {
const entries = fs_1.default.readdirSync(dir, { withFileTypes: true, recursive: false });
for (const subEntries of entries) {
const res = path_1.default.resolve(dir, subEntries.name);
if (subEntries.isDirectory()) {
if (!ignoreDirs?.test(subEntries.name)) {
yield* getAllFilesSync(res, suffix);
}
}
else if (suffix.test(subEntries.name)) {
yield res;
}
}
}
const rFileRegex = /\.[rR]$/;
/**
* Retrieves all R files in a given directory (asynchronously)
* @param input - directory-path to start the search from, can be a file as well. Will just return the file then.
* @param limit - limit the number of files to be retrieved
* @returns Number of files processed (normally ≤ `limit`, is ≥ `limit` if limit was reached).
* Will be `1`, if `input` is an R file (and `0` if it isn't).
* @see getAllFiles
*/
async function* allRFiles(input, limit = Number.MAX_VALUE) {
let count = 0;
if (fs_1.default.statSync(input).isFile()) {
if (rFileRegex.test(input)) {
yield { request: 'file', content: input };
return 1;
}
log_1.log.warn(`Input ${input} is not an R file`);
return 0;
}
for await (const f of getAllFiles(input, rFileRegex)) {
if (++count > limit) {
return count;
}
yield { request: 'file', content: f };
}
return count;
}
/**
* Retrieves all R files in a given set of directories and files (asynchronously)
* @param inputs - Files or directories to validate for R-files
* @param limit - Limit the number of files to be retrieved
* @returns Number of files processed (≤ limit)
* @see allRFiles
*/
async function* allRFilesFrom(inputs, limit) {
limit ??= Number.MAX_VALUE;
if (inputs.length === 0) {
log_1.log.info('No inputs given, nothing to do');
return 0;
}
let count = 0;
for (const input of inputs) {
count += yield* allRFiles(input, limit - count);
}
return count;
}
/**
* Writes the given table as a CSV file.
* @param table - The table to write
* @param file - The file path to write the CSV to
* @param sep - The separator to use (default: `,`)
* @param newline - The newline character to use (default: `\n`)
*/
function writeTableAsCsv(table, file, sep = ',', newline = '\n') {
const csv = [table.header.join(sep), ...table.rows.map(row => row.join(sep))].join(newline);
fs_1.default.writeFileSync(file, csv);
}
/**
* Reads a file line by line and calls the given function for each line.
* The `lineNumber` starts at `0`.
* The `maxLines` option limits the maximum number of read lines and is `Infinity` by default.
* @returns Whether all lines have been successfully read (`false` if `maxLines` was reached)
*
* See {@link readLineByLineSync} for a synchronous version.
*/
async function readLineByLine(filePath, onLine, maxLines = Infinity) {
if (!(await fs_1.default.promises.stat(filePath).catch(() => { }))?.isFile()) {
log_1.log.warn(`File ${filePath} does not exist`);
return false;
}
const reader = new n_readlines_1.default(filePath);
let line;
let counter = 0;
// eslint-disable-next-line no-cond-assign
while (line = reader.next()) {
if (counter >= maxLines) {
return false;
}
await onLine(line, counter++);
}
return true;
}
/**
* Reads a file line by line and calls the given function for each line.
* The `lineNumber` starts at `0`.
* The `maxLines` option limits the maximum number of read lines and is `Infinity` by default.
* @returns Whether the file exists and all lines have been successfully read (`false` if `maxLines` was reached)
*
* See {@link readLineByLine} for an asynchronous version.
*/
function readLineByLineSync(filePath, onLine, maxLines = Infinity) {
if (!fs_1.default.statSync(filePath, { throwIfNoEntry: false })?.isFile()) {
log_1.log.warn(`File ${filePath} does not exist`);
return false;
}
const reader = new n_readlines_1.default(filePath);
let line;
let counter = 0;
// eslint-disable-next-line no-cond-assign
while (line = reader.next()) {
if (counter >= maxLines) {
return false;
}
onLine(line, counter++);
}
return true;
}
/**
* Chops off the last part of the given directory path after a path separator, essentially returning the path's parent directory.
* If an absolute path is passed, the returned path is also absolute.
* @param directory - The directory whose parent to return
*/
function getParentDirectory(directory) {
// apparently this is somehow the best way to do it in node, what
return directory.split(path_1.default.sep).slice(0, -1).join(path_1.default.sep);
}
/**
* Checks whether the given path-like object is a file that exists on the local filesystem.
*/
function isFilePath(p) {
return fs_1.default.existsSync(p) && fs_1.default.statSync(p).isFile();
}
//# sourceMappingURL=files.js.map