@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
137 lines • 5.5 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DocMaker = exports.WikiChangeType = void 0;
var WikiChangeType;
(function (WikiChangeType) {
WikiChangeType[WikiChangeType["Changed"] = 0] = "Changed";
WikiChangeType[WikiChangeType["UnimportantChange"] = 1] = "UnimportantChange";
WikiChangeType[WikiChangeType["Identical"] = 2] = "Identical";
})(WikiChangeType || (exports.WikiChangeType = WikiChangeType = {}));
const DefaultReplacementPatterns = [
// eslint-disable-next-line no-irregular-whitespace -- we may produce it in output
[/\d+(\.\d+)?( |\s*)?ms/g, ''],
[/tmp[%A-Za-z0-9-]+/g, ''],
[/"?(timing|searchTimeMs|processTimeMs|id|tsId)"?:\s*\d+(\.\d)?,?/g, ''],
[/"format":"compact".+/gmius, ''],
[/%%\s*\d*-+/g, ''],
[/"[rR]": "\d+\.\d+\.\d+.*?"/g, ''],
[/R\s*\d+\.\d+\.\d+/g, ''],
[/v\d+\.\d+\.\d+/g, ''],
// clean paths
[/%2Fhome%2F([a-zA-Z0-9._-]+%2F)*/g, ''],
// async wrapper depends on whether the promise got fulfilled already
[/async|%20/g, ''],
[/\s*Copied mermaid url to clipboard\s*\([^)]+\)/gmi, '']
];
/**
* Abstract base class for generating wiki files.
* **Please make sure to register your WikiMaker implementation in the CLI wiki tool to have it executed:
* `src/cli/wiki.ts`.**
*
* If this wiki page produces multiple pages ("sub files"), you can use `writeSubFile` inside the `text` method
* to write those additional files.
*/
class DocMaker {
target;
filename;
purpose;
printHeader;
currentArgs;
writtenSubfiles = new Set();
/**
* Creates a new WikiMaker instance.
* @param target - The target path where the wiki file will be generated.
* @param filename - The name of the file being generated. Probably use `module.filename`.
* @param purpose - The purpose of the file, e.g., 'wiki context for types'.
* @param printHeader - Whether to print the auto-generation header. Default is `true`. Only mark this `false` if you plan to add it yourself.
* @protected
*/
constructor(target, filename, purpose, printHeader = true) {
this.filename = filename;
this.purpose = purpose;
this.target = target;
this.printHeader = printHeader;
}
/**
* Gets the target path where the wiki file will be generated.
*/
getTarget() {
return this.target;
}
/**
* Gets the name of the producer of this wiki file.
*/
getProducer() {
return this.filename;
}
/**
* Gets the set of subfiles written during the last `make` call.
*/
getWrittenSubfiles() {
return this.writtenSubfiles;
}
/**
* Generates or updates the wiki file at the given target location.
* @returns `true` if the file was created or updated, `false` if it was identical and not changed.
*/
async make(args) {
this.currentArgs = args;
this.writtenSubfiles = new Set();
const newText = (this.printHeader ? (await args.ctx.header(this.filename, this.purpose)) + '\n' : '') + await this.text(args);
if (args.force || this.didUpdate(this.target, newText, args.readFileSync(this.target)?.toString()) === WikiChangeType.Changed) {
args.writeFileSync(this.target, newText);
return true;
}
return this.writtenSubfiles.size > 0;
}
/**
* Please note that for subfiles you have to always add your own header
*/
writeSubFile(path, data) {
if (!this.currentArgs) {
throw new Error('DocMaker: writeSubFile called outside of make()');
}
if (this.currentArgs.force || this.didUpdate(path, data, this.currentArgs.readFileSync(path)?.toString()) === WikiChangeType.Changed) {
this.currentArgs.writeFileSync(path.toString(), data);
this.writtenSubfiles.add(path.toString());
return true;
}
return false;
}
/**
* Normalizes the given wiki text for comparison.
*/
normalizeText(text) {
// drop first two meta lines
let result = text.split('\n').slice(2).join('\n');
for (const [pattern, replacement] of DefaultReplacementPatterns) {
result = result.replace(pattern, replacement);
}
return result.trim();
}
/**
* Determines the type of change between the old and new text.
*/
didUpdate(path, newText, oldText) {
if (oldText === newText) {
return WikiChangeType.Identical;
}
const normOld = this.normalizeText(oldText ?? '');
const normNew = this.normalizeText(newText);
const same = normOld === normNew;
if (!same) {
// find first diff
for (let i = 0; i < Math.min(normOld.length, normNew.length); i++) {
if (normOld[i] !== normNew[i]) {
const contextOld = normOld.slice(Math.max(0, i - 20), Math.min(normOld.length, i + 20));
const contextNew = normNew.slice(Math.max(0, i - 20), Math.min(normNew.length, i + 20));
console.log(` [${path.toString()}] First diff at pos ${i}:\n - Old: ...${contextOld}...\n + New: ...${contextNew}...`);
break;
}
}
}
return same ? WikiChangeType.UnimportantChange : WikiChangeType.Changed;
}
}
exports.DocMaker = DocMaker;
//# sourceMappingURL=doc-maker.js.map