UNPKG

@sanity/cli

Version:

Sanity CLI tool for managing Sanity installations, managing plugins, schemas and datasets

390 lines (389 loc) • 16.8 kB
import { createClient } from "@sanity/client"; import fs from "node:fs"; import path from "node:path"; import debugIt from "debug"; import require$$0 from "fs"; import require$$1 from "path"; import require$$2 from "os"; import require$$3 from "crypto"; const debug = debugIt("sanity:cli"), requireFunc = typeof __webpack_require__ == "function" ? __non_webpack_require__ : require; function dynamicRequire(request) { const mod = requireFunc(request); return mod.__esModule && mod.default ? mod.default : mod; } dynamicRequire.resolve = requireFunc.resolve; dynamicRequire.cache = requireFunc.cache; function getSanityCliConfig(cwd, clearCache = !1) { const jsConfigPath = path.join(cwd, "sanity.cli.js"), tsConfigPath = path.join(cwd, "sanity.cli.ts"), [js, ts] = [fs.existsSync(jsConfigPath), fs.existsSync(tsConfigPath)]; return !js && !ts ? null : !js && ts ? { config: importConfig(tsConfigPath, clearCache), path: tsConfigPath } : (js && ts && warn("Found both `sanity.cli.js` and `sanity.cli.ts` - using sanity.cli.js"), { config: importConfig(jsConfigPath, clearCache), path: jsConfigPath }); } function importConfig(filePath, clearCache) { try { if (clearCache) { const resolvedPath = dynamicRequire.resolve(filePath); delete dynamicRequire.cache[resolvedPath]; } const config = dynamicRequire(filePath); if (config === null || typeof config != "object") throw new Error("Module export is not a configuration object"); return "default" in config ? config.default : config; } catch (err) { return err.code === "MODULE_NOT_FOUND" && err.message.includes("sanity/cli") || console.error(`Error reading "${filePath}": ${err.message}`), null; } } function warn(warning) { typeof process.send == "function" ? process.send({ type: "warning", warning }) : console.warn(warning); } function resolveRootDir(cwd) { try { return resolveProjectRoot(cwd) || cwd; } catch (err) { throw new Error(`Error occurred trying to resolve project root: ${err.message}`); } } function hasSanityConfig(basePath, configName) { return [ fileExists(path.join(basePath, `${configName}.js`)), fileExists(path.join(basePath, `${configName}.ts`)), isSanityV2StudioRoot(basePath) ].some(Boolean); } function resolveProjectRoot(basePath, iterations = 0) { if (hasSanityConfig(basePath, "sanity.config")) return basePath; const parentDir = path.resolve(basePath, ".."); return parentDir === basePath || iterations > 30 ? !1 : resolveProjectRoot(parentDir, iterations + 1); } function isSanityV2StudioRoot(basePath) { try { const content = fs.readFileSync(path.join(basePath, "sanity.json"), "utf8"), isRoot = !!JSON.parse(content)?.root; return isRoot && debug("Found Sanity v2 studio root at %s", basePath), isRoot; } catch { return !1; } } function fileExists(filePath) { return fs.existsSync(filePath); } function getCliClientImpl(options = {}) { if (typeof process != "object") throw new Error("getCliClient() should only be called from node.js scripts"); const { cwd = process.env.SANITY_BASE_PATH || process.cwd(), useCdn = !1, apiVersion = "2022-06-06", projectId, dataset, token = getCliClient.__internal__getToken() } = options; if (projectId && dataset) return createClient({ projectId, dataset, apiVersion, useCdn, token }); const rootDir = resolveRootDir(cwd), { config } = getSanityCliConfig(rootDir) || {}; if (!config) throw new Error("Unable to resolve CLI configuration"); const apiConfig = config?.api || {}; if (!apiConfig.projectId || !apiConfig.dataset) throw new Error("Unable to resolve project ID/dataset from CLI configuration"); return createClient({ projectId: apiConfig.projectId, dataset: apiConfig.dataset, apiVersion, useCdn, token }); } getCliClientImpl.__internal__getToken = () => { }; const getCliClient = getCliClientImpl; function defineCliConfig(config) { return config; } function createCliConfig(config) { return config; } var main$1 = { exports: {} }, version = "16.6.1", require$$4 = { version }, hasRequiredMain$1; function requireMain$1() { if (hasRequiredMain$1) return main$1.exports; hasRequiredMain$1 = 1; const fs2 = require$$0, path2 = require$$1, os = require$$2, crypto = require$$3, version2 = require$$4.version, LINE = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/mg; function parse(src) { const obj = {}; let lines = src.toString(); lines = lines.replace(/\r\n?/mg, ` `); let match; for (; (match = LINE.exec(lines)) != null; ) { const key = match[1]; let value = match[2] || ""; value = value.trim(); const maybeQuote = value[0]; value = value.replace(/^(['"`])([\s\S]*)\1$/mg, "$2"), maybeQuote === '"' && (value = value.replace(/\\n/g, ` `), value = value.replace(/\\r/g, "\r")), obj[key] = value; } return obj; } function _parseVault(options) { options = options || {}; const vaultPath = _vaultPath(options); options.path = vaultPath; const result = DotenvModule.configDotenv(options); if (!result.parsed) { const err = new Error(`MISSING_DATA: Cannot parse ${vaultPath} for an unknown reason`); throw err.code = "MISSING_DATA", err; } const keys = _dotenvKey(options).split(","), length = keys.length; let decrypted; for (let i = 0; i < length; i++) try { const key = keys[i].trim(), attrs = _instructions(result, key); decrypted = DotenvModule.decrypt(attrs.ciphertext, attrs.key); break; } catch (error) { if (i + 1 >= length) throw error; } return DotenvModule.parse(decrypted); } function _warn(message) { console.log(`[dotenv@${version2}][WARN] ${message}`); } function _debug(message) { console.log(`[dotenv@${version2}][DEBUG] ${message}`); } function _log(message) { console.log(`[dotenv@${version2}] ${message}`); } function _dotenvKey(options) { return options && options.DOTENV_KEY && options.DOTENV_KEY.length > 0 ? options.DOTENV_KEY : process.env.DOTENV_KEY && process.env.DOTENV_KEY.length > 0 ? process.env.DOTENV_KEY : ""; } function _instructions(result, dotenvKey) { let uri; try { uri = new URL(dotenvKey); } catch (error) { if (error.code === "ERR_INVALID_URL") { const err = new Error("INVALID_DOTENV_KEY: Wrong format. Must be in valid uri format like dotenv://:key_1234@dotenvx.com/vault/.env.vault?environment=development"); throw err.code = "INVALID_DOTENV_KEY", err; } throw error; } const key = uri.password; if (!key) { const err = new Error("INVALID_DOTENV_KEY: Missing key part"); throw err.code = "INVALID_DOTENV_KEY", err; } const environment = uri.searchParams.get("environment"); if (!environment) { const err = new Error("INVALID_DOTENV_KEY: Missing environment part"); throw err.code = "INVALID_DOTENV_KEY", err; } const environmentKey = `DOTENV_VAULT_${environment.toUpperCase()}`, ciphertext = result.parsed[environmentKey]; if (!ciphertext) { const err = new Error(`NOT_FOUND_DOTENV_ENVIRONMENT: Cannot locate environment ${environmentKey} in your .env.vault file.`); throw err.code = "NOT_FOUND_DOTENV_ENVIRONMENT", err; } return { ciphertext, key }; } function _vaultPath(options) { let possibleVaultPath = null; if (options && options.path && options.path.length > 0) if (Array.isArray(options.path)) for (const filepath of options.path) fs2.existsSync(filepath) && (possibleVaultPath = filepath.endsWith(".vault") ? filepath : `${filepath}.vault`); else possibleVaultPath = options.path.endsWith(".vault") ? options.path : `${options.path}.vault`; else possibleVaultPath = path2.resolve(process.cwd(), ".env.vault"); return fs2.existsSync(possibleVaultPath) ? possibleVaultPath : null; } function _resolveHome(envPath) { return envPath[0] === "~" ? path2.join(os.homedir(), envPath.slice(1)) : envPath; } function _configVault(options) { const debug2 = !!(options && options.debug), quiet = options && "quiet" in options ? options.quiet : !0; (debug2 || !quiet) && _log("Loading env from encrypted .env.vault"); const parsed = DotenvModule._parseVault(options); let processEnv = process.env; return options && options.processEnv != null && (processEnv = options.processEnv), DotenvModule.populate(processEnv, parsed, options), { parsed }; } function configDotenv(options) { const dotenvPath = path2.resolve(process.cwd(), ".env"); let encoding = "utf8"; const debug2 = !!(options && options.debug), quiet = options && "quiet" in options ? options.quiet : !0; options && options.encoding ? encoding = options.encoding : debug2 && _debug("No encoding is specified. UTF-8 is used by default"); let optionPaths = [dotenvPath]; if (options && options.path) if (!Array.isArray(options.path)) optionPaths = [_resolveHome(options.path)]; else { optionPaths = []; for (const filepath of options.path) optionPaths.push(_resolveHome(filepath)); } let lastError; const parsedAll = {}; for (const path3 of optionPaths) try { const parsed = DotenvModule.parse(fs2.readFileSync(path3, { encoding })); DotenvModule.populate(parsedAll, parsed, options); } catch (e) { debug2 && _debug(`Failed to load ${path3} ${e.message}`), lastError = e; } let processEnv = process.env; if (options && options.processEnv != null && (processEnv = options.processEnv), DotenvModule.populate(processEnv, parsedAll, options), debug2 || !quiet) { const keysCount = Object.keys(parsedAll).length, shortPaths = []; for (const filePath of optionPaths) try { const relative = path2.relative(process.cwd(), filePath); shortPaths.push(relative); } catch (e) { debug2 && _debug(`Failed to load ${filePath} ${e.message}`), lastError = e; } _log(`injecting env (${keysCount}) from ${shortPaths.join(",")}`); } return lastError ? { parsed: parsedAll, error: lastError } : { parsed: parsedAll }; } function config(options) { if (_dotenvKey(options).length === 0) return DotenvModule.configDotenv(options); const vaultPath = _vaultPath(options); return vaultPath ? DotenvModule._configVault(options) : (_warn(`You set DOTENV_KEY but you are missing a .env.vault file at ${vaultPath}. Did you forget to build it?`), DotenvModule.configDotenv(options)); } function decrypt(encrypted, keyStr) { const key = Buffer.from(keyStr.slice(-64), "hex"); let ciphertext = Buffer.from(encrypted, "base64"); const nonce = ciphertext.subarray(0, 12), authTag = ciphertext.subarray(-16); ciphertext = ciphertext.subarray(12, -16); try { const aesgcm = crypto.createDecipheriv("aes-256-gcm", key, nonce); return aesgcm.setAuthTag(authTag), `${aesgcm.update(ciphertext)}${aesgcm.final()}`; } catch (error) { const isRange = error instanceof RangeError, invalidKeyLength = error.message === "Invalid key length", decryptionFailed = error.message === "Unsupported state or unable to authenticate data"; if (isRange || invalidKeyLength) { const err = new Error("INVALID_DOTENV_KEY: It must be 64 characters long (or more)"); throw err.code = "INVALID_DOTENV_KEY", err; } else if (decryptionFailed) { const err = new Error("DECRYPTION_FAILED: Please check your DOTENV_KEY"); throw err.code = "DECRYPTION_FAILED", err; } else throw error; } } function populate(processEnv, parsed, options = {}) { const debug2 = !!(options && options.debug), override = !!(options && options.override); if (typeof parsed != "object") { const err = new Error("OBJECT_REQUIRED: Please check the processEnv argument being passed to populate"); throw err.code = "OBJECT_REQUIRED", err; } for (const key of Object.keys(parsed)) Object.prototype.hasOwnProperty.call(processEnv, key) ? (override === !0 && (processEnv[key] = parsed[key]), debug2 && _debug(override === !0 ? `"${key}" is already defined and WAS overwritten` : `"${key}" is already defined and was NOT overwritten`)) : processEnv[key] = parsed[key]; } const DotenvModule = { configDotenv, _configVault, _parseVault, config, decrypt, parse, populate }; return main$1.exports.configDotenv = DotenvModule.configDotenv, main$1.exports._configVault = DotenvModule._configVault, main$1.exports._parseVault = DotenvModule._parseVault, main$1.exports.config = DotenvModule.config, main$1.exports.decrypt = DotenvModule.decrypt, main$1.exports.parse = DotenvModule.parse, main$1.exports.populate = DotenvModule.populate, main$1.exports = DotenvModule, main$1.exports; } var mainExports$1 = requireMain$1(), main = {}, hasRequiredMain; function requireMain() { if (hasRequiredMain) return main; hasRequiredMain = 1; function _interpolate(envValue, environment, config) { const matches = envValue.match(/(.?\${*[\w]*(?::-[\w/]*)?}*)/g) || []; return matches.reduce(function(newEnv, match, index) { const parts = /(.?)\${*([\w]*(?::-[\w/]*)?)?}*/g.exec(match); if (!parts || parts.length === 0) return newEnv; const prefix = parts[1]; let value, replacePart; if (prefix === "\\") replacePart = parts[0], value = replacePart.replace("\\$", "$"); else { const keyParts = parts[2].split(":-"), key = keyParts[0]; if (replacePart = parts[0].substring(prefix.length), value = Object.prototype.hasOwnProperty.call(environment, key) ? environment[key] : config.parsed[key] || keyParts[1] || "", keyParts.length > 1 && value) { const replaceNested = matches[index + 1]; matches[index + 1] = "", newEnv = newEnv.replace(replaceNested, ""); } value = _interpolate(value, environment, config); } return newEnv.replace(replacePart, value); }, envValue); } function expand(config) { const environment = config.ignoreProcessEnv ? {} : process.env; for (const configKey in config.parsed) { const value = Object.prototype.hasOwnProperty.call(environment, configKey) ? environment[configKey] : config.parsed[configKey]; config.parsed[configKey] = _interpolate(value, environment, config); } for (const processKey in config.parsed) environment[processKey] = config.parsed[processKey]; return config; } return main.expand = expand, main; } var mainExports = requireMain(); function loadEnv(mode, envDir, prefixes = ["VITE_"]) { if (mode === "local") throw new Error( '"local" cannot be used as a mode name because it conflicts with the .local postfix for .env files.' ); const env = {}, envFiles = [ /** default file */ ".env", /** local file */ ".env.local", /** mode file */ `.env.${mode}`, /** mode local file */ `.env.${mode}.local` ], parsed = Object.fromEntries( envFiles.flatMap((file) => { const envPath = lookupFile(envDir, [file], { rootDir: envDir }); return envPath ? Object.entries(mainExports$1.parse(fs.readFileSync(envPath))) : []; }) ); parsed.NODE_ENV && process.env.VITE_USER_NODE_ENV === void 0 && (process.env.VITE_USER_NODE_ENV = parsed.NODE_ENV), parsed.BROWSER && process.env.BROWSER === void 0 && (process.env.BROWSER = parsed.BROWSER), parsed.BROWSER_ARGS && process.env.BROWSER_ARGS === void 0 && (process.env.BROWSER_ARGS = parsed.BROWSER_ARGS); try { mainExports.expand({ parsed }); } catch (e) { throw e.message.includes("split") ? new Error("dotenv-expand failed to expand env vars. Maybe you need to escape `$`?") : e; } for (const [key, value] of Object.entries(parsed)) prefixes.some((prefix) => key.startsWith(prefix)) && (env[key] = value); for (const key in process.env) prefixes.some((prefix) => key.startsWith(prefix)) && (env[key] = process.env[key]); return env; } function lookupFile(dir, formats, options) { for (const format of formats) { const fullPath = path.join(dir, format); if (fs.existsSync(fullPath) && fs.statSync(fullPath).isFile()) return fullPath; } const parentDir = path.dirname(dir); if (parentDir !== dir && (!options?.rootDir || parentDir.startsWith(options?.rootDir))) return lookupFile(parentDir, formats, options); } export { createCliConfig, defineCliConfig, getCliClient, loadEnv }; //# sourceMappingURL=index.mjs.map