UNPKG

@azure/static-web-apps-cli

Version:
203 lines 8.87 kB
import chalk from "chalk"; import { existsSync, promises as fsPromises } from "node:fs"; import * as path from "node:path"; import * as process from "node:process"; import { logger } from "./logger.js"; const { readFile, writeFile } = fsPromises; export const swaCliConfigSchemaUrl = "https://aka.ms/azure/static-web-apps-cli/schema"; export const swaCliConfigFilename = "swa-cli.config.json"; /** * Holds the current configuration of the CLI loaded from the `swa-cli.config.json` file. */ let currentSwaCliConfigFromFile; /** * Get the current configuration of the CLI stored in `{@link currentSwaCliConfigFromFile}`. * * @returns The current configuration of the CLI loaded from the `swa-cli.config.json` file. */ export const getCurrentSwaCliConfigFromFile = () => currentSwaCliConfigFromFile; export const setCurrentSwaCliConfigFromFile = (config) => (currentSwaCliConfigFromFile = config); /** * Checks if the given configuration file exists. * * @param configFilePath The path to the config file. * @returns True if the config file exists. False otherwise. */ export const swaCliConfigFileExists = (configFilePath) => existsSync(configFilePath); /** * Checks if the specified value match the loaded configuration name. * * @param name The name to check. * @returns True if the config loaded matches the name, false otherwise. */ export function matchLoadedConfigName(name) { const configName = currentSwaCliConfigFromFile?.name; return configName && configName === name; } /** * Loads the configuration from the `swa-cli.config.json` file (if available). * * @param configName The name of the configuration to load. * @param configFilePath The path to the `swa-cli.config.json` file. * @returns An object with the `{@link SWACLIOptions}` config or an empty object if the config file, or the config entry were not found. */ export async function getConfigFileOptions(configName, configFilePath) { logger.silly(`Getting config file options from "${configFilePath}"...`); let configFilePathResolved = path.resolve(configFilePath); if (!swaCliConfigFileExists(configFilePathResolved)) { logger.silly(`Config file does not exist at "${configFilePath}"`); // Handle the case when the user runs the command outside the project path if (!configName || !swaCliConfigFileExists(path.resolve(configName, swaCliConfigFilename))) { return {}; } logger.warn(`WARNING: Config file does not exist at "${configFilePath}", but can be detected at "${path.join(configName, swaCliConfigFilename)}". Do you mean "swa --config ${path.join(configName, swaCliConfigFilename)} <command> ${configName} [options]"?`); configFilePathResolved = path.resolve(configName, swaCliConfigFilename); } const cliConfig = await tryParseSwaCliConfig(configFilePathResolved); if (!cliConfig.configurations) { logger.warn(`${swaCliConfigFilename} is missing the "configurations" property. No options will be loaded.`); return {}; } // Use configuration root path as the outputLocation const configDir = path.dirname(configFilePathResolved); process.chdir(configDir); logger.silly(`Changed directory to ${configDir}`); if (!configName) { const hasMultipleConfig = Object.entries(cliConfig.configurations).length > 1; if (hasMultipleConfig) { // Show as a log not warning because the user may want to use the default config logger.log(`Multiple configurations found in "${swaCliConfigFilename}", but none was specified.`); logger.log(`Specify which configuration to use with "swa <command> --config-name <configName>"\n`); } const [configName, config] = Object.entries(cliConfig.configurations)[0]; printConfigMsg(configName, configFilePathResolved); currentSwaCliConfigFromFile = { name: configName, filePath: configFilePathResolved, config, }; return { ...config }; } const config = cliConfig.configurations?.[configName]; if (config) { logger.silly(`Found configuration "${configName}" in "${swaCliConfigFilename}"`); printConfigMsg(configName, configFilePath); currentSwaCliConfigFromFile = { name: configName, filePath: configFilePath, config, }; return { ...config }; } return {}; } /** * Parse the `swa-cli.config.json` file and return the parsed object. * * @param configFilePath The path to the `swa-cli.config.json` file. * @returns The parsed `swa-cli.config.json` file. * @throws If the file cannot be parsed. */ async function tryParseSwaCliConfig(configFilePath) { try { return JSON.parse((await readFile(configFilePath)).toString("utf-8")); } catch (e) { logger.error(`Error parsing swa-cli.config.json file at ${configFilePath}`); if (e instanceof Error) { logger.error(e); } return {}; } } /** * Prints a message to the console indicating which configuration was used. * * @param name The name of the configuration. * @param configFilePath The path to the `swa-cli.config.json` file. */ function printConfigMsg(name, configFilePath) { if (!process.env.SWA_CLI_INTERNAL_COMMAND) { logger.log(`Using configuration "${name}" from file:`); logger.log(chalk.green(` ${configFilePath}`)); logger.log(""); } } /** * Checks if the config file contains a configuration entry with the given name. * @param configFilePath The path to the `swa-cli.config.json` file. * @param name The name of the configuration entry. * @returns True if the config file contains a configuration entry with the given name. False otherwise. */ export async function hasConfigurationNameInConfigFile(configFilePath, name) { const configJson = await tryParseSwaCliConfig(configFilePath); return configJson.configurations?.[name] !== undefined; } /** * Writes the current configuration ({@link currentSwaCliConfigFromFile}) to the `swa-cli.config.json` file. * * @param config The configuration object to save. */ export async function updateSwaCliConfigFile(config) { const currentSwaCliConfigFromFile = getCurrentSwaCliConfigFromFile(); if (currentSwaCliConfigFromFile === undefined) { logger.error("No configuration file currently loaded", true); } else { logger.silly(`Updating configuration file at ${currentSwaCliConfigFromFile.filePath}`); await writeConfigFile(currentSwaCliConfigFromFile.filePath, currentSwaCliConfigFromFile.name, config); } } /** * Appends or update the given configuration entry to the `swa-cli.config.json` file. * * @param configFilePath The path to the `swa-cli.config.json` file. * @param configName The name of the configuration entry to be added or updated. * @param config The configuration object to save. */ export async function writeConfigFile(configFilePath, configName, config) { let configFile = { // TODO: find node_modules/ path and use local schema if found $schema: swaCliConfigSchemaUrl, configurations: {}, }; if (swaCliConfigFileExists(configFilePath)) { logger.silly(`Loading existing swa-cli.config.json file at ${configFilePath}`); try { const configJson = await readFile(configFilePath, "utf-8"); configFile = JSON.parse(configJson); } catch (error) { logger.error(`Error parsing ${configFilePath}`); if (error instanceof Error) { logger.error(error); } logger.error("Cannot update existing configuration file."); logger.error("Please fix or delete your swa-cli.config.json file and try again."); return; } } if (typeof configFile !== "object" || configFile.constructor !== Object) { logger.error(`Error parsing ${configFilePath}`); logger.error("Invalid configuration content found."); logger.error("Please fix or delete your swa-cli.config.json file and try again."); return; } if (configFile.configurations === undefined) { logger.silly(`Creating "configurations" property in swa-cli.config.json file at ${configFilePath}`); configFile.configurations = {}; } configFile.configurations[configName] = config; try { logger.silly(`Writing configuration "${configName}" to swa-cli.config.json`); logger.silly(config); await writeFile(configFilePath, JSON.stringify(configFile, null, 2)); } catch (error) { logger.error(`Error writing configuration to ${configFilePath}`); if (error instanceof Error) { logger.error(error); } } } //# sourceMappingURL=cli-config.js.map