164 lines
4.7 KiB
JavaScript
164 lines
4.7 KiB
JavaScript
const fs = require("node:fs");
|
|
const fsPromises = require("node:fs/promises");
|
|
const path = require("node:path");
|
|
const readline = require("node:readline/promises");
|
|
const { stdin, stdout } = require("node:process");
|
|
|
|
/**
|
|
* Normalise une entrée utilisateur vers le dossier du bundle.
|
|
* @param {string} input Chemin saisi par l'utilisateur.
|
|
* @returns {string} Chemin absolu du bundle.
|
|
*/
|
|
function resolveBundlePath(input) {
|
|
if (typeof input !== "string" || !input.trim()) {
|
|
throw new Error("Le chemin du bundle est vide.");
|
|
}
|
|
|
|
const resolved = path.resolve(input);
|
|
if (resolved.toLowerCase().endsWith(`${path.sep}index.md`)) {
|
|
return path.dirname(resolved);
|
|
}
|
|
return resolved;
|
|
}
|
|
|
|
/**
|
|
* Vérifie qu'un dossier correspond bien à un bundle Hugo.
|
|
* @param {string} bundleDir Chemin absolu du bundle.
|
|
*/
|
|
function ensureBundleExists(bundleDir) {
|
|
if (!fs.existsSync(bundleDir)) {
|
|
throw new Error(`Le bundle ${bundleDir} est introuvable.`);
|
|
}
|
|
|
|
const stats = fs.statSync(bundleDir);
|
|
if (!stats.isDirectory()) {
|
|
throw new Error(`Le bundle ${bundleDir} n'est pas un dossier.`);
|
|
}
|
|
|
|
const indexPath = path.join(bundleDir, "index.md");
|
|
if (!fs.existsSync(indexPath)) {
|
|
throw new Error(`Le bundle ${bundleDir} ne contient pas index.md.`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Pose une question simple à l'utilisateur.
|
|
* @param {string} query Texte affiché dans le terminal.
|
|
* @returns {Promise<string>} Réponse nettoyée.
|
|
*/
|
|
async function askQuestion(query) {
|
|
const rl = readline.createInterface({ input: stdin, output: stdout });
|
|
const answer = await rl.question(query);
|
|
rl.close();
|
|
return answer.trim();
|
|
}
|
|
|
|
/**
|
|
* Cherche le bundle modifié le plus récemment sous un répertoire racine.
|
|
* @param {string} rootDir Racine à parcourir.
|
|
* @returns {Promise<string|null>} Chemin absolu du dernier bundle trouvé.
|
|
*/
|
|
async function findLatestBundle(rootDir) {
|
|
let latestPath = null;
|
|
let latestTime = 0;
|
|
|
|
await walk(rootDir);
|
|
|
|
return latestPath;
|
|
|
|
/**
|
|
* Parcourt récursivement l'arborescence et conserve le bundle le plus récent.
|
|
* @param {string} currentDir Dossier en cours d'analyse.
|
|
*/
|
|
async function walk(currentDir) {
|
|
const entries = await fsPromises.readdir(currentDir, { withFileTypes: true });
|
|
let hasIndex = false;
|
|
|
|
for (const entry of entries) {
|
|
if (entry.isFile() && entry.name === "index.md") {
|
|
hasIndex = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (hasIndex) {
|
|
const stats = await fsPromises.stat(currentDir);
|
|
if (stats.mtimeMs > latestTime) {
|
|
latestTime = stats.mtimeMs;
|
|
latestPath = currentDir;
|
|
}
|
|
return;
|
|
}
|
|
|
|
for (const entry of entries) {
|
|
if (!entry.isDirectory()) {
|
|
continue;
|
|
}
|
|
|
|
const childDir = path.join(currentDir, entry.name);
|
|
await walk(childDir);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Résout le bundle cible à partir d'un chemin manuel ou du dernier bundle trouvé.
|
|
* @param {string|null|undefined} manualPath Chemin optionnel fourni en argument.
|
|
* @param {{ contentDir: string, prompts?: { confirmLatest: Function, manualPath: string } }} options Options de résolution.
|
|
* @returns {Promise<string>} Chemin absolu du bundle retenu.
|
|
*/
|
|
async function promptForBundlePath(manualPath, options) {
|
|
let contentDir = path.resolve("content");
|
|
if (options && typeof options.contentDir === "string" && options.contentDir.trim()) {
|
|
contentDir = path.resolve(options.contentDir);
|
|
}
|
|
|
|
const defaultPrompts = {
|
|
confirmLatest(latest) {
|
|
return `Use latest bundle found: ${latest}? (Y/n) `;
|
|
},
|
|
manualPath: "Enter the relative path to your bundle: ",
|
|
};
|
|
let prompts = defaultPrompts;
|
|
|
|
if (options && options.prompts && typeof options.prompts === "object") {
|
|
prompts = {
|
|
confirmLatest: defaultPrompts.confirmLatest,
|
|
manualPath: defaultPrompts.manualPath,
|
|
};
|
|
|
|
if (typeof options.prompts.confirmLatest === "function") {
|
|
prompts.confirmLatest = options.prompts.confirmLatest;
|
|
}
|
|
|
|
if (typeof options.prompts.manualPath === "string" && options.prompts.manualPath.trim()) {
|
|
prompts.manualPath = options.prompts.manualPath;
|
|
}
|
|
}
|
|
|
|
if (typeof manualPath === "string" && manualPath.trim()) {
|
|
return resolveBundlePath(manualPath);
|
|
}
|
|
|
|
const latest = await findLatestBundle(contentDir);
|
|
if (!latest) {
|
|
throw new Error("Aucun bundle n'a été trouvé sous content/.");
|
|
}
|
|
|
|
const confirm = await askQuestion(prompts.confirmLatest(latest));
|
|
if (confirm.toLowerCase() === "n") {
|
|
const inputPath = await askQuestion(prompts.manualPath);
|
|
return resolveBundlePath(inputPath);
|
|
}
|
|
|
|
return latest;
|
|
}
|
|
|
|
module.exports = {
|
|
resolveBundlePath,
|
|
ensureBundleExists,
|
|
askQuestion,
|
|
findLatestBundle,
|
|
promptForBundlePath,
|
|
};
|