@intlayer/chokidar
Version:
Uses chokidar to scan and build Intlayer declaration files into dictionaries based on Intlayer configuration.
262 lines • 8.22 kB
JavaScript
import readline from "readline";
import { getConfiguration } from "@intlayer/config";
import { sortAlphabetically } from "./utils.mjs";
const LINE_DETECTOR = "\u200B\u200B\u200B";
const SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
const RESET = "\x1B[0m";
const GREEN = "\x1B[32m";
const RED = "\x1B[31m";
const BLUE = "\x1B[34m";
const GREY = "\x1B[90m";
const GREY_DARK = "\x1B[90m";
class Logger {
dictionariesStatuses = [];
spinnerTimer = null;
maxDictionaryKeyLength = 0;
previousLineCount = 0;
lineDifCounter = 0;
originalStdoutWrite = null;
extraLines = 0;
isUpdating = false;
config;
// Singleton instance
static instance;
constructor() {
}
static getInstance() {
if (!Logger.instance) {
Logger.instance = new Logger();
}
return Logger.instance;
}
init(localDictionariesKeys, distantDictionariesKeys, configuration = getConfiguration()) {
this.config = configuration;
const allKeys = Array.from(
new Set(
[...localDictionariesKeys, ...distantDictionariesKeys].sort(
sortAlphabetically
)
)
);
this.maxDictionaryKeyLength = allKeys.reduce(
(max, key) => key.length > max ? key.length : max,
0
);
this.dictionariesStatuses = allKeys.map((dictionaryKey) => {
const states = [];
if (localDictionariesKeys.includes(dictionaryKey)) {
states.push({
type: "local",
status: "pending",
spinnerFrameIndex: 0
});
}
if (distantDictionariesKeys.includes(dictionaryKey)) {
states.push({
type: "distant",
status: "pending",
spinnerFrameIndex: 0
});
}
return {
dictionaryKey,
state: states
};
});
this.startSpinner();
this.updateAllStatusLines();
}
// New method to add dictionary keys after initialization
addDictionaryKeys(type, dictionaryKeys) {
for (const dictionaryKey of dictionaryKeys) {
if (dictionaryKey.length > this.maxDictionaryKeyLength) {
this.maxDictionaryKeyLength = dictionaryKey.length;
}
let statusObj = this.dictionariesStatuses.find(
(ds) => ds.dictionaryKey === dictionaryKey
);
if (!statusObj) {
statusObj = {
dictionaryKey,
state: []
};
this.dictionariesStatuses.push(statusObj);
const newState = {
type,
status: "pending",
spinnerFrameIndex: 0
};
statusObj.state.push(newState);
} else {
const existingState = statusObj.state.find((s) => s.type === type);
if (!existingState) {
const newState = {
type,
status: "pending",
spinnerFrameIndex: 0
};
statusObj.state.push(newState);
}
}
}
this.updateAllStatusLines();
}
startSpinner() {
if (!this.spinnerTimer) {
this.spinnerTimer = setInterval(() => {
this.updateAllStatusLines();
}, 100);
}
}
stopSpinner() {
if (this.spinnerTimer) {
clearInterval(this.spinnerTimer);
this.spinnerTimer = null;
}
}
// Method to update the terminal output
updateOutput(content) {
if (this.config?.log.mode !== "verbose") return;
if (!this.originalStdoutWrite) {
this.originalStdoutWrite = process.stdout.write.bind(process.stdout);
this.extraLines = 0;
const write = (chunk, encoding, callback) => {
const str = typeof chunk === "string" ? chunk : chunk.toString();
const newLines = (str.match(/\n/g) ?? []).length;
if (!this.isUpdating) {
this.extraLines += newLines;
}
return this.originalStdoutWrite(chunk, encoding, callback);
};
process.stdout.write = write;
}
this.isUpdating = true;
const contentLines = content.split("\n");
const indexOfLineDetector = contentLines.indexOf(LINE_DETECTOR.trim());
if (indexOfLineDetector > 0) {
this.lineDifCounter = indexOfLineDetector;
} else {
this.lineDifCounter = 0;
}
const totalLinesToMoveUp = this.previousLineCount + this.lineDifCounter + this.extraLines;
readline.moveCursor(process.stdout, 0, -totalLinesToMoveUp);
readline.clearScreenDown(process.stdout);
contentLines.forEach((line) => {
process.stdout.write(`${line}\x1B[K
`);
});
this.previousLineCount = contentLines.length;
this.extraLines = 0;
this.isUpdating = false;
}
stop() {
this.stopSpinner();
}
updateStatus(dictionaries) {
for (const { dictionaryKey, type, status } of dictionaries) {
const statusObj = this.dictionariesStatuses.find(
(ds) => ds.dictionaryKey === dictionaryKey
);
if (statusObj) {
const state = statusObj.state.find((s) => s.type === type);
if (state) {
Object.assign(state, status);
} else {
if (status.status === void 0) {
status.status = "pending";
}
const newState = {
type,
status: status.status,
icon: status.icon ?? "",
error: status.error,
errorMessage: status.errorMessage,
spinnerFrameIndex: status.spinnerFrameIndex ?? 0
};
statusObj.state.push(newState);
}
} else {
const newState = {
type,
status: status.status ?? "pending",
icon: status.icon ?? "",
error: status.error,
errorMessage: status.errorMessage,
spinnerFrameIndex: status.spinnerFrameIndex ?? 0
};
this.dictionariesStatuses.push({
dictionaryKey,
state: [newState]
});
}
}
this.updateAllStatusLines();
}
getStatusIcon(status) {
const statusIcons = {
pending: "\u23F2",
fetching: "",
// Spinner handled separately
built: "\u2714",
imported: "\u2714",
error: "\u2716"
};
return statusIcons[status] ?? "";
}
getStatusLine(statusObj) {
const keyPadding = this.maxDictionaryKeyLength - statusObj.dictionaryKey.length;
const paddedKey = `${statusObj.dictionaryKey}${" ".repeat(keyPadding)}`;
const states = statusObj.state.map((state) => {
let colorStart = "";
let colorEnd = "";
let icon = this.getStatusIcon(state.status);
if (state.status === "fetching") {
icon = SPINNER_FRAMES[state.spinnerFrameIndex % SPINNER_FRAMES.length];
colorStart = BLUE;
colorEnd = RESET;
} else if (state.status === "error") {
colorStart = RED;
colorEnd = RESET;
} else if (state.status === "imported" || state.status === "built") {
colorStart = GREEN;
colorEnd = RESET;
} else {
colorStart = GREY;
colorEnd = RESET;
}
const statusBlock = `${GREY_DARK}[${state.type}: ${colorStart}${icon} ${state.status}${GREY_DARK}]${colorEnd}`;
return `${colorStart}${statusBlock}${colorEnd}`;
});
return `${this.config?.log.prefix}- ${paddedKey} ${states.join(" ")}`;
}
// Replace logUpdate calls with your custom methods
updateAllStatusLines() {
const terminalHeight = process.stdout.rows;
const maxVisibleLines = terminalHeight - 1;
const lines = this.dictionariesStatuses.map((statusObj) => {
statusObj.state.forEach((state) => {
if (state.status === "fetching") {
state.spinnerFrameIndex = (state.spinnerFrameIndex + 1) % SPINNER_FRAMES.length;
}
});
return this.getStatusLine(statusObj);
});
let content;
if (lines.length > maxVisibleLines) {
const visibleLines = lines.slice(0, maxVisibleLines - 5);
const summary = `${this.config?.log.prefix}... and ${lines.length - visibleLines.length} more`;
content = LINE_DETECTOR + visibleLines.join("\n") + "\n" + summary;
} else {
content = lines.join("\n");
}
this.updateOutput(content);
}
getStatuses() {
return this.dictionariesStatuses;
}
}
const logger = Logger.getInstance();
export {
logger
};
//# sourceMappingURL=log.mjs.map