Loading CHANGES.md +6 −0 Original line number Diff line number Diff line Loading @@ -72,6 +72,10 @@ To be released. - The `--allow-private-address` or `-p` option allows looking up WebFinger information for private addresses (e.g., `localhost`). - Added `--dry-run` option to `fedify init` command. This option allows users to preview what files and configurations would be created without actually creating them. [[#263], [#298] by Lee ByeongJun] - Fixed a bug where the `fedify node` command had failed to correctly render the favicon in terminal emulators that do not support 24-bit colors. [[#168], [#282] by Hyeonseo Kim] Loading @@ -80,10 +84,12 @@ To be released. [#248]: https://github.com/fedify-dev/fedify/issues/248 [#260]: https://github.com/fedify-dev/fedify/issues/260 [#262]: https://github.com/fedify-dev/fedify/issues/262 [#263]: https://github.com/fedify-dev/fedify/issues/263 [#278]: https://github.com/fedify-dev/fedify/pull/278 [#281]: https://github.com/fedify-dev/fedify/pull/281 [#282]: https://github.com/fedify-dev/fedify/pull/282 [#285]: https://github.com/fedify-dev/fedify/pull/285 [#298]: https://github.com/fedify-dev/fedify/pull/298 [#300]: https://github.com/fedify-dev/fedify/pull/300 Loading cli/init.test.ts 0 → 100644 +253 −0 Original line number Diff line number Diff line import { assertEquals, assertStringIncludes } from "@std/assert"; import { join } from "@std/path"; import { exists } from "@std/fs"; const CLI_PATH = join(import.meta.dirname!, "mod.ts"); async function runInit( args: string[], ): Promise<{ output: string; success: boolean }> { const cmd = new Deno.Command("deno", { args: ["run", "-A", CLI_PATH, "init", ...args], stdout: "piped", stderr: "piped", stdin: "null", }); const process = cmd.spawn(); const output = await process.output(); const decoder = new TextDecoder(); const stdout = decoder.decode(output.stdout); const stderr = decoder.decode(output.stderr); return { output: stdout + stderr, success: output.success, }; } Deno.test("init --dry-run shows preview without creating files", async () => { const testDir = await Deno.makeTempDir(); const projectDir = join(testDir, "test-project"); try { const result = await runInit([ projectDir, "--dry-run", "--runtime", "deno", ]); // Check that dry-run mode is indicated assertStringIncludes(result.output, "🔍 DRY RUN MODE"); assertStringIncludes(result.output, "Would create files:"); assertStringIncludes(result.output, "Would install dependencies:"); assertStringIncludes(result.output, "federation.ts"); assertStringIncludes(result.output, "logging.ts"); assertStringIncludes(result.output, "main.ts"); assertStringIncludes(result.output, "deno.json"); assertStringIncludes(result.output, ".env"); // Verify no files were actually created assertEquals( await exists(projectDir), false, "Project directory should not be created", ); } finally { await Deno.remove(testDir, { recursive: true }); } }); Deno.test("init --dry-run with web framework shows correct files", async () => { const testDir = await Deno.makeTempDir(); const projectDir = join(testDir, "test-hono-project"); try { const result = await runInit([ projectDir, "--dry-run", "--runtime", "deno", "--web-framework", "hono", ]); // Check Hono-specific files assertStringIncludes(result.output, "src/federation.ts"); assertStringIncludes(result.output, "src/app.tsx"); assertStringIncludes(result.output, "src/index.ts"); assertStringIncludes(result.output, "@hono/hono"); // Verify no files were created assertEquals(await exists(projectDir), false); } finally { await Deno.remove(testDir, { recursive: true }); } }); Deno.test("init --dry-run with external stores shows dependencies", async () => { 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", "--message-queue", "redis", ]); // Check Redis dependencies assertStringIncludes(result.output, "@fedify/redis"); assertStringIncludes(result.output, "ioredis"); assertStringIncludes(result.output, "REDIS_URL"); // Check Redis imports in federation.ts assertStringIncludes(result.output, "RedisKvStore"); assertStringIncludes(result.output, "RedisMessageQueue"); // Verify no files were created assertEquals(await exists(projectDir), false); } finally { await Deno.remove(testDir, { recursive: true }); } }); Deno.test("init --dry-run shows command for framework initialization", async () => { const testDir = await Deno.makeTempDir(); const projectDir = join(testDir, "test-nitro-project"); try { const result = await runInit([ projectDir, "--dry-run", "--runtime", "node", "--package-manager", "npm", "--web-framework", "nitro", ]); // Check that initialization command is shown assertStringIncludes(result.output, "Would run command:"); assertStringIncludes(result.output, "giget@latest nitro"); // Check Node.js specific files assertStringIncludes(result.output, "package.json"); assertStringIncludes(result.output, "biome.json"); // Verify no files were created assertEquals(await exists(projectDir), false); } finally { await Deno.remove(testDir, { recursive: true }); } }); 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(); try { // Create a file in the directory await Deno.writeTextFile(join(testDir, "existing.txt"), "content"); const result = await runInit([ testDir, "--dry-run", "--runtime", "deno", ]); assertStringIncludes(result.output, "The directory is not empty"); assertEquals(result.success, false); } finally { await Deno.remove(testDir, { recursive: true }); } }); Deno.test("init --dry-run shows prepend files for Fresh", async () => { const testDir = await Deno.makeTempDir(); const projectDir = join(testDir, "test-fresh-project"); try { const result = await runInit([ projectDir, "--dry-run", "--runtime", "deno", "--web-framework", "fresh", ]); // Check that prepend files are shown assertStringIncludes(result.output, "Would prepend to files:"); assertStringIncludes(result.output, "fresh.config.ts"); // Verify no files were created assertEquals(await exists(projectDir), false); } finally { await Deno.remove(testDir, { recursive: true }); } }); Deno.test("init --dry-run shows dev dependencies for Node.js", async () => { const testDir = await Deno.makeTempDir(); const projectDir = join(testDir, "test-node-project"); try { const result = await runInit([ projectDir, "--dry-run", "--runtime", "node", "--package-manager", "npm", ]); // Check dev dependencies assertStringIncludes(result.output, "Would install dev dependencies:"); assertStringIncludes(result.output, "@biomejs/biome"); // Verify no files were created assertEquals(await exists(projectDir), false); } finally { await Deno.remove(testDir, { recursive: true }); } }); cli/init.ts +158 −48 Original line number Diff line number Diff line Loading @@ -615,7 +615,12 @@ export const command = new Command() "-q, --message-queue <message-queue:message-queue>", "Choose the message queue to use for background jobs.", ) .option( "--dry-run", "Show what would be created without actually creating files.", ) .action(async (options, dir: string) => { const dryRun = options.dryRun ?? false; const projectName = basename( await exists(dir) ? await Deno.realPath(dir) : normalize(dir), ); Loading Loading @@ -1001,12 +1006,42 @@ await configure({ ...initializer.files, }; const { prependFiles } = initializer; await Deno.mkdir(dir, { recursive: true }); for await (const _ of Deno.readDir(dir)) { if (dryRun) { console.log( colors.bold.yellow("🔍 DRY RUN MODE - No files will be created\n"), ); } // Check if directory is empty const checkDirectoryEmpty = async (path: string) => { try { for await (const _ of Deno.readDir(path)) { console.error("The directory is not empty. Aborting."); Deno.exit(1); } } catch (e) { if (!(e instanceof Deno.errors.NotFound)) { throw e; } } }; if (dryRun) { await checkDirectoryEmpty(dir); } else { await Deno.mkdir(dir, { recursive: true }); await checkDirectoryEmpty(dir); } if (initializer.command != null) { if (dryRun) { console.log(colors.bold.cyan("📦 Would run command:")); console.log( ` ${ [initializer.command[0], ...initializer.command.slice(1)].join(" ") }\n`, ); } else { const cmd = new Deno.Command(initializer.command[0], { args: initializer.command.slice(1), cwd: dir, Loading @@ -1020,8 +1055,10 @@ await configure({ Deno.exit(1); } } } if (runtime !== "deno") { const packageJsonPath = join(dir, "package.json"); if (!dryRun) { try { await Deno.stat(packageJsonPath); } catch (e) { Loading @@ -1030,6 +1067,7 @@ await configure({ } else throw e; } } } const dependencies: Record<string, string> = { "@fedify/fedify": `^${await getLatestFedifyVersion(metadata.version)}`, "@logtape/logtape": "^0.8.2", Loading @@ -1037,12 +1075,22 @@ await configure({ ...kvStoreDesc?.dependencies, ...mqDesc?.dependencies, }; if (dryRun) { const deps = Object.entries(dependencies) .map(([name, version]) => `${name}@${version}`) .join("\n"); if (deps) { console.log(colors.bold.cyan("📦 Would install dependencies:")); console.log(`${deps}\n`); } } else { await addDependencies( runtime, packageManager, dir, dependencies, ); } if (runtime !== "deno") { const devDependencies: Record<string, string> = { "@biomejs/biome": "^1.8.3", Loading @@ -1050,6 +1098,15 @@ await configure({ ...kvStoreDesc?.devDependencies, ...mqDesc?.devDependencies, }; if (dryRun) { const devDeps = Object.entries(devDependencies) .map(([name, version]) => `${name}@${version}`) .join("\n"); if (devDeps) { console.log(colors.bold.cyan("📦 Would install dev dependencies:")); console.log(`${devDeps}\n`); } } else { await addDependencies( runtime, packageManager, Loading @@ -1058,13 +1115,33 @@ await configure({ true, ); } } if (dryRun) { console.log(colors.bold.green("📄 Would create files:\n")); for (const [filename, content] of Object.entries(files)) { const path = join(dir, filename); displayFileContent(path, content); } } else { for (const [filename, content] of Object.entries(files)) { const path = join(dir, filename); const dirName = dirname(path); await Deno.mkdir(dirName, { recursive: true }); await Deno.writeTextFile(path, content); } } if (prependFiles != null) { if (dryRun) { console.log(colors.bold.blue("Would prepend to files:\n")); for (const [filename, prefix] of Object.entries(prependFiles)) { const path = join(dir, filename); console.log(colors.blue(`${path}`)); console.log(colors.gray("─".repeat(60))); console.log(colors.gray("Prepending:")); console.log(prefix); console.log(colors.gray("─".repeat(60)) + "\n"); } } else { for (const [filename, prefix] of Object.entries(prependFiles)) { const path = join(dir, filename); const dirName = dirname(path); Loading @@ -1075,7 +1152,11 @@ await configure({ ); } } } if (runtime === "deno") { if (dryRun) { console.log(colors.bold.green("Would create/update JSON files:\n")); } await rewriteJsonFile( join(dir, "deno.json"), {}, Loading @@ -1094,6 +1175,7 @@ await configure({ ], tasks: { ...cfg.tasks, ...initializer.tasks }, }), dryRun, ); await rewriteJsonFile( join(dir, ".vscode", "settings.json"), Loading Loading @@ -1142,6 +1224,7 @@ await configure({ }, ...vsCodeSettings, }), dryRun, ); await rewriteJsonFile( join(dir, ".vscode", "extensions.json"), Loading @@ -1153,8 +1236,12 @@ await configure({ ]), ...vsCodeExtensions, }), dryRun, ); } else { if (dryRun) { console.log(colors.bold.green("Would create/update JSON files:\n")); } await rewriteJsonFile( join(dir, "package.json"), {}, Loading @@ -1163,6 +1250,7 @@ await configure({ ...cfg, scripts: { ...cfg.scripts, ...initializer.tasks }, }), dryRun, ); if (initializer.compilerOptions != null) { await rewriteJsonFile( Loading @@ -1175,6 +1263,7 @@ await configure({ ...initializer.compilerOptions, }, }), dryRun, ); } await rewriteJsonFile( Loading Loading @@ -1222,6 +1311,7 @@ await configure({ }, ...vsCodeSettings, }), dryRun, ); await rewriteJsonFile( join(dir, ".vscode", "extensions.json"), Loading @@ -1233,6 +1323,7 @@ await configure({ ]), ...vsCodeExtensions, }), dryRun, ); await rewriteJsonFile( join(dir, "biome.json"), Loading @@ -1256,6 +1347,7 @@ await configure({ rules: { recommended: true }, }, }), dryRun, ); } console.error(initializer.instruction); Loading Loading @@ -1290,6 +1382,18 @@ ${d(" ")} ${f(" |___/")} `); } function displayFileContent( path: string, content: string, emoji: string = "📄", pathColor: (text: string) => string = colors.green, ) { console.log(pathColor(`${emoji} ${path}`)); console.error(colors.gray("─".repeat(60))); console.log(content); console.error(colors.gray("─".repeat(60)) + "\n"); } async function isCommandAvailable( { checkCommand, outputPattern }: { checkCommand: [string, ...string[]]; Loading Loading @@ -1410,6 +1514,7 @@ async function rewriteJsonFile( empty: any, // deno-lint-ignore no-explicit-any rewriter: (json: any) => any, dryRun: boolean = false, ): Promise<void> { let jsonText: string | null = null; try { Loading @@ -1419,9 +1524,14 @@ async function rewriteJsonFile( } let json = jsonText == null ? empty : JSON.parse(jsonText); json = rewriter(json); if (dryRun) { displayFileContent(path, JSON.stringify(json, null, 2)); } else { await Deno.mkdir(dirname(path), { recursive: true }); await Deno.writeTextFile(path, JSON.stringify(json, null, 2) + "\n"); } } function uniqueArray<T extends boolean | number | string>(a: T[]): T[] { const result: T[] = []; Loading docs/cli.md +22 −0 Original line number Diff line number Diff line Loading @@ -196,6 +196,28 @@ option. The available options are: If it's omitted, the in-process message queue (which is for development purpose) will be used. ### `--dry-run`: Preview without creating files *This option is available since Fedify 1.8.0.* The `--dry-run` option allows you to preview what files and configurations would be created without actually creating them. This is useful for reviewing the project structure before committing to the initialization. ~~~~ sh fedify init my-fedify-project --dry-run ~~~~ When using `--dry-run`, the command will: - Display all files that would be created with their contents - Show which dependencies would be installed - Preview any commands that would be executed - Not create any directories or files on your filesystem This option works with all other initialization options, allowing you to preview different configurations before making a decision. `fedify lookup`: Looking up an ActivityPub object ------------------------------------------------- Loading Loading
CHANGES.md +6 −0 Original line number Diff line number Diff line Loading @@ -72,6 +72,10 @@ To be released. - The `--allow-private-address` or `-p` option allows looking up WebFinger information for private addresses (e.g., `localhost`). - Added `--dry-run` option to `fedify init` command. This option allows users to preview what files and configurations would be created without actually creating them. [[#263], [#298] by Lee ByeongJun] - Fixed a bug where the `fedify node` command had failed to correctly render the favicon in terminal emulators that do not support 24-bit colors. [[#168], [#282] by Hyeonseo Kim] Loading @@ -80,10 +84,12 @@ To be released. [#248]: https://github.com/fedify-dev/fedify/issues/248 [#260]: https://github.com/fedify-dev/fedify/issues/260 [#262]: https://github.com/fedify-dev/fedify/issues/262 [#263]: https://github.com/fedify-dev/fedify/issues/263 [#278]: https://github.com/fedify-dev/fedify/pull/278 [#281]: https://github.com/fedify-dev/fedify/pull/281 [#282]: https://github.com/fedify-dev/fedify/pull/282 [#285]: https://github.com/fedify-dev/fedify/pull/285 [#298]: https://github.com/fedify-dev/fedify/pull/298 [#300]: https://github.com/fedify-dev/fedify/pull/300 Loading
cli/init.test.ts 0 → 100644 +253 −0 Original line number Diff line number Diff line import { assertEquals, assertStringIncludes } from "@std/assert"; import { join } from "@std/path"; import { exists } from "@std/fs"; const CLI_PATH = join(import.meta.dirname!, "mod.ts"); async function runInit( args: string[], ): Promise<{ output: string; success: boolean }> { const cmd = new Deno.Command("deno", { args: ["run", "-A", CLI_PATH, "init", ...args], stdout: "piped", stderr: "piped", stdin: "null", }); const process = cmd.spawn(); const output = await process.output(); const decoder = new TextDecoder(); const stdout = decoder.decode(output.stdout); const stderr = decoder.decode(output.stderr); return { output: stdout + stderr, success: output.success, }; } Deno.test("init --dry-run shows preview without creating files", async () => { const testDir = await Deno.makeTempDir(); const projectDir = join(testDir, "test-project"); try { const result = await runInit([ projectDir, "--dry-run", "--runtime", "deno", ]); // Check that dry-run mode is indicated assertStringIncludes(result.output, "🔍 DRY RUN MODE"); assertStringIncludes(result.output, "Would create files:"); assertStringIncludes(result.output, "Would install dependencies:"); assertStringIncludes(result.output, "federation.ts"); assertStringIncludes(result.output, "logging.ts"); assertStringIncludes(result.output, "main.ts"); assertStringIncludes(result.output, "deno.json"); assertStringIncludes(result.output, ".env"); // Verify no files were actually created assertEquals( await exists(projectDir), false, "Project directory should not be created", ); } finally { await Deno.remove(testDir, { recursive: true }); } }); Deno.test("init --dry-run with web framework shows correct files", async () => { const testDir = await Deno.makeTempDir(); const projectDir = join(testDir, "test-hono-project"); try { const result = await runInit([ projectDir, "--dry-run", "--runtime", "deno", "--web-framework", "hono", ]); // Check Hono-specific files assertStringIncludes(result.output, "src/federation.ts"); assertStringIncludes(result.output, "src/app.tsx"); assertStringIncludes(result.output, "src/index.ts"); assertStringIncludes(result.output, "@hono/hono"); // Verify no files were created assertEquals(await exists(projectDir), false); } finally { await Deno.remove(testDir, { recursive: true }); } }); Deno.test("init --dry-run with external stores shows dependencies", async () => { 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", "--message-queue", "redis", ]); // Check Redis dependencies assertStringIncludes(result.output, "@fedify/redis"); assertStringIncludes(result.output, "ioredis"); assertStringIncludes(result.output, "REDIS_URL"); // Check Redis imports in federation.ts assertStringIncludes(result.output, "RedisKvStore"); assertStringIncludes(result.output, "RedisMessageQueue"); // Verify no files were created assertEquals(await exists(projectDir), false); } finally { await Deno.remove(testDir, { recursive: true }); } }); Deno.test("init --dry-run shows command for framework initialization", async () => { const testDir = await Deno.makeTempDir(); const projectDir = join(testDir, "test-nitro-project"); try { const result = await runInit([ projectDir, "--dry-run", "--runtime", "node", "--package-manager", "npm", "--web-framework", "nitro", ]); // Check that initialization command is shown assertStringIncludes(result.output, "Would run command:"); assertStringIncludes(result.output, "giget@latest nitro"); // Check Node.js specific files assertStringIncludes(result.output, "package.json"); assertStringIncludes(result.output, "biome.json"); // Verify no files were created assertEquals(await exists(projectDir), false); } finally { await Deno.remove(testDir, { recursive: true }); } }); 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(); try { // Create a file in the directory await Deno.writeTextFile(join(testDir, "existing.txt"), "content"); const result = await runInit([ testDir, "--dry-run", "--runtime", "deno", ]); assertStringIncludes(result.output, "The directory is not empty"); assertEquals(result.success, false); } finally { await Deno.remove(testDir, { recursive: true }); } }); Deno.test("init --dry-run shows prepend files for Fresh", async () => { const testDir = await Deno.makeTempDir(); const projectDir = join(testDir, "test-fresh-project"); try { const result = await runInit([ projectDir, "--dry-run", "--runtime", "deno", "--web-framework", "fresh", ]); // Check that prepend files are shown assertStringIncludes(result.output, "Would prepend to files:"); assertStringIncludes(result.output, "fresh.config.ts"); // Verify no files were created assertEquals(await exists(projectDir), false); } finally { await Deno.remove(testDir, { recursive: true }); } }); Deno.test("init --dry-run shows dev dependencies for Node.js", async () => { const testDir = await Deno.makeTempDir(); const projectDir = join(testDir, "test-node-project"); try { const result = await runInit([ projectDir, "--dry-run", "--runtime", "node", "--package-manager", "npm", ]); // Check dev dependencies assertStringIncludes(result.output, "Would install dev dependencies:"); assertStringIncludes(result.output, "@biomejs/biome"); // Verify no files were created assertEquals(await exists(projectDir), false); } finally { await Deno.remove(testDir, { recursive: true }); } });
cli/init.ts +158 −48 Original line number Diff line number Diff line Loading @@ -615,7 +615,12 @@ export const command = new Command() "-q, --message-queue <message-queue:message-queue>", "Choose the message queue to use for background jobs.", ) .option( "--dry-run", "Show what would be created without actually creating files.", ) .action(async (options, dir: string) => { const dryRun = options.dryRun ?? false; const projectName = basename( await exists(dir) ? await Deno.realPath(dir) : normalize(dir), ); Loading Loading @@ -1001,12 +1006,42 @@ await configure({ ...initializer.files, }; const { prependFiles } = initializer; await Deno.mkdir(dir, { recursive: true }); for await (const _ of Deno.readDir(dir)) { if (dryRun) { console.log( colors.bold.yellow("🔍 DRY RUN MODE - No files will be created\n"), ); } // Check if directory is empty const checkDirectoryEmpty = async (path: string) => { try { for await (const _ of Deno.readDir(path)) { console.error("The directory is not empty. Aborting."); Deno.exit(1); } } catch (e) { if (!(e instanceof Deno.errors.NotFound)) { throw e; } } }; if (dryRun) { await checkDirectoryEmpty(dir); } else { await Deno.mkdir(dir, { recursive: true }); await checkDirectoryEmpty(dir); } if (initializer.command != null) { if (dryRun) { console.log(colors.bold.cyan("📦 Would run command:")); console.log( ` ${ [initializer.command[0], ...initializer.command.slice(1)].join(" ") }\n`, ); } else { const cmd = new Deno.Command(initializer.command[0], { args: initializer.command.slice(1), cwd: dir, Loading @@ -1020,8 +1055,10 @@ await configure({ Deno.exit(1); } } } if (runtime !== "deno") { const packageJsonPath = join(dir, "package.json"); if (!dryRun) { try { await Deno.stat(packageJsonPath); } catch (e) { Loading @@ -1030,6 +1067,7 @@ await configure({ } else throw e; } } } const dependencies: Record<string, string> = { "@fedify/fedify": `^${await getLatestFedifyVersion(metadata.version)}`, "@logtape/logtape": "^0.8.2", Loading @@ -1037,12 +1075,22 @@ await configure({ ...kvStoreDesc?.dependencies, ...mqDesc?.dependencies, }; if (dryRun) { const deps = Object.entries(dependencies) .map(([name, version]) => `${name}@${version}`) .join("\n"); if (deps) { console.log(colors.bold.cyan("📦 Would install dependencies:")); console.log(`${deps}\n`); } } else { await addDependencies( runtime, packageManager, dir, dependencies, ); } if (runtime !== "deno") { const devDependencies: Record<string, string> = { "@biomejs/biome": "^1.8.3", Loading @@ -1050,6 +1098,15 @@ await configure({ ...kvStoreDesc?.devDependencies, ...mqDesc?.devDependencies, }; if (dryRun) { const devDeps = Object.entries(devDependencies) .map(([name, version]) => `${name}@${version}`) .join("\n"); if (devDeps) { console.log(colors.bold.cyan("📦 Would install dev dependencies:")); console.log(`${devDeps}\n`); } } else { await addDependencies( runtime, packageManager, Loading @@ -1058,13 +1115,33 @@ await configure({ true, ); } } if (dryRun) { console.log(colors.bold.green("📄 Would create files:\n")); for (const [filename, content] of Object.entries(files)) { const path = join(dir, filename); displayFileContent(path, content); } } else { for (const [filename, content] of Object.entries(files)) { const path = join(dir, filename); const dirName = dirname(path); await Deno.mkdir(dirName, { recursive: true }); await Deno.writeTextFile(path, content); } } if (prependFiles != null) { if (dryRun) { console.log(colors.bold.blue("Would prepend to files:\n")); for (const [filename, prefix] of Object.entries(prependFiles)) { const path = join(dir, filename); console.log(colors.blue(`${path}`)); console.log(colors.gray("─".repeat(60))); console.log(colors.gray("Prepending:")); console.log(prefix); console.log(colors.gray("─".repeat(60)) + "\n"); } } else { for (const [filename, prefix] of Object.entries(prependFiles)) { const path = join(dir, filename); const dirName = dirname(path); Loading @@ -1075,7 +1152,11 @@ await configure({ ); } } } if (runtime === "deno") { if (dryRun) { console.log(colors.bold.green("Would create/update JSON files:\n")); } await rewriteJsonFile( join(dir, "deno.json"), {}, Loading @@ -1094,6 +1175,7 @@ await configure({ ], tasks: { ...cfg.tasks, ...initializer.tasks }, }), dryRun, ); await rewriteJsonFile( join(dir, ".vscode", "settings.json"), Loading Loading @@ -1142,6 +1224,7 @@ await configure({ }, ...vsCodeSettings, }), dryRun, ); await rewriteJsonFile( join(dir, ".vscode", "extensions.json"), Loading @@ -1153,8 +1236,12 @@ await configure({ ]), ...vsCodeExtensions, }), dryRun, ); } else { if (dryRun) { console.log(colors.bold.green("Would create/update JSON files:\n")); } await rewriteJsonFile( join(dir, "package.json"), {}, Loading @@ -1163,6 +1250,7 @@ await configure({ ...cfg, scripts: { ...cfg.scripts, ...initializer.tasks }, }), dryRun, ); if (initializer.compilerOptions != null) { await rewriteJsonFile( Loading @@ -1175,6 +1263,7 @@ await configure({ ...initializer.compilerOptions, }, }), dryRun, ); } await rewriteJsonFile( Loading Loading @@ -1222,6 +1311,7 @@ await configure({ }, ...vsCodeSettings, }), dryRun, ); await rewriteJsonFile( join(dir, ".vscode", "extensions.json"), Loading @@ -1233,6 +1323,7 @@ await configure({ ]), ...vsCodeExtensions, }), dryRun, ); await rewriteJsonFile( join(dir, "biome.json"), Loading @@ -1256,6 +1347,7 @@ await configure({ rules: { recommended: true }, }, }), dryRun, ); } console.error(initializer.instruction); Loading Loading @@ -1290,6 +1382,18 @@ ${d(" ")} ${f(" |___/")} `); } function displayFileContent( path: string, content: string, emoji: string = "📄", pathColor: (text: string) => string = colors.green, ) { console.log(pathColor(`${emoji} ${path}`)); console.error(colors.gray("─".repeat(60))); console.log(content); console.error(colors.gray("─".repeat(60)) + "\n"); } async function isCommandAvailable( { checkCommand, outputPattern }: { checkCommand: [string, ...string[]]; Loading Loading @@ -1410,6 +1514,7 @@ async function rewriteJsonFile( empty: any, // deno-lint-ignore no-explicit-any rewriter: (json: any) => any, dryRun: boolean = false, ): Promise<void> { let jsonText: string | null = null; try { Loading @@ -1419,9 +1524,14 @@ async function rewriteJsonFile( } let json = jsonText == null ? empty : JSON.parse(jsonText); json = rewriter(json); if (dryRun) { displayFileContent(path, JSON.stringify(json, null, 2)); } else { await Deno.mkdir(dirname(path), { recursive: true }); await Deno.writeTextFile(path, JSON.stringify(json, null, 2) + "\n"); } } function uniqueArray<T extends boolean | number | string>(a: T[]): T[] { const result: T[] = []; Loading
docs/cli.md +22 −0 Original line number Diff line number Diff line Loading @@ -196,6 +196,28 @@ option. The available options are: If it's omitted, the in-process message queue (which is for development purpose) will be used. ### `--dry-run`: Preview without creating files *This option is available since Fedify 1.8.0.* The `--dry-run` option allows you to preview what files and configurations would be created without actually creating them. This is useful for reviewing the project structure before committing to the initialization. ~~~~ sh fedify init my-fedify-project --dry-run ~~~~ When using `--dry-run`, the command will: - Display all files that would be created with their contents - Show which dependencies would be installed - Preview any commands that would be executed - Not create any directories or files on your filesystem This option works with all other initialization options, allowing you to preview different configurations before making a decision. `fedify lookup`: Looking up an ActivityPub object ------------------------------------------------- Loading