myst-cli
Version:
Command line tools for MyST
169 lines (168 loc) โข 5.47 kB
JavaScript
import fs from 'node:fs';
import path from 'node:path';
import chalk from 'chalk';
import inquirer from 'inquirer';
import { ExportFormats } from 'myst-frontmatter';
import { collectAllBuildExportOptions, getProjectPaths } from './build.js';
import { getLogOutputFolder, getTexOutputFolder } from './pdf/create.js';
function promptContinue() {
return {
name: 'cont',
message: 'Would you like to continue?',
type: 'confirm',
default: true,
};
}
const ALL_OPTS = {
docx: true,
pdf: true,
tex: true,
xml: true,
md: true,
meca: true,
site: true,
html: true,
temp: true,
logs: true,
cache: true,
exports: true,
execute: true,
templates: true,
};
const DEFAULT_OPTS = {
docx: true,
pdf: true,
tex: true,
xml: true,
md: true,
meca: true,
site: true,
html: true,
temp: true,
logs: true,
exports: true,
execute: true,
};
function coerceOpts(opts) {
const { docx, pdf, tex, xml, md, meca, site, html, temp, logs, cache, exports, execute, templates, all, } = opts;
if (all)
return { ...opts, ...ALL_OPTS };
if (!docx &&
!pdf &&
!tex &&
!xml &&
!md &&
!meca &&
!site &&
!html &&
!temp &&
!logs &&
!cache &&
!exports &&
!execute &&
!templates) {
return { ...opts, ...DEFAULT_OPTS };
}
return { ...opts };
}
/**
* Returns true if 'item' is a subpath under folder
*
* e.g. isSubpath('_build/exports/out.pdf', '_build/exports') => true
* isSubpath('_build/exports', '_build/exports/out.pdf') => false
* isSubpath('_build/exports', '_build/exports') => false
*/
function isSubpath(item, folder) {
if (item === folder)
return false;
const itemParts = item.split(path.sep);
const folderParts = folder.split(path.sep);
let subpath = true;
folderParts.forEach((part, index) => {
if (itemParts[index] !== part)
subpath = false;
});
return subpath;
}
function isSubpathOfAny(item, folders) {
let subpath = false;
folders.forEach((folder) => {
if (isSubpath(item, folder))
subpath = true;
});
return subpath;
}
function deduplicatePaths(paths) {
const uniquePaths = [...new Set(paths)];
return uniquePaths.filter((item) => !isSubpathOfAny(item, [...uniquePaths]));
}
export async function clean(session, files, opts) {
opts = coerceOpts(opts);
const { site, html, temp, logs, cache, exports, execute, templates, yes } = opts;
let pathsToDelete = [];
const exportOptionsList = await collectAllBuildExportOptions(session, files, opts);
if (exports) {
exportOptionsList.forEach((exportOptions) => {
pathsToDelete.push(exportOptions.output);
if (exportOptions.format === ExportFormats.pdftex) {
pathsToDelete.push(getLogOutputFolder(exportOptions.output));
pathsToDelete.push(getTexOutputFolder(exportOptions.output));
}
});
}
let buildFolders = [];
if (temp || logs || cache || exports || execute || templates || html) {
const projectPaths = [
...getProjectPaths(session),
...exportOptionsList.map((exp) => exp.$project),
];
projectPaths
.filter((projPath) => Boolean(projPath))
.forEach((projPath) => {
buildFolders.push(path.join(projPath, '_build'));
});
buildFolders.push(session.buildPath());
}
buildFolders = [...new Set(buildFolders)].sort();
if (temp || logs || cache || exports || templates || execute || html) {
buildFolders.forEach((folder) => {
if (temp)
pathsToDelete.push(path.join(folder, 'temp'));
if (logs)
pathsToDelete.push(path.join(folder, 'logs'));
if (cache)
pathsToDelete.push(path.join(folder, 'cache'));
if (exports)
pathsToDelete.push(path.join(folder, 'exports'));
if (templates)
pathsToDelete.push(path.join(folder, 'templates'));
if (html)
pathsToDelete.push(path.join(folder, 'html'));
if (execute)
pathsToDelete.push(path.join(folder, 'execute'));
});
}
if (site) {
pathsToDelete.push(session.sitePath());
}
pathsToDelete = deduplicatePaths(pathsToDelete.filter((p) => fs.existsSync(p))).sort();
if (pathsToDelete.length === 0) {
session.log.info(chalk.yellow(`๐งน Your folders are already so clean! โจ`));
return;
}
session.log.info(`Deleting all the following paths:\n\n - ${pathsToDelete.join('\n - ')}\n`);
const cont = yes || (await inquirer.prompt([promptContinue()])).cont;
if (cont) {
pathsToDelete.forEach((pathToDelete) => {
session.log.info(`๐ Deleting: ${pathToDelete}`);
fs.rmSync(pathToDelete, { recursive: true, force: true });
});
// Delete any empty build folders
buildFolders.forEach((buildFolder) => {
if (fs.existsSync(buildFolder) && fs.readdirSync(buildFolder).length === 0) {
session.log.debug(`Deleting empty build folder: ${buildFolder}`);
fs.rmSync(buildFolder, { recursive: true, force: true });
}
});
}
}