@next-boilerplate/cli-helpers
Version:
CLI helper for Next Boilerplate
268 lines (261 loc) • 8.72 kB
JavaScript
import fs from 'fs-extra';
import path from 'path';
import { l as logger, g as getStoreDataDirectory } from './helpers-CCFUMAr-.mjs';
import { stderr } from 'process';
import { moveCursor, clearLine } from 'readline';
import { configSchema } from './config/index.mjs';
import { pluginConfigFileName, pluginConfigSchema, pluginsFolder } from './plugins/index.mjs';
import 'child_process';
import 'globby';
import 'tar';
import './stores/index.mjs';
import 'zod';
import './utils/index.mjs';
let gora = null;
const loadOra = async () => {
if (gora) return gora;
const resolvedOra = await import('./index-cI4uERyT.mjs').then((_ora) => _ora.default);
gora = resolvedOra;
return resolvedOra;
};
const { addPrefixToArgs, errorText, infoText, log, successText, warnText } = logger.utils;
const clearLastLines = (count) => {
for (let i = 0; i < count; i++) {
moveCursor(stderr, 0, -1);
clearLine(stderr, 0);
}
};
const windowLog = (maxLines, opts) => {
const lines = [];
let numberOfLines = 0;
let hasPrintedTopPrefix = false;
let prefixValue = "";
const handleLine = (line, kind) => {
const rows = process.stdout.rows;
maxLines = Math.min(maxLines, rows - 1 - (opts?.topPrefix ? 1 : 0));
const toClear = Math.min(numberOfLines, maxLines);
clearLastLines(toClear);
const columns = process.stdout.columns;
if (line) {
lines.push({ content: line, kind });
const lineLength = Math.ceil(line.length / columns);
numberOfLines += lineLength;
}
if (numberOfLines > maxLines) {
const removedLine = lines.shift();
if (removedLine) {
const removedLineLength = Math.ceil(removedLine.content.length / columns);
numberOfLines -= removedLineLength;
}
}
const printable = lines.map((l) => {
const wrapper = l.kind === "error" ? errorText : l.kind === "info" ? infoText : l.kind === "success" ? successText : l.kind === "warn" ? warnText : log;
const value = addPrefixToArgs(logger.prefix, wrapper(l.content));
return value.join(" ");
});
if (!opts?.noPrint) {
if (opts?.topPrefix) {
if (hasPrintedTopPrefix) {
clearLastLines(1);
}
if (line === null) {
prefixValue = opts.topPrefix();
}
stderr.write(prefixValue + "\n");
hasPrintedTopPrefix = true;
}
printable.forEach((l) => {
stderr.write(l);
});
}
return printable;
};
const print = (line, type = "log") => {
handleLine(line, type);
};
let interval = null;
if (opts) {
handleLine(null, "log");
const { topInterval, topPrefix } = opts;
if (topInterval && topPrefix)
interval = setInterval(() => {
handleLine(null, "log");
}, topInterval);
}
const stop = ({
noClear,
successMessage,
endPrint
} = {}) => {
if (interval) {
clearInterval(interval);
}
if (!noClear) {
const toClear = Math.min(numberOfLines, maxLines) + 1;
clearLastLines(toClear);
}
if (endPrint && endPrint?.length > 0) {
endPrint.forEach((l) => {
logger[l.kind](l.content);
});
}
if (successMessage) {
logger.success(successMessage);
}
};
return { print, stop };
};
const isCI = () => process.env.CI !== void 0 || process.env.GITHUB_ACTIONS !== void 0;
const levelOrder = {
error: 1,
warn: 2,
log: 3,
info: 4,
success: 5
};
async function startTask(options) {
const { maxLines = 10, name, successMessage } = options;
if (isCI()) {
return {
log: logger.log,
error: logger.error,
warn: logger.warn,
info: logger.info,
success: logger.log,
stop: (message) => {
if (message) {
logger.log(message);
}
}
};
}
const ora = await loadOra();
const spinner = ora({
text: name,
isSilent: true,
isEnabled: false
}).start();
const window = windowLog(maxLines, {
topPrefix: () => spinner.frame(),
topInterval: 100
});
const rows = process.stdout.rows;
const curMax = Math.min(maxLines, rows - 2);
const endPrint = [];
const registerHandlePrint = (kind) => (data) => {
const lines = data.toString().split("\n").filter((l) => l.length > 0).slice(-curMax - 1);
lines.forEach((line) => {
window.print(line + "\n", kind);
});
if (kind === "error" || kind === "warn") {
endPrint.push({ content: data, kind });
}
};
return {
log: registerHandlePrint("log"),
error: registerHandlePrint("error"),
warn: registerHandlePrint("warn"),
info: registerHandlePrint("info"),
success: registerHandlePrint("success"),
stop: (message) => {
window.stop({
successMessage: message ?? successMessage,
noClear: options.noClear,
endPrint: endPrint.sort((a, b) => {
return levelOrder[b.kind] - levelOrder[a.kind];
})
});
}
};
}
function stopTask(window, opts) {
window.stop(opts);
}
loadOra();
const task = { startTask, stopTask };
const getPluginPath = ({ assetsDirectory, plugin }) => path.join(
getStoreDataDirectory({
store: plugin.store,
assetsDirectory
}),
pluginsFolder,
plugin.name
// The name in the config file is the relative path from the store
);
const applyConfigurationTask = async ({
assetsDirectory,
root,
noTask
}) => {
let _applyConfigTask = null;
if (!noTask) {
_applyConfigTask = await task.startTask({
name: "Apply template config... \u{1F9F0}"
});
}
const applyConfigTask = _applyConfigTask ?? {
log: (message) => logger.info(message),
error: (message) => logger.error(message),
stop: (message) => logger.success(message)
};
applyConfigTask.log("Retrieving the config");
const configPath = !root.endsWith(pluginConfigFileName) ? path.join(root, pluginConfigFileName) : root;
if (!await fs.exists(configPath)) {
applyConfigTask.error(`The config file ${configPath} doesn't exist`);
applyConfigTask.stop();
throw new Error(`The config file ${configPath} doesn't exist`);
}
const { data: config, error } = configSchema.safeParse(await fs.readJson(configPath));
if (error) {
applyConfigTask.error(`The config file ${configPath} is not valid`);
applyConfigTask.stop();
logger.error(error);
throw error;
}
applyConfigTask.log("Applying plugins");
for (const plugin of config.plugins) {
const pluginPath = getPluginPath({ assetsDirectory, plugin });
if (!await fs.exists(pluginPath)) {
applyConfigTask.error(`The plugin ${plugin.name} doesn't exist at ${pluginPath}`);
applyConfigTask.stop();
throw new Error(`The plugin ${plugin.name} doesn't exist at ${pluginPath}`);
}
const pluginConfigPath = path.join(pluginPath, pluginConfigFileName);
if (!await fs.exists(pluginConfigPath)) {
applyConfigTask.error(`The plugin config for ${plugin.name} doesn't exist at ${pluginConfigPath}`);
applyConfigTask.stop();
throw new Error(`The plugin config for ${plugin.name} doesn't exist at ${pluginConfigPath}`);
}
const { data: pluginConfig, error: error2 } = pluginConfigSchema.safeParse(await fs.readJson(pluginConfigPath));
if (error2) {
applyConfigTask.error(`The plugin config file ${pluginConfigPath} is not valid. Contact developer`);
applyConfigTask.stop();
logger.error(error2);
throw error2;
}
const relativeDestinationPaths = pluginConfig.paths.map((p) => p.to);
for (const relativeDestinationPath of relativeDestinationPaths) {
const destinationPath = path.join(root, relativeDestinationPath);
if (await fs.exists(destinationPath)) {
applyConfigTask.error(`A file/folder already exists at the destination ${destinationPath}`);
applyConfigTask.stop();
throw new Error(`A file/folder already exists at the destination ${destinationPath}`);
}
}
}
applyConfigTask.log("Applying the plugins");
for (const plugin of config.plugins) {
const pluginPath = getPluginPath({ assetsDirectory, plugin });
const pluginConfigPath = path.join(pluginPath, pluginConfigFileName);
const pluginConfig = await fs.readJson(pluginConfigPath);
for (const { from, to } of pluginConfig.paths) {
const sourcePath = path.join(pluginPath, from);
const destinationPath = path.join(root, to);
applyConfigTask.log(`Copying the plugin ${plugin.name} to the destination ${destinationPath}`);
await fs.copy(sourcePath, destinationPath);
}
}
applyConfigTask.stop("The template config has been applied! \u{1F389}");
logger.info("You can now delete the config file if you want");
};
export { applyConfigurationTask };