Loading cli/init.test.ts +78 −31 Original line number Diff line number Diff line Loading @@ -150,37 +150,6 @@ Deno.test("init --dry-run shows command for framework initialization", async () } }); Deno.test("init without --dry-run creates actual files", async () => { const testDir = await Deno.makeTempDir(); const projectDir = join(testDir, "test-actual-project"); try { // Run without --dry-run const result = await runInit([ projectDir, "--runtime", "deno", ]); // Should not show dry-run header assertEquals(result.output.includes("DRY RUN MODE"), false); // Verify files were actually created assertEquals( await exists(projectDir), true, "Project directory should be created", ); assertEquals(await exists(join(projectDir, "federation.ts")), true); assertEquals(await exists(join(projectDir, "logging.ts")), true); assertEquals(await exists(join(projectDir, "main.ts")), true); assertEquals(await exists(join(projectDir, "deno.json")), true); assertEquals(await exists(join(projectDir, ".env")), true); } finally { await Deno.remove(testDir, { recursive: true }); } }); Deno.test("init --dry-run fails on non-empty directory", async () => { const testDir = await Deno.makeTempDir(); Loading Loading @@ -251,3 +220,81 @@ Deno.test("init --dry-run shows dev dependencies for Node.js", async () => { await Deno.remove(testDir, { recursive: true }); } }); Deno.test("init - check version for AMQP package", async () => { const amqpData = await Deno.readTextFile( join(import.meta.dirname!, "../amqp/deno.json"), ); const testDir = await Deno.makeTempDir(); const projectDir = join(testDir, "test-amqp-project"); try { const result = await runInit([ projectDir, "--dry-run", "--runtime", "deno", "--message-queue", "amqp", ]); assertStringIncludes( result.output, `@fedify/amqp@${JSON.parse(amqpData).version.trim()}`, ); assertEquals(await exists(projectDir), false); } finally { await Deno.remove(testDir, { recursive: true }); } }); Deno.test("init - check version for Redis package", async () => { const redisData = await Deno.readTextFile( join(import.meta.dirname!, "../redis/deno.json"), ); const testDir = await Deno.makeTempDir(); const projectDir = join(testDir, "test-redis-project"); try { const result = await runInit([ projectDir, "--dry-run", "--runtime", "deno", "--kv-store", "redis", ]); assertStringIncludes( result.output, `@fedify/redis@${JSON.parse(redisData).version.trim()}`, ); assertEquals(await exists(projectDir), false); } finally { await Deno.remove(testDir, { recursive: true }); } }); Deno.test("init - check version for Postgres package", async () => { const postgresData = await Deno.readTextFile( join(import.meta.dirname!, "../postgres/deno.json"), ); const testDir = await Deno.makeTempDir(); const projectDir = join(testDir, "test-postgres-project"); try { const result = await runInit([ projectDir, "--dry-run", "--runtime", "deno", "--kv-store", "postgres", ]); assertStringIncludes( result.output, `@fedify/postgres@${JSON.parse(postgresData).version.trim()}`, ); assertEquals(await exists(projectDir), false); } finally { await Deno.remove(testDir, { recursive: true }); } }); cli/init.ts +24 −26 Original line number Diff line number Diff line Loading @@ -5,9 +5,17 @@ import { getLogger } from "@logtape/logtape"; import { stringify } from "@std/dotenv/stringify"; import { exists } from "@std/fs"; import { basename, dirname, join, normalize } from "@std/path"; import { format, greaterThan, parse } from "@std/semver"; import metadata from "./deno.json" with { type: "json" }; const packagesMetaData: Record<`@fedify/${string}`, string> = { "@fedify/fedify": metadata.version, "@fedify/redis": metadata.version, "@fedify/postgres": metadata.version, "@fedify/amqp": metadata.version, "@fedify/express": metadata.version, "@fedify/h3": metadata.version, }; const logger = getLogger(["fedify", "cli", "init"]); type Runtime = "deno" | "bun" | "node"; Loading Loading @@ -306,7 +314,7 @@ Then, try look up an actor from your server: init: (projectName, runtime, pm) => ({ dependencies: { express: "^4.19.2", "@fedify/express": "^0.2.1", "@fedify/express": getLatestVersion("@fedify/express"), ...(runtime === "node" ? { "@dotenvx/dotenvx": "^1.14.1", Loading Loading @@ -392,7 +400,7 @@ Then, try look up an actor from your server: ".", ], dependencies: { "@fedify/h3": "^0.1.2", "@fedify/h3": getLatestVersion("@fedify/h3"), }, federationFile: "server/federation.ts", loggingFile: "server/logging.ts", Loading Loading @@ -449,7 +457,7 @@ const kvStores: Record<KvStore, KvStoreDescription> = { redis: { label: "Redis", dependencies: { "@fedify/redis": "^0.2.1", "@fedify/redis": getLatestVersion("@fedify/redis"), "npm:ioredis": "^5.4.1", }, imports: { "@fedify/redis": ["RedisKvStore"], ioredis: ["Redis"] }, Loading @@ -465,7 +473,7 @@ const kvStores: Record<KvStore, KvStoreDescription> = { postgres: { label: "PostgreSQL", dependencies: { "@fedify/postgres": "^0.2.0", "@fedify/postgres": getLatestVersion("@fedify/postgres"), "npm:postgres": "^3.4.5", }, imports: { "@fedify/postgres": ["PostgresKvStore"], postgres: "postgres" }, Loading Loading @@ -504,7 +512,7 @@ const messageQueues: Record<MessageQueue, MessageQueueDescription> = { redis: { label: "Redis", dependencies: { "@fedify/redis": "^0.2.1", "@fedify/redis": getLatestVersion("@fedify/redis"), "npm:ioredis": "^5.4.1", }, imports: { "@fedify/redis": ["RedisMessageQueue"], ioredis: ["Redis"] }, Loading @@ -520,7 +528,7 @@ const messageQueues: Record<MessageQueue, MessageQueueDescription> = { postgres: { label: "PostgreSQL", dependencies: { "@fedify/postgres": "^0.2.0", "@fedify/postgres": getLatestVersion("@fedify/postgres"), "npm:postgres": "^3.4.5", }, imports: { Loading @@ -539,7 +547,7 @@ const messageQueues: Record<MessageQueue, MessageQueueDescription> = { amqp: { label: "AMQP (e.g., RabbitMQ)", dependencies: { "@fedify/amqp": "^0.1.0", "@fedify/amqp": getLatestVersion("@fedify/amqp"), "npm:amqplib": "^0.10.4", }, devDependencies: { Loading Loading @@ -1069,7 +1077,7 @@ await configure({ } } const dependencies: Record<string, string> = { "@fedify/fedify": `^${await getLatestFedifyVersion(metadata.version)}`, "@fedify/fedify": getLatestVersion("@fedify/fedify"), "@logtape/logtape": "^0.8.2", ...initializer.dependencies, ...kvStoreDesc?.dependencies, Loading Loading @@ -1489,25 +1497,15 @@ async function addDependencies( } } async function getLatestFedifyVersion(version: string): Promise<string> { const response = await fetch("https://jsr.io/@fedify/fedify/meta.json", { headers: { Accept: "application/json", }, }); // deno-lint-ignore no-explicit-any const result = await response.json() as any; let maxVersion = parse("0.0.0"); for (const v in result.versions) { if (v === version) return version; else if (result.versions[v].yanked) continue; const semVer = parse(v); if (semVer.prerelease != null && semVer.prerelease.length > 0) continue; if (greaterThan(semVer, maxVersion)) maxVersion = semVer; function getLatestVersion(packageName: `@fedify/${string}`): string { const version = packagesMetaData[packageName]; if (!version) { throw new Error( `Version for package "${packageName}" not found in local metadata.`, ); } return format(maxVersion); return version; } async function rewriteJsonFile( path: string, // deno-lint-ignore no-explicit-any Loading Loading
cli/init.test.ts +78 −31 Original line number Diff line number Diff line Loading @@ -150,37 +150,6 @@ Deno.test("init --dry-run shows command for framework initialization", async () } }); Deno.test("init without --dry-run creates actual files", async () => { const testDir = await Deno.makeTempDir(); const projectDir = join(testDir, "test-actual-project"); try { // Run without --dry-run const result = await runInit([ projectDir, "--runtime", "deno", ]); // Should not show dry-run header assertEquals(result.output.includes("DRY RUN MODE"), false); // Verify files were actually created assertEquals( await exists(projectDir), true, "Project directory should be created", ); assertEquals(await exists(join(projectDir, "federation.ts")), true); assertEquals(await exists(join(projectDir, "logging.ts")), true); assertEquals(await exists(join(projectDir, "main.ts")), true); assertEquals(await exists(join(projectDir, "deno.json")), true); assertEquals(await exists(join(projectDir, ".env")), true); } finally { await Deno.remove(testDir, { recursive: true }); } }); Deno.test("init --dry-run fails on non-empty directory", async () => { const testDir = await Deno.makeTempDir(); Loading Loading @@ -251,3 +220,81 @@ Deno.test("init --dry-run shows dev dependencies for Node.js", async () => { await Deno.remove(testDir, { recursive: true }); } }); Deno.test("init - check version for AMQP package", async () => { const amqpData = await Deno.readTextFile( join(import.meta.dirname!, "../amqp/deno.json"), ); const testDir = await Deno.makeTempDir(); const projectDir = join(testDir, "test-amqp-project"); try { const result = await runInit([ projectDir, "--dry-run", "--runtime", "deno", "--message-queue", "amqp", ]); assertStringIncludes( result.output, `@fedify/amqp@${JSON.parse(amqpData).version.trim()}`, ); assertEquals(await exists(projectDir), false); } finally { await Deno.remove(testDir, { recursive: true }); } }); Deno.test("init - check version for Redis package", async () => { const redisData = await Deno.readTextFile( join(import.meta.dirname!, "../redis/deno.json"), ); const testDir = await Deno.makeTempDir(); const projectDir = join(testDir, "test-redis-project"); try { const result = await runInit([ projectDir, "--dry-run", "--runtime", "deno", "--kv-store", "redis", ]); assertStringIncludes( result.output, `@fedify/redis@${JSON.parse(redisData).version.trim()}`, ); assertEquals(await exists(projectDir), false); } finally { await Deno.remove(testDir, { recursive: true }); } }); Deno.test("init - check version for Postgres package", async () => { const postgresData = await Deno.readTextFile( join(import.meta.dirname!, "../postgres/deno.json"), ); const testDir = await Deno.makeTempDir(); const projectDir = join(testDir, "test-postgres-project"); try { const result = await runInit([ projectDir, "--dry-run", "--runtime", "deno", "--kv-store", "postgres", ]); assertStringIncludes( result.output, `@fedify/postgres@${JSON.parse(postgresData).version.trim()}`, ); assertEquals(await exists(projectDir), false); } finally { await Deno.remove(testDir, { recursive: true }); } });
cli/init.ts +24 −26 Original line number Diff line number Diff line Loading @@ -5,9 +5,17 @@ import { getLogger } from "@logtape/logtape"; import { stringify } from "@std/dotenv/stringify"; import { exists } from "@std/fs"; import { basename, dirname, join, normalize } from "@std/path"; import { format, greaterThan, parse } from "@std/semver"; import metadata from "./deno.json" with { type: "json" }; const packagesMetaData: Record<`@fedify/${string}`, string> = { "@fedify/fedify": metadata.version, "@fedify/redis": metadata.version, "@fedify/postgres": metadata.version, "@fedify/amqp": metadata.version, "@fedify/express": metadata.version, "@fedify/h3": metadata.version, }; const logger = getLogger(["fedify", "cli", "init"]); type Runtime = "deno" | "bun" | "node"; Loading Loading @@ -306,7 +314,7 @@ Then, try look up an actor from your server: init: (projectName, runtime, pm) => ({ dependencies: { express: "^4.19.2", "@fedify/express": "^0.2.1", "@fedify/express": getLatestVersion("@fedify/express"), ...(runtime === "node" ? { "@dotenvx/dotenvx": "^1.14.1", Loading Loading @@ -392,7 +400,7 @@ Then, try look up an actor from your server: ".", ], dependencies: { "@fedify/h3": "^0.1.2", "@fedify/h3": getLatestVersion("@fedify/h3"), }, federationFile: "server/federation.ts", loggingFile: "server/logging.ts", Loading Loading @@ -449,7 +457,7 @@ const kvStores: Record<KvStore, KvStoreDescription> = { redis: { label: "Redis", dependencies: { "@fedify/redis": "^0.2.1", "@fedify/redis": getLatestVersion("@fedify/redis"), "npm:ioredis": "^5.4.1", }, imports: { "@fedify/redis": ["RedisKvStore"], ioredis: ["Redis"] }, Loading @@ -465,7 +473,7 @@ const kvStores: Record<KvStore, KvStoreDescription> = { postgres: { label: "PostgreSQL", dependencies: { "@fedify/postgres": "^0.2.0", "@fedify/postgres": getLatestVersion("@fedify/postgres"), "npm:postgres": "^3.4.5", }, imports: { "@fedify/postgres": ["PostgresKvStore"], postgres: "postgres" }, Loading Loading @@ -504,7 +512,7 @@ const messageQueues: Record<MessageQueue, MessageQueueDescription> = { redis: { label: "Redis", dependencies: { "@fedify/redis": "^0.2.1", "@fedify/redis": getLatestVersion("@fedify/redis"), "npm:ioredis": "^5.4.1", }, imports: { "@fedify/redis": ["RedisMessageQueue"], ioredis: ["Redis"] }, Loading @@ -520,7 +528,7 @@ const messageQueues: Record<MessageQueue, MessageQueueDescription> = { postgres: { label: "PostgreSQL", dependencies: { "@fedify/postgres": "^0.2.0", "@fedify/postgres": getLatestVersion("@fedify/postgres"), "npm:postgres": "^3.4.5", }, imports: { Loading @@ -539,7 +547,7 @@ const messageQueues: Record<MessageQueue, MessageQueueDescription> = { amqp: { label: "AMQP (e.g., RabbitMQ)", dependencies: { "@fedify/amqp": "^0.1.0", "@fedify/amqp": getLatestVersion("@fedify/amqp"), "npm:amqplib": "^0.10.4", }, devDependencies: { Loading Loading @@ -1069,7 +1077,7 @@ await configure({ } } const dependencies: Record<string, string> = { "@fedify/fedify": `^${await getLatestFedifyVersion(metadata.version)}`, "@fedify/fedify": getLatestVersion("@fedify/fedify"), "@logtape/logtape": "^0.8.2", ...initializer.dependencies, ...kvStoreDesc?.dependencies, Loading Loading @@ -1489,25 +1497,15 @@ async function addDependencies( } } async function getLatestFedifyVersion(version: string): Promise<string> { const response = await fetch("https://jsr.io/@fedify/fedify/meta.json", { headers: { Accept: "application/json", }, }); // deno-lint-ignore no-explicit-any const result = await response.json() as any; let maxVersion = parse("0.0.0"); for (const v in result.versions) { if (v === version) return version; else if (result.versions[v].yanked) continue; const semVer = parse(v); if (semVer.prerelease != null && semVer.prerelease.length > 0) continue; if (greaterThan(semVer, maxVersion)) maxVersion = semVer; function getLatestVersion(packageName: `@fedify/${string}`): string { const version = packagesMetaData[packageName]; if (!version) { throw new Error( `Version for package "${packageName}" not found in local metadata.`, ); } return format(maxVersion); return version; } async function rewriteJsonFile( path: string, // deno-lint-ignore no-explicit-any Loading