@rollup-extras/plugin-clean
Version:
Rollup plugin to clean a directory during build.
173 lines (149 loc) • 5.03 kB
JavaScript
import { rm } from 'node:fs/promises';
import { dirname, normalize } from 'node:path';
import { createLogger, LogLevel } from '@niceties/logger';
import { multiConfigPluginBase } from '@rollup-extras/utils/multi-config-plugin-base';
import { getOptions } from '@rollup-extras/utils/options';
export default function (options = {}) {
const inProgress = new Map();
const hasChildrenInProgress = new Map();
let deleted = false;
const normalizedOptions = getOptions(
options,
{
pluginName: '@rollup-extras/plugin-clean',
deleteOnce: true,
verbose: false,
outputPlugin: true,
},
'targets'
);
const { pluginName, deleteOnce, verbose, outputPlugin } = normalizedOptions;
let { targets } = normalizedOptions;
const instance = multiConfigPluginBase(false, pluginName, cleanup);
const baseAddInstance = (instance.api.addInstance);
const baseRenderStart =
(
(instance).renderStart
);
if (outputPlugin) {
instance.renderStart = async function (
outputOptions,
inputOptions
) {
baseRenderStart.call(this, outputOptions, inputOptions);
await renderStart(outputOptions);
};
} else {
instance.buildStart = buildStart;
instance.options = optionsHook;
}
instance.api.addInstance = () => {
const instance = baseAddInstance();
const baseRenderStart =
(
(instance).renderStart
);
if (outputPlugin) {
instance.renderStart = async function (
outputOptions,
inputOptions
) {
baseRenderStart.call(this, outputOptions, inputOptions);
await renderStart(outputOptions);
};
} else {
instance.buildStart = buildStart;
instance.options = optionsHook;
}
return instance;
};
return instance;
async function optionsHook(config) {
if (!targets && config) {
targets = (
(Array.isArray(config.output) ? config.output.map(item => item.dir) : [config.output?.dir]).filter(Boolean)
);
}
return null;
}
async function buildStart() {
if (deleted) {
return;
}
await Promise.all( (targets).map(removeDir));
}
async function renderStart(options) {
if (deleted) {
return;
}
if (!targets) {
if (options.dir) {
await removeDir(options.dir);
}
} else {
await Promise.all(targets.map(removeDir));
}
}
async function removeDir(dir) {
const normalizedDir = normalizeSlash(normalize(dir));
if (inProgress.has(normalizedDir)) {
return outputPlugin && inProgress.get(normalizedDir);
}
let removePromise;
let parentsInProgress;
if (hasChildrenInProgress.has(dir)) {
removePromise = Promise.resolve(hasChildrenInProgress.get(dir)).then(() => doRemove(normalizedDir));
} else {
parentsInProgress = Array.from(parentDirs(dir)).filter(item => inProgress.has(item));
if (parentsInProgress.length > 0) {
return inProgress.get( (parentsInProgress[0]));
}
removePromise = doRemove(normalizedDir);
}
inProgress.set(normalizedDir, removePromise);
for (const parentDir of parentDirs(dir)) {
if (!hasChildrenInProgress.has(parentDir)) {
hasChildrenInProgress.set(parentDir, removePromise);
} else {
hasChildrenInProgress.set(
parentDir,
(
(Promise.all([removePromise, hasChildrenInProgress.get(parentDir)]))
)
);
}
}
return removePromise;
}
async function doRemove(normalizedDir) {
const logger = createLogger(pluginName);
try {
logger.start(`cleaning '${normalizedDir}'`, verbose ? LogLevel.info : LogLevel.verbose);
await rm(normalizedDir, { recursive: true });
logger.finish(`cleaned '${normalizedDir}'`);
} catch ( e) {
const loglevel = (e).code === 'ENOENT' ? undefined : LogLevel.warn;
logger.finish(`failed cleaning '${normalizedDir}'`, loglevel, (e));
}
}
function cleanup() {
inProgress.clear();
hasChildrenInProgress.clear();
deleted = deleteOnce;
}
}
function normalizeSlash(dir) {
if (dir.endsWith('/')) {
return `${dir.substring(0, dir.length - 1)}`;
}
return dir;
}
function* parentDirs(dir) {
for (;;) {
dir = dirname(dir);
if (dir === '.' || dir === '/') {
break;
}
yield dir;
}
}