From 6b69bae096238fa77f1906fe7d54611321c51d57 Mon Sep 17 00:00:00 2001 From: ChanHaeng Lee <2chanhaeng@gmail.com> Date: Sun, 25 Jan 2026 18:01:20 +0000 Subject: [PATCH 01/22] Ignore plans/ --- .gitignore | 1 + .hongdown.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index c6158e03b..3c36dafdb 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ repomix-output.xml t.ts t2.ts plan.md +plans/ diff --git a/.hongdown.toml b/.hongdown.toml index 960644376..b06c80d6a 100644 --- a/.hongdown.toml +++ b/.hongdown.toml @@ -10,6 +10,7 @@ exclude = [ "WARP.md", "packages/fedify/src/cfworkers/**", "plan.md", + "**/plans/**", ] [heading] From 3b05f494410c5c180addeb0b1c92356201514789 Mon Sep 17 00:00:00 2001 From: ChanHaeng Lee <2chanhaeng@gmail.com> Date: Sun, 8 Feb 2026 17:06:06 +0000 Subject: [PATCH 02/22] separate `@fedify/init` from `@fedify/cli` --- deno.json | 3 +- deno.lock | 21 ++ packages/cli/deno.json | 29 +-- packages/cli/package.json | 1 + packages/cli/src/init/mod.ts | 4 +- packages/cli/tsdown.config.ts | 12 -- packages/init/README.md | 93 +++++++++ packages/init/deno.json | 51 +++++ packages/init/package.json | 67 +++++++ .../src/init => init/src}/action/configs.ts | 2 +- .../src/init => init/src}/action/const.ts | 3 +- .../{cli/src/init => init/src}/action/deps.ts | 2 +- .../{cli/src/init => init/src}/action/dir.ts | 0 .../{cli/src/init => init/src}/action/env.ts | 2 +- .../src/init => init/src}/action/install.ts | 2 +- .../{cli/src/init => init/src}/action/mod.ts | 29 ++- .../src/init => init/src}/action/notice.ts | 2 +- .../src/init => init/src}/action/patch.ts | 2 +- .../init => init/src}/action/precommand.ts | 2 +- .../src/init => init/src}/action/recommend.ts | 2 +- .../{cli/src/init => init/src}/action/set.ts | 2 +- .../src/init => init/src}/action/templates.ts | 2 +- .../src/init => init/src}/action/utils.ts | 0 .../{cli/src/init => init/src}/ask/dir.ts | 2 +- packages/{cli/src/init => init/src}/ask/kv.ts | 2 +- .../{cli/src/init => init/src}/ask/mod.ts | 0 packages/{cli/src/init => init/src}/ask/mq.ts | 2 +- packages/{cli/src/init => init/src}/ask/pm.ts | 0 packages/{cli/src/init => init/src}/ask/wf.ts | 0 .../{cli/src/init => init/src}/command.ts | 2 +- packages/{cli/src/init => init/src}/const.ts | 0 packages/init/src/globals.ts | 7 + .../src/init => init/src}/json/biome.json | 0 .../{cli/src/init => init/src}/json/kv.json | 0 .../{cli/src/init => init/src}/json/mq.json | 0 .../{cli/src/init => init/src}/json/pm.json | 0 .../{cli/src/init => init/src}/json/rt.json | 0 .../src}/json/vscode-settings-for-deno.json | 0 .../src}/json/vscode-settings.json | 0 packages/{cli/src/init => init/src}/lib.ts | 4 +- packages/init/src/mod.ts | 2 + .../src}/templates/defaults/federation.ts.tpl | 0 .../src}/templates/defaults/logging.ts.tpl | 0 .../src}/templates/elysia/index/bun.ts.tpl | 0 .../src}/templates/elysia/index/deno.ts.tpl | 0 .../src}/templates/elysia/index/node.ts.tpl | 0 .../src}/templates/express/app.ts.tpl | 0 .../src}/templates/express/index.ts.tpl | 0 .../src}/templates/hono/app.tsx.tpl | 0 .../src}/templates/hono/index/bun.ts.tpl | 0 .../src}/templates/hono/index/deno.ts.tpl | 0 .../src}/templates/hono/index/node.ts.tpl | 0 .../src}/templates/next/middleware.ts.tpl | 0 .../src}/templates/nitro/.env.test.tpl | 0 .../src}/templates/nitro/nitro.config.ts.tpl | 0 .../src}/templates/nitro/server/error.ts.tpl | 0 .../nitro/server/middleware/federation.ts.tpl | 0 .../{cli/src/init => init/src}/test/action.ts | 2 +- .../{cli/src/init => init/src}/test/create.ts | 18 +- .../{cli/src/init => init/src}/test/fill.ts | 0 .../{cli/src/init => init/src}/test/lookup.ts | 2 +- .../{cli/src/init => init/src}/test/mod.ts | 0 .../{cli/src/init => init/src}/test/run.ts | 2 +- .../{cli/src/init => init/src}/test/types.ts | 0 .../{cli/src/init => init/src}/test/utils.ts | 2 +- packages/{cli/src/init => init/src}/types.ts | 2 +- packages/init/src/utils.ts | 189 ++++++++++++++++++ .../src/init => init/src}/webframeworks.ts | 2 +- packages/init/tsdown.config.ts | 24 +++ pnpm-lock.yaml | 40 ++++ pnpm-workspace.yaml | 1 + 71 files changed, 552 insertions(+), 86 deletions(-) create mode 100644 packages/init/README.md create mode 100644 packages/init/deno.json create mode 100644 packages/init/package.json rename packages/{cli/src/init => init/src}/action/configs.ts (99%) rename packages/{cli/src/init => init/src}/action/const.ts (92%) rename packages/{cli/src/init => init/src}/action/deps.ts (99%) rename packages/{cli/src/init => init/src}/action/dir.ts (100%) rename packages/{cli/src/init => init/src}/action/env.ts (91%) rename packages/{cli/src/init => init/src}/action/install.ts (91%) rename packages/{cli/src/init => init/src}/action/mod.ts (69%) rename packages/{cli/src/init => init/src}/action/notice.ts (99%) rename packages/{cli/src/init => init/src}/action/patch.ts (99%) rename packages/{cli/src/init => init/src}/action/precommand.ts (92%) rename packages/{cli/src/init => init/src}/action/recommend.ts (96%) rename packages/{cli/src/init => init/src}/action/set.ts (97%) rename packages/{cli/src/init => init/src}/action/templates.ts (98%) rename packages/{cli/src/init => init/src}/action/utils.ts (100%) rename packages/{cli/src/init => init/src}/ask/dir.ts (97%) rename packages/{cli/src/init => init/src}/ask/kv.ts (98%) rename packages/{cli/src/init => init/src}/ask/mod.ts (100%) rename packages/{cli/src/init => init/src}/ask/mq.ts (98%) rename packages/{cli/src/init => init/src}/ask/pm.ts (100%) rename packages/{cli/src/init => init/src}/ask/wf.ts (100%) rename packages/{cli/src/init => init/src}/command.ts (98%) rename packages/{cli/src/init => init/src}/const.ts (100%) create mode 100644 packages/init/src/globals.ts rename packages/{cli/src/init => init/src}/json/biome.json (100%) rename packages/{cli/src/init => init/src}/json/kv.json (100%) rename packages/{cli/src/init => init/src}/json/mq.json (100%) rename packages/{cli/src/init => init/src}/json/pm.json (100%) rename packages/{cli/src/init => init/src}/json/rt.json (100%) rename packages/{cli/src/init => init/src}/json/vscode-settings-for-deno.json (100%) rename packages/{cli/src/init => init/src}/json/vscode-settings.json (100%) rename packages/{cli/src/init => init/src}/lib.ts (98%) create mode 100644 packages/init/src/mod.ts rename packages/{cli/src/init => init/src}/templates/defaults/federation.ts.tpl (100%) rename packages/{cli/src/init => init/src}/templates/defaults/logging.ts.tpl (100%) rename packages/{cli/src/init => init/src}/templates/elysia/index/bun.ts.tpl (100%) rename packages/{cli/src/init => init/src}/templates/elysia/index/deno.ts.tpl (100%) rename packages/{cli/src/init => init/src}/templates/elysia/index/node.ts.tpl (100%) rename packages/{cli/src/init => init/src}/templates/express/app.ts.tpl (100%) rename packages/{cli/src/init => init/src}/templates/express/index.ts.tpl (100%) rename packages/{cli/src/init => init/src}/templates/hono/app.tsx.tpl (100%) rename packages/{cli/src/init => init/src}/templates/hono/index/bun.ts.tpl (100%) rename packages/{cli/src/init => init/src}/templates/hono/index/deno.ts.tpl (100%) rename packages/{cli/src/init => init/src}/templates/hono/index/node.ts.tpl (100%) rename packages/{cli/src/init => init/src}/templates/next/middleware.ts.tpl (100%) rename packages/{cli/src/init => init/src}/templates/nitro/.env.test.tpl (100%) rename packages/{cli/src/init => init/src}/templates/nitro/nitro.config.ts.tpl (100%) rename packages/{cli/src/init => init/src}/templates/nitro/server/error.ts.tpl (100%) rename packages/{cli/src/init => init/src}/templates/nitro/server/middleware/federation.ts.tpl (100%) rename packages/{cli/src/init => init/src}/test/action.ts (95%) rename packages/{cli/src/init => init/src}/test/create.ts (97%) rename packages/{cli/src/init => init/src}/test/fill.ts (100%) rename packages/{cli/src/init => init/src}/test/lookup.ts (99%) rename packages/{cli/src/init => init/src}/test/mod.ts (100%) rename packages/{cli/src/init => init/src}/test/run.ts (96%) rename packages/{cli/src/init => init/src}/test/types.ts (100%) rename packages/{cli/src/init => init/src}/test/utils.ts (93%) rename packages/{cli/src/init => init/src}/types.ts (98%) create mode 100644 packages/init/src/utils.ts rename packages/{cli/src/init => init/src}/webframeworks.ts (99%) create mode 100644 packages/init/tsdown.config.ts diff --git a/deno.json b/deno.json index bff4e5b3f..2cc42567f 100644 --- a/deno.json +++ b/deno.json @@ -11,6 +11,7 @@ "./packages/fixture", "./packages/fresh", "./packages/h3", + "./packages/init", "./packages/hono", "./packages/koa", "./packages/lint", @@ -162,7 +163,7 @@ "./packages" ], "exclude": [ - "./packages/cli/src/init/templates/**" + "./packages/init/src/templates/**" ] } } diff --git a/deno.lock b/deno.lock index 84cd2ac41..7345964d1 100644 --- a/deno.lock +++ b/deno.lock @@ -6057,6 +6057,27 @@ "jsr:@std/assert@^1.0.13" ] }, + "packages/init": { + "dependencies": [ + "jsr:@logtape/logtape@2", + "jsr:@optique/core@0.9", + "jsr:@optique/run@0.9", + "npm:@fxts/core@^1.21.1", + "npm:@inquirer/prompts@^7.8.4", + "npm:chalk@^5.6.2", + "npm:es-toolkit@^1.43.0", + "npm:inquirer-toggle@^1.0.1" + ], + "packageJson": { + "dependencies": [ + "npm:@inquirer/prompts@^7.8.4", + "npm:@optique/core@0.9", + "npm:@optique/run@0.9", + "npm:chalk@^5.6.2", + "npm:inquirer-toggle@^1.0.1" + ] + } + }, "packages/koa": { "dependencies": [ "npm:koa@2" diff --git a/packages/cli/deno.json b/packages/cli/deno.json index fb64a2642..c24657039 100644 --- a/packages/cli/deno.json +++ b/packages/cli/deno.json @@ -30,7 +30,11 @@ "fedify-cli-*.zip" ], "publish": { - "exclude": ["**/*.test.ts", "tsdown.config.ts", "scripts/"] + "exclude": [ + "**/*.test.ts", + "tsdown.config.ts", + "scripts/" + ] }, "tasks": { "codegen": "deno task -f @fedify/vocab compile", @@ -58,27 +62,8 @@ "dependencies": [ "codegen" ] - }, - "test-init": { - "command": "FEDIFY_TEST_MODE=true deno run --allow-all src/init/test/mod.ts test-init", - "dependencies": [ - "codegen" - ] } }, - "fmt": { - "exclude": [ - "src/init/templates/**" - ] - }, - "lint": { - "exclude": [ - "src/init/templates/**" - ] - }, - "test": { - "exclude": [ - "src/init/test/**" - ] - } + "fmt": {}, + "lint": {} } diff --git a/packages/cli/package.json b/packages/cli/package.json index 2b59a1f4d..4cb6dfd7d 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -45,6 +45,7 @@ }, "dependencies": { "@fedify/fedify": "workspace:*", + "@fedify/init": "workspace:*", "@fedify/relay": "workspace:*", "@fedify/sqlite": "workspace:*", "@fedify/vocab": "workspace:*", diff --git a/packages/cli/src/init/mod.ts b/packages/cli/src/init/mod.ts index ec200713e..05626f949 100644 --- a/packages/cli/src/init/mod.ts +++ b/packages/cli/src/init/mod.ts @@ -1,3 +1 @@ -export { default as runInit } from "./action/mod.ts"; -export { initCommand, testInitCommand } from "./command.ts"; -export { default as runTestInit } from "./test/action.ts"; +export { initCommand, runInit } from "@fedify/init"; diff --git a/packages/cli/tsdown.config.ts b/packages/cli/tsdown.config.ts index 461378f04..72481113a 100644 --- a/packages/cli/tsdown.config.ts +++ b/packages/cli/tsdown.config.ts @@ -1,11 +1,8 @@ -import { cp } from "node:fs/promises"; -import { join } from "node:path"; import { defineConfig } from "tsdown"; export default defineConfig({ entry: [ "src/mod.ts", - "src/init/test/mod.ts", "src/kv.bun.ts", "src/kv.node.ts", ], @@ -29,13 +26,4 @@ export default defineConfig({ `; return outputOptions; }, - hooks: { - "build:done": async (ctx) => { - await cp( - join("src", "init", "templates"), - join(ctx.options.outDir, "init", "templates"), - { recursive: true }, - ); - }, - }, }); diff --git a/packages/init/README.md b/packages/init/README.md new file mode 100644 index 000000000..892fb2f50 --- /dev/null +++ b/packages/init/README.md @@ -0,0 +1,93 @@ +@fedify/init: Project initializer for Fedify +============================================ + +[![JSR][JSR badge]][JSR] +[![npm][npm badge]][npm] + +This package provides the project initialization functionality for [Fedify], +an ActivityPub server framework. It scaffolds new Fedify project directories +with support for various web frameworks, package managers, key-value stores, +and message queues. + +This package powers the `fedify init` command in the [`@fedify/cli`] toolchain, +and can also be used as a standalone library. + +[JSR badge]: https://jsr.io/badges/@fedify/init +[JSR]: https://jsr.io/@fedify/init +[npm badge]: https://img.shields.io/npm/v/@fedify/init?logo=npm +[npm]: https://www.npmjs.com/package/@fedify/init +[Fedify]: https://fedify.dev/ +[`@fedify/cli`]: https://jsr.io/@fedify/cli + + +Supported options +----------------- + +The initializer supports the following project configurations: + + - **Web frameworks**: [Hono], [Nitro], [Next.js], [Elysia], [Express] + - **Package managers**: Deno, pnpm, Bun, Yarn, npm + - **Key-value stores**: Deno KV, Redis, PostgreSQL + - **Message queues**: Deno KV, Redis, PostgreSQL, AMQP + +[Hono]: https://hono.dev/ +[Nitro]: https://nitro.build/ +[Next.js]: https://nextjs.org/ +[Elysia]: https://elysiajs.com/ +[Express]: https://expressjs.com/ + + +Installation +------------ + +~~~~ sh +deno add jsr:@fedify/init # Deno +npm add @fedify/init # npm +pnpm add @fedify/init # pnpm +yarn add @fedify/init # Yarn +bun add @fedify/init # Bun +~~~~ + + +API +--- + +The package exports the following: + + - `runInit`: The main initialization action handler. + - `initCommand`: The CLI command definition for `init`. + +~~~~ typescript +import { initCommand, runInit } from "@fedify/init"; +~~~~ + + +Test +---- + +The `test-init` task is useful for contributors working on `@fedify/init`, +especially when adding support for a new framework/library or modifying the +scaffolding logic. It tests the project initialization by running +`fedify init` across all combinations of supported options on temporary +directories, verifying that the generated projects are valid. + +To run the test using Deno: + +~~~~ sh +deno task test-init +~~~~ + +Or using pnpm: + +~~~~ sh +pnpm test-init +~~~~ + +You can also filter specific options to test a subset of combinations: + +~~~~ sh +deno task test-init -w hono -p deno +~~~~ + +Use `--no-dry-run` to test with actual file creation and dependency +installation, or `--no-hyd-run` to only log outputs without creating files. diff --git a/packages/init/deno.json b/packages/init/deno.json new file mode 100644 index 000000000..2a914c3c4 --- /dev/null +++ b/packages/init/deno.json @@ -0,0 +1,51 @@ +{ + "name": "@fedify/init", + "version": "2.0.0", + "license": "MIT", + "exports": "./src/mod.ts", + "imports": { + "@fedify/init/": "./src/", + "@fxts/core": "npm:@fxts/core@^1.21.1", + "@inquirer/prompts": "npm:@inquirer/prompts@^7.8.4", + "@logtape/logtape": "jsr:@logtape/logtape@^2.0.0", + "@optique/core": "jsr:@optique/core@^0.9.0", + "@optique/run": "jsr:@optique/run@^0.9.0", + "chalk": "npm:chalk@^5.6.2", + "es-toolkit": "npm:es-toolkit@^1.43.0", + "inquirer-toggle": "npm:inquirer-toggle@^1.0.1" + }, + "exclude": [ + "dist/", + "node_modules/" + ], + "publish": { + "exclude": [ + "**/*.test.ts" + ] + }, + "tasks": { + "check": "deno fmt --check && deno lint && deno check src/**/*.ts", + "run": "deno run --allow-all src/mod.ts", + "test-init": "FEDIFY_TEST_MODE=true deno run --allow-all src/test/mod.ts test-init" + }, + "fmt": { + "exclude": [ + "src/templates/**" + ] + }, + "lint": { + "exclude": [ + "src/templates/**" + ], + "rules": { + "exclude": [ + "no-slow-types" + ] + } + }, + "test": { + "exclude": [ + "src/init/test/**" + ] + } +} diff --git a/packages/init/package.json b/packages/init/package.json new file mode 100644 index 000000000..920b2da44 --- /dev/null +++ b/packages/init/package.json @@ -0,0 +1,67 @@ +{ + "name": "@fedify/init", + "version": "2.0.0", + "description": "Project initializer for Fedify", + "keywords": [ + "fedify", + "activitypub", + "cli", + "init" + ], + "homepage": "https://fedify.dev/", + "bugs": { + "url": "https://github.com/fedify-dev/fedify/issues" + }, + "license": "MIT", + "author": { + "name": "Hong Minhee", + "email": "hong@minhee.org", + "url": "https://hongminhee.org/" + }, + "funding": [ + "https://opencollective.com/fedify", + "https://github.com/sponsors/dahlia" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/fedify-dev/fedify.git", + "directory": "packages/init" + }, + "type": "module", + "main": "./dist/mod.js", + "types": "./dist/mod.d.ts", + "exports": { + ".": { + "types": "./dist/mod.d.ts", + "import": "./dist/mod.js" + }, + "./package.json": "./package.json" + }, + "files": [ + "dist/", + "package.json", + "README.md" + ], + "dependencies": { + "@fxts/core": "catalog:", + "@inquirer/prompts": "^7.8.4", + "@logtape/logtape": "catalog:", + "@optique/core": "^0.9.0", + "@optique/run": "^0.9.0", + "chalk": "^5.6.2", + "es-toolkit": "catalog:", + "inquirer-toggle": "^1.0.1" + }, + "devDependencies": { + "@types/node": "catalog:", + "tsdown": "catalog:", + "typescript": "catalog:" + }, + "scripts": { + "build:self": "tsdown", + "build": "pnpm --filter @fedify/init... run build:self", + "prepack": "pnpm build", + "prepublish": "pnpm build", + "test-init": "deno task test-init" + } +} diff --git a/packages/cli/src/init/action/configs.ts b/packages/init/src/action/configs.ts similarity index 99% rename from packages/cli/src/init/action/configs.ts rename to packages/init/src/action/configs.ts index f742ac40e..e9ebbd2cd 100644 --- a/packages/cli/src/init/action/configs.ts +++ b/packages/init/src/action/configs.ts @@ -10,7 +10,7 @@ import { import { uniq } from "es-toolkit"; import { realpathSync } from "node:fs"; import { join as joinPath, relative } from "node:path"; -import { merge } from "../../utils.ts"; +import { merge } from "../utils.ts"; import biome from "../json/biome.json" with { type: "json" }; import vscodeSettingsForDeno from "../json/vscode-settings-for-deno.json" with { type: "json", diff --git a/packages/cli/src/init/action/const.ts b/packages/init/src/action/const.ts similarity index 92% rename from packages/cli/src/init/action/const.ts rename to packages/init/src/action/const.ts index c1ac4820f..03885ef54 100644 --- a/packages/cli/src/init/action/const.ts +++ b/packages/init/src/action/const.ts @@ -2,8 +2,7 @@ import { join as joinPath } from "node:path"; export const PACKAGES_PATH = joinPath( import.meta.dirname!, // action - "..", // init "..", // src - "..", // cli + "..", // init "..", // packages ); diff --git a/packages/cli/src/init/action/deps.ts b/packages/init/src/action/deps.ts similarity index 99% rename from packages/cli/src/init/action/deps.ts rename to packages/init/src/action/deps.ts index b65be1665..bbcd1cd9f 100644 --- a/packages/cli/src/init/action/deps.ts +++ b/packages/init/src/action/deps.ts @@ -8,7 +8,7 @@ import { when, } from "@fxts/core"; import { join as joinPath } from "node:path"; -import { merge, replace } from "../../utils.ts"; +import { merge, replace } from "../utils.ts"; import { PACKAGE_VERSION } from "../lib.ts"; import type { InitCommandData, PackageManager } from "../types.ts"; import { PACKAGES_PATH } from "./const.ts"; diff --git a/packages/cli/src/init/action/dir.ts b/packages/init/src/action/dir.ts similarity index 100% rename from packages/cli/src/init/action/dir.ts rename to packages/init/src/action/dir.ts diff --git a/packages/cli/src/init/action/env.ts b/packages/init/src/action/env.ts similarity index 91% rename from packages/cli/src/init/action/env.ts rename to packages/init/src/action/env.ts index ac37fa887..edb63bfde 100644 --- a/packages/cli/src/init/action/env.ts +++ b/packages/init/src/action/env.ts @@ -1,5 +1,5 @@ import { entries, forEach, pipeLazy, tap, toArray, when } from "@fxts/core"; -import { notEmpty } from "../../utils.ts"; +import { notEmpty } from "../utils.ts"; import type { InitCommandIo } from "../types.ts"; import { noticeConfigEnv, noticeEnvKeyValue } from "./notice.ts"; diff --git a/packages/cli/src/init/action/install.ts b/packages/init/src/action/install.ts similarity index 91% rename from packages/cli/src/init/action/install.ts rename to packages/init/src/action/install.ts index 8d5a69d56..d9392e24e 100644 --- a/packages/cli/src/init/action/install.ts +++ b/packages/init/src/action/install.ts @@ -1,5 +1,5 @@ import { apply, pipe } from "@fxts/core"; -import { CommandError, runSubCommand } from "../../utils.ts"; +import { CommandError, runSubCommand } from "../utils.ts"; import type { InitCommandData } from "../types.ts"; const installDependencies = (data: InitCommandData) => diff --git a/packages/cli/src/init/action/mod.ts b/packages/init/src/action/mod.ts similarity index 69% rename from packages/cli/src/init/action/mod.ts rename to packages/init/src/action/mod.ts index d1c343ba0..918966c98 100644 --- a/packages/cli/src/init/action/mod.ts +++ b/packages/init/src/action/mod.ts @@ -1,9 +1,9 @@ import { pipe, tap, unless, when } from "@fxts/core"; import process from "node:process"; -import { set } from "../../utils.ts"; import askOptions from "../ask/mod.ts"; import type { InitCommand } from "../command.ts"; import type { InitCommandData } from "../types.ts"; +import { set } from "../utils.ts"; import { makeDirIfHyd } from "./dir.ts"; import recommendConfigEnv from "./env.ts"; import installDependencies from "./install.ts"; @@ -20,20 +20,19 @@ import setData from "./set.ts"; import { hasCommand, isDry } from "./utils.ts"; /** - * options: InitCommand - * ├ drawDinosaur - * ┌─────┴──────┐ - * │ askOptions │ InitCommand -> InitCommandOptions - * └─────┬──────┘ - * ├ noticeOptions - * ┌────┴────┐ - * │ setData │ InitCommandOptions -> InitCommandData - * └────┬────┘ - * ┌─┴─┐ isDry - * handleDryRun ┤ ├ handleHydRun - * └─┬─┘ - * ├ recommendConfigEnv - * ├ noticeHowToRun + * Execution flow of the `runInit` function: + * + * 1. Receives options of type `InitCommand`. + * 2. Prints a dinosaur ASCII art via `drawDinosaur`. + * 3. Prompts the user for options via `askOptions`, + * converting `InitCommand` into `InitCommandOptions`. + * 4. Displays the selected options via `noticeOptions`. + * 5. Converts `InitCommandOptions` into `InitCommandData` via `setData`. + * 6. Branches based on `isDry`: + * - If dry run, executes `handleDryRun`. + * - Otherwise, executes `handleHydRun`. + * 7. Recommends configuration environment via `recommendConfigEnv`. + * 8. Shows how to run the project via `noticeHowToRun`. */ const runInit = (options: InitCommand) => pipe( diff --git a/packages/cli/src/init/action/notice.ts b/packages/init/src/action/notice.ts similarity index 99% rename from packages/cli/src/init/action/notice.ts rename to packages/init/src/action/notice.ts index d9db16124..802efce1a 100644 --- a/packages/cli/src/init/action/notice.ts +++ b/packages/init/src/action/notice.ts @@ -5,7 +5,7 @@ import { printErrorMessage, printMessage, type RequiredNotNull, -} from "../../utils.ts"; +} from "../utils.ts"; import type { InitCommand } from "../command.ts"; import type { InitCommandData } from "../types.ts"; diff --git a/packages/cli/src/init/action/patch.ts b/packages/init/src/action/patch.ts similarity index 99% rename from packages/cli/src/init/action/patch.ts rename to packages/init/src/action/patch.ts index 9365efa1f..868fc3f7a 100644 --- a/packages/cli/src/init/action/patch.ts +++ b/packages/init/src/action/patch.ts @@ -1,7 +1,7 @@ import { always, apply, entries, map, pipe, pipeLazy, tap } from "@fxts/core"; import { toMerged } from "es-toolkit"; import { readFile } from "node:fs/promises"; -import { formatJson, merge, replaceAll, set } from "../../utils.ts"; +import { formatJson, merge, replaceAll, set } from "../utils.ts"; import { createFile, throwUnlessNotExists } from "../lib.ts"; import type { InitCommandData } from "../types.ts"; import { diff --git a/packages/cli/src/init/action/precommand.ts b/packages/init/src/action/precommand.ts similarity index 92% rename from packages/cli/src/init/action/precommand.ts rename to packages/init/src/action/precommand.ts index af6527e90..039652b91 100644 --- a/packages/cli/src/init/action/precommand.ts +++ b/packages/init/src/action/precommand.ts @@ -1,4 +1,4 @@ -import { CommandError, exit, runSubCommand } from "../../utils.ts"; +import { CommandError, exit, runSubCommand } from "../utils.ts"; import type { InitCommandData } from "../types.ts"; /** diff --git a/packages/cli/src/init/action/recommend.ts b/packages/init/src/action/recommend.ts similarity index 96% rename from packages/cli/src/init/action/recommend.ts rename to packages/init/src/action/recommend.ts index 3077589a6..e82d8011d 100644 --- a/packages/cli/src/init/action/recommend.ts +++ b/packages/init/src/action/recommend.ts @@ -1,5 +1,5 @@ import { map, peek, pipeLazy, tap, unless, when } from "@fxts/core"; -import { notEmpty } from "../../utils.ts"; +import { notEmpty } from "../utils.ts"; import type { InitCommandIo } from "../types.ts"; import { getDependencies, getDevDependencies } from "./deps.ts"; import { diff --git a/packages/cli/src/init/action/set.ts b/packages/init/src/action/set.ts similarity index 97% rename from packages/cli/src/init/action/set.ts rename to packages/init/src/action/set.ts index 74e2f195b..b2cadb295 100644 --- a/packages/cli/src/init/action/set.ts +++ b/packages/init/src/action/set.ts @@ -2,7 +2,7 @@ import { pipe } from "@fxts/core"; import { existsSync } from "node:fs"; import { realpath } from "node:fs/promises"; import { basename, normalize } from "node:path"; -import { merge, set } from "../../utils.ts"; +import { merge, set } from "../utils.ts"; import { kvStores, messageQueues } from "../lib.ts"; import type { InitCommandData, diff --git a/packages/cli/src/init/action/templates.ts b/packages/init/src/action/templates.ts similarity index 98% rename from packages/cli/src/init/action/templates.ts rename to packages/init/src/action/templates.ts index a74890146..4ff727219 100644 --- a/packages/cli/src/init/action/templates.ts +++ b/packages/init/src/action/templates.ts @@ -1,6 +1,6 @@ import { entries, join, map, pipe } from "@fxts/core"; import { toMerged } from "es-toolkit"; -import { replace } from "../../utils.ts"; +import { replace } from "../utils.ts"; import { readTemplate } from "../lib.ts"; import type { InitCommandData, PackageManager } from "../types.ts"; diff --git a/packages/cli/src/init/action/utils.ts b/packages/init/src/action/utils.ts similarity index 100% rename from packages/cli/src/init/action/utils.ts rename to packages/init/src/action/utils.ts diff --git a/packages/cli/src/init/ask/dir.ts b/packages/init/src/ask/dir.ts similarity index 97% rename from packages/cli/src/init/ask/dir.ts rename to packages/init/src/ask/dir.ts index b4c99acce..513de170c 100644 --- a/packages/cli/src/init/ask/dir.ts +++ b/packages/init/src/ask/dir.ts @@ -3,7 +3,7 @@ import { input } from "@inquirer/prompts"; import { message } from "@optique/core/message"; import { printError } from "@optique/run"; import toggle from "inquirer-toggle"; -import { getCwd, getOsType, runSubCommand } from "../../utils.ts"; +import { getCwd, getOsType, runSubCommand } from "../utils.ts"; import { isDirectoryEmpty, logger } from "../lib.ts"; /** diff --git a/packages/cli/src/init/ask/kv.ts b/packages/init/src/ask/kv.ts similarity index 98% rename from packages/cli/src/init/ask/kv.ts rename to packages/init/src/ask/kv.ts index 33f0ebdce..8fa2b1a89 100644 --- a/packages/cli/src/init/ask/kv.ts +++ b/packages/init/src/ask/kv.ts @@ -1,6 +1,6 @@ import { pipe, tap, throwError, unless, when } from "@fxts/core/index.js"; import { select } from "@inquirer/prompts"; -import { printErrorMessage } from "../../utils.ts"; +import { printErrorMessage } from "../utils.ts"; import { KV_STORE } from "../const.ts"; import { isTest, kvStores } from "../lib.ts"; import type { KvStore, PackageManager } from "../types.ts"; diff --git a/packages/cli/src/init/ask/mod.ts b/packages/init/src/ask/mod.ts similarity index 100% rename from packages/cli/src/init/ask/mod.ts rename to packages/init/src/ask/mod.ts diff --git a/packages/cli/src/init/ask/mq.ts b/packages/init/src/ask/mq.ts similarity index 98% rename from packages/cli/src/init/ask/mq.ts rename to packages/init/src/ask/mq.ts index 27c906da3..7527d272a 100644 --- a/packages/cli/src/init/ask/mq.ts +++ b/packages/init/src/ask/mq.ts @@ -1,6 +1,6 @@ import { pipe, tap, throwError, unless, when } from "@fxts/core/index.js"; import { select } from "@inquirer/prompts"; -import { printErrorMessage } from "../../utils.ts"; +import { printErrorMessage } from "../utils.ts"; import { MESSAGE_QUEUE } from "../const.ts"; import { isTest, messageQueues } from "../lib.ts"; import type { MessageQueue, PackageManager } from "../types.ts"; diff --git a/packages/cli/src/init/ask/pm.ts b/packages/init/src/ask/pm.ts similarity index 100% rename from packages/cli/src/init/ask/pm.ts rename to packages/init/src/ask/pm.ts diff --git a/packages/cli/src/init/ask/wf.ts b/packages/init/src/ask/wf.ts similarity index 100% rename from packages/cli/src/init/ask/wf.ts rename to packages/init/src/ask/wf.ts diff --git a/packages/cli/src/init/command.ts b/packages/init/src/command.ts similarity index 98% rename from packages/cli/src/init/command.ts rename to packages/init/src/command.ts index fb51bd881..f8648fff2 100644 --- a/packages/cli/src/init/command.ts +++ b/packages/init/src/command.ts @@ -14,13 +14,13 @@ import { or, } from "@optique/core"; import { path } from "@optique/run"; -import { debugOption } from "../globals.ts"; import { KV_STORE, MESSAGE_QUEUE, PACKAGE_MANAGER, WEB_FRAMEWORK, } from "./const.ts"; +import { debugOption } from "./globals.ts"; const webFramework = optional(option( "-w", diff --git a/packages/cli/src/init/const.ts b/packages/init/src/const.ts similarity index 100% rename from packages/cli/src/init/const.ts rename to packages/init/src/const.ts diff --git a/packages/init/src/globals.ts b/packages/init/src/globals.ts new file mode 100644 index 000000000..79c26d39b --- /dev/null +++ b/packages/init/src/globals.ts @@ -0,0 +1,7 @@ +import { message, object, option } from "@optique/core"; + +export const debugOption = object("Global options", { + debug: option("-d", "--debug", { + description: message`Enable debug mode.`, + }), +}); diff --git a/packages/cli/src/init/json/biome.json b/packages/init/src/json/biome.json similarity index 100% rename from packages/cli/src/init/json/biome.json rename to packages/init/src/json/biome.json diff --git a/packages/cli/src/init/json/kv.json b/packages/init/src/json/kv.json similarity index 100% rename from packages/cli/src/init/json/kv.json rename to packages/init/src/json/kv.json diff --git a/packages/cli/src/init/json/mq.json b/packages/init/src/json/mq.json similarity index 100% rename from packages/cli/src/init/json/mq.json rename to packages/init/src/json/mq.json diff --git a/packages/cli/src/init/json/pm.json b/packages/init/src/json/pm.json similarity index 100% rename from packages/cli/src/init/json/pm.json rename to packages/init/src/json/pm.json diff --git a/packages/cli/src/init/json/rt.json b/packages/init/src/json/rt.json similarity index 100% rename from packages/cli/src/init/json/rt.json rename to packages/init/src/json/rt.json diff --git a/packages/cli/src/init/json/vscode-settings-for-deno.json b/packages/init/src/json/vscode-settings-for-deno.json similarity index 100% rename from packages/cli/src/init/json/vscode-settings-for-deno.json rename to packages/init/src/json/vscode-settings-for-deno.json diff --git a/packages/cli/src/init/json/vscode-settings.json b/packages/init/src/json/vscode-settings.json similarity index 100% rename from packages/cli/src/init/json/vscode-settings.json rename to packages/init/src/json/vscode-settings.json diff --git a/packages/cli/src/init/lib.ts b/packages/init/src/lib.ts similarity index 98% rename from packages/cli/src/init/lib.ts rename to packages/init/src/lib.ts index a9c785f68..48c8e5050 100644 --- a/packages/cli/src/init/lib.ts +++ b/packages/init/src/lib.ts @@ -17,8 +17,7 @@ import { readFileSync } from "node:fs"; import { mkdir, readdir, writeFile } from "node:fs/promises"; import { dirname, join as joinPath } from "node:path"; import process from "node:process"; -import metadata from "../../deno.json" with { type: "json" }; -import { isNotFoundError, runSubCommand } from "../utils.ts"; +import metadata from "../deno.json" with { type: "json" }; import kv from "./json/kv.json" with { type: "json" }; import mq from "./json/mq.json" with { type: "json" }; import pm from "./json/pm.json" with { type: "json" }; @@ -30,6 +29,7 @@ import type { PackageManagers, Runtimes, } from "./types.ts"; +import { isNotFoundError, runSubCommand } from "./utils.ts"; import webFrameworks from "./webframeworks.ts"; export const PACKAGE_VERSION = metadata.version; diff --git a/packages/init/src/mod.ts b/packages/init/src/mod.ts new file mode 100644 index 000000000..a7d28e3bb --- /dev/null +++ b/packages/init/src/mod.ts @@ -0,0 +1,2 @@ +export { default as runInit } from "./action/mod.ts"; +export { initCommand } from "./command.ts"; diff --git a/packages/cli/src/init/templates/defaults/federation.ts.tpl b/packages/init/src/templates/defaults/federation.ts.tpl similarity index 100% rename from packages/cli/src/init/templates/defaults/federation.ts.tpl rename to packages/init/src/templates/defaults/federation.ts.tpl diff --git a/packages/cli/src/init/templates/defaults/logging.ts.tpl b/packages/init/src/templates/defaults/logging.ts.tpl similarity index 100% rename from packages/cli/src/init/templates/defaults/logging.ts.tpl rename to packages/init/src/templates/defaults/logging.ts.tpl diff --git a/packages/cli/src/init/templates/elysia/index/bun.ts.tpl b/packages/init/src/templates/elysia/index/bun.ts.tpl similarity index 100% rename from packages/cli/src/init/templates/elysia/index/bun.ts.tpl rename to packages/init/src/templates/elysia/index/bun.ts.tpl diff --git a/packages/cli/src/init/templates/elysia/index/deno.ts.tpl b/packages/init/src/templates/elysia/index/deno.ts.tpl similarity index 100% rename from packages/cli/src/init/templates/elysia/index/deno.ts.tpl rename to packages/init/src/templates/elysia/index/deno.ts.tpl diff --git a/packages/cli/src/init/templates/elysia/index/node.ts.tpl b/packages/init/src/templates/elysia/index/node.ts.tpl similarity index 100% rename from packages/cli/src/init/templates/elysia/index/node.ts.tpl rename to packages/init/src/templates/elysia/index/node.ts.tpl diff --git a/packages/cli/src/init/templates/express/app.ts.tpl b/packages/init/src/templates/express/app.ts.tpl similarity index 100% rename from packages/cli/src/init/templates/express/app.ts.tpl rename to packages/init/src/templates/express/app.ts.tpl diff --git a/packages/cli/src/init/templates/express/index.ts.tpl b/packages/init/src/templates/express/index.ts.tpl similarity index 100% rename from packages/cli/src/init/templates/express/index.ts.tpl rename to packages/init/src/templates/express/index.ts.tpl diff --git a/packages/cli/src/init/templates/hono/app.tsx.tpl b/packages/init/src/templates/hono/app.tsx.tpl similarity index 100% rename from packages/cli/src/init/templates/hono/app.tsx.tpl rename to packages/init/src/templates/hono/app.tsx.tpl diff --git a/packages/cli/src/init/templates/hono/index/bun.ts.tpl b/packages/init/src/templates/hono/index/bun.ts.tpl similarity index 100% rename from packages/cli/src/init/templates/hono/index/bun.ts.tpl rename to packages/init/src/templates/hono/index/bun.ts.tpl diff --git a/packages/cli/src/init/templates/hono/index/deno.ts.tpl b/packages/init/src/templates/hono/index/deno.ts.tpl similarity index 100% rename from packages/cli/src/init/templates/hono/index/deno.ts.tpl rename to packages/init/src/templates/hono/index/deno.ts.tpl diff --git a/packages/cli/src/init/templates/hono/index/node.ts.tpl b/packages/init/src/templates/hono/index/node.ts.tpl similarity index 100% rename from packages/cli/src/init/templates/hono/index/node.ts.tpl rename to packages/init/src/templates/hono/index/node.ts.tpl diff --git a/packages/cli/src/init/templates/next/middleware.ts.tpl b/packages/init/src/templates/next/middleware.ts.tpl similarity index 100% rename from packages/cli/src/init/templates/next/middleware.ts.tpl rename to packages/init/src/templates/next/middleware.ts.tpl diff --git a/packages/cli/src/init/templates/nitro/.env.test.tpl b/packages/init/src/templates/nitro/.env.test.tpl similarity index 100% rename from packages/cli/src/init/templates/nitro/.env.test.tpl rename to packages/init/src/templates/nitro/.env.test.tpl diff --git a/packages/cli/src/init/templates/nitro/nitro.config.ts.tpl b/packages/init/src/templates/nitro/nitro.config.ts.tpl similarity index 100% rename from packages/cli/src/init/templates/nitro/nitro.config.ts.tpl rename to packages/init/src/templates/nitro/nitro.config.ts.tpl diff --git a/packages/cli/src/init/templates/nitro/server/error.ts.tpl b/packages/init/src/templates/nitro/server/error.ts.tpl similarity index 100% rename from packages/cli/src/init/templates/nitro/server/error.ts.tpl rename to packages/init/src/templates/nitro/server/error.ts.tpl diff --git a/packages/cli/src/init/templates/nitro/server/middleware/federation.ts.tpl b/packages/init/src/templates/nitro/server/middleware/federation.ts.tpl similarity index 100% rename from packages/cli/src/init/templates/nitro/server/middleware/federation.ts.tpl rename to packages/init/src/templates/nitro/server/middleware/federation.ts.tpl diff --git a/packages/cli/src/init/test/action.ts b/packages/init/src/test/action.ts similarity index 95% rename from packages/cli/src/init/test/action.ts rename to packages/init/src/test/action.ts index 1babf9421..d22635b59 100644 --- a/packages/cli/src/init/test/action.ts +++ b/packages/init/src/test/action.ts @@ -1,5 +1,5 @@ import { pipe, tap, when } from "@fxts/core"; -import { set } from "../../utils.ts"; +import { set } from "../utils.ts"; import type { TestInitCommand } from "../command.ts"; import { fillEmptyOptions } from "./fill.ts"; import runTests from "./run.ts"; diff --git a/packages/cli/src/init/test/create.ts b/packages/init/src/test/create.ts similarity index 97% rename from packages/cli/src/init/test/create.ts rename to packages/init/src/test/create.ts index a71a6d804..3b57a2428 100644 --- a/packages/cli/src/init/test/create.ts +++ b/packages/init/src/test/create.ts @@ -3,14 +3,6 @@ import { values } from "@optique/core"; import { appendFile, mkdir } from "node:fs/promises"; import { join, sep } from "node:path"; import process from "node:process"; -import { - CommandError, - type GeneratedType, - printErrorMessage, - printMessage, - product, - runSubCommand, -} from "../../utils.ts"; import packageManagers from "../json/pm.json" with { type: "json" }; import { kvStores, messageQueues } from "../lib.ts"; import type { @@ -19,6 +11,14 @@ import type { PackageManager, WebFramework, } from "../types.ts"; +import { + CommandError, + type GeneratedType, + printErrorMessage, + printMessage, + product, + runSubCommand, +} from "../utils.ts"; import webFrameworks from "../webframeworks.ts"; import type { InitTestData, MultipleOption } from "./types.ts"; @@ -34,7 +34,7 @@ async ( const result = await runSubCommand( toArray(genInitCommand(testDir, dry, options)), { - cwd: join(import.meta.dirname!, "../../.."), + cwd: join(import.meta.dirname!, "../.."), stdio: ["ignore", "pipe", "pipe"], }, ); diff --git a/packages/cli/src/init/test/fill.ts b/packages/init/src/test/fill.ts similarity index 100% rename from packages/cli/src/init/test/fill.ts rename to packages/init/src/test/fill.ts diff --git a/packages/cli/src/init/test/lookup.ts b/packages/init/src/test/lookup.ts similarity index 99% rename from packages/cli/src/init/test/lookup.ts rename to packages/init/src/test/lookup.ts index 821c3ab49..c070c0ba6 100644 --- a/packages/cli/src/init/test/lookup.ts +++ b/packages/init/src/test/lookup.ts @@ -6,7 +6,7 @@ import { createWriteStream } from "node:fs"; import { join, sep } from "node:path"; import process from "node:process"; import type Stream from "node:stream"; -import { printErrorMessage, printMessage, runSubCommand } from "../../utils.ts"; +import { printErrorMessage, printMessage, runSubCommand } from "../utils.ts"; import { getDevCommand } from "../lib.ts"; import type { KvStore, diff --git a/packages/cli/src/init/test/mod.ts b/packages/init/src/test/mod.ts similarity index 100% rename from packages/cli/src/init/test/mod.ts rename to packages/init/src/test/mod.ts diff --git a/packages/cli/src/init/test/run.ts b/packages/init/src/test/run.ts similarity index 96% rename from packages/cli/src/init/test/run.ts rename to packages/init/src/test/run.ts index a1cd0a1f5..ec31d0102 100644 --- a/packages/cli/src/init/test/run.ts +++ b/packages/init/src/test/run.ts @@ -1,7 +1,7 @@ import { always, filter, map, pipe, tap, unless } from "@fxts/core"; import { optionNames } from "@optique/core"; import { join } from "node:path"; -import { printMessage } from "../../utils.ts"; +import { printMessage } from "../utils.ts"; import createTestApp, { filterOptions, generateTestCases } from "./create.ts"; import runServerAndLookupUser from "./lookup.ts"; import type { InitTestData } from "./types.ts"; diff --git a/packages/cli/src/init/test/types.ts b/packages/init/src/test/types.ts similarity index 100% rename from packages/cli/src/init/test/types.ts rename to packages/init/src/test/types.ts diff --git a/packages/cli/src/init/test/utils.ts b/packages/init/src/test/utils.ts similarity index 93% rename from packages/cli/src/init/test/utils.ts rename to packages/init/src/test/utils.ts index a43804a41..47ce93676 100644 --- a/packages/cli/src/init/test/utils.ts +++ b/packages/init/src/test/utils.ts @@ -1,7 +1,7 @@ import { rmdir } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; -import { printMessage } from "../../utils.ts"; +import { printMessage } from "../utils.ts"; export const genRunId = () => `${Date.now()}-${Math.random().toString(36).slice(2)}`; diff --git a/packages/cli/src/init/types.ts b/packages/init/src/types.ts similarity index 98% rename from packages/cli/src/init/types.ts rename to packages/init/src/types.ts index 532ada05c..689c19128 100644 --- a/packages/cli/src/init/types.ts +++ b/packages/init/src/types.ts @@ -1,5 +1,4 @@ import type { Message } from "@optique/core"; -import type { RequiredNotNull } from "../utils.ts"; import type { InitCommand } from "./command.ts"; import type { KV_STORE, @@ -7,6 +6,7 @@ import type { PACKAGE_MANAGER, WEB_FRAMEWORK, } from "./const.ts"; +import type { RequiredNotNull } from "./utils.ts"; export type PackageManager = typeof PACKAGE_MANAGER[number]; export type WebFramework = typeof WEB_FRAMEWORK[number]; diff --git a/packages/init/src/utils.ts b/packages/init/src/utils.ts new file mode 100644 index 000000000..9ec3ccc3b --- /dev/null +++ b/packages/init/src/utils.ts @@ -0,0 +1,189 @@ +import { isObject } from "@fxts/core"; +import { message } from "@optique/core"; +import { print, printError } from "@optique/run"; +import { Chalk } from "chalk"; +import { flow, toMerged } from "es-toolkit"; +import { spawn } from "node:child_process"; +import process from "node:process"; + +export const colorEnabled: boolean = process.stdout.isTTY && + !("NO_COLOR" in process.env && process.env.NO_COLOR !== ""); + +export const colors = new Chalk(colorEnabled ? {} : { level: 0 }); + +export type RequiredNotNull = { + [P in keyof T]: NonNullable; +}; + +export const isPromise = (value: unknown): value is Promise => + value instanceof Promise; + +export function set( + key: K, + f: (value: T) => S, +): ( + obj: T, +) => S extends Promise ? Promise }> + : T & { [P in K]: S } { + return ((obj) => { + const result = f(obj); + if (isPromise ? U : never>(result)) { + return result.then((value) => ({ ...obj, [key]: value })) as S extends + Promise ? Promise< + T & { [P in K]: Awaited } + > + : never; + } + return ({ ...obj, [key]: result }) as S extends Promise ? never + : T & { [P in K]: S }; + }); +} + +export const merge = + (source: Parameters[1] = {}) => + (target: Parameters[0] = {}) => toMerged(target, source); + +export const replace = ( + pattern: string | RegExp, + replacement: string | ((substring: string, ...args: unknown[]) => string), +) => +(text: string): string => text.replace(pattern, replacement as string); + +export const replaceAll = ( + pattern: string | RegExp, + replacement: string | ((substring: string, ...args: unknown[]) => string), +) => +(text: string): string => text.replaceAll(pattern, replacement as string); + +export const formatJson = (obj: unknown) => JSON.stringify(obj, null, 2) + "\n"; + +export const notEmpty = (s: T) => + s.length > 0; + +export const isNotFoundError = (e: unknown): e is { code: "ENOENT" } => + isObject(e) && "code" in e && e.code === "ENOENT"; + +export class CommandError extends Error { + public commandLine: string; + constructor( + message: string, + public stdout: string, + public stderr: string, + public code: number, + public command: string[], + ) { + super(message); + this.name = "CommandError"; + this.commandLine = command.join(" "); + } +} + +export const runSubCommand = async [2]>( + command: string[], + options: Opt, +): Promise<{ + stdout: string; + stderr: string; +}> => { + const commands = command.reduce((acc, cur) => { + if (cur === "&&") { + acc.push([]); + } else { + if (acc.length === 0) acc.push([]); + acc[acc.length - 1].push(cur); + } + return acc; + }, []); + + const results = { stdout: "", stderr: "" }; + + for (const cmd of commands) { + try { + const result = await runSingularCommand(cmd, options); + results.stdout += (results.stdout ? "\n" : "") + result.stdout; + results.stderr += (results.stderr ? "\n" : "") + result.stderr; + } catch (error) { + if (error instanceof CommandError) { + results.stdout += (results.stdout ? "\n" : "") + error.stdout; + results.stderr += (results.stderr ? "\n" : "") + error.stderr; + } + throw error; + } + } + return results; +}; + +const runSingularCommand = ( + command: string[], + options: Parameters[2], +) => + new Promise<{ + stdout: string; + stderr: string; + }>((resolve, reject) => { + let stdout = ""; + let stderr = ""; + const child = spawn(command[0], command.slice(1), options); + + child.stdout?.on("data", (data) => { + stdout += data.toString(); + }); + child.stderr?.on("data", (data) => { + stderr += data.toString(); + }); + + child.on("close", (code) => { + if (code === 0) { + resolve({ + stdout: stdout.trim(), + stderr: stderr.trim(), + }); + } else { + reject( + new CommandError( + `Command exited with code ${code ?? "unknown"}`, + stdout.trim(), + stderr.trim(), + code ?? -1, + command, + ), + ); + } + }); + + child.on("error", (error) => { + reject(error); + }); + }); + +export const getCwd = () => process.cwd(); + +export const getOsType = () => process.platform; + +export const exit = (code: number) => process.exit(code); + +export type ItersItems[]> = T extends [] ? [] + : T extends [infer Head, ...infer Tail] + ? Head extends Iterable + ? Tail extends Iterable[] ? [Item, ...ItersItems] + : [Item] + : never + : never; + +export function* product[]>( + ...[head, ...tail]: T +): Generator> { + if (!head) yield [] as ItersItems; + else { + for (const x of head) { + for (const xs of product(...tail)) yield [x, ...xs] as ItersItems; + } + } +} + +export type GeneratedType = T extends + Generator ? R : never; + +type PrintMessage = (...args: Parameters) => void; +export const printMessage: PrintMessage = flow(message, print); +export const printErrorMessage: PrintMessage = flow(message, printError); diff --git a/packages/cli/src/init/webframeworks.ts b/packages/init/src/webframeworks.ts similarity index 99% rename from packages/cli/src/init/webframeworks.ts rename to packages/init/src/webframeworks.ts index abc7f0b1b..457c8cdc2 100644 --- a/packages/cli/src/init/webframeworks.ts +++ b/packages/init/src/webframeworks.ts @@ -1,5 +1,4 @@ import { pipe } from "@fxts/core"; -import { replace } from "../utils.ts"; import { PACKAGE_MANAGER } from "./const.ts"; import { getInstruction, @@ -10,6 +9,7 @@ import { readTemplate, } from "./lib.ts"; import type { WebFrameworks } from "./types.ts"; +import { replace } from "./utils.ts"; const webFrameworks: WebFrameworks = { hono: { diff --git a/packages/init/tsdown.config.ts b/packages/init/tsdown.config.ts new file mode 100644 index 000000000..b98934d3f --- /dev/null +++ b/packages/init/tsdown.config.ts @@ -0,0 +1,24 @@ +import { cp } from "node:fs/promises"; +import { join } from "node:path"; +import { defineConfig } from "tsdown"; + +export default defineConfig({ + entry: ["src/mod.ts", "src/test/mod.ts"], + platform: "node", + unbundle: true, + external: [/^node:/], + hooks: { + "build:done": async (ctx) => { + await cp( + join("src", "templates"), + join(ctx.options.outDir, "templates"), + { recursive: true }, + ); + await cp( + join("src", "json"), + join(ctx.options.outDir, "json"), + { recursive: true }, + ); + }, + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7e6d02af8..2f0658335 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -738,6 +738,9 @@ importers: '@fedify/fedify': specifier: workspace:* version: link:../fedify + '@fedify/init': + specifier: workspace:* + version: link:../init '@fedify/relay': specifier: workspace:* version: link:../relay @@ -1099,6 +1102,43 @@ importers: specifier: 'catalog:' version: 5.9.3 + packages/init: + dependencies: + '@fxts/core': + specifier: 'catalog:' + version: 1.20.0 + '@inquirer/prompts': + specifier: ^7.8.4 + version: 7.10.1(@types/node@22.19.1) + '@logtape/logtape': + specifier: 'catalog:' + version: 2.0.0 + '@optique/core': + specifier: ^0.9.0 + version: 0.9.0 + '@optique/run': + specifier: ^0.9.0 + version: 0.9.0 + chalk: + specifier: ^5.6.2 + version: 5.6.2 + es-toolkit: + specifier: 'catalog:' + version: 1.43.0 + inquirer-toggle: + specifier: ^1.0.1 + version: 1.0.1 + devDependencies: + '@types/node': + specifier: 'catalog:' + version: 22.19.1 + tsdown: + specifier: 'catalog:' + version: 0.12.9(typescript@5.9.3) + typescript: + specifier: 'catalog:' + version: 5.9.3 + packages/koa: dependencies: '@fedify/fedify': diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index cc1cd4cf9..1200fa819 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -9,6 +9,7 @@ packages: - packages/fedify - packages/fixture - packages/h3 +- packages/init - packages/hono - packages/koa - packages/lint From 319fc2aa33e3106e739363d1a24f7de90b9625d6 Mon Sep 17 00:00:00 2001 From: ChanHaeng Lee <2chanhaeng@gmail.com> Date: Sun, 8 Feb 2026 19:31:55 +0000 Subject: [PATCH 03/22] add execute init code --- packages/init/src/test/create.ts | 2 +- packages/init/src/test/execute.ts | 12 ++++++++++++ packages/init/src/test/mod.ts | 1 - 3 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 packages/init/src/test/execute.ts diff --git a/packages/init/src/test/create.ts b/packages/init/src/test/create.ts index 3b57a2428..44dd17a70 100644 --- a/packages/init/src/test/create.ts +++ b/packages/init/src/test/create.ts @@ -70,7 +70,7 @@ function* genInitCommand( yield "deno"; yield "run"; yield "-A"; - yield "src/mod.ts"; + yield "src/test/execute.ts"; yield "init"; yield testDir; yield "-w"; diff --git a/packages/init/src/test/execute.ts b/packages/init/src/test/execute.ts new file mode 100644 index 000000000..4607a039e --- /dev/null +++ b/packages/init/src/test/execute.ts @@ -0,0 +1,12 @@ +import { run } from "@optique/run"; +import runInit from "../action/mod.ts"; +import { initCommand } from "../command.ts"; + +export default async function executeInit() { + await runInit(run(initCommand, { + programName: "fedify-init", + help: "both", + })); +} + +await executeInit(); diff --git a/packages/init/src/test/mod.ts b/packages/init/src/test/mod.ts index 2ccae1dc1..319a625b3 100644 --- a/packages/init/src/test/mod.ts +++ b/packages/init/src/test/mod.ts @@ -1,4 +1,3 @@ -#!/usr/bin/env node import { run } from "@optique/run"; import { testInitCommand } from "../command.ts"; import runTestInit from "./action.ts"; From 22ef34ba85ccf5e609b6ec4c19886b064b2a26fc Mon Sep 17 00:00:00 2001 From: ChanHaeng Lee <2chanhaeng@gmail.com> Date: Mon, 9 Feb 2026 19:38:45 +0000 Subject: [PATCH 04/22] Check db before test init --- packages/init/src/const.ts | 1 + packages/init/src/json/db-to-check.json | 17 +++++++ packages/init/src/test/action.ts | 2 + packages/init/src/test/db.ts | 67 +++++++++++++++++++++++++ packages/init/src/test/types.ts | 3 ++ packages/init/src/test/utils.ts | 4 +- 6 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 packages/init/src/json/db-to-check.json create mode 100644 packages/init/src/test/db.ts diff --git a/packages/init/src/const.ts b/packages/init/src/const.ts index 2b43ffcb2..0cbfec5b9 100644 --- a/packages/init/src/const.ts +++ b/packages/init/src/const.ts @@ -8,3 +8,4 @@ export const WEB_FRAMEWORK = [ ] as const; export const MESSAGE_QUEUE = ["denokv", "redis", "postgres", "amqp"] as const; export const KV_STORE = ["denokv", "redis", "postgres"] as const; +export const DB_TO_CHECK = ["redis", "postgres", "amqp"] as const; diff --git a/packages/init/src/json/db-to-check.json b/packages/init/src/json/db-to-check.json new file mode 100644 index 000000000..46a64c36e --- /dev/null +++ b/packages/init/src/json/db-to-check.json @@ -0,0 +1,17 @@ +{ + "redis": { + "name": "Redis", + "defaultPort": 6379, + "documentation": "https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-redis/" + }, + "postgres": { + "name": "PostgreSQL", + "defaultPort": 5432, + "documentation": "https://www.postgresql.org/download/" + }, + "amqp": { + "name": "RabbitMQ", + "defaultPort": 5672, + "documentation": "https://www.rabbitmq.com/docs/download" + } +} diff --git a/packages/init/src/test/action.ts b/packages/init/src/test/action.ts index d22635b59..8b64f7d1f 100644 --- a/packages/init/src/test/action.ts +++ b/packages/init/src/test/action.ts @@ -1,6 +1,7 @@ import { pipe, tap, when } from "@fxts/core"; import { set } from "../utils.ts"; import type { TestInitCommand } from "../command.ts"; +import { checkRequiredDbs } from "./db.ts"; import { fillEmptyOptions } from "./fill.ts"; import runTests from "./run.ts"; import { @@ -17,6 +18,7 @@ const runTestInit = (options: TestInitCommand) => set("testDirPrefix", genTestDirPrefix), tap(emptyTestDir), fillEmptyOptions, + tap(checkRequiredDbs), tap(logTestDir), tap(when(isDryRun, runTests(true))), tap(when(isHydRun, runTests(false))), diff --git a/packages/init/src/test/db.ts b/packages/init/src/test/db.ts new file mode 100644 index 000000000..291901745 --- /dev/null +++ b/packages/init/src/test/db.ts @@ -0,0 +1,67 @@ +import { concat, filter, pipe, toArray, uniq } from "@fxts/core"; +import type { TestInitCommand } from "../command.ts"; +import { DB_TO_CHECK } from "../const.ts"; +import DB_INFO from "../json/db-to-check.json" with { type: "json" }; +import { printErrorMessage, printMessage } from "../utils.ts"; +import type { DbToCheckType, DefineAllOptions } from "./types.ts"; + +/** + * This function checks if a given port is open by attempting to fetch from + * localhost at that port. So, may give false positives if the service does not + * respond to HTTP requests. + * @param port The port number to check. + * @returns A promise that resolves to true if the port is open, else false. + */ +async function isPortOpen(port: number): Promise { + try { + await fetch(`http://localhost:${port}`, { + signal: AbortSignal.timeout(3000), + }); + return true; + } catch (error: unknown) { + if (error instanceof TypeError) { + const msg = error.message.toLowerCase(); + if (msg.includes("refused") || msg.includes("econnrefused")) { + return false; + } + return true; + } + if (error instanceof DOMException && error.name === "TimeoutError") { + return false; + } + return false; + } +} + +const getRequiredDbs = ( + { kvStore, messageQueue }: DefineAllOptions, +): DbToCheckType[] => + pipe( + kvStore, + concat(messageQueue), + uniq, + filter((db): db is DbToCheckType => + DB_TO_CHECK.includes(db as DbToCheckType) + ), + toArray, + ); + +export async function checkRequiredDbs( + options: DefineAllOptions, +): Promise { + const dbs = Array.from(getRequiredDbs(options)); + if (dbs.length === 0) return; + + printMessage`Checking required databases...`; + + for (const db of dbs) { + const info = DB_INFO[db]; + const port = String(info.defaultPort); + const running = await isPortOpen(info.defaultPort); + if (running) { + printMessage` ${info.name} is running on port ${port}.`; + } else { + printErrorMessage`${info.name} is not running on port ${port}. Tests requiring ${info.name} may fail. Install: ${info.documentation}`; + } + } +} diff --git a/packages/init/src/test/types.ts b/packages/init/src/test/types.ts index 4bba63a9f..9b2e61af9 100644 --- a/packages/init/src/test/types.ts +++ b/packages/init/src/test/types.ts @@ -1,4 +1,5 @@ import type { TestInitCommand } from "../command.ts"; +import type { DB_TO_CHECK } from "../const.ts"; export interface InitTestData extends DefineAllOptions { runId: string; @@ -25,3 +26,5 @@ export type DefineAllOptions = & Omit & { [K in MultipleOption]: (TestInitCommand[K][number] & string)[] } & { [R in RunMode]: boolean }; + +export type DbToCheckType = (typeof DB_TO_CHECK)[number]; diff --git a/packages/init/src/test/utils.ts b/packages/init/src/test/utils.ts index 47ce93676..b7ed69b1d 100644 --- a/packages/init/src/test/utils.ts +++ b/packages/init/src/test/utils.ts @@ -1,4 +1,4 @@ -import { rmdir } from "node:fs/promises"; +import { rm } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { printMessage } from "../utils.ts"; @@ -12,7 +12,7 @@ export const genTestDirPrefix = ({ runId }: T) => export const emptyTestDir = < T extends { testDirPrefix: string }, >({ testDirPrefix }: T) => - rmdir(testDirPrefix, { recursive: true }).catch(() => {}); + rm(testDirPrefix, { recursive: true }).catch(() => {}); export const logTestDir = < T extends { runId: string; testDirPrefix: string }, From 8f735e88a5bc42d8ef0bf699921f468fbfa4dcd5 Mon Sep 17 00:00:00 2001 From: ChanHaeng Lee <2chanhaeng@gmail.com> Date: Tue, 10 Feb 2026 11:47:48 +0000 Subject: [PATCH 05/22] Add `@fedify/lint` when `fedify init` --- packages/init/src/action/configs.ts | 5 +- packages/init/src/ask/pm.ts | 22 +++++- packages/init/src/json/biome.json | 5 +- packages/init/src/lib.ts | 18 +---- .../templates/defaults/eslint.config.ts.tpl | 3 + packages/init/src/webframeworks.ts | 73 ++++++++++++++++--- 6 files changed, 92 insertions(+), 34 deletions(-) create mode 100644 packages/init/src/templates/defaults/eslint.config.ts.tpl diff --git a/packages/init/src/action/configs.ts b/packages/init/src/action/configs.ts index e9ebbd2cd..2d58b87bd 100644 --- a/packages/init/src/action/configs.ts +++ b/packages/init/src/action/configs.ts @@ -10,7 +10,6 @@ import { import { uniq } from "es-toolkit"; import { realpathSync } from "node:fs"; import { join as joinPath, relative } from "node:path"; -import { merge } from "../utils.ts"; import biome from "../json/biome.json" with { type: "json" }; import vscodeSettingsForDeno from "../json/vscode-settings-for-deno.json" with { type: "json", @@ -19,6 +18,7 @@ import vscodeSettings from "../json/vscode-settings.json" with { type: "json", }; import type { InitCommandData } from "../types.ts"; +import { merge } from "../utils.ts"; import { PACKAGES_PATH } from "./const.ts"; import { getDependencies, getDevDependencies, joinDepsReg } from "./deps.ts"; @@ -38,6 +38,7 @@ export const loadDenoConfig = ( unstable: getUnstable(data), nodeModulesDir: "auto", imports: joinDepsReg("deno")(getDependencies(data)), + lint: { plugins: ["jsr:@fedify/lint"] }, ...(data.testMode ? { links: getLinks(data) } : {}), }, }); @@ -116,7 +117,7 @@ export const devToolConfigs = { }, vscExt: { path: joinPath(".vscode", "extensions.json"), - data: { recommendations: ["biomejs.biome"] }, + data: { recommendations: ["biomejs.biome", "dbaeumer.vscode-eslint"] }, }, vscSet: { path: joinPath(".vscode", "settings.json"), diff --git a/packages/init/src/ask/pm.ts b/packages/init/src/ask/pm.ts index 1f9e1c91f..b4c309bd5 100644 --- a/packages/init/src/ask/pm.ts +++ b/packages/init/src/ask/pm.ts @@ -1,8 +1,16 @@ +import { pipe, when } from "@fxts/core"; import { select } from "@inquirer/prompts"; import { message } from "@optique/core/message"; import { print } from "@optique/run"; import { PACKAGE_MANAGER } from "../const.ts"; -import { getInstallUrl, getLabel, isPackageManagerAvailable } from "../lib.ts"; +import { + getInstallUrl, + isPackageManagerAvailable, + kvStores, + messageQueues, + packageManagers, + runtimes, +} from "../lib.ts"; import type { PackageManager, WebFramework } from "../types.ts"; import webFrameworks from "../webframeworks.ts"; @@ -56,3 +64,15 @@ const noticeInstallUrl = (pm: PackageManager) => { print(message` You can install it from following link: ${url}`); print(message` or choose another package manager:`); }; + +const getLabel = (name: string) => + pipe( + name, + whenHasLabel(webFrameworks), + whenHasLabel(packageManagers), + whenHasLabel(messageQueues), + whenHasLabel(kvStores), + whenHasLabel(runtimes), + ); +const whenHasLabel = >(desc: T) => + when((name: string) => name in desc, (name) => desc[name as keyof T].label); diff --git a/packages/init/src/json/biome.json b/packages/init/src/json/biome.json index 5ac9bb449..902345891 100644 --- a/packages/init/src/json/biome.json +++ b/packages/init/src/json/biome.json @@ -9,9 +9,6 @@ "indentWidth": 2 }, "linter": { - "enabled": true, - "rules": { - "recommended": true - } + "enabled": false } } diff --git a/packages/init/src/lib.ts b/packages/init/src/lib.ts index 48c8e5050..e8d43a806 100644 --- a/packages/init/src/lib.ts +++ b/packages/init/src/lib.ts @@ -7,7 +7,6 @@ import { negate, pipe, throwIf, - when, } from "@fxts/core"; import { getLogger } from "@logtape/logtape"; import type { Message } from "@optique/core"; @@ -30,7 +29,6 @@ import type { Runtimes, } from "./types.ts"; import { isNotFoundError, runSubCommand } from "./utils.ts"; -import webFrameworks from "./webframeworks.ts"; export const PACKAGE_VERSION = metadata.version; export const logger = getLogger(["fedify", "cli", "init"]); @@ -60,20 +58,8 @@ const convertPattern = ( ), fromEntries, ) as Record & { outputPattern: RegExp }>; -const packageManagers = convertPattern(pm) as PackageManagers; -const runtimes = convertPattern(rt) as Runtimes; - -export const getLabel = (name: string) => - pipe( - name, - whenHasLabel(webFrameworks), - whenHasLabel(packageManagers), - whenHasLabel(messageQueues), - whenHasLabel(kvStores), - whenHasLabel(runtimes), - ); -const whenHasLabel = >(desc: T) => - when((name: string) => name in desc, (name) => desc[name as keyof T].label); +export const packageManagers = convertPattern(pm) as PackageManagers; +export const runtimes = convertPattern(rt) as Runtimes; export const getInstallUrl = (pm: PackageManager) => packageManagers[pm].installUrl; diff --git a/packages/init/src/templates/defaults/eslint.config.ts.tpl b/packages/init/src/templates/defaults/eslint.config.ts.tpl new file mode 100644 index 000000000..9ac90a302 --- /dev/null +++ b/packages/init/src/templates/defaults/eslint.config.ts.tpl @@ -0,0 +1,3 @@ +import fedifyLint from "@fedify/lint"; + +export default fedifyLint; diff --git a/packages/init/src/webframeworks.ts b/packages/init/src/webframeworks.ts index 457c8cdc2..612a232de 100644 --- a/packages/init/src/webframeworks.ts +++ b/packages/init/src/webframeworks.ts @@ -18,6 +18,7 @@ const webFrameworks: WebFrameworks = { init: ({ projectName, packageManager: pm }) => ({ dependencies: pm === "deno" ? { + ...defaultDenoDependencies, "@std/dotenv": "^0.225.2", "@hono/hono": "^4.5.0", "@hongminhee/x-forwarded-fetch": "^0.2.0", @@ -37,7 +38,10 @@ const webFrameworks: WebFrameworks = { "x-forwarded-fetch": "^0.2.0", "@fedify/hono": PACKAGE_VERSION, }, - devDependencies: pm === "bun" ? { "@types/bun": "^1.1.6" } : {}, + devDependencies: { + ...defaultDevDependencies, + ...(pm === "bun" ? { "@types/bun": "^1.1.6" } : {}), + }, federationFile: "src/federation.ts", loggingFile: "src/logging.ts", files: { @@ -50,6 +54,9 @@ const webFrameworks: WebFrameworks = { "src/index.ts": readTemplate( `hono/index/${packageManagerToRuntime(pm)}.ts`, ), + ...(pm !== "deno" + ? { "eslint.config.ts": readTemplate("defaults/eslint.config.ts") } + : {}), }, compilerOptions: pm === "deno" ? undefined : { "lib": ["ESNext", "DOM"], @@ -74,6 +81,7 @@ const webFrameworks: WebFrameworks = { : pm === "bun" ? "bun run ./src/index.ts" : "dotenvx run -- node --import tsx ./src/index.ts", + ...(pm !== "deno" ? { "lint": "eslint ." } : {}), }, instruction: getInstruction(pm, 8000), }), @@ -85,6 +93,7 @@ const webFrameworks: WebFrameworks = { init: ({ projectName, packageManager: pm }) => ({ dependencies: pm === "deno" ? { + ...defaultDenoDependencies, elysia: "npm:elysia@^1.3.6", "@fedify/elysia": PACKAGE_VERSION, } @@ -104,21 +113,23 @@ const webFrameworks: WebFrameworks = { } : {}), }, - devDependencies: pm === "bun" - ? { "@types/bun": "^1.2.19" } - : pm === "deno" - ? {} - : { + devDependencies: { + ...(pm === "bun" ? { "@types/bun": "^1.2.19" } : { tsx: "^4.21.0", "@types/node": "^25.0.3", typescript: "^5.9.3", - }, + }), + ...defaultDevDependencies, + }, federationFile: "src/federation.ts", loggingFile: "src/logging.ts", files: { "src/index.ts": readTemplate( `elysia/index/${packageManagerToRuntime(pm)}.ts`, ).replace(/\/\* logger \*\//, projectName), + ...(pm !== "deno" + ? { "eslint.config.ts": readTemplate("defaults/eslint.config.ts") } + : {}), }, compilerOptions: pm === "deno" || pm === "bun" ? undefined : { "lib": ["ESNext", "DOM"], @@ -144,6 +155,7 @@ const webFrameworks: WebFrameworks = { "build": "tsc src/index.ts --outDir dist", "start": "NODE_ENV=production node dist/index.js", }), + ...(pm !== "deno" ? { "lint": "eslint ." } : {}), }, instruction: getInstruction(pm, 3000), }), @@ -159,10 +171,12 @@ const webFrameworks: WebFrameworks = { ...(pm !== "deno" && pm !== "bun" ? { "@dotenvx/dotenvx": "^1.14.1", tsx: "^4.17.0" } : {}), + ...(pm === "deno" ? defaultDenoDependencies : {}), }, devDependencies: { "@types/express": "^4.17.21", ...(pm === "bun" ? { "@types/bun": "^1.1.6" } : {}), + ...defaultDevDependencies, }, federationFile: "src/federation.ts", loggingFile: "src/logging.ts", @@ -170,6 +184,9 @@ const webFrameworks: WebFrameworks = { "src/app.ts": readTemplate("express/app.ts") .replace(/\/\* logger \*\//, projectName), "src/index.ts": readTemplate("express/index.ts"), + ...(pm !== "deno" + ? { "eslint.config.ts": readTemplate("defaults/eslint.config.ts") } + : {}), }, compilerOptions: pm === "deno" ? undefined : { "lib": ["ESNext", "DOM"], @@ -192,6 +209,7 @@ const webFrameworks: WebFrameworks = { : pm === "deno" ? "deno run --allow-net --allow-env --allow-sys ./src/index.ts" : "dotenvx run -- node --import tsx ./src/index.ts", + ...(pm !== "deno" ? { "lint": "eslint ." } : {}), }, instruction: getInstruction(pm, 8000), }), @@ -202,7 +220,11 @@ const webFrameworks: WebFrameworks = { packageManagers: PACKAGE_MANAGER, init: ({ packageManager: pm, testMode }) => ({ command: getNitroInitCommand(pm), - dependencies: { "@fedify/h3": PACKAGE_VERSION }, + dependencies: { + "@fedify/h3": PACKAGE_VERSION, + ...(pm === "deno" ? defaultDenoDependencies : {}), + }, + devDependencies: defaultDevDependencies, federationFile: "server/federation.ts", loggingFile: "server/logging.ts", files: { @@ -214,6 +236,12 @@ const webFrameworks: WebFrameworks = { ...( testMode ? { ".env": readTemplate("nitro/.env.test") } : {} ), + ...(pm !== "deno" + ? { "eslint.config.ts": readTemplate("defaults/eslint.config.ts") } + : {}), + }, + tasks: { + ...(pm !== "deno" ? { "lint": "eslint ." } : {}), }, instruction: getInstruction(pm, 3000), }), @@ -225,14 +253,37 @@ const webFrameworks: WebFrameworks = { init: ({ packageManager: pm }) => ({ label: "Next.js", command: getNextInitCommand(pm), - dependencies: { "@fedify/next": PACKAGE_VERSION }, - devDependencies: { "@types/node": "^20.11.2" }, + dependencies: { + "@fedify/next": PACKAGE_VERSION, + ...(pm === "deno" ? defaultDenoDependencies : {}), + }, + devDependencies: { + "@types/node": "^20.11.2", + ...defaultDevDependencies, + }, federationFile: "federation/index.ts", loggingFile: "logging.ts", - files: { "middleware.ts": readTemplate("next/middleware.ts") }, + files: { + "middleware.ts": readTemplate("next/middleware.ts"), + ...(pm !== "deno" + ? { "eslint.config.ts": readTemplate("defaults/eslint.config.ts") } + : {}), + }, + tasks: { + ...(pm !== "deno" ? { "lint": "eslint ." } : {}), + }, instruction: getInstruction(pm, 3000), }), defaultPort: 3000, }, } as const; export default webFrameworks; + +const defaultDevDependencies = { + "eslint": "^9.0.0", + "@fedify/lint": PACKAGE_VERSION, +}; + +const defaultDenoDependencies = { + "@fedify/lint": PACKAGE_VERSION, +}; From 5e233bd5e1d49d231f85c00e2f51eb63a9917abc Mon Sep 17 00:00:00 2001 From: ChanHaeng Lee <2chanhaeng@gmail.com> Date: Tue, 10 Feb 2026 13:25:34 +0000 Subject: [PATCH 06/22] Create `create-fedify-app` --- deno.json | 1 + deno.lock | 13 ++++ packages/create-fedify-app/deno.json | 27 ++++++++ packages/create-fedify-app/package.json | 62 +++++++++++++++++ packages/create-fedify-app/src/mod.ts | 18 +++++ packages/create-fedify-app/tsdown.config.ts | 8 +++ packages/init/src/command.ts | 41 +++++++----- packages/init/src/globals.ts | 7 -- packages/init/src/mod.ts | 2 +- pnpm-lock.yaml | 74 ++++++++++++--------- pnpm-workspace.yaml | 1 + 11 files changed, 198 insertions(+), 56 deletions(-) create mode 100644 packages/create-fedify-app/deno.json create mode 100644 packages/create-fedify-app/package.json create mode 100644 packages/create-fedify-app/src/mod.ts create mode 100644 packages/create-fedify-app/tsdown.config.ts delete mode 100644 packages/init/src/globals.ts diff --git a/deno.json b/deno.json index 2cc42567f..d7f5bfe74 100644 --- a/deno.json +++ b/deno.json @@ -4,6 +4,7 @@ "./packages/cfworkers", "./packages/cli", "./packages/debugger", + "./packages/create-fedify-app", "./packages/denokv", "./packages/express", "./packages/fastify", diff --git a/deno.lock b/deno.lock index 7345964d1..d450807bd 100644 --- a/deno.lock +++ b/deno.lock @@ -5998,6 +5998,19 @@ ] } }, + "packages/create-fedify-app": { + "dependencies": [ + "jsr:@fedify/init@2", + "jsr:@optique/core@0.9", + "jsr:@optique/run@0.9" + ], + "packageJson": { + "dependencies": [ + "npm:@optique/core@0.9", + "npm:@optique/run@0.9" + ] + } + }, "packages/denokv": { "dependencies": [ "jsr:@std/assert@^1.0.13", diff --git a/packages/create-fedify-app/deno.json b/packages/create-fedify-app/deno.json new file mode 100644 index 000000000..d2e908256 --- /dev/null +++ b/packages/create-fedify-app/deno.json @@ -0,0 +1,27 @@ +{ + "name": "create-fedify-app", + "version": "2.0.0", + "license": "MIT", + "exports": "./src/mod.ts", + "imports": { + "@fedify/init": "jsr:@fedify/init@^2.0.0", + "@optique/core": "jsr:@optique/core@^0.9.0", + "@optique/run": "jsr:@optique/run@^0.9.0" + }, + "exclude": [ + "dist/", + "node_modules/" + ], + "publish": { + "exclude": [ + "**/*.test.ts" + ] + }, + "lint": { + "rules": { + "exclude": [ + "no-slow-types" + ] + } + } +} diff --git a/packages/create-fedify-app/package.json b/packages/create-fedify-app/package.json new file mode 100644 index 000000000..820c4fda3 --- /dev/null +++ b/packages/create-fedify-app/package.json @@ -0,0 +1,62 @@ +{ + "name": "create-fedify-app", + "version": "2.0.0", + "description": "Create a new Fedify application", + "keywords": [ + "fedify", + "activitypub", + "cli", + "init", + "fediverse" + ], + "homepage": "https://fedify.dev/", + "bugs": { + "url": "https://github.com/fedify-dev/fedify/issues" + }, + "license": "MIT", + "author": { + "name": "Hong Minhee", + "email": "hong@minhee.org", + "url": "https://hongminhee.org/" + }, + "funding": [ + "https://opencollective.com/fedify", + "https://github.com/sponsors/dahlia" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/fedify-dev/fedify.git", + "directory": "packages/create-fedify-app" + }, + "type": "module", + "bin": { + "create-fedify-app": "./dist/mod.js" + }, + "exports": "./dist/mod.js", + "files": [ + "dist/", + "package.json", + "README.md" + ], + "engines": { + "node": ">=20.0.0", + "bun": ">=1.2.0" + }, + "dependencies": { + "@fedify/init": "workspace:*", + "@optique/core": "^0.9.0", + "@optique/run": "^0.9.0", + "es-toolkit": "catalog:" + }, + "devDependencies": { + "@types/node": "catalog:", + "tsdown": "catalog:", + "typescript": "catalog:" + }, + "scripts": { + "build:self": "tsdown", + "build": "pnpm --filter create-fedify-app... run build:self", + "prepack": "pnpm build", + "prepublish": "pnpm build" + } +} diff --git a/packages/create-fedify-app/src/mod.ts b/packages/create-fedify-app/src/mod.ts new file mode 100644 index 000000000..29a20255d --- /dev/null +++ b/packages/create-fedify-app/src/mod.ts @@ -0,0 +1,18 @@ +import { initOptions, runInit } from "@fedify/init"; +import { message, optionNames } from "@optique/core"; +import { run } from "@optique/run"; +import { merge } from "es-toolkit"; + +const result = run(initOptions, { + programName: "create-fedify-app", + description: message`Create a new Fedify project. + +Unless you specify all options (${optionNames(["-w", "--web-framework"])}, ${ + optionNames(["-p", "--package-manager"]) + }, ${optionNames(["-k", "--kv-store"])}, and ${ + optionNames(["-m", "--message-queue"]) + }), it will prompt you to select the options interactively.`, + help: "both", +}); + +await runInit(merge(result, { command: "init" } as const)); diff --git a/packages/create-fedify-app/tsdown.config.ts b/packages/create-fedify-app/tsdown.config.ts new file mode 100644 index 000000000..bc0724fe7 --- /dev/null +++ b/packages/create-fedify-app/tsdown.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from "tsdown"; + +export default defineConfig({ + entry: ["src/mod.ts"], + platform: "node", + unbundle: true, + external: [/^node:/], +}); diff --git a/packages/init/src/command.ts b/packages/init/src/command.ts index f8648fff2..cb6d80530 100644 --- a/packages/init/src/command.ts +++ b/packages/init/src/command.ts @@ -20,7 +20,6 @@ import { PACKAGE_MANAGER, WEB_FRAMEWORK, } from "./const.ts"; -import { debugOption } from "./globals.ts"; const webFramework = optional(option( "-w", @@ -57,23 +56,33 @@ const messageQueue = optional(option( }, )); +const debugOption = object("Global options", { + debug: option("-d", "--debug", { + description: message`Enable debug mode.`, + }), +}); + +export const initOptions = object("Initialization options", { + dir: optional(argument(path({ metavar: "DIR" }), { + description: + message`The project directory to initialize. If a specified directory does not exist, it will be created.`, + })), + webFramework, + packageManager, + kvStore, + messageQueue, + dryRun: option("--dry-run", { + description: message`Perform a trial run with no changes made.`, + }), + debugOption, +}); + export const initCommand = command( "init", - object("Initialization options", { - command: constant("init"), - dir: optional(argument(path({ metavar: "DIR" }), { - description: - message`The project directory to initialize. If a specified directory does not exist, it will be created.`, - })), - webFramework, - packageManager, - kvStore, - messageQueue, - dryRun: option("--dry-run", { - description: message`Perform a trial run with no changes made.`, - }), - debugOption, - }), + merge( + initOptions, + object({ command: constant("init") }), + ), { brief: message`Initialize a new Fedify project directory.`, description: message`Initialize a new Fedify project directory. diff --git a/packages/init/src/globals.ts b/packages/init/src/globals.ts deleted file mode 100644 index 79c26d39b..000000000 --- a/packages/init/src/globals.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { message, object, option } from "@optique/core"; - -export const debugOption = object("Global options", { - debug: option("-d", "--debug", { - description: message`Enable debug mode.`, - }), -}); diff --git a/packages/init/src/mod.ts b/packages/init/src/mod.ts index a7d28e3bb..5e8d6f7ee 100644 --- a/packages/init/src/mod.ts +++ b/packages/init/src/mod.ts @@ -1,2 +1,2 @@ export { default as runInit } from "./action/mod.ts"; -export { initCommand } from "./command.ts"; +export { initCommand, initOptions } from "./command.ts"; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2f0658335..56547a3d1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -854,6 +854,31 @@ importers: specifier: 'catalog:' version: 5.9.3 + packages/create-fedify-app: + dependencies: + '@fedify/init': + specifier: workspace:* + version: link:../init + '@optique/core': + specifier: ^0.9.0 + version: 0.9.0 + '@optique/run': + specifier: ^0.9.0 + version: 0.9.0 + es-toolkit: + specifier: 'catalog:' + version: 1.43.0 + devDependencies: + '@types/node': + specifier: 'catalog:' + version: 22.19.1 + tsdown: + specifier: 'catalog:' + version: 0.12.9(typescript@5.9.3) + typescript: + specifier: 'catalog:' + version: 5.9.3 + packages/debugger: dependencies: '@fedify/fedify': @@ -13830,8 +13855,8 @@ snapshots: '@typescript-eslint/parser': 8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.3) eslint: 9.32.0(jiti@2.5.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.3))(eslint@9.32.0(jiti@2.5.1)))(eslint@9.32.0(jiti@2.5.1)) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.3))(eslint@9.32.0(jiti@2.5.1)))(eslint@9.32.0(jiti@2.5.1)))(eslint@9.32.0(jiti@2.5.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.32.0(jiti@2.5.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.32.0(jiti@2.5.1)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.32.0(jiti@2.5.1)) eslint-plugin-react: 7.37.5(eslint@9.32.0(jiti@2.5.1)) eslint-plugin-react-hooks: 5.2.0(eslint@9.32.0(jiti@2.5.1)) @@ -13850,8 +13875,8 @@ snapshots: '@typescript-eslint/parser': 8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2) eslint: 9.32.0(jiti@2.5.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.32.0(jiti@2.5.1)))(eslint@9.32.0(jiti@2.5.1)) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.32.0(jiti@2.5.1)))(eslint@9.32.0(jiti@2.5.1)))(eslint@9.32.0(jiti@2.5.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.32.0(jiti@2.5.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.32.0(jiti@2.5.1)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.32.0(jiti@2.5.1)) eslint-plugin-react: 7.37.5(eslint@9.32.0(jiti@2.5.1)) eslint-plugin-react-hooks: 5.2.0(eslint@9.32.0(jiti@2.5.1)) @@ -13874,22 +13899,22 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.32.0(jiti@2.5.1)))(eslint@9.32.0(jiti@2.5.1)): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@8.57.1): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.1 - eslint: 9.32.0(jiti@2.5.1) + eslint: 8.57.1 get-tsconfig: 4.10.1 is-bun-module: 2.0.0 stable-hash: 0.0.5 tinyglobby: 0.2.15 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.32.0(jiti@2.5.1)))(eslint@9.32.0(jiti@2.5.1)))(eslint@9.32.0(jiti@2.5.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.3))(eslint@9.32.0(jiti@2.5.1)))(eslint@9.32.0(jiti@2.5.1)): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.32.0(jiti@2.5.1)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.1 @@ -13900,22 +13925,7 @@ snapshots: tinyglobby: 0.2.15 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.3))(eslint@9.32.0(jiti@2.5.1)))(eslint@9.32.0(jiti@2.5.1)))(eslint@9.32.0(jiti@2.5.1)) - transitivePeerDependencies: - - supports-color - - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@8.57.1): - dependencies: - '@nolyfill/is-core-module': 1.0.39 - debug: 4.4.1 - eslint: 8.57.1 - get-tsconfig: 4.10.1 - is-bun-module: 2.0.0 - stable-hash: 0.0.5 - tinyglobby: 0.2.15 - unrs-resolver: 1.11.1 - optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.32.0(jiti@2.5.1)) transitivePeerDependencies: - supports-color @@ -13930,25 +13940,25 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.32.0(jiti@2.5.1)))(eslint@9.32.0(jiti@2.5.1)))(eslint@9.32.0(jiti@2.5.1)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.32.0(jiti@2.5.1)): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2) eslint: 9.32.0(jiti@2.5.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.32.0(jiti@2.5.1)))(eslint@9.32.0(jiti@2.5.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.32.0(jiti@2.5.1)) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.3))(eslint@9.32.0(jiti@2.5.1)))(eslint@9.32.0(jiti@2.5.1)))(eslint@9.32.0(jiti@2.5.1)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.32.0(jiti@2.5.1)): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.3) eslint: 9.32.0(jiti@2.5.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.3))(eslint@9.32.0(jiti@2.5.1)))(eslint@9.32.0(jiti@2.5.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.32.0(jiti@2.5.1)) transitivePeerDependencies: - supports-color @@ -13981,7 +13991,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.32.0(jiti@2.5.1)))(eslint@9.32.0(jiti@2.5.1)))(eslint@9.32.0(jiti@2.5.1)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.32.0(jiti@2.5.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -13992,7 +14002,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.32.0(jiti@2.5.1) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.32.0(jiti@2.5.1)))(eslint@9.32.0(jiti@2.5.1)))(eslint@9.32.0(jiti@2.5.1)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.32.0(jiti@2.5.1)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -14010,7 +14020,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.3))(eslint@9.32.0(jiti@2.5.1)))(eslint@9.32.0(jiti@2.5.1)))(eslint@9.32.0(jiti@2.5.1)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.32.0(jiti@2.5.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -14021,7 +14031,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.32.0(jiti@2.5.1) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.3))(eslint@9.32.0(jiti@2.5.1)))(eslint@9.32.0(jiti@2.5.1)))(eslint@9.32.0(jiti@2.5.1)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.32.0(jiti@2.5.1)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 1200fa819..41b4da51a 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -3,6 +3,7 @@ packages: - packages/cfworkers - packages/cli - packages/debugger +- packages/create-fedify-app - packages/elysia - packages/express - packages/fastify From e3495e1ad43b3bb02afc8e9bfd8148afaec30ce5 Mon Sep 17 00:00:00 2001 From: ChanHaeng Lee <2chanhaeng@gmail.com> Date: Tue, 10 Feb 2026 14:20:58 +0000 Subject: [PATCH 07/22] Add docs about changes --- AGENTS.md | 4 +++ CHANGES.md | 28 +++++++++++++++++ CONTRIBUTING.md | 5 +++ docs/cli.md | 42 +++++++++++++++++++++++++ docs/install.md | 28 +++++++++++++++++ packages/create-fedify-app/README.md | 46 ++++++++++++++++++++++++++++ packages/fedify/README.md | 5 +++ 7 files changed, 158 insertions(+) create mode 100644 packages/create-fedify-app/README.md diff --git a/AGENTS.md b/AGENTS.md index 2a1261288..22eac9c7a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -87,6 +87,10 @@ for the complete package list. ### Other key directories + - *packages/init/*: Project initializer (`@fedify/init`) for Fedify. + Separated from `@fedify/cli` to enable standalone use. + - *packages/create-fedify-app/*: Standalone CLI (`create-fedify-app`) + for creating new Fedify projects via `npx create-fedify-app`. - *docs/*: Documentation built with VitePress (see *docs/README.md*) - *examples/*: Example projects diff --git a/CHANGES.md b/CHANGES.md index 25e2be18a..5e58a780f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -476,6 +476,34 @@ To be released. - Requires a Workers KV namespace for lock management. - Due to Workers KV eventual consistency, ordering is best-effort. +### @fedify/init + + - Created project initializer as the *@fedify/init* package. Separated + the `fedify init` functionality from *@fedify/cli* into a standalone + package to improve modularity and enable reuse by other tools such as + `create-fedify-app`. [[#482] by Chanhaeng Lee] + + - Added `runInit()` function as the main initialization action handler. + - Added `initCommand` and `initOptions` for CLI integration. + - Added `testInitCommand` for comprehensive testing of all init + combinations. + +[#482]: https://github.com/fedify-dev/fedify/issues/482 + +### `create-fedify-app` + + - Created standalone project scaffolding CLI as the *create-fedify-app* + package. This enables creating new Fedify projects without installing + the full `@fedify/cli` toolchain. [[#351] by Chanhaeng Lee] + + - Supports `npx create-fedify-app`, `pnpm create fedify-app`, + `yarn create fedify-app`, and `bunx create-fedify-app`. + - Uses `@fedify/init` internally for all initialization logic. + - Supports the same interactive prompts and CLI options as + `fedify init`. + +[#351]: https://github.com/fedify-dev/fedify/issues/351 + Version 1.10.3 -------------- diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9d643c4a5..aa47c1e8f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -332,6 +332,8 @@ The repository is organized as a monorepo with the following packages: - *packages/cli/*: The Fedify CLI (@fedify/cli). Built with [Deno] and tested with Deno, Node.js, and [Bun]. Uses `deno compile` to create standalone executables. + - *packages/create-fedify-app/*: Standalone CLI (create-fedify-app) for + creating new Fedify projects. Wraps @fedify/init. - *packages/amqp/*: AMQP/RabbitMQ driver (@fedify/amqp) for Fedify. - *packages/cfworkers/*: Cloudflare Workers integration (@fedify/cfworkers) for Fedify. @@ -344,6 +346,9 @@ The repository is organized as a monorepo with the following packages: - *packages/fresh/*: Fresh integration (@fedify/fresh) for Fedify. - *packages/h3/*: h3 framework integration (@fedify/h3) for Fedify. - *packages/hono/*: Hono integration (@fedify/hono) for Fedify. + - *packages/init/*: Project initializer (@fedify/init) for Fedify. + Separated from @fedify/cli to enable standalone use and + `npx create-fedify-app`. - *packages/koa/*: Koa integration (@fedify/koa) for Fedify. - *packages/lint/*: Linting utilities (@fedify/lint) for Fedify. - *packages/postgres/*: PostgreSQL drivers (@fedify/postgres) for Fedify. diff --git a/docs/cli.md b/docs/cli.md index 201b0c435..9ba5f0f74 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -126,6 +126,12 @@ project. It will ask you a few questions to set up the project: - Message queue: In-memory, [Redis], [PostgreSQL], [AMQP] (e.g., [RabbitMQ]), or [Deno KV] (if Deno) +> [!TIP] +> Projects created with `fedify init` automatically include [`@fedify/lint`] +> for consistent code linting. Deno projects get a lint plugin configured in +> *deno.json*, while Node.js and Bun projects get an *eslint.config.ts* with +> `@fedify/lint`. + Alternatively, you can specify the options in the command line to skip some of interactive prompts: @@ -142,6 +148,7 @@ interactive prompts: [Deno KV]: https://deno.com/kv [AMQP]: https://www.amqp.org/ [RabbitMQ]: https://www.rabbitmq.com/ +[`@fedify/lint`]: /manual/lint ### `-r`/`--runtime`: JavaScript runtime @@ -227,6 +234,41 @@ This option works with all other initialization options, allowing you to preview different configurations before making a decision. +`create-fedify-app`: Creating a Fedify project without CLI +---------------------------------------------------------- + +*This command is available since Fedify 2.0.0.* + +If you prefer not to install the `@fedify/cli` toolchain globally, you can +use `create-fedify-app` to scaffold a new Fedify project directly: + +::: code-group + +~~~~ sh [npm] +npx create-fedify-app my-fedify-project +~~~~ + +~~~~ sh [pnpm] +pnpm create fedify-app my-fedify-project +~~~~ + +~~~~ sh [Yarn] +yarn create fedify-app my-fedify-project +~~~~ + +~~~~ sh [Bun] +bunx create-fedify-app my-fedify-project +~~~~ + +::: + +This is functionally equivalent to `fedify init` and supports the same +interactive prompts and command-line options (`-r`, `-p`, `-w`, `-k`, `-q`, +`--dry-run`). See the +[`fedify init`](#fedify-init-initializing-a-fedify-project) section above for +details on available options. + + `fedify lookup`: Looking up an ActivityPub object ------------------------------------------------- diff --git a/docs/install.md b/docs/install.md index 79f552474..86da497bb 100644 --- a/docs/install.md +++ b/docs/install.md @@ -65,6 +65,34 @@ in the *CLI toolchain* docs. [![The “fedify init” command demo](https://asciinema.org/a/671658.svg)](https://asciinema.org/a/671658) +### Alternative: Using `create-fedify-app` + +If you don't want to install the `fedify` CLI globally, you can use +`create-fedify-app` directly: + +::: code-group + +~~~~ sh [npm] +npx create-fedify-app your-project-dir +~~~~ + +~~~~ sh [pnpm] +pnpm create fedify-app your-project-dir +~~~~ + +~~~~ sh [Yarn] +yarn create fedify-app your-project-dir +~~~~ + +~~~~ sh [Bun] +bunx create-fedify-app your-project-dir +~~~~ + +::: + +This works the same way as `fedify init` and will guide you through the same +project setup wizard. + Manual installation ------------------- diff --git a/packages/create-fedify-app/README.md b/packages/create-fedify-app/README.md new file mode 100644 index 000000000..86f156318 --- /dev/null +++ b/packages/create-fedify-app/README.md @@ -0,0 +1,46 @@ +Create-Fedify-app: Create a new Fedify project +============================================== + +[![npm][npm badge]][npm] + +This package provides a standalone CLI tool for creating new [Fedify] projects. +It allows you to scaffold a new project without installing the full +[`@fedify/cli`] toolchain, powered by [`@fedify/init`] internally. + +[npm badge]: https://img.shields.io/npm/v/create-fedify-app?logo=npm +[npm]: https://www.npmjs.com/package/create-fedify-app +[Fedify]: https://fedify.dev/ +[`@fedify/cli`]: https://jsr.io/@fedify/cli +[`@fedify/init`]: https://jsr.io/@fedify/init + + +Usage +----- + +~~~~ sh +npx create-fedify-app my-project +pnpm create fedify-app my-project +yarn create fedify-app my-project +bunx create-fedify-app my-project +~~~~ + + +Supported options +----------------- + +The tool supports the same project configurations as `fedify init`: + + - **Web frameworks**: [Hono], [Nitro], [Next.js], [Elysia], [Express] + - **Package managers**: Deno, pnpm, Bun, Yarn, npm + - **Key-value stores**: Deno KV, Redis, PostgreSQL + - **Message queues**: Deno KV, Redis, PostgreSQL, AMQP + +See the [`@fedify/init`] package or the [Fedify CLI docs] for details on +available options (`-r`, `-p`, `-w`, `-k`, `-q`, `--dry-run`). + +[Hono]: https://hono.dev/ +[Nitro]: https://nitro.build/ +[Next.js]: https://nextjs.org/ +[Elysia]: https://elysiajs.com/ +[Express]: https://expressjs.com/ +[Fedify CLI docs]: https://fedify.dev/cli diff --git a/packages/fedify/README.md b/packages/fedify/README.md index fcbacb8f1..e6eac9136 100644 --- a/packages/fedify/README.md +++ b/packages/fedify/README.md @@ -98,6 +98,7 @@ Here is the list of packages: | ------------------------------------------------- | -------------------------------- | -------------------------------- | ---------------------------------------- | | [@fedify/fedify](/packages/fedify/) | [JSR] | [npm] | The core framework of Fedify | | [@fedify/cli](/packages/cli/) | [JSR][jsr:@fedify/cli] | [npm][npm:@fedify/cli] | CLI toolchain for testing and debugging | +| [create-fedify-app](/packages/create-fedify-app/) | | [npm][npm:create-fedify-app] | Create a new Fedify project (npx) | | [@fedify/amqp](/packages/amqp/) | [JSR][jsr:@fedify/amqp] | [npm][npm:@fedify/amqp] | AMQP/RabbitMQ driver | | [@fedify/cfworkers](/packages/cfworkers/) | [JSR][jsr:@fedify/cfworkers] | [npm][npm:@fedify/cfworkers] | Cloudflare Workers integration | | [@fedify/debugger](/packages/debugger/) | [JSR][jsr:@fedify/debugger] | [npm][npm:@fedify/debugger] | Embedded ActivityPub debug dashboard | @@ -108,6 +109,7 @@ Here is the list of packages: | [@fedify/fresh](/packages/fresh/) | [JSR][jsr:@fedify/fresh] | | Fresh integration | | [@fedify/h3](/packages/h3/) | [JSR][jsr:@fedify/h3] | [npm][npm:@fedify/h3] | H3 integration | | [@fedify/hono](/packages/hono/) | [JSR][jsr:@fedify/hono] | [npm][npm:@fedify/hono] | Hono integration | +| [@fedify/init](/packages/init/) | [JSR][jsr:@fedify/init] | [npm][npm:@fedify/init] | Project initializer for Fedify | | [@fedify/koa](/packages/koa/) | [JSR][jsr:@fedify/koa] | [npm][npm:@fedify/koa] | Koa integration | | [@fedify/lint](/packages/lint/) | [JSR][jsr:@fedify/lint] | [npm][npm:@fedify/lint] | Linting utilities | | [@fedify/nestjs](/packages/nestjs/) | | [npm][npm:@fedify/nestjs] | NestJS integration | @@ -125,6 +127,7 @@ Here is the list of packages: [jsr:@fedify/cli]: https://jsr.io/@fedify/cli [npm:@fedify/cli]: https://www.npmjs.com/package/@fedify/cli +[npm:create-fedify-app]: https://www.npmjs.com/package/create-fedify-app [jsr:@fedify/amqp]: https://jsr.io/@fedify/amqp [npm:@fedify/amqp]: https://www.npmjs.com/package/@fedify/amqp [jsr:@fedify/cfworkers]: https://jsr.io/@fedify/cfworkers @@ -142,6 +145,8 @@ Here is the list of packages: [npm:@fedify/h3]: https://www.npmjs.com/package/@fedify/h3 [jsr:@fedify/hono]: https://jsr.io/@fedify/hono [npm:@fedify/hono]: https://www.npmjs.com/package/@fedify/hono +[jsr:@fedify/init]: https://jsr.io/@fedify/init +[npm:@fedify/init]: https://www.npmjs.com/package/@fedify/init [jsr:@fedify/koa]: https://jsr.io/@fedify/koa [npm:@fedify/koa]: https://www.npmjs.com/package/@fedify/koa [jsr:@fedify/lint]: https://jsr.io/@fedify/lint From 72f69a9f048cf262d5462ff46b4617eb88c1cce4 Mon Sep 17 00:00:00 2001 From: ChanHaeng Lee <2chanhaeng@gmail.com> Date: Tue, 10 Feb 2026 14:57:50 +0000 Subject: [PATCH 08/22] Remove `create-fedify-app` from Deno workspace --- deno.lock | 13 ------------- packages/create-fedify-app/deno.json | 27 --------------------------- 2 files changed, 40 deletions(-) delete mode 100644 packages/create-fedify-app/deno.json diff --git a/deno.lock b/deno.lock index d450807bd..7345964d1 100644 --- a/deno.lock +++ b/deno.lock @@ -5998,19 +5998,6 @@ ] } }, - "packages/create-fedify-app": { - "dependencies": [ - "jsr:@fedify/init@2", - "jsr:@optique/core@0.9", - "jsr:@optique/run@0.9" - ], - "packageJson": { - "dependencies": [ - "npm:@optique/core@0.9", - "npm:@optique/run@0.9" - ] - } - }, "packages/denokv": { "dependencies": [ "jsr:@std/assert@^1.0.13", diff --git a/packages/create-fedify-app/deno.json b/packages/create-fedify-app/deno.json deleted file mode 100644 index d2e908256..000000000 --- a/packages/create-fedify-app/deno.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "create-fedify-app", - "version": "2.0.0", - "license": "MIT", - "exports": "./src/mod.ts", - "imports": { - "@fedify/init": "jsr:@fedify/init@^2.0.0", - "@optique/core": "jsr:@optique/core@^0.9.0", - "@optique/run": "jsr:@optique/run@^0.9.0" - }, - "exclude": [ - "dist/", - "node_modules/" - ], - "publish": { - "exclude": [ - "**/*.test.ts" - ] - }, - "lint": { - "rules": { - "exclude": [ - "no-slow-types" - ] - } - } -} From e3d408d49bd561d8c7ff3ecd22f2df4d875d7fcc Mon Sep 17 00:00:00 2001 From: ChanHaeng Lee <2chanhaeng@gmail.com> Date: Tue, 10 Feb 2026 15:15:17 +0000 Subject: [PATCH 09/22] Add `--allow-slow-types` flag on `deno publish --dry-run` test --- .github/workflows/main.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 1401c622c..3c5a2925f 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -216,7 +216,7 @@ jobs: - run: '[[ "$(jq -r .version deno.json)" = "$(jq -r .version package.json)" ]]' working-directory: ${{ github.workspace }}/packages/fedify/ - run: mise run codegen - - run: deno publish --dry-run + - run: deno publish --dry-run --allow-slow-types - run: pnpm publish --recursive --dry-run --no-git-checks --ignore-scripts # =========================================================================== From eb05766ed5e1ca7128d0072c9ec317ba28b7586d Mon Sep 17 00:00:00 2001 From: ChanHaeng Lee <2chanhaeng@gmail.com> Date: Wed, 11 Feb 2026 10:53:35 +0000 Subject: [PATCH 10/22] Fix templates path --- packages/cli/scripts/pack.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/scripts/pack.ts b/packages/cli/scripts/pack.ts index b4284c9d4..f8ec1d2fe 100644 --- a/packages/cli/scripts/pack.ts +++ b/packages/cli/scripts/pack.ts @@ -25,7 +25,7 @@ async function compile(os: OS, arch: Arch, into: string): Promise { throw new Error(`Unsupported os/arch: ${os}/${arch}`); } await $`deno compile --allow-all --include=${ - join("src", "init", "templates") + join("..", "init", "src", "templates") } --node-modules-dir=none --target=${target} --output=${into} ${ join(dirname(import.meta.dirname!), "src", "mod.ts") }`; From 0394a9549c68de3611d0a90a0b2db086f7b1d16c Mon Sep 17 00:00:00 2001 From: ChanHaeng Lee <2chanhaeng@gmail.com> Date: Thu, 12 Feb 2026 11:43:32 +0000 Subject: [PATCH 11/22] Rename `create-fedify-app` to `@fedify/create` --- AGENTS.md | 4 +- CHANGES.md | 10 ++--- CONTRIBUTING.md | 4 +- docs/cli.md | 43 ++++--------------- docs/install.md | 12 +++--- .../{create-fedify-app => create}/README.md | 16 +++---- .../package.json | 8 ++-- .../{create-fedify-app => create}/src/mod.ts | 2 +- .../tsdown.config.ts | 0 packages/fedify/README.md | 4 +- pnpm-lock.yaml | 2 +- pnpm-workspace.yaml | 2 +- 12 files changed, 40 insertions(+), 67 deletions(-) rename packages/{create-fedify-app => create}/README.md (75%) rename packages/{create-fedify-app => create}/package.json (86%) rename packages/{create-fedify-app => create}/src/mod.ts (94%) rename packages/{create-fedify-app => create}/tsdown.config.ts (100%) diff --git a/AGENTS.md b/AGENTS.md index 22eac9c7a..c4a072c11 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -89,8 +89,8 @@ for the complete package list. - *packages/init/*: Project initializer (`@fedify/init`) for Fedify. Separated from `@fedify/cli` to enable standalone use. - - *packages/create-fedify-app/*: Standalone CLI (`create-fedify-app`) - for creating new Fedify projects via `npx create-fedify-app`. + - *packages/create/*: Standalone CLI (`@fedify/create`) + for creating new Fedify projects via `npm init @fedify`. - *docs/*: Documentation built with VitePress (see *docs/README.md*) - *examples/*: Example projects diff --git a/CHANGES.md b/CHANGES.md index 5e58a780f..08f56245a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -481,7 +481,7 @@ To be released. - Created project initializer as the *@fedify/init* package. Separated the `fedify init` functionality from *@fedify/cli* into a standalone package to improve modularity and enable reuse by other tools such as - `create-fedify-app`. [[#482] by Chanhaeng Lee] + `@fedify/create`. [[#482] by Chanhaeng Lee] - Added `runInit()` function as the main initialization action handler. - Added `initCommand` and `initOptions` for CLI integration. @@ -490,14 +490,14 @@ To be released. [#482]: https://github.com/fedify-dev/fedify/issues/482 -### `create-fedify-app` +### @fedify/create - - Created standalone project scaffolding CLI as the *create-fedify-app* + - Created standalone project scaffolding CLI as the *@fedify/create* package. This enables creating new Fedify projects without installing the full `@fedify/cli` toolchain. [[#351] by Chanhaeng Lee] - - Supports `npx create-fedify-app`, `pnpm create fedify-app`, - `yarn create fedify-app`, and `bunx create-fedify-app`. + - Supports `npm init @fedify`, `pnpm create @fedify`, + `yarn create @fedify`, and `bunx @fedify/create`. - Uses `@fedify/init` internally for all initialization logic. - Supports the same interactive prompts and CLI options as `fedify init`. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index aa47c1e8f..1b3126e8a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -332,7 +332,7 @@ The repository is organized as a monorepo with the following packages: - *packages/cli/*: The Fedify CLI (@fedify/cli). Built with [Deno] and tested with Deno, Node.js, and [Bun]. Uses `deno compile` to create standalone executables. - - *packages/create-fedify-app/*: Standalone CLI (create-fedify-app) for + - *packages/create/*: Standalone CLI (@fedify/create) for creating new Fedify projects. Wraps @fedify/init. - *packages/amqp/*: AMQP/RabbitMQ driver (@fedify/amqp) for Fedify. - *packages/cfworkers/*: Cloudflare Workers integration (@fedify/cfworkers) for @@ -348,7 +348,7 @@ The repository is organized as a monorepo with the following packages: - *packages/hono/*: Hono integration (@fedify/hono) for Fedify. - *packages/init/*: Project initializer (@fedify/init) for Fedify. Separated from @fedify/cli to enable standalone use and - `npx create-fedify-app`. + `npm init @fedify`. - *packages/koa/*: Koa integration (@fedify/koa) for Fedify. - *packages/lint/*: Linting utilities (@fedify/lint) for Fedify. - *packages/postgres/*: PostgreSQL drivers (@fedify/postgres) for Fedify. diff --git a/docs/cli.md b/docs/cli.md index 9ba5f0f74..ec7ceaddb 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -132,6 +132,13 @@ project. It will ask you a few questions to set up the project: > *deno.json*, while Node.js and Bun projects get an *eslint.config.ts* with > `@fedify/lint`. +> [!NOTE] +> If you find the full `@fedify/cli` toolchain too heavy for your needs, you +> can use [`@fedify/create`] instead to scaffold a new Fedify project without +> installing the CLI globally. See the +> [*Alternative: Using `@fedify/create`*](./install.md#alternative-using-fedify-create) +> section for details. + Alternatively, you can specify the options in the command line to skip some of interactive prompts: @@ -149,6 +156,7 @@ interactive prompts: [AMQP]: https://www.amqp.org/ [RabbitMQ]: https://www.rabbitmq.com/ [`@fedify/lint`]: /manual/lint +[`@fedify/create`]: https://www.npmjs.com/package/@fedify/create ### `-r`/`--runtime`: JavaScript runtime @@ -234,41 +242,6 @@ This option works with all other initialization options, allowing you to preview different configurations before making a decision. -`create-fedify-app`: Creating a Fedify project without CLI ----------------------------------------------------------- - -*This command is available since Fedify 2.0.0.* - -If you prefer not to install the `@fedify/cli` toolchain globally, you can -use `create-fedify-app` to scaffold a new Fedify project directly: - -::: code-group - -~~~~ sh [npm] -npx create-fedify-app my-fedify-project -~~~~ - -~~~~ sh [pnpm] -pnpm create fedify-app my-fedify-project -~~~~ - -~~~~ sh [Yarn] -yarn create fedify-app my-fedify-project -~~~~ - -~~~~ sh [Bun] -bunx create-fedify-app my-fedify-project -~~~~ - -::: - -This is functionally equivalent to `fedify init` and supports the same -interactive prompts and command-line options (`-r`, `-p`, `-w`, `-k`, `-q`, -`--dry-run`). See the -[`fedify init`](#fedify-init-initializing-a-fedify-project) section above for -details on available options. - - `fedify lookup`: Looking up an ActivityPub object ------------------------------------------------- diff --git a/docs/install.md b/docs/install.md index 86da497bb..b06c43bca 100644 --- a/docs/install.md +++ b/docs/install.md @@ -65,27 +65,27 @@ in the *CLI toolchain* docs. [![The “fedify init” command demo](https://asciinema.org/a/671658.svg)](https://asciinema.org/a/671658) -### Alternative: Using `create-fedify-app` +### Alternative: Using `@fedify/create` If you don't want to install the `fedify` CLI globally, you can use -`create-fedify-app` directly: +`@fedify/create` directly: ::: code-group ~~~~ sh [npm] -npx create-fedify-app your-project-dir +npm init @fedify your-project-dir ~~~~ ~~~~ sh [pnpm] -pnpm create fedify-app your-project-dir +pnpm create @fedify your-project-dir ~~~~ ~~~~ sh [Yarn] -yarn create fedify-app your-project-dir +yarn create @fedify your-project-dir ~~~~ ~~~~ sh [Bun] -bunx create-fedify-app your-project-dir +bunx @fedify/create your-project-dir ~~~~ ::: diff --git a/packages/create-fedify-app/README.md b/packages/create/README.md similarity index 75% rename from packages/create-fedify-app/README.md rename to packages/create/README.md index 86f156318..7008f7043 100644 --- a/packages/create-fedify-app/README.md +++ b/packages/create/README.md @@ -1,5 +1,5 @@ -Create-Fedify-app: Create a new Fedify project -============================================== +@fedify/create: Create a new Fedify project +=========================================== [![npm][npm badge]][npm] @@ -7,8 +7,8 @@ This package provides a standalone CLI tool for creating new [Fedify] projects. It allows you to scaffold a new project without installing the full [`@fedify/cli`] toolchain, powered by [`@fedify/init`] internally. -[npm badge]: https://img.shields.io/npm/v/create-fedify-app?logo=npm -[npm]: https://www.npmjs.com/package/create-fedify-app +[npm badge]: https://img.shields.io/npm/v/@fedify/create?logo=npm +[npm]: https://www.npmjs.com/package/@fedify/create [Fedify]: https://fedify.dev/ [`@fedify/cli`]: https://jsr.io/@fedify/cli [`@fedify/init`]: https://jsr.io/@fedify/init @@ -18,10 +18,10 @@ Usage ----- ~~~~ sh -npx create-fedify-app my-project -pnpm create fedify-app my-project -yarn create fedify-app my-project -bunx create-fedify-app my-project +npm init @fedify my-project +pnpm create @fedify my-project +yarn create @fedify my-project +bunx @fedify/create my-project ~~~~ diff --git a/packages/create-fedify-app/package.json b/packages/create/package.json similarity index 86% rename from packages/create-fedify-app/package.json rename to packages/create/package.json index 820c4fda3..1be0ba5ed 100644 --- a/packages/create-fedify-app/package.json +++ b/packages/create/package.json @@ -1,5 +1,5 @@ { - "name": "create-fedify-app", + "name": "@fedify/create", "version": "2.0.0", "description": "Create a new Fedify application", "keywords": [ @@ -26,11 +26,11 @@ "repository": { "type": "git", "url": "git+https://github.com/fedify-dev/fedify.git", - "directory": "packages/create-fedify-app" + "directory": "packages/create" }, "type": "module", "bin": { - "create-fedify-app": "./dist/mod.js" + "@fedify/create": "./dist/mod.js" }, "exports": "./dist/mod.js", "files": [ @@ -55,7 +55,7 @@ }, "scripts": { "build:self": "tsdown", - "build": "pnpm --filter create-fedify-app... run build:self", + "build": "pnpm --filter @fedify/create... run build:self", "prepack": "pnpm build", "prepublish": "pnpm build" } diff --git a/packages/create-fedify-app/src/mod.ts b/packages/create/src/mod.ts similarity index 94% rename from packages/create-fedify-app/src/mod.ts rename to packages/create/src/mod.ts index 29a20255d..c2b32817f 100644 --- a/packages/create-fedify-app/src/mod.ts +++ b/packages/create/src/mod.ts @@ -4,7 +4,7 @@ import { run } from "@optique/run"; import { merge } from "es-toolkit"; const result = run(initOptions, { - programName: "create-fedify-app", + programName: "@fedify/create", description: message`Create a new Fedify project. Unless you specify all options (${optionNames(["-w", "--web-framework"])}, ${ diff --git a/packages/create-fedify-app/tsdown.config.ts b/packages/create/tsdown.config.ts similarity index 100% rename from packages/create-fedify-app/tsdown.config.ts rename to packages/create/tsdown.config.ts diff --git a/packages/fedify/README.md b/packages/fedify/README.md index e6eac9136..45d71223c 100644 --- a/packages/fedify/README.md +++ b/packages/fedify/README.md @@ -98,7 +98,7 @@ Here is the list of packages: | ------------------------------------------------- | -------------------------------- | -------------------------------- | ---------------------------------------- | | [@fedify/fedify](/packages/fedify/) | [JSR] | [npm] | The core framework of Fedify | | [@fedify/cli](/packages/cli/) | [JSR][jsr:@fedify/cli] | [npm][npm:@fedify/cli] | CLI toolchain for testing and debugging | -| [create-fedify-app](/packages/create-fedify-app/) | | [npm][npm:create-fedify-app] | Create a new Fedify project (npx) | +| [@fedify/create](/packages/create/) | | [npm][npm:@fedify/create] | Create a new Fedify project | | [@fedify/amqp](/packages/amqp/) | [JSR][jsr:@fedify/amqp] | [npm][npm:@fedify/amqp] | AMQP/RabbitMQ driver | | [@fedify/cfworkers](/packages/cfworkers/) | [JSR][jsr:@fedify/cfworkers] | [npm][npm:@fedify/cfworkers] | Cloudflare Workers integration | | [@fedify/debugger](/packages/debugger/) | [JSR][jsr:@fedify/debugger] | [npm][npm:@fedify/debugger] | Embedded ActivityPub debug dashboard | @@ -127,7 +127,7 @@ Here is the list of packages: [jsr:@fedify/cli]: https://jsr.io/@fedify/cli [npm:@fedify/cli]: https://www.npmjs.com/package/@fedify/cli -[npm:create-fedify-app]: https://www.npmjs.com/package/create-fedify-app +[npm:@fedify/create]: https://www.npmjs.com/package/@fedify/create [jsr:@fedify/amqp]: https://jsr.io/@fedify/amqp [npm:@fedify/amqp]: https://www.npmjs.com/package/@fedify/amqp [jsr:@fedify/cfworkers]: https://jsr.io/@fedify/cfworkers diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 56547a3d1..1c169cd5d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -854,7 +854,7 @@ importers: specifier: 'catalog:' version: 5.9.3 - packages/create-fedify-app: + packages/create: dependencies: '@fedify/init': specifier: workspace:* diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 41b4da51a..0024a237d 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -3,7 +3,7 @@ packages: - packages/cfworkers - packages/cli - packages/debugger -- packages/create-fedify-app +- packages/create - packages/elysia - packages/express - packages/fastify From 3a9b492205efe071598f3760f7b597509c77adff Mon Sep 17 00:00:00 2001 From: ChanHaeng Lee <2chanhaeng@gmail.com> Date: Thu, 12 Feb 2026 11:51:12 +0000 Subject: [PATCH 12/22] Remove `create-fedify-app` from `deno.json` --- deno.json | 1 - 1 file changed, 1 deletion(-) diff --git a/deno.json b/deno.json index d7f5bfe74..2cc42567f 100644 --- a/deno.json +++ b/deno.json @@ -4,7 +4,6 @@ "./packages/cfworkers", "./packages/cli", "./packages/debugger", - "./packages/create-fedify-app", "./packages/denokv", "./packages/express", "./packages/fastify", From 0f81f63f0b57788b4d696ba46cf0ba70a8923413 Mon Sep 17 00:00:00 2001 From: ChanHaeng Lee <2chanhaeng@gmail.com> Date: Thu, 12 Feb 2026 12:28:48 +0000 Subject: [PATCH 13/22] Remove `test-init` from `cli/package.json` --- packages/cli/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 4cb6dfd7d..c1b12080d 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -93,7 +93,6 @@ "prepublish": "pnpm build", "pretest": "pnpm build", "test": "node --test --experimental-transform-types 'src/**/*.test.ts' '!src/init/test/**'", - "test-init": "deno task test-init", "pretest:bun": "pnpm build", "test:bun": "bun test", "run": "pnpm build && node dist/mod.js", From 19f29825714ae5a1c4013d86f19bb4c81e83304f Mon Sep 17 00:00:00 2001 From: ChanHaeng Lee <2chanhaeng@gmail.com> Date: Thu, 12 Feb 2026 12:35:46 +0000 Subject: [PATCH 14/22] Check port with `node:net` --- packages/init/src/test/db.ts | 42 ++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/packages/init/src/test/db.ts b/packages/init/src/test/db.ts index 291901745..f25c351b0 100644 --- a/packages/init/src/test/db.ts +++ b/packages/init/src/test/db.ts @@ -1,4 +1,5 @@ import { concat, filter, pipe, toArray, uniq } from "@fxts/core"; +import { createConnection } from "node:net"; import type { TestInitCommand } from "../command.ts"; import { DB_TO_CHECK } from "../const.ts"; import DB_INFO from "../json/db-to-check.json" with { type: "json" }; @@ -6,31 +7,30 @@ import { printErrorMessage, printMessage } from "../utils.ts"; import type { DbToCheckType, DefineAllOptions } from "./types.ts"; /** - * This function checks if a given port is open by attempting to fetch from - * localhost at that port. So, may give false positives if the service does not - * respond to HTTP requests. + * Checks if a given port is open by attempting a raw TCP connection to + * localhost at that port. This works reliably for non-HTTP services like + * Redis, PostgreSQL, or AMQP. * @param port The port number to check. + * @param timeout The timeout in milliseconds. Defaults to 3000. * @returns A promise that resolves to true if the port is open, else false. */ -async function isPortOpen(port: number): Promise { - try { - await fetch(`http://localhost:${port}`, { - signal: AbortSignal.timeout(3000), +function isPortOpen(port: number, timeout = 3000): Promise { + return new Promise((resolve) => { + const socket = createConnection({ port, host: "localhost" }); + socket.setTimeout(timeout); + socket.once("connect", () => { + socket.destroy(); + resolve(true); }); - return true; - } catch (error: unknown) { - if (error instanceof TypeError) { - const msg = error.message.toLowerCase(); - if (msg.includes("refused") || msg.includes("econnrefused")) { - return false; - } - return true; - } - if (error instanceof DOMException && error.name === "TimeoutError") { - return false; - } - return false; - } + socket.once("timeout", () => { + socket.destroy(); + resolve(false); + }); + socket.once("error", () => { + socket.destroy(); + resolve(false); + }); + }); } const getRequiredDbs = ( From accba8ee538fa00c4dab8ac62bc0a4bd2a9523a1 Mon Sep 17 00:00:00 2001 From: ChanHaeng Lee <2chanhaeng@gmail.com> Date: Thu, 12 Feb 2026 12:37:20 +0000 Subject: [PATCH 15/22] Add a shebang to execute --- packages/create/src/mod.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/create/src/mod.ts b/packages/create/src/mod.ts index c2b32817f..c5a94cf0a 100644 --- a/packages/create/src/mod.ts +++ b/packages/create/src/mod.ts @@ -1,3 +1,5 @@ +#!/usr/bin/env node + import { initOptions, runInit } from "@fedify/init"; import { message, optionNames } from "@optique/core"; import { run } from "@optique/run"; From abfdbf0f42723de440c9c51e59a1ac3422a648a7 Mon Sep 17 00:00:00 2001 From: ChanHaeng Lee <2chanhaeng@gmail.com> Date: Fri, 13 Feb 2026 18:28:53 +0000 Subject: [PATCH 16/22] Fix `GeneratedType` --- packages/cli/src/utils.ts | 2 +- packages/init/src/utils.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/utils.ts b/packages/cli/src/utils.ts index 355eddd6c..fcb53068d 100644 --- a/packages/cli/src/utils.ts +++ b/packages/cli/src/utils.ts @@ -267,7 +267,7 @@ export function* product[]>( } export type GeneratedType = T extends - Generator ? R : never; + Generator ? R : never; type PrintMessage = (...args: Parameters) => void; export const printMessage: PrintMessage = flow(message, print); diff --git a/packages/init/src/utils.ts b/packages/init/src/utils.ts index 9ec3ccc3b..d4ef23cbd 100644 --- a/packages/init/src/utils.ts +++ b/packages/init/src/utils.ts @@ -182,7 +182,7 @@ export function* product[]>( } export type GeneratedType = T extends - Generator ? R : never; + Generator ? R : never; type PrintMessage = (...args: Parameters) => void; export const printMessage: PrintMessage = flow(message, print); From 401dc93a2332d48a907298aa1d6e89ae27d2a9cf Mon Sep 17 00:00:00 2001 From: ChanHaeng Lee <2chanhaeng@gmail.com> Date: Fri, 13 Feb 2026 20:23:37 +0000 Subject: [PATCH 17/22] Fix `@fedify/create` README --- packages/create/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/create/README.md b/packages/create/README.md index 7008f7043..b5c2ccb75 100644 --- a/packages/create/README.md +++ b/packages/create/README.md @@ -36,7 +36,8 @@ The tool supports the same project configurations as `fedify init`: - **Message queues**: Deno KV, Redis, PostgreSQL, AMQP See the [`@fedify/init`] package or the [Fedify CLI docs] for details on -available options (`-r`, `-p`, `-w`, `-k`, `-q`, `--dry-run`). +available options (`-p`, `-w`, `-k`, `-m`, `--dry-run`). +More information is in the [`@fedify/init`] package. [Hono]: https://hono.dev/ [Nitro]: https://nitro.build/ From 4dcefaf54ba9f3f1ecf8e3c157a54a1a92610c7d Mon Sep 17 00:00:00 2001 From: ChanHaeng Lee <2chanhaeng@gmail.com> Date: Fri, 13 Feb 2026 20:29:41 +0000 Subject: [PATCH 18/22] Change the author of packages --- packages/create/package.json | 6 +++--- packages/init/package.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/create/package.json b/packages/create/package.json index 1be0ba5ed..f6c95a3ce 100644 --- a/packages/create/package.json +++ b/packages/create/package.json @@ -15,9 +15,9 @@ }, "license": "MIT", "author": { - "name": "Hong Minhee", - "email": "hong@minhee.org", - "url": "https://hongminhee.org/" + "name": "ChanHaeng Lee", + "email": "2chanhaeng@gmail.com", + "url": "https://chomu.dev/" }, "funding": [ "https://opencollective.com/fedify", diff --git a/packages/init/package.json b/packages/init/package.json index 920b2da44..4d697b546 100644 --- a/packages/init/package.json +++ b/packages/init/package.json @@ -14,9 +14,9 @@ }, "license": "MIT", "author": { - "name": "Hong Minhee", - "email": "hong@minhee.org", - "url": "https://hongminhee.org/" + "name": "ChanHaeng Lee", + "email": "2chanhaeng@gmail.com", + "url": "https://chomu.dev/" }, "funding": [ "https://opencollective.com/fedify", From 1a27f182c5f88b894cdf8c6929287713519337bd Mon Sep 17 00:00:00 2001 From: ChanHaeng Lee <2chanhaeng@gmail.com> Date: Fri, 13 Feb 2026 20:47:02 +0000 Subject: [PATCH 19/22] Fix deps --- deno.json | 3 ++ deno.lock | 68 ++----------------------------- packages/cli/deno.json | 5 --- packages/cli/package.json | 10 ++--- packages/create/package.json | 4 +- packages/init/deno.json | 7 ---- packages/init/package.json | 6 +-- pnpm-lock.yaml | 79 ++++++++---------------------------- pnpm-workspace.yaml | 3 ++ 9 files changed, 35 insertions(+), 150 deletions(-) diff --git a/deno.json b/deno.json index 2cc42567f..2644e64e2 100644 --- a/deno.json +++ b/deno.json @@ -41,6 +41,8 @@ "@opentelemetry/core": "npm:@opentelemetry/core@^2.5.0", "@opentelemetry/sdk-trace-base": "npm:@opentelemetry/sdk-trace-base@^2.5.0", "@opentelemetry/semantic-conventions": "npm:@opentelemetry/semantic-conventions@^1.39.0", + "@optique/core": "jsr:@optique/core@^0.9.0", + "@optique/run": "jsr:@optique/run@^0.9.0", "@std/assert": "jsr:@std/assert@^1.0.13", "@std/async": "jsr:@std/async@^1.0.13", "@std/encoding": "jsr:@std/encoding@^1.0.10", @@ -51,6 +53,7 @@ "@types/node": "npm:@types/node@^22.16.0", "amqplib": "npm:amqplib@^0.10.9", "byte-encodings": "npm:byte-encodings@^1.0.11", + "chalk": "npm:chalk@^5.6.2", "es-toolkit": "npm:es-toolkit@^1.43.0", "h3": "npm:h3@^1.15.0", "hono": "jsr:@hono/hono@^4.8.3", diff --git a/deno.lock b/deno.lock index 7345964d1..ab0a53a06 100644 --- a/deno.lock +++ b/deno.lock @@ -82,8 +82,6 @@ "npm:@opentelemetry/sdk-trace-base@1.30.1": "1.30.1_@opentelemetry+api@1.9.0", "npm:@opentelemetry/sdk-trace-base@^2.5.0": "2.5.0_@opentelemetry+api@1.9.0", "npm:@opentelemetry/semantic-conventions@^1.39.0": "1.39.0", - "npm:@optique/core@0.9": "0.9.0", - "npm:@optique/run@0.9": "0.9.0", "npm:@poppanator/http-constants@^1.1.1": "1.1.1", "npm:@preact/signals@^2.2.1": "2.5.1_preact@10.19.6", "npm:@preact/signals@^2.3.2": "2.5.1_preact@10.19.6", @@ -104,7 +102,6 @@ "npm:chalk@^5.6.2": "5.6.2", "npm:cli-highlight@^2.1.11": "2.1.11", "npm:cli-table3@~0.6.5": "0.6.5", - "npm:enquirer@^2.4.1": "2.4.1", "npm:env-paths@3": "3.0.0", "npm:es-toolkit@^1.30.0": "1.43.0", "npm:es-toolkit@^1.31.0": "1.43.0", @@ -125,7 +122,6 @@ "npm:hono@^4.8.3": "4.11.3", "npm:icojs@~0.19.5": "0.19.5", "npm:inquirer-toggle@^1.0.1": "1.0.1", - "npm:inquirer@^12.9.4": "12.11.1_@types+node@22.19.3", "npm:ioredis@^5.8.2": "5.9.1", "npm:jimp@^1.6.0": "1.6.0", "npm:json-canon@^1.0.1": "1.0.1", @@ -278,7 +274,7 @@ "@optique/run@0.9.0": { "integrity": "d71b0a05a1342e874fe1718a01d10384ca6b3de3245ae485f6565d6e2f0c16ad", "dependencies": [ - "jsr:@optique/core@0.9" + "jsr:@optique/core" ] }, "@std/assert@0.224.0": { @@ -2085,15 +2081,6 @@ "@opentelemetry/semantic-conventions@1.39.0": { "integrity": "sha512-R5R9tb2AXs2IRLNKLBJDynhkfmx7mX0vi8NkhZb3gUkPWHn6HXk5J8iQ/dql0U3ApfWym4kXXmBDRGO+oeOfjg==" }, - "@optique/core@0.9.0": { - "integrity": "sha512-PN5SwVRK9BPmFUKzcdNYDCV4Q18HGfR4H/1MG1yQYmA1RDNVKTuCV146dTIqI2H8F4lD/WMTiNsTl2wbGr9u1Q==" - }, - "@optique/run@0.9.0": { - "integrity": "sha512-S9hZfXPICeyIq3HArJ61yeSaadZQTnTKc4g03wcYRXLPYd6h/K7tCeVQnOkJ4k3b01TuiuHrartxwwxr3j/dnA==", - "dependencies": [ - "@optique/core" - ] - }, "@oxc-project/types@0.103.0": { "integrity": "sha512-bkiYX5kaXWwUessFRSoXFkGIQTmc6dLGdxuRTrC+h8PSnIdZyuXHHlLAeTmOue5Br/a0/a7dHH0Gca6eXn9MKg==" }, @@ -2845,9 +2832,6 @@ "url-parse" ] }, - "ansi-colors@4.1.3": { - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==" - }, "ansi-escapes@4.3.2": { "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dependencies": [ @@ -3319,13 +3303,6 @@ "encodeurl@2.0.0": { "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==" }, - "enquirer@2.4.1": { - "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", - "dependencies": [ - "ansi-colors", - "strip-ansi@6.0.1" - ] - }, "env-paths@3.0.0": { "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==" }, @@ -4035,22 +4012,6 @@ "@inquirer/core@8.2.4" ] }, - "inquirer@12.11.1_@types+node@22.19.3": { - "integrity": "sha512-9VF7mrY+3OmsAfjH3yKz/pLbJ5z22E23hENKw3/LNSaA/sAt3v49bDRY+Ygct1xwuKT+U+cBfTzjCPySna69Qw==", - "dependencies": [ - "@inquirer/ansi", - "@inquirer/core@10.3.2_@types+node@22.19.3", - "@inquirer/prompts", - "@inquirer/type@3.0.10_@types+node@22.19.3", - "@types/node@22.19.3", - "mute-stream@2.0.0", - "run-async", - "rxjs" - ], - "optionalPeers": [ - "@types/node@22.19.3" - ] - }, "ioredis@5.9.1": { "integrity": "sha512-BXNqFQ66oOsR82g9ajFFsR8ZKrjVvYCLyeML9IvSMAsP56XH2VXBdZjmI11p65nXXJxTEt1hie3J2QeFJVgrtQ==", "dependencies": [ @@ -4999,9 +4960,6 @@ ], "bin": true }, - "run-async@4.0.6": { - "integrity": "sha512-IoDlSLTs3Yq593mb3ZoKWKXMNu3UpObxhgA/Xuid5p4bbfi2jdY1Hj0m1K+0/tEuQTxIGMhQDqGjKb7RuxGpAQ==" - }, "rxjs@7.8.2": { "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", "dependencies": [ @@ -5889,6 +5847,8 @@ "jsr:@hono/hono@^4.8.3", "jsr:@logtape/file@2", "jsr:@logtape/logtape@2", + "jsr:@optique/core@0.9", + "jsr:@optique/run@0.9", "jsr:@std/assert@^1.0.13", "jsr:@std/async@^1.0.13", "jsr:@std/encoding@^1.0.10", @@ -5908,6 +5868,7 @@ "npm:@types/node@^22.16.0", "npm:amqplib@~0.10.9", "npm:byte-encodings@^1.0.11", + "npm:chalk@^5.6.2", "npm:es-toolkit@^1.43.0", "npm:h3@^1.15.0", "npm:ioredis@^5.8.2", @@ -5957,18 +5918,13 @@ "dependencies": [ "jsr:@hongminhee/localtunnel@0.3", "jsr:@hono/hono@^4.8.3", - "jsr:@optique/core@0.9", - "jsr:@optique/run@0.9", - "npm:@inquirer/prompts@^7.8.4", "npm:@jimp/core@^1.6.0", "npm:@jimp/wasm-webp@^1.6.0", "npm:@poppanator/http-constants@^1.1.1", - "npm:chalk@^5.6.2", "npm:cli-table3@~0.6.5", "npm:env-paths@3", "npm:fetch-mock@^12.5.4", "npm:icojs@~0.19.5", - "npm:inquirer-toggle@^1.0.1", "npm:ora@^8.2.0", "npm:shiki@^1.6.4", "npm:srvx@~0.8.7" @@ -5976,21 +5932,14 @@ "packageJson": { "dependencies": [ "npm:@hongminhee/localtunnel@0.3", - "npm:@inquirer/prompts@^7.8.4", "npm:@jimp/core@^1.6.0", "npm:@jimp/wasm-webp@^1.6.0", - "npm:@optique/core@0.9", - "npm:@optique/run@0.9", "npm:@poppanator/http-constants@^1.1.1", - "npm:chalk@^5.6.2", "npm:cli-highlight@^2.1.11", "npm:cli-table3@~0.6.5", - "npm:enquirer@^2.4.1", "npm:env-paths@3", "npm:hono@^4.8.3", "npm:icojs@~0.19.5", - "npm:inquirer-toggle@^1.0.1", - "npm:inquirer@^12.9.4", "npm:jimp@^1.6.0", "npm:ora@^8.2.0", "npm:shiki@^1.6.4", @@ -6059,21 +6008,12 @@ }, "packages/init": { "dependencies": [ - "jsr:@logtape/logtape@2", - "jsr:@optique/core@0.9", - "jsr:@optique/run@0.9", - "npm:@fxts/core@^1.21.1", "npm:@inquirer/prompts@^7.8.4", - "npm:chalk@^5.6.2", - "npm:es-toolkit@^1.43.0", "npm:inquirer-toggle@^1.0.1" ], "packageJson": { "dependencies": [ "npm:@inquirer/prompts@^7.8.4", - "npm:@optique/core@0.9", - "npm:@optique/run@0.9", - "npm:chalk@^5.6.2", "npm:inquirer-toggle@^1.0.1" ] } diff --git a/packages/cli/deno.json b/packages/cli/deno.json index c24657039..61fdf8f63 100644 --- a/packages/cli/deno.json +++ b/packages/cli/deno.json @@ -5,19 +5,14 @@ "exports": "./src/mod.ts", "imports": { "@hongminhee/localtunnel": "jsr:@hongminhee/localtunnel@^0.3.0", - "@inquirer/prompts": "npm:@inquirer/prompts@^7.8.4", "@jimp/core": "npm:@jimp/core@^1.6.0", "@jimp/wasm-webp": "npm:@jimp/wasm-webp@^1.6.0", - "@optique/core": "jsr:@optique/core@^0.9.0", - "@optique/run": "jsr:@optique/run@^0.9.0", "@poppanator/http-constants": "npm:@poppanator/http-constants@^1.1.1", - "chalk": "npm:chalk@^5.6.2", "cli-table3": "npm:cli-table3@^0.6.5", "env-paths": "npm:env-paths@^3.0.0", "fetch-mock": "npm:fetch-mock@^12.5.4", "hono": "jsr:@hono/hono@^4.8.3", "icojs": "npm:icojs@^0.19.5", - "inquirer-toggle": "npm:inquirer-toggle@^1.0.1", "ora": "npm:ora@^8.2.0", "shiki": "npm:shiki@^1.6.4", "srvx": "npm:srvx@^0.8.7", diff --git a/packages/cli/package.json b/packages/cli/package.json index c1b12080d..0b220c2b3 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -54,27 +54,23 @@ "@fedify/webfinger": "workspace:*", "@fxts/core": "catalog:", "@hongminhee/localtunnel": "^0.3.0", - "@inquirer/prompts": "^7.8.4", "@jimp/core": "^1.6.0", "@jimp/wasm-webp": "^1.6.0", "@js-temporal/polyfill": "catalog:", "@logtape/file": "catalog:", "@logtape/logtape": "catalog:", - "@optique/core": "^0.9.0", - "@optique/run": "^0.9.0", + "@optique/core": "catalog:", + "@optique/run": "catalog:", "@poppanator/http-constants": "^1.1.1", "byte-encodings": "catalog:", - "chalk": "^5.6.2", + "chalk": "catalog:", "cli-highlight": "^2.1.11", "cli-table3": "^0.6.5", - "enquirer": "^2.4.1", "env-paths": "^3.0.0", "es-toolkit": "catalog:", "fetch-mock": "catalog:", "hono": "^4.8.3", "icojs": "^0.19.5", - "inquirer": "^12.9.4", - "inquirer-toggle": "^1.0.1", "jimp": "^1.6.0", "ora": "^8.2.0", "shiki": "^1.6.4", diff --git a/packages/create/package.json b/packages/create/package.json index f6c95a3ce..f3308c672 100644 --- a/packages/create/package.json +++ b/packages/create/package.json @@ -44,8 +44,8 @@ }, "dependencies": { "@fedify/init": "workspace:*", - "@optique/core": "^0.9.0", - "@optique/run": "^0.9.0", + "@optique/core": "catalog:", + "@optique/run": "catalog:", "es-toolkit": "catalog:" }, "devDependencies": { diff --git a/packages/init/deno.json b/packages/init/deno.json index 2a914c3c4..634f0eb44 100644 --- a/packages/init/deno.json +++ b/packages/init/deno.json @@ -4,14 +4,7 @@ "license": "MIT", "exports": "./src/mod.ts", "imports": { - "@fedify/init/": "./src/", - "@fxts/core": "npm:@fxts/core@^1.21.1", "@inquirer/prompts": "npm:@inquirer/prompts@^7.8.4", - "@logtape/logtape": "jsr:@logtape/logtape@^2.0.0", - "@optique/core": "jsr:@optique/core@^0.9.0", - "@optique/run": "jsr:@optique/run@^0.9.0", - "chalk": "npm:chalk@^5.6.2", - "es-toolkit": "npm:es-toolkit@^1.43.0", "inquirer-toggle": "npm:inquirer-toggle@^1.0.1" }, "exclude": [ diff --git a/packages/init/package.json b/packages/init/package.json index 4d697b546..64786133a 100644 --- a/packages/init/package.json +++ b/packages/init/package.json @@ -46,9 +46,9 @@ "@fxts/core": "catalog:", "@inquirer/prompts": "^7.8.4", "@logtape/logtape": "catalog:", - "@optique/core": "^0.9.0", - "@optique/run": "^0.9.0", - "chalk": "^5.6.2", + "@optique/core": "catalog:", + "@optique/run": "catalog:", + "chalk": "catalog:", "es-toolkit": "catalog:", "inquirer-toggle": "^1.0.1" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1c169cd5d..7bbde0106 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -48,6 +48,12 @@ catalogs: '@opentelemetry/semantic-conventions': specifier: ^1.39.0 version: 1.39.0 + '@optique/core': + specifier: ^0.9.0 + version: 0.9.0 + '@optique/run': + specifier: ^0.9.0 + version: 0.9.0 '@std/assert': specifier: jsr:^1.0.13 version: 1.0.13 @@ -84,6 +90,9 @@ catalogs: byte-encodings: specifier: ^1.0.11 version: 1.0.11 + chalk: + specifier: ^5.6.2 + version: 5.6.2 elysia: specifier: ^1.3.8 version: 1.3.8 @@ -765,9 +774,6 @@ importers: '@hongminhee/localtunnel': specifier: ^0.3.0 version: 0.3.0 - '@inquirer/prompts': - specifier: ^7.8.4 - version: 7.10.1(@types/node@22.19.1) '@jimp/core': specifier: ^1.6.0 version: 1.6.0 @@ -784,10 +790,10 @@ importers: specifier: 'catalog:' version: 2.0.0 '@optique/core': - specifier: ^0.9.0 + specifier: 'catalog:' version: 0.9.0 '@optique/run': - specifier: ^0.9.0 + specifier: 'catalog:' version: 0.9.0 '@poppanator/http-constants': specifier: ^1.1.1 @@ -796,7 +802,7 @@ importers: specifier: 'catalog:' version: 1.0.11 chalk: - specifier: ^5.6.2 + specifier: 'catalog:' version: 5.6.2 cli-highlight: specifier: ^2.1.11 @@ -804,9 +810,6 @@ importers: cli-table3: specifier: ^0.6.5 version: 0.6.5 - enquirer: - specifier: ^2.4.1 - version: 2.4.1 env-paths: specifier: ^3.0.0 version: 3.0.0 @@ -822,12 +825,6 @@ importers: icojs: specifier: ^0.19.5 version: 0.19.5(@jimp/custom@0.22.12) - inquirer: - specifier: ^12.9.4 - version: 12.11.1(@types/node@22.19.1) - inquirer-toggle: - specifier: ^1.0.1 - version: 1.0.1 jimp: specifier: ^1.6.0 version: 1.6.0 @@ -860,10 +857,10 @@ importers: specifier: workspace:* version: link:../init '@optique/core': - specifier: ^0.9.0 + specifier: 'catalog:' version: 0.9.0 '@optique/run': - specifier: ^0.9.0 + specifier: 'catalog:' version: 0.9.0 es-toolkit: specifier: 'catalog:' @@ -1139,13 +1136,13 @@ importers: specifier: 'catalog:' version: 2.0.0 '@optique/core': - specifier: ^0.9.0 + specifier: 'catalog:' version: 0.9.0 '@optique/run': - specifier: ^0.9.0 + specifier: 'catalog:' version: 0.9.0 chalk: - specifier: ^5.6.2 + specifier: 'catalog:' version: 5.6.2 es-toolkit: specifier: 'catalog:' @@ -5005,10 +5002,6 @@ packages: resolution: {integrity: sha512-jwSftI4QjS3mizvnSnOrPGYiUnm1vI2OP1iXeOUz5pb74Ua0nbf6nPyyTzuiCLEE3fMpaJORXh2K/TQ08H5xGA==} engines: {node: '>=10'} - ansi-colors@4.1.3: - resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} - engines: {node: '>=6'} - ansi-escapes@4.3.2: resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} engines: {node: '>=8'} @@ -5821,10 +5814,6 @@ packages: resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} engines: {node: '>=10.13.0'} - enquirer@2.4.1: - resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} - engines: {node: '>=8.6'} - entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} @@ -6515,15 +6504,6 @@ packages: inquirer-toggle@1.0.1: resolution: {integrity: sha512-0cReq29SpyO4JnoVmGBZJPoBv8sBzsGXw3MDjNxilOzhAFxIvC8mOFj34bCMtlFYKfkBKNYVLmmnP/qmrVuVMg==} - inquirer@12.11.1: - resolution: {integrity: sha512-9VF7mrY+3OmsAfjH3yKz/pLbJ5z22E23hENKw3/LNSaA/sAt3v49bDRY+Ygct1xwuKT+U+cBfTzjCPySna69Qw==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - internal-slot@1.1.0: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} @@ -8080,10 +8060,6 @@ packages: roughjs@4.6.6: resolution: {integrity: sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==} - run-async@4.0.6: - resolution: {integrity: sha512-IoDlSLTs3Yq593mb3ZoKWKXMNu3UpObxhgA/Xuid5p4bbfi2jdY1Hj0m1K+0/tEuQTxIGMhQDqGjKb7RuxGpAQ==} - engines: {node: '>=0.12.0'} - run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -12815,8 +12791,6 @@ snapshots: buffer-more-ints: 1.0.0 url-parse: 1.5.10 - ansi-colors@4.1.3: {} - ansi-escapes@4.3.2: dependencies: type-fest: 0.21.3 @@ -13617,11 +13591,6 @@ snapshots: graceful-fs: 4.2.11 tapable: 2.2.2 - enquirer@2.4.1: - dependencies: - ansi-colors: 4.1.3 - strip-ansi: 6.0.1 - entities@4.5.0: {} env-paths@3.0.0: {} @@ -14815,18 +14784,6 @@ snapshots: dependencies: '@inquirer/core': 8.2.4 - inquirer@12.11.1(@types/node@22.19.1): - dependencies: - '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@22.19.1) - '@inquirer/prompts': 7.10.1(@types/node@22.19.1) - '@inquirer/type': 3.0.10(@types/node@22.19.1) - mute-stream: 2.0.0 - run-async: 4.0.6 - rxjs: 7.8.2 - optionalDependencies: - '@types/node': 22.19.1 - internal-slot@1.1.0: dependencies: es-errors: 1.3.0 @@ -16572,8 +16529,6 @@ snapshots: points-on-curve: 0.2.0 points-on-path: 0.2.1 - run-async@4.0.6: {} - run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 0024a237d..76bc0256f 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -51,6 +51,8 @@ catalog: "@opentelemetry/sdk-node": ^0.211.0 "@opentelemetry/sdk-trace-base": ^2.5.0 "@opentelemetry/semantic-conventions": ^1.39.0 + "@optique/core": ^0.9.0 + "@optique/run": ^0.9.0 "@std/assert": "jsr:^1.0.13" "@std/async": "jsr:^1.0.13" "@std/path": "jsr:^1.0.6" @@ -64,6 +66,7 @@ catalog: amqplib: ^0.10.9 asn1js: ^3.0.6 byte-encodings: ^1.0.11 + chalk: ^5.6.2 elysia: ^1.3.8 es-toolkit: 1.43.0 express: ^4.0.0 From 384401bdbf691cc782b1dbceb1e44a0381beed03 Mon Sep 17 00:00:00 2001 From: ChanHaeng Lee <2chanhaeng@gmail.com> Date: Fri, 13 Feb 2026 21:00:32 +0000 Subject: [PATCH 20/22] Fix test exclude pattern --- packages/init/deno.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/init/deno.json b/packages/init/deno.json index 634f0eb44..9b0018ef5 100644 --- a/packages/init/deno.json +++ b/packages/init/deno.json @@ -38,7 +38,7 @@ }, "test": { "exclude": [ - "src/init/test/**" + "src/test/**" ] } } From 5987ab14da272d6c5edff640951a8662ff135097 Mon Sep 17 00:00:00 2001 From: ChanHaeng Lee <2chanhaeng@gmail.com> Date: Fri, 13 Feb 2026 21:06:32 +0000 Subject: [PATCH 21/22] Add `engines` field to the packages.json --- packages/init/package.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/init/package.json b/packages/init/package.json index 64786133a..5ce89eeac 100644 --- a/packages/init/package.json +++ b/packages/init/package.json @@ -27,6 +27,11 @@ "url": "git+https://github.com/fedify-dev/fedify.git", "directory": "packages/init" }, + "engines": { + "node": ">=20.0.0", + "bun": ">=1.2.0", + "denp": ">=2.0.0" + }, "type": "module", "main": "./dist/mod.js", "types": "./dist/mod.d.ts", From b0ec83ddfb66034809325fb251c38cc86276dfed Mon Sep 17 00:00:00 2001 From: ChanHaeng Lee <2chanhaeng@gmail.com> Date: Fri, 13 Feb 2026 21:09:30 +0000 Subject: [PATCH 22/22] Fix message --- packages/init/src/command.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/init/src/command.ts b/packages/init/src/command.ts index cb6d80530..020b25524 100644 --- a/packages/init/src/command.ts +++ b/packages/init/src/command.ts @@ -122,7 +122,7 @@ export const testInitCommand = command( optional(or(noHydRun, noDryRun)), ), { - brief: message`Test an initializing command .`, + brief: message`Test an initializing command.`, description: message`Test an initializing command on temporary directories. Unless you specify all options (${optionNames(["-w", "--web-framework"])}, ${