UNPKG

astro

Version:

Astro is a modern site builder with web best practices, performance, and DX front-of-mind.

299 lines (298 loc) • 9.82 kB
import { fileURLToPath } from "node:url"; import { formatWithOptions } from "node:util"; import dlv from "dlv"; import { flattie } from "flattie"; import { bgGreen, black, bold, dim, yellow } from "kleur/colors"; import { resolveConfig } from "../../core/config/config.js"; import { createSettings } from "../../core/config/settings.js"; import { collectErrorMetadata } from "../../core/errors/dev/utils.js"; import * as msg from "../../core/messages.js"; import { apply as applyPolyfill } from "../../core/polyfill.js"; import { DEFAULT_PREFERENCES } from "../../preferences/defaults.js"; import { coerce, isValidKey } from "../../preferences/index.js"; import { createLoggerFromFlags, flagsToAstroInlineConfig } from "../flags.js"; const PREFERENCES_SUBCOMMANDS = [ "get", "set", "enable", "disable", "delete", "reset", "list" ]; function isValidSubcommand(subcommand) { return PREFERENCES_SUBCOMMANDS.includes(subcommand); } async function preferences(subcommand, key, value, { flags }) { applyPolyfill(); if (!isValidSubcommand(subcommand) || flags?.help || flags?.h) { msg.printHelp({ commandName: "astro preferences", usage: "[command]", tables: { Commands: [ ["list", "Pretty print all current preferences"], ["list --json", "Log all current preferences as a JSON object"], ["get [key]", "Log current preference value"], ["set [key] [value]", "Update preference value"], ["reset [key]", "Reset preference value to default"], ["enable [key]", "Set a boolean preference to true"], ["disable [key]", "Set a boolean preference to false"] ], Flags: [ [ "--global", "Scope command to global preferences (all Astro projects) rather than the current project" ] ] } }); return 0; } const inlineConfig = flagsToAstroInlineConfig(flags); const logger = createLoggerFromFlags(flags); const { astroConfig } = await resolveConfig(inlineConfig ?? {}, "dev"); const settings = await createSettings(astroConfig, fileURLToPath(astroConfig.root)); const opts = { location: flags.global ? "global" : void 0, json: !!flags.json }; if (subcommand === "list") { return listPreferences(settings, opts); } if (subcommand === "enable" || subcommand === "disable") { key = `${key}.enabled`; } if (!isValidKey(key)) { logger.error("preferences", `Unknown preference "${key}" `); return 1; } if (subcommand === "set" && value === void 0) { const type = typeof dlv(DEFAULT_PREFERENCES, key); console.error( msg.formatErrorMessage( collectErrorMetadata(new Error(`Please provide a ${type} value for "${key}"`)), true ) ); return 1; } switch (subcommand) { case "get": return getPreference(settings, key, opts); case "set": return setPreference(settings, key, value, opts); case "reset": case "delete": return resetPreference(settings, key, opts); case "enable": return enablePreference(settings, key, opts); case "disable": return disablePreference(settings, key, opts); } } async function getPreference(settings, key, { location = "project" }) { try { let value = await settings.preferences.get(key, { location }); if (value && typeof value === "object" && !Array.isArray(value)) { if (Object.keys(value).length === 0) { value = dlv(DEFAULT_PREFERENCES, key); console.log(msg.preferenceDefaultIntro(key)); } prettyPrint({ [key]: value }); return 0; } if (value === void 0) { const defaultValue = await settings.preferences.get(key); console.log(msg.preferenceDefault(key, defaultValue)); return 0; } console.log(msg.preferenceGet(key, value)); return 0; } catch { } return 1; } async function setPreference(settings, key, value, { location }) { try { const defaultType = typeof dlv(DEFAULT_PREFERENCES, key); if (typeof coerce(key, value) !== defaultType) { throw new Error(`${key} expects a "${defaultType}" value!`); } await settings.preferences.set(key, coerce(key, value), { location }); console.log(msg.preferenceSet(key, value)); return 0; } catch (e) { if (e instanceof Error) { console.error(msg.formatErrorMessage(collectErrorMetadata(e), true)); return 1; } throw e; } } async function enablePreference(settings, key, { location }) { try { await settings.preferences.set(key, true, { location }); console.log(msg.preferenceEnabled(key.replace(".enabled", ""))); return 0; } catch { } return 1; } async function disablePreference(settings, key, { location }) { try { await settings.preferences.set(key, false, { location }); console.log(msg.preferenceDisabled(key.replace(".enabled", ""))); return 0; } catch { } return 1; } async function resetPreference(settings, key, { location }) { try { await settings.preferences.set(key, void 0, { location }); console.log(msg.preferenceReset(key)); return 0; } catch { } return 1; } function annotate(flat, annotation) { return Object.fromEntries( Object.entries(flat).map(([key, value]) => [key, { annotation, value }]) ); } function userValues(flatDefault, flatProject, flatGlobal) { const result = {}; for (const key of Object.keys(flatDefault)) { if (key in flatProject) { result[key] = { value: flatProject[key], annotation: "" }; if (key in flatGlobal) { result[key].annotation += ` (also modified globally)`; } } else if (key in flatGlobal) { result[key] = { value: flatGlobal[key], annotation: "(global)" }; } } return result; } async function listPreferences(settings, { location, json }) { if (json) { const resolved = await settings.preferences.getAll(); console.log(JSON.stringify(resolved, null, 2)); return 0; } const { global, project, fromAstroConfig, defaults } = await settings.preferences.list({ location }); const flatProject = flattie(project); const flatGlobal = flattie(global); const flatDefault = flattie(defaults); const flatUser = userValues(flatDefault, flatProject, flatGlobal); const userKeys = Object.keys(flatUser); if (userKeys.length > 0) { const badge = bgGreen(black(` Your Preferences `)); const table = formatTable(flatUser, ["Preference", "Value"]); console.log(["", badge, table].join("\n")); } else { const badge = bgGreen(black(` Your Preferences `)); const message = dim("No preferences set"); console.log(["", badge, "", message].join("\n")); } const flatUnset = annotate(Object.assign({}, flatDefault), ""); for (const key of userKeys) { delete flatUnset[key]; } const unsetKeys = Object.keys(flatUnset); if (unsetKeys.length > 0) { const badge = bgGreen(black(` Default Preferences `)); const table = formatTable(flatUnset, ["Preference", "Value"]); console.log(["", badge, table].join("\n")); } else { const badge = bgGreen(black(` Default Preferences `)); const message = dim("All preferences have been set"); console.log(["", badge, "", message].join("\n")); } if (fromAstroConfig.devToolbar?.enabled === false && flatUser["devToolbar.enabled"]?.value !== false) { console.log( yellow( "The dev toolbar is currently disabled. To enable it, set devToolbar: {enabled: true} in your astroConfig file." ) ); } return 0; } function prettyPrint(value) { const flattened = flattie(value); const table = formatTable(flattened, ["Preference", "Value"]); console.log(table); } const chars = { h: "\u2500", hThick: "\u2501", hThickCross: "\u253F", v: "\u2502", vRight: "\u251C", vRightThick: "\u251D", vLeft: "\u2524", vLeftThick: "\u2525", hTop: "\u2534", hBottom: "\u252C", topLeft: "\u256D", topRight: "\u256E", bottomLeft: "\u2570", bottomRight: "\u256F" }; function annotatedFormat(mv) { return mv.annotation ? `${mv.value} ${mv.annotation}` : mv.value.toString(); } function formatAnnotated(mv, style = (v) => v.toString()) { return mv.annotation ? `${style(mv.value)} ${dim(mv.annotation)}` : style(mv.value); } function formatTable(object, columnLabels) { const [colA, colB] = columnLabels; const colALength = [colA, ...Object.keys(object)].reduce(longest, 0) + 3; const colBLength = [colB, ...Object.values(object).map(annotatedFormat)].reduce(longest, 0) + 3; function formatRow(_i, a, b, style = (v) => v.toString()) { return `${dim(chars.v)} ${style(a)} ${space(colALength - a.length - 2)} ${dim( chars.v )} ${formatAnnotated(b, style)} ${space(colBLength - annotatedFormat(b).length - 3)} ${dim( chars.v )}`; } const top = dim( `${chars.topLeft}${chars.h.repeat(colALength + 1)}${chars.hBottom}${chars.h.repeat( colBLength )}${chars.topRight}` ); const bottom = dim( `${chars.bottomLeft}${chars.h.repeat(colALength + 1)}${chars.hTop}${chars.h.repeat( colBLength )}${chars.bottomRight}` ); const divider = dim( `${chars.vRightThick}${chars.hThick.repeat(colALength + 1)}${chars.hThickCross}${chars.hThick.repeat(colBLength)}${chars.vLeftThick}` ); const rows = [top, formatRow(-1, colA, { value: colB, annotation: "" }, bold), divider]; let i = 0; for (const [key, value] of Object.entries(object)) { rows.push(formatRow(i, key, value, (v) => formatWithOptions({ colors: true }, v))); i++; } rows.push(bottom); return rows.join("\n"); } function space(len) { return " ".repeat(len); } const longest = (a, b) => { const { length: len } = b.toString(); return a > len ? a : len; }; export { preferences };