diff --git a/src/adapters/index.ts b/src/adapters/index.ts index ea5a02b..92aeeda 100644 --- a/src/adapters/index.ts +++ b/src/adapters/index.ts @@ -38,7 +38,8 @@ export async function getAdapter(): Promise { } export class NoSupportedFrameworkError extends Error { - message = "No supported framework found (Next.js, Nuxt, or SvelteKit required)"; + name = "NoSupportedFrameworkError"; + message = "No supported framework found. Run this command in a Next.js, Nuxt, or SvelteKit project."; } export abstract class Adapter { diff --git a/src/commands/init.ts b/src/commands/init.ts index 2057ed6..db9b7ee 100644 --- a/src/commands/init.ts +++ b/src/commands/init.ts @@ -18,14 +18,18 @@ import { UnknownProjectRootError, } from "../project"; import { checkIsTypeBuilderEnabled, TypeBuilderRequiredError } from "../project"; +import { createRepo } from "./repo-create"; const config = { name: "prismic init", description: ` - Initialize a Prismic project by creating a prismic.config.json file. + Initialize a new Prismic project by creating a repository and + prismic.config.json file. Detects the project framework, installs + dependencies, and syncs models from Prismic. - Detects the project framework, installs dependencies, and syncs models - from Prismic. If a slicemachine.config.json exists, it will be migrated. + Use --repo to connect to an existing repository instead. If a + slicemachine.config.json exists, its repository and settings will be + migrated. `, options: { repo: { type: "string", short: "r", description: "Repository name" }, @@ -63,12 +67,6 @@ export default createCommand(config, async ({ values }) => { } } - const repo = explicitRepo ?? legacySliceMachineConfig?.repositoryName; - if (!repo) { - throw new CommandError("Missing required flag: --repo"); - } - - // Validate repo membership let token = await getToken(); const host = await getHost(); let profile: Profile; @@ -96,20 +94,28 @@ export default createCommand(config, async ({ values }) => { } } - const repoMeta = profile.repositories.find((repository) => repository.domain === repo); - if (!repoMeta) { - throw new CommandError( - `Repository "${repo}" not found in your account. Check the name or request access to the repository.`, - ); - } + let repo = explicitRepo ?? legacySliceMachineConfig?.repositoryName; + if (repo) { + const hasRepoAccess = profile.repositories.some((repository) => repository.domain === repo); + if (!hasRepoAccess) { + throw new CommandError( + `Repository "${repo}" not found in your account. Check the name or request access to the repository.`, + ); + } - const isTypeBuilderEnabled = await checkIsTypeBuilderEnabled(repo, { token, host }); - if (!isTypeBuilderEnabled) { - throw new TypeBuilderRequiredError(); + const isTypeBuilderEnabled = await checkIsTypeBuilderEnabled(repo, { token, host }); + if (!isTypeBuilderEnabled) { + throw new TypeBuilderRequiredError(); + } } const adapter = await getAdapter(); + if (!repo) { + repo = await createRepo({ token, host }); + console.info(`Created repository: ${repo}`); + } + // Create prismic.config.json try { const documentAPIEndpoint = diff --git a/src/commands/repo-create.ts b/src/commands/repo-create.ts index e20e9a2..3e66ef7 100644 --- a/src/commands/repo-create.ts +++ b/src/commands/repo-create.ts @@ -19,6 +19,18 @@ export default createCommand(config, async ({ values }) => { const token = await getToken(); const host = await getHost(); + const domain = await createRepo({ name, token, host }); + + console.info(`Repository created: ${domain}`); + console.info(`URL: https://${domain}.${host}/`); +}); + +export async function createRepo(config: { + name?: string; + token: string | undefined; + host: string; +}): Promise { + const { name, token, host } = config; const domain = await findAvailableDomain({ token, host }); if (!domain) { @@ -38,10 +50,8 @@ export default createCommand(config, async ({ values }) => { throw error; } - console.info(`Repository created: ${domain}`); - console.info(`URL: https://${domain}.${host}/`); - console.info(`Run \`prismic init --repo ${domain}\` to initialize a project.`); -}); + return domain; +} async function findAvailableDomain(config: { token: string | undefined; diff --git a/src/index.ts b/src/index.ts index 563aa53..d04ce43 100644 --- a/src/index.ts +++ b/src/index.ts @@ -23,6 +23,7 @@ import webhook from "./commands/webhook"; import whoami from "./commands/whoami"; import { UPDATE_NOTIFIER_STATE_PATH } from "./config"; import { CommandError, createCommandRouter } from "./lib/command"; +import { decodePayload } from "./lib/jwt"; import { ForbiddenRequestError, NotFoundRequestError, @@ -42,7 +43,6 @@ import { sentrySetUser, setupSentry, } from "./lib/sentry"; -import { decodePayload } from "./lib/jwt"; import { dedent } from "./lib/string"; import { initUpdateNotifier } from "./lib/update-notifier"; import { InvalidPrismicConfigError, MissingPrismicConfigError } from "./project"; diff --git a/test/init.test.ts b/test/init.test.ts index 6d4c5b8..b154366 100644 --- a/test/init.test.ts +++ b/test/init.test.ts @@ -14,16 +14,21 @@ it("fails if prismic.config.json already exists", async ({ expect, prismic }) => expect(stderr).toContain("already initialized"); }); -it("fails if --repo is not provided and no legacy config exists", async ({ +it("creates a repo if --repo is not provided and no legacy config exists", async ({ expect, project, prismic, }) => { await rm(new URL("prismic.config.json", project)); - const { exitCode, stderr } = await prismic("init"); - expect(exitCode).toBe(1); - expect(stderr).toContain("Missing required flag"); -}); + const { exitCode, stdout } = await prismic("init"); + expect(exitCode).toBe(0); + expect(stdout).toContain("Created repository:"); + expect(stdout).toContain("Initialized Prismic for repository"); + + const configRaw = await readFile(new URL("prismic.config.json", project), "utf-8"); + const config = JSON.parse(configRaw); + expect(config.repositoryName).toMatch(/^[a-f0-9]{8}$/); +}, 60_000); it("initializes a project with --repo when logged in", async ({ expect,