diff --git a/package.json b/package.json index 12a6287..50dc0bc 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,10 @@ "./dist" ], "type": "module", + "exports": { + "./env": "./dist/env.mjs", + "./env/register": "./dist/env/register.mjs" + }, "publishConfig": { "access": "public" }, diff --git a/src/active-environment.ts b/src/active-environment.ts new file mode 100644 index 0000000..ee8ee20 --- /dev/null +++ b/src/active-environment.ts @@ -0,0 +1,96 @@ +import { mkdirSync, readFileSync, realpathSync, writeFileSync } from "node:fs"; +import { fileURLToPath } from "node:url"; + +import { getConfigDir } from "./lib/config-dir"; +import { findUpwardSync } from "./lib/file"; +import { stringify } from "./lib/json"; + +// The active environment lives in the CLI, keyed by project so nothing lands in +// the repo. Reads are synchronous so a framework config can set the environment +// variable before the framework reads it at build time. This module is published +// as `prismic/env`, so it stays dependency-light. + +const CONFIG_FILENAME = "prismic.config.json"; + +const ENVIRONMENTS_PATH = new URL( + "environments.json", + getConfigDir("prismic", process.env.PRISMIC_CONFIG_DIR), +); + +const FRAMEWORK_ENV_VARS: Record = { + next: "NEXT_PUBLIC_PRISMIC_ENVIRONMENT", + nuxt: "NUXT_PUBLIC_PRISMIC_ENVIRONMENT", + "@sveltejs/kit": "VITE_PRISMIC_ENVIRONMENT", +}; + +export function getActiveEnvironment(): string | undefined { + const project = findProjectRoot(); + if (!project) return undefined; + return readState()[project]; +} + +export function setActiveEnvironment(domain: string): void { + const project = findProjectRoot(); + if (!project) return; + const state = readState(); + state[project] = domain; + writeState(state); +} + +export function unsetActiveEnvironment(): void { + const project = findProjectRoot(); + if (!project) return; + const state = readState(); + if (!(project in state)) return; + delete state[project]; + writeState(state); +} + +export function getFrameworkEnvVar(): string | undefined { + const packageJson = readPackageJson(); + if (!packageJson) return undefined; + const dependencies = { + ...packageJson.dependencies, + ...packageJson.devDependencies, + ...packageJson.peerDependencies, + }; + for (const dependency in FRAMEWORK_ENV_VARS) { + if (dependency in dependencies) return FRAMEWORK_ENV_VARS[dependency]; + } + return undefined; +} + +function readState(): Record { + try { + return JSON.parse(readFileSync(ENVIRONMENTS_PATH, "utf8")); + } catch { + return {}; + } +} + +function writeState(state: Record): void { + mkdirSync(new URL(".", ENVIRONMENTS_PATH), { recursive: true }); + writeFileSync(ENVIRONMENTS_PATH, stringify(state)); +} + +function findProjectRoot(): string | undefined { + const configPath = findUpwardSync(CONFIG_FILENAME); + if (!configPath) return undefined; + return realpathSync(fileURLToPath(new URL(".", configPath))); +} + +type PackageJson = { + dependencies?: Record; + devDependencies?: Record; + peerDependencies?: Record; +}; + +function readPackageJson(): PackageJson | undefined { + const packageJsonPath = findUpwardSync("package.json"); + if (!packageJsonPath) return undefined; + try { + return JSON.parse(readFileSync(packageJsonPath, "utf8")); + } catch { + return undefined; + } +} diff --git a/src/adapters/index.ts b/src/adapters/index.ts index 4e3fc7d..0fa362c 100644 --- a/src/adapters/index.ts +++ b/src/adapters/index.ts @@ -1,12 +1,12 @@ import type { CustomType, SharedSlice } from "@prismicio/types-internal/lib/customtypes"; import { pascalCase } from "change-case"; -import { rm } from "node:fs/promises"; +import { readFile, rm, writeFile } from "node:fs/promises"; import { pathToFileURL } from "node:url"; import { generateTypes } from "prismic-ts-codegen"; import { glob } from "tinyglobby"; -import { readJsonFile, writeFileRecursive } from "../lib/file"; +import { exists, readJsonFile, writeFileRecursive } from "../lib/file"; import { stringify } from "../lib/json"; import { readPackageJson } from "../lib/packageJson"; import { appendTrailingSlash } from "../lib/url"; @@ -42,6 +42,21 @@ export class NoSupportedFrameworkError extends Error { "No supported framework found. Run this command in a Next.js, Nuxt, or SvelteKit project."; } +// Adds `import "prismic/env/register"` to the first existing framework config so +// the active environment is applied at build time. +export async function addEnvRegisterImport(candidates: string[]): Promise { + const projectRoot = await findProjectRoot(); + for (const candidate of candidates) { + const configUrl = new URL(candidate, projectRoot); + if (!(await exists(configUrl))) continue; + const contents = await readFile(configUrl, "utf8"); + if (!contents.includes("prismic/env/register")) { + await writeFile(configUrl, `import "prismic/env/register";\n\n${contents}`); + } + return; + } +} + export abstract class Adapter { abstract readonly id: string; diff --git a/src/adapters/nextjs.templates.ts b/src/adapters/nextjs.templates.ts index fce35bc..09c8d8a 100644 --- a/src/adapters/nextjs.templates.ts +++ b/src/adapters/nextjs.templates.ts @@ -357,7 +357,7 @@ export function prismicIOFileTemplate(args: { /** * The project's Prismic repository name. */ - export const repositoryName = prismicConfig.repositoryName; + export const repositoryName = process.env.NEXT_PUBLIC_PRISMIC_ENVIRONMENT || prismicConfig.repositoryName; ${createClientContents} `; @@ -369,7 +369,7 @@ export function prismicIOFileTemplate(args: { /** * The project's Prismic repository name. */ - export const repositoryName = prismicConfig.repositoryName; + export const repositoryName = process.env.NEXT_PUBLIC_PRISMIC_ENVIRONMENT || prismicConfig.repositoryName; ${createClientContents} `; diff --git a/src/adapters/nextjs.ts b/src/adapters/nextjs.ts index c21d83b..3a0feb0 100644 --- a/src/adapters/nextjs.ts +++ b/src/adapters/nextjs.ts @@ -5,7 +5,7 @@ import { createRequire } from "node:module"; import { relative } from "node:path"; import { fileURLToPath } from "node:url"; -import { Adapter } from "."; +import { Adapter, addEnvRegisterImport } from "."; import { getHost, getToken } from "../auth"; import { addPreview, getPreviews, getSimulatorUrl, setSimulatorUrl } from "../clients/core"; import { exists, writeFileRecursive } from "../lib/file"; @@ -29,6 +29,7 @@ export class NextJsAdapter extends Adapter { async setupProject(): Promise { await addDependencies({ + prismic: `^${await getNpmPackageVersion("prismic")}`, "@prismicio/client": `^${await getNpmPackageVersion("@prismicio/client")}`, "@prismicio/react": `^${await getNpmPackageVersion("@prismicio/react")}`, "@prismicio/next": `^${await getNpmPackageVersion("@prismicio/next")}`, @@ -38,6 +39,7 @@ export class NextJsAdapter extends Adapter { await createPreviewRoute(); await createExitPreviewRoute(); await createRevalidateRoute(); + await addEnvRegisterImport(["next.config.ts", "next.config.mjs", "next.config.js"]); } async onProjectInitialized(): Promise { diff --git a/src/adapters/sveltekit.templates.ts b/src/adapters/sveltekit.templates.ts index 9a07509..364ca0e 100644 --- a/src/adapters/sveltekit.templates.ts +++ b/src/adapters/sveltekit.templates.ts @@ -16,7 +16,7 @@ export function prismicIOFileTemplate(args: { typescript: boolean }): string { /** * The project's Prismic repository name. */ - export const repositoryName = prismicConfig.repositoryName; + export const repositoryName = import.meta.env.VITE_PRISMIC_ENVIRONMENT || prismicConfig.repositoryName; /** * Creates a Prismic client for the project's repository. The client is used to @@ -45,7 +45,7 @@ export function prismicIOFileTemplate(args: { typescript: boolean }): string { /** * The project's Prismic repository name. */ - export const repositoryName = prismicConfig.repositoryName; + export const repositoryName = import.meta.env.VITE_PRISMIC_ENVIRONMENT || prismicConfig.repositoryName; /** * Creates a Prismic client for the project's repository. The client is used to diff --git a/src/adapters/sveltekit.ts b/src/adapters/sveltekit.ts index d200626..679cd3b 100644 --- a/src/adapters/sveltekit.ts +++ b/src/adapters/sveltekit.ts @@ -7,7 +7,7 @@ import { createRequire } from "node:module"; import { relative } from "node:path"; import { fileURLToPath } from "node:url"; -import { Adapter } from "."; +import { Adapter, addEnvRegisterImport } from "."; import { getHost, getToken } from "../auth"; import { addPreview, getPreviews, getSimulatorUrl, setSimulatorUrl } from "../clients/core"; import { exists, writeFileRecursive } from "../lib/file"; @@ -31,6 +31,7 @@ export class SvelteKitAdapter extends Adapter { async setupProject(): Promise { await addDependencies({ + prismic: `^${await getNpmPackageVersion("prismic")}`, "@prismicio/client": `^${await getNpmPackageVersion("@prismicio/client")}`, "@prismicio/svelte": `^${await getNpmPackageVersion("@prismicio/svelte")}`, }); @@ -42,6 +43,7 @@ export class SvelteKitAdapter extends Adapter { await createRootLayoutServerFile(); await createRootLayoutFile(); await modifyViteConfig(); + await addEnvRegisterImport(["vite.config.ts", "vite.config.js"]); } async onProjectInitialized(): Promise { diff --git a/src/commands/env-active.ts b/src/commands/env-active.ts new file mode 100644 index 0000000..e477af0 --- /dev/null +++ b/src/commands/env-active.ts @@ -0,0 +1,15 @@ +import { createCommand, type CommandConfig } from "../lib/command"; +import { getRepositoryName } from "../project"; + +const config = { + name: "prismic env active", + description: ` + Print the active environment. + + Prints the production environment when no environment is active. + `, +} satisfies CommandConfig; + +export default createCommand(config, async () => { + console.info(await getRepositoryName()); +}); diff --git a/src/commands/env-list.ts b/src/commands/env-list.ts new file mode 100644 index 0000000..7c162af --- /dev/null +++ b/src/commands/env-list.ts @@ -0,0 +1,50 @@ +import { getActiveEnvironment } from "../active-environment"; +import { getHost, getToken } from "../auth"; +import { getUserEnvironments } from "../environments"; +import { createCommand, type CommandConfig } from "../lib/command"; +import { stringify } from "../lib/json"; +import { formatTable } from "../lib/string"; +import { readConfig } from "../project"; + +const config = { + name: "prismic env list", + description: ` + List the environments available for the project, marking the active one. + `, + options: { + json: { type: "boolean", description: "Output as JSON" }, + }, +} satisfies CommandConfig; + +export default createCommand(config, async ({ values }) => { + const { json } = values; + + const { repositoryName } = await readConfig(); + const token = await getToken(); + const host = await getHost(); + + const environments = await getUserEnvironments({ repo: repositoryName, token, host }); + const activeEnvironment = getActiveEnvironment() ?? repositoryName; + + if (json) { + const results = environments.map((environment) => ({ + kind: environment.kind, + name: environment.name, + domain: environment.domain, + active: environment.domain === activeEnvironment, + })); + console.info(stringify(results)); + return; + } + + if (environments.length === 0) { + console.info("No environments found."); + return; + } + + const rows = environments.map((environment) => { + const activeLabel = environment.domain === activeEnvironment ? " (active)" : ""; + return [environment.domain, `${environment.name}${activeLabel}`]; + }); + console.info(formatTable(rows, { headers: ["DOMAIN", "NAME"] })); +}); diff --git a/src/commands/env-set.ts b/src/commands/env-set.ts new file mode 100644 index 0000000..53f6a52 --- /dev/null +++ b/src/commands/env-set.ts @@ -0,0 +1,38 @@ +import { setActiveEnvironment, unsetActiveEnvironment } from "../active-environment"; +import { getHost, getToken } from "../auth"; +import { resolveEnvironment } from "../environments"; +import { createCommand, type CommandConfig } from "../lib/command"; +import { readConfig } from "../project"; + +const config = { + name: "prismic env set", + description: ` + Set the active environment for the project. + + The active environment is stored by the CLI and used by every command and by + the project at build time. Setting the production environment is the same as + \`prismic env unset\`. + `, + positionals: { + name: { description: "Environment domain", required: true }, + }, +} satisfies CommandConfig; + +export default createCommand(config, async ({ positionals }) => { + const [name] = positionals; + + const { repositoryName } = await readConfig(); + const token = await getToken(); + const host = await getHost(); + + const domain = await resolveEnvironment(name, { repo: repositoryName, token, host }); + + if (domain === repositoryName) { + unsetActiveEnvironment(); + console.info(`Reset to the production environment "${repositoryName}".`); + return; + } + + setActiveEnvironment(domain); + console.info(`Set the active environment to "${domain}".`); +}); diff --git a/src/commands/env-unset.ts b/src/commands/env-unset.ts new file mode 100644 index 0000000..98d0574 --- /dev/null +++ b/src/commands/env-unset.ts @@ -0,0 +1,18 @@ +import { unsetActiveEnvironment } from "../active-environment"; +import { createCommand, type CommandConfig } from "../lib/command"; +import { readConfig } from "../project"; + +const config = { + name: "prismic env unset", + description: ` + Reset the active environment to the production environment. + `, +} satisfies CommandConfig; + +export default createCommand(config, async () => { + const { repositoryName } = await readConfig(); + + unsetActiveEnvironment(); + + console.info(`Reset to the production environment "${repositoryName}".`); +}); diff --git a/src/commands/env.ts b/src/commands/env.ts new file mode 100644 index 0000000..d2e75f4 --- /dev/null +++ b/src/commands/env.ts @@ -0,0 +1,28 @@ +import { createCommandRouter } from "../lib/command"; +import envActive from "./env-active"; +import envList from "./env-list"; +import envSet from "./env-set"; +import envUnset from "./env-unset"; + +export default createCommandRouter({ + name: "prismic env", + description: "Manage the active environment for a Prismic project.", + commands: { + set: { + handler: envSet, + description: "Set the active environment", + }, + unset: { + handler: envUnset, + description: "Reset to the production environment", + }, + active: { + handler: envActive, + description: "Print the active environment", + }, + list: { + handler: envList, + description: "List environments", + }, + }, +}); diff --git a/src/environments.ts b/src/environments.ts index 86de69f..c73a962 100644 --- a/src/environments.ts +++ b/src/environments.ts @@ -1,10 +1,11 @@ import { type Environment, getEnvironments } from "./clients/core"; import { getProfile } from "./clients/user"; -export async function resolveEnvironment( - env: string, - config: { repo: string; token: string | undefined; host: string }, -): Promise { +export async function getUserEnvironments(config: { + repo: string; + token: string | undefined; + host: string; +}): Promise { const { repo, token, host } = config; const [profile, environments] = await Promise.all([ @@ -12,15 +13,23 @@ export async function resolveEnvironment( getEnvironments({ repo, token, host }), ]); - const availableEnvironments = environments.filter( + return environments.filter( (environment) => (environment.kind === "prod" || environment.kind === "stage") && environment.users.some((user) => user.id === profile.shortId), ); - const match = availableEnvironments.find((environment) => environment.domain === env); +} + +export async function resolveEnvironment( + env: string, + config: { repo: string; token: string | undefined; host: string }, +): Promise { + const environments = await getUserEnvironments(config); + + const match = environments.find((environment) => environment.domain === env); if (match) return match.domain; - throw new InvalidEnvironmentError(env, availableEnvironments, repo); + throw new InvalidEnvironmentError(env, environments, config.repo); } export class InvalidEnvironmentError extends Error { diff --git a/src/exports/env.ts b/src/exports/env.ts new file mode 100644 index 0000000..1b79689 --- /dev/null +++ b/src/exports/env.ts @@ -0,0 +1 @@ +export { getActiveEnvironment } from "../active-environment"; diff --git a/src/exports/register.ts b/src/exports/register.ts new file mode 100644 index 0000000..8849c23 --- /dev/null +++ b/src/exports/register.ts @@ -0,0 +1,11 @@ +import { getActiveEnvironment, getFrameworkEnvVar } from "../active-environment"; + +// Side-effect import for a framework config (e.g. `import "prismic/env/register"`). +// Sets the framework's environment variable to the active environment, leaving an +// existing value (e.g. from CI) untouched. + +const variable = getFrameworkEnvVar(); +const active = getActiveEnvironment(); +if (variable && active && process.env[variable] == null) { + process.env[variable] = active; +} diff --git a/src/index.ts b/src/index.ts index 8e7c819..d22a37f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,6 +7,7 @@ import { getAdapter, NoSupportedFrameworkError } from "./adapters"; import { cleanupLegacyAuthFile, getHost, getToken, spawnTokenRefresh } from "./auth"; import { getProfile } from "./clients/user"; import docs from "./commands/docs"; +import envCommand from "./commands/env"; import field from "./commands/field"; import gen from "./commands/gen"; import init from "./commands/init"; @@ -94,6 +95,10 @@ const router = createCommandRouter({ handler: status, description: "Show local vs remote model differences", }, + env: { + handler: envCommand, + description: "Manage the active environment", + }, locale: { handler: locale, description: "Manage locales", diff --git a/src/lib/file.ts b/src/lib/file.ts index bf64473..d95d037 100644 --- a/src/lib/file.ts +++ b/src/lib/file.ts @@ -1,3 +1,4 @@ +import { existsSync } from "node:fs"; import { access, mkdir, readFile, writeFile } from "node:fs/promises"; import { pathToFileURL } from "node:url"; import * as z from "zod/mini"; @@ -40,6 +41,22 @@ export async function findUpward( } } +export function findUpwardSync(name: string, config: { start?: URL } = {}): URL | undefined { + const { start = pathToFileURL(process.cwd()) } = config; + + let dir = appendTrailingSlash(start); + + while (true) { + const path = new URL(name, dir); + if (existsSync(path)) return path; + + const parent = new URL("..", dir); + if (parent.href === dir.href) return undefined; + + dir = parent; + } +} + export async function exists(path: URL): Promise { try { await access(path); diff --git a/src/lib/packageJson.ts b/src/lib/packageJson.ts index 9b71f11..e530e2a 100644 --- a/src/lib/packageJson.ts +++ b/src/lib/packageJson.ts @@ -75,6 +75,18 @@ const INSTALL_COMMANDS = { bun: ["bun", "install"], }; +const ADD_COMMANDS: Record = { + npm: "npm install", + yarn: "yarn add", + pnpm: "pnpm add", + bun: "bun add", +}; + +export async function getAddCommand(pkg: string): Promise { + const packageManager = await detectPackageManager(); + return `${ADD_COMMANDS[packageManager]} ${pkg}`; +} + export async function installDependencies(): Promise { const packageJsonPath = await findPackageJson(); const cwd = new URL(".", packageJsonPath); diff --git a/src/lib/update-notifier.ts b/src/lib/update-notifier.ts index fbd4ee5..b8f6518 100644 --- a/src/lib/update-notifier.ts +++ b/src/lib/update-notifier.ts @@ -6,7 +6,7 @@ import * as z from "zod/mini"; import packageJson from "../../package.json" with { type: "json" }; import { stringify } from "./json"; -import { getNpmPackageVersion } from "./packageJson"; +import { getAddCommand, getNpmPackageVersion, readPackageJson } from "./packageJson"; const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; @@ -29,7 +29,14 @@ export async function initUpdateNotifier(options: UpdateNotifierOptions): Promis const currentVersion = packageJson.version; if (state?.latestKnownVersion && isNewer(state.latestKnownVersion, currentVersion)) { - const message = `Update available: ${currentVersion} → ${state.latestKnownVersion}. Run \`npx ${options.npmPackageName}@latest --version\` to update.`; + const isInstalled = await isInstalledAsDependency(options.npmPackageName); + + let updateCommand = `npx ${options.npmPackageName}@latest --version`; + if (isInstalled) { + updateCommand = await getAddCommand(`${options.npmPackageName}@latest`); + } + + const message = `Update available: ${currentVersion} → ${state.latestKnownVersion}. Run \`${updateCommand}\` to update.`; process.on("exit", () => { try { console.error(`\n${message}`); @@ -49,6 +56,19 @@ export async function initUpdateNotifier(options: UpdateNotifierOptions): Promis } } +async function isInstalledAsDependency(name: string): Promise { + try { + const packageJson = await readPackageJson(); + return Boolean( + packageJson.dependencies?.[name] || + packageJson.devDependencies?.[name] || + packageJson.peerDependencies?.[name], + ); + } catch { + return false; + } +} + function shouldSkip(): boolean { if (process.env.NO_UPDATE_NOTIFIER === "0") return false; if (process.env.NO_UPDATE_NOTIFIER === "1") return true; diff --git a/src/project.ts b/src/project.ts index cfbd1b4..3f392b5 100644 --- a/src/project.ts +++ b/src/project.ts @@ -4,6 +4,7 @@ import { readFile, realpath, rm, writeFile } from "node:fs/promises"; import { fileURLToPath, pathToFileURL } from "node:url"; import * as z from "zod/mini"; +import { getActiveEnvironment } from "./active-environment"; import { getRepository } from "./clients/repository"; import { env } from "./env"; import { exists, findUpward } from "./lib/file"; @@ -212,6 +213,9 @@ export async function safeGetRepositoryName(): Promise { } export async function getRepositoryName(): Promise { + const activeEnvironment = getActiveEnvironment(); + if (activeEnvironment) return activeEnvironment; + try { const config = await readConfig(); return config.repositoryName; diff --git a/test/env-active.test.ts b/test/env-active.test.ts new file mode 100644 index 0000000..d4873ec --- /dev/null +++ b/test/env-active.test.ts @@ -0,0 +1,13 @@ +import { it } from "./it"; + +it("supports --help", async ({ expect, prismic }) => { + const { stdout, exitCode } = await prismic("env", ["active", "--help"]); + expect(exitCode).toBe(0); + expect(stdout).toContain("prismic env active [options]"); +}); + +it("prints the production environment by default", async ({ expect, prismic, repo }) => { + const { stdout, exitCode } = await prismic("env", ["active"]); + expect(exitCode).toBe(0); + expect(stdout.trim()).toBe(repo); +}); diff --git a/test/env-list.test.ts b/test/env-list.test.ts new file mode 100644 index 0000000..4847a47 --- /dev/null +++ b/test/env-list.test.ts @@ -0,0 +1,23 @@ +import { it } from "./it"; + +it("supports --help", async ({ expect, prismic }) => { + const { stdout, exitCode } = await prismic("env", ["list", "--help"]); + expect(exitCode).toBe(0); + expect(stdout).toContain("prismic env list [options]"); +}); + +it("lists environments and marks the active one", async ({ expect, prismic, repo }) => { + const { stdout, exitCode } = await prismic("env", ["list"]); + expect(exitCode).toBe(0); + expect(stdout).toContain(repo); + expect(stdout).toContain("(active)"); +}); + +it("lists environments as JSON", async ({ expect, prismic, repo }) => { + const { stdout, exitCode } = await prismic("env", ["list", "--json"]); + expect(exitCode).toBe(0); + const parsed = JSON.parse(stdout); + expect(parsed).toEqual( + expect.arrayContaining([expect.objectContaining({ domain: repo, active: true })]), + ); +}); diff --git a/test/env-set.test.ts b/test/env-set.test.ts new file mode 100644 index 0000000..5ee241f --- /dev/null +++ b/test/env-set.test.ts @@ -0,0 +1,23 @@ +import { it } from "./it"; + +it("supports --help", async ({ expect, prismic }) => { + const { stdout, exitCode } = await prismic("env", ["set", "--help"]); + expect(exitCode).toBe(0); + expect(stdout).toContain("prismic env set [options]"); +}); + +it("rejects an unknown environment", async ({ expect, prismic, repo }) => { + const { stderr, exitCode } = await prismic("env", ["set", "does-not-exist"]); + expect(exitCode).toBe(1); + expect(stderr).toContain(`No environments available on repository "${repo}".`); +}); + +it("resets to production when set to the production environment", async ({ + expect, + prismic, + repo, +}) => { + const { stdout, exitCode } = await prismic("env", ["set", repo]); + expect(exitCode).toBe(0); + expect(stdout).toContain(`Reset to the production environment "${repo}".`); +}); diff --git a/test/env-unset.test.ts b/test/env-unset.test.ts new file mode 100644 index 0000000..fa6ac65 --- /dev/null +++ b/test/env-unset.test.ts @@ -0,0 +1,13 @@ +import { it } from "./it"; + +it("supports --help", async ({ expect, prismic }) => { + const { stdout, exitCode } = await prismic("env", ["unset", "--help"]); + expect(exitCode).toBe(0); + expect(stdout).toContain("prismic env unset [options]"); +}); + +it("resets to the production environment", async ({ expect, prismic, repo }) => { + const { stdout, exitCode } = await prismic("env", ["unset"]); + expect(exitCode).toBe(0); + expect(stdout).toContain(`Reset to the production environment "${repo}".`); +}); diff --git a/test/env.test.ts b/test/env.test.ts new file mode 100644 index 0000000..58a3ced --- /dev/null +++ b/test/env.test.ts @@ -0,0 +1,13 @@ +import { it } from "./it"; + +it("supports --help", async ({ expect, prismic }) => { + const { stdout, exitCode } = await prismic("env", ["--help"]); + expect(exitCode).toBe(0); + expect(stdout).toContain("prismic env [options]"); +}); + +it("prints help by default", async ({ expect, prismic }) => { + const { stdout, exitCode } = await prismic("env"); + expect(exitCode).toBe(0); + expect(stdout).toContain("prismic env [options]"); +}); diff --git a/tsdown.config.ts b/tsdown.config.ts index 241d2f1..b449a08 100644 --- a/tsdown.config.ts +++ b/tsdown.config.ts @@ -6,6 +6,8 @@ const TEST = MODE === "test"; export default defineConfig({ entry: { index: "./src/index.ts", + env: "./src/exports/env.ts", + "env/register": "./src/exports/register.ts", "subprocesses/refreshToken": "./src/subprocesses/refreshToken.ts", "subprocesses/sendSegmentEvents": "./src/subprocesses/sendSegmentEvents.ts", "subprocesses/updateVersionState": "./src/subprocesses/updateVersionState.ts",