From cbe983bc3a252370e7c7311b472c0eaf15770af5 Mon Sep 17 00:00:00 2001 From: ChanHaeng Lee <2chanhaeng@gmail.com> Date: Tue, 31 Mar 2026 04:19:42 +0000 Subject: [PATCH 01/15] Run `mise prepare` before `mise test:init` --- mise.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/mise.toml b/mise.toml index ec6805555..d10d48986 100644 --- a/mise.toml +++ b/mise.toml @@ -176,6 +176,7 @@ run = "deno run -A examples/test-examples/mod.ts" [tasks."test:init"] description = "Run tests for the init package" run = "deno task -f @fedify/init test-init" +depends = ["prepare"] # Snapshot updates # Note: vocab uses @std/testing/snapshot which only works on Deno From f8999635447ce219a018efbe3be3970faf1c2f27 Mon Sep 17 00:00:00 2001 From: ChanHaeng Lee <2chanhaeng@gmail.com> Date: Tue, 31 Mar 2026 07:45:27 +0000 Subject: [PATCH 02/15] Add `env` attr to `WebFrameworkInitializer` --- packages/init/src/action/patch.ts | 2 +- packages/init/src/action/set.ts | 10 ++++++++-- packages/init/src/types.ts | 12 ++++++++++-- packages/init/src/webframeworks/astro.ts | 2 ++ packages/init/src/webframeworks/elysia.ts | 11 +++++------ packages/init/src/webframeworks/nitro.ts | 4 +--- 6 files changed, 27 insertions(+), 14 deletions(-) diff --git a/packages/init/src/action/patch.ts b/packages/init/src/action/patch.ts index 553cfca05..764722b50 100644 --- a/packages/init/src/action/patch.ts +++ b/packages/init/src/action/patch.ts @@ -1,9 +1,9 @@ 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 { createFile, throwUnlessNotExists } from "../lib.ts"; import type { InitCommandData } from "../types.ts"; +import { formatJson, merge, replaceAll, set } from "../utils.ts"; import { devToolConfigs, loadDenoConfig, diff --git a/packages/init/src/action/set.ts b/packages/init/src/action/set.ts index 24574c02e..e50eecb31 100644 --- a/packages/init/src/action/set.ts +++ b/packages/init/src/action/set.ts @@ -12,6 +12,7 @@ import type { MessageQueue, MessageQueueDescription, WebFrameworkDescription, + WebFrameworkInitializer, } from "../types.ts"; import webFrameworks from "../webframeworks/mod.ts"; @@ -58,6 +59,11 @@ const setMq = set( const setEnv = set( "env", < - T extends { kv: KvStoreDescription; mq: MessageQueueDescription }, - >({ kv, mq }: T) => merge(kv.env)(mq.env), + T extends { + initializer: WebFrameworkInitializer; + kv: KvStoreDescription; + mq: MessageQueueDescription; + }, + >({ initializer, kv, mq }: T) => + merge(initializer.env)(merge(kv.env)(mq.env)), ); diff --git a/packages/init/src/types.ts b/packages/init/src/types.ts index 94b8eb3d1..28bc8a7e0 100644 --- a/packages/init/src/types.ts +++ b/packages/init/src/types.ts @@ -79,8 +79,16 @@ export interface WebFrameworkInitializer { federationFile: string; /** Relative path where the logging configuration file will be created. */ loggingFile: string; - /** Additional files to create, keyed by relative path to file content. */ - files?: Record; + /** + * Additional files to create, keyed by relative path to file content. + * Do not use `".env"` as a key — use the {@link env} property instead so + * that environment variables are properly merged with KV/MQ env vars. + */ + files?: Record & { ".env"?: never }; + /** Environment variables required by this framework, keyed by name to + * default value. Merged together with KV store and message queue env vars + * into the generated `.env` file. */ + env?: object; /** TypeScript compiler options to include in `tsconfig.json`. */ compilerOptions?: Record; /** Task scripts keyed by task name (e.g., `"dev"`, `"prod"`, `"lint"`). */ diff --git a/packages/init/src/webframeworks/astro.ts b/packages/init/src/webframeworks/astro.ts index f99e0a477..29cbd2c38 100644 --- a/packages/init/src/webframeworks/astro.ts +++ b/packages/init/src/webframeworks/astro.ts @@ -22,6 +22,8 @@ const astroDescription: WebFrameworkDescription = { : { "@astrojs/node": deps["npm:@astrojs/node"], "@fedify/astro": PACKAGE_VERSION, + ...(pm !== "bun" && + { "@dotenvx/dotenvx": deps["npm:@dotenvx/dotenvx"] }), }, devDependencies: { ...defaultDevDependencies, diff --git a/packages/init/src/webframeworks/elysia.ts b/packages/init/src/webframeworks/elysia.ts index dd5ac7e36..5f93d0991 100644 --- a/packages/init/src/webframeworks/elysia.ts +++ b/packages/init/src/webframeworks/elysia.ts @@ -22,15 +22,14 @@ const elysiaDescription: WebFrameworkDescription = { "@fedify/elysia": PACKAGE_VERSION, } : { + "@dotenvx/dotenvx": deps["npm:@dotenvx/dotenvx"], elysia: deps["npm:elysia"], "@elysiajs/node": deps["npm:@elysiajs/node"], "@fedify/elysia": PACKAGE_VERSION, - ...(pm === "pnpm" - ? { - "@sinclair/typebox": deps["npm:@sinclair/typebox"], - "openapi-types": deps["npm:openapi-types"], - } - : {}), + ...(pm === "pnpm" && { + "@sinclair/typebox": deps["npm:@sinclair/typebox"], + "openapi-types": deps["npm:openapi-types"], + }), }, devDependencies: { ...(pm === "bun" ? { "@types/bun": deps["npm:@types/bun"] } : { diff --git a/packages/init/src/webframeworks/nitro.ts b/packages/init/src/webframeworks/nitro.ts index 7a1fd09d0..59607bf4a 100644 --- a/packages/init/src/webframeworks/nitro.ts +++ b/packages/init/src/webframeworks/nitro.ts @@ -17,15 +17,13 @@ const nitroDescription: WebFrameworkDescription = { devDependencies: defaultDevDependencies, federationFile: "server/federation.ts", loggingFile: "server/logging.ts", + env: testMode ? { HOST: "127.0.0.1" } : {}, files: { "server/middleware/federation.ts": await readTemplate( "nitro/server/middleware/federation.ts", ), "server/error.ts": await readTemplate("nitro/server/error.ts"), "nitro.config.ts": await readTemplate("nitro/nitro.config.ts"), - ...( - testMode ? { ".env": await readTemplate("nitro/.env.test") } : {} - ), ...(pm !== "deno" ? { "eslint.config.ts": await readTemplate("defaults/eslint.config.ts"), From becca39e32c6f03ecd7ef7b84751dddf90f07d05 Mon Sep 17 00:00:00 2001 From: ChanHaeng Lee <2chanhaeng@gmail.com> Date: Tue, 31 Mar 2026 08:39:30 +0000 Subject: [PATCH 03/15] Add `@std/dotenv` if have env --- packages/init/src/action/deps.ts | 6 ++++-- packages/init/src/action/templates.ts | 8 ++++++-- packages/init/src/templates/bare-bones/main/deno.ts.tpl | 1 - packages/init/src/templates/hono/index/deno.ts.tpl | 1 - packages/init/src/webframeworks/hono.ts | 1 - 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/init/src/action/deps.ts b/packages/init/src/action/deps.ts index d0cfad4f5..7166e3006 100644 --- a/packages/init/src/action/deps.ts +++ b/packages/init/src/action/deps.ts @@ -26,9 +26,9 @@ type Deps = Record; * @returns A record of dependencies with their versions */ export const getDependencies = ( - { initializer, kv, mq, testMode, packageManager }: Pick< + { initializer, kv, mq, env, testMode, packageManager }: Pick< InitCommandData, - "initializer" | "kv" | "mq" | "packageManager" | "testMode" + "initializer" | "kv" | "mq" | "env" | "packageManager" | "testMode" >, ): Deps => pipe( @@ -36,6 +36,8 @@ export const getDependencies = ( "@fedify/fedify": PACKAGE_VERSION, "@fedify/vocab": PACKAGE_VERSION, "@logtape/logtape": deps["@logtape/logtape"], + ...(packageManager === "deno" && Object.keys(env).length > 0 && + { "@std/dotenv": deps["@std/dotenv"] }), }, merge(initializer.dependencies), merge(kv.dependencies), diff --git a/packages/init/src/action/templates.ts b/packages/init/src/action/templates.ts index 8d3e23864..099823f19 100644 --- a/packages/init/src/action/templates.ts +++ b/packages/init/src/action/templates.ts @@ -1,8 +1,8 @@ import { entries, join, map, pipe } from "@fxts/core"; import { toMerged } from "es-toolkit"; -import { replace } from "../utils.ts"; import { readTemplate } from "../lib.ts"; import type { InitCommandData, PackageManager } from "../types.ts"; +import { replace } from "../utils.ts"; /** * Loads the federation configuration file content from template. @@ -48,7 +48,7 @@ export const loadLogging = async ({ projectName }: InitCommandData) => * @param param0 - Destructured object containing kv and mq configurations * @returns A multi-line string containing all necessary import statements */ -export const getImports = ({ kv, mq }: InitCommandData) => +export const getImports = ({ kv, mq, packageManager, env }: InitCommandData) => pipe( toMerged(kv.imports, mq.imports), entries, @@ -62,6 +62,10 @@ export const getImports = ({ kv, mq }: InitCommandData) => } from ${JSON.stringify(module)};` ), join("\n"), + (imports) => + packageManager === "deno" && Object.keys(env).length > 0 + ? `import "@std/dotenv/load";\n${imports}` + : imports, ); /** diff --git a/packages/init/src/templates/bare-bones/main/deno.ts.tpl b/packages/init/src/templates/bare-bones/main/deno.ts.tpl index 7d1c08896..c50d91c5f 100644 --- a/packages/init/src/templates/bare-bones/main/deno.ts.tpl +++ b/packages/init/src/templates/bare-bones/main/deno.ts.tpl @@ -1,4 +1,3 @@ -import "@std/dotenv/load"; import { behindProxy } from "@hongminhee/x-forwarded-fetch"; import federation from "./federation.ts"; import "./logging.ts"; diff --git a/packages/init/src/templates/hono/index/deno.ts.tpl b/packages/init/src/templates/hono/index/deno.ts.tpl index 4377a96be..4cf42e975 100644 --- a/packages/init/src/templates/hono/index/deno.ts.tpl +++ b/packages/init/src/templates/hono/index/deno.ts.tpl @@ -1,5 +1,4 @@ import { behindProxy } from "@hongminhee/x-forwarded-fetch"; -import "@std/dotenv/load"; import app from "./app.tsx"; import "./logging.ts"; diff --git a/packages/init/src/webframeworks/hono.ts b/packages/init/src/webframeworks/hono.ts index 4937b5ac1..9f74ee8b3 100644 --- a/packages/init/src/webframeworks/hono.ts +++ b/packages/init/src/webframeworks/hono.ts @@ -15,7 +15,6 @@ const honoDescription: WebFrameworkDescription = { dependencies: pm === "deno" ? { ...defaultDenoDependencies, - "@std/dotenv": deps["@std/dotenv"], "@hono/hono": deps["@hono/hono"], "@hongminhee/x-forwarded-fetch": deps["@hongminhee/x-forwarded-fetch"], "@fedify/hono": PACKAGE_VERSION, From 26fa12f1cb681910b0eea339b2749e0d14be60dd Mon Sep 17 00:00:00 2001 From: ChanHaeng Lee <2chanhaeng@gmail.com> Date: Tue, 31 Mar 2026 10:24:03 +0000 Subject: [PATCH 04/15] Organize tasks --- packages/init/src/types.ts | 4 +- packages/init/src/webframeworks/astro.ts | 52 +++++++++---------- packages/init/src/webframeworks/bare-bones.ts | 32 +++++++----- packages/init/src/webframeworks/elysia.ts | 48 +++++++++-------- packages/init/src/webframeworks/express.ts | 35 ++++++++----- packages/init/src/webframeworks/hono.ts | 35 +++++++------ packages/init/src/webframeworks/next.ts | 4 +- packages/init/src/webframeworks/nitro.ts | 10 ++-- packages/init/src/webframeworks/solidstart.ts | 41 ++++++++------- packages/init/src/webframeworks/utils.ts | 6 +-- 10 files changed, 140 insertions(+), 127 deletions(-) diff --git a/packages/init/src/types.ts b/packages/init/src/types.ts index 28bc8a7e0..13cd9ab1c 100644 --- a/packages/init/src/types.ts +++ b/packages/init/src/types.ts @@ -40,7 +40,7 @@ export type Runtimes = Record; * whether it is installed. */ export interface RuntimeDescription { - /** Human-readable name of the runtime (e.g., `"Deno"`, `"Node.js"`). */ + /** Human-readable name of the runtime (e.g., `"Deno"`, `"Node.js", "Bun"`). */ label: string; /** Shell command to run for checking availability (e.g., `["deno", "--version"]`). */ checkCommand: [string, ...string[]]; @@ -92,7 +92,7 @@ export interface WebFrameworkInitializer { /** TypeScript compiler options to include in `tsconfig.json`. */ compilerOptions?: Record; /** Task scripts keyed by task name (e.g., `"dev"`, `"prod"`, `"lint"`). */ - tasks?: Record; + tasks?: object; /** Instructions shown to the user after project initialization is complete. */ instruction: Message; } diff --git a/packages/init/src/webframeworks/astro.ts b/packages/init/src/webframeworks/astro.ts index 29cbd2c38..8aab497d4 100644 --- a/packages/init/src/webframeworks/astro.ts +++ b/packages/init/src/webframeworks/astro.ts @@ -3,7 +3,7 @@ import deps from "../json/deps.json" with { type: "json" }; import { PACKAGE_VERSION, readTemplate } from "../lib.ts"; import type { PackageManager, WebFrameworkDescription } from "../types.ts"; import { defaultDenoDependencies, defaultDevDependencies } from "./const.ts"; -import { getInstruction } from "./utils.ts"; +import { getInstruction, pmToRt } from "./utils.ts"; const astroDescription: WebFrameworkDescription = { label: "Astro", @@ -41,33 +41,11 @@ const astroDescription: WebFrameworkDescription = { `astro/astro.config.${pm === "deno" ? "deno" : "node"}.ts`, ), "src/middleware.ts": await readTemplate("astro/src/middleware.ts"), - ...(pm !== "deno" - ? { - "eslint.config.ts": await readTemplate("defaults/eslint.config.ts"), - } - : {}), - }, - compilerOptions: undefined, - tasks: { - ...(pm === "deno" - ? { - dev: "deno run -A npm:astro dev", - build: "deno run -A npm:astro build", - preview: "deno run -A npm:astro preview", - } - : pm === "bun" - ? { - dev: "bunx astro dev", - build: "bunx astro build", - preview: "bunx astro preview", - } - : { - dev: "astro dev", - build: "astro build", - preview: "astro preview", - }), - ...(pm !== "deno" ? { lint: "eslint ." } : {}), + ...(pm !== "deno" && { + "eslint.config.ts": await readTemplate("defaults/eslint.config.ts"), + }), }, + tasks: TASKS[pmToRt(pm)], instruction: getInstruction(pm, 4321), }), }; @@ -99,3 +77,23 @@ function* getAstroInitCommand( const createAstroAppCommand = (pm: PackageManager): string[] => pm === "deno" ? ["deno", "init", "-y", "--npm"] : [pm, "create"]; + +const TASKS = { + "deno": { + dev: "deno run -A npm:astro dev", + build: "deno run -A npm:astro build", + preview: "deno run -A npm:astro preview", + }, + "bun": { + dev: "bunx astro dev", + build: "bunx astro build", + preview: "bunx astro preview", + lint: "eslint .", + }, + "node": { + dev: "dotenvx run -- astro dev", + build: "dotenvx run -- astro build", + preview: "dotenvx run -- astro preview", + lint: "eslint .", + }, +}; diff --git a/packages/init/src/webframeworks/bare-bones.ts b/packages/init/src/webframeworks/bare-bones.ts index fc4daf93c..cef71ec6d 100644 --- a/packages/init/src/webframeworks/bare-bones.ts +++ b/packages/init/src/webframeworks/bare-bones.ts @@ -3,7 +3,7 @@ import deps from "../json/deps.json" with { type: "json" }; import { readTemplate } from "../lib.ts"; import type { WebFrameworkDescription } from "../types.ts"; import { defaultDenoDependencies, defaultDevDependencies } from "./const.ts"; -import { getInstruction, packageManagerToRuntime } from "./utils.ts"; +import { getInstruction, pmToRt } from "./utils.ts"; const bareBonesDescription: WebFrameworkDescription = { label: "Bare-bones", @@ -34,7 +34,7 @@ const bareBonesDescription: WebFrameworkDescription = { loggingFile: "src/logging.ts", files: { "src/main.ts": await readTemplate( - `bare-bones/main/${packageManagerToRuntime(pm)}.ts`, + `bare-bones/main/${pmToRt(pm)}.ts`, ), ...(pm !== "deno" ? { @@ -57,20 +57,24 @@ const bareBonesDescription: WebFrameworkDescription = { "noEmit": true, "strict": true, }) as Record, - tasks: { - "dev": pm === "deno" - ? "deno run -A --watch ./src/main.ts" - : pm === "bun" - ? "bun run --hot ./src/main.ts" - : "dotenvx run -- tsx watch ./src/main.ts", - "prod": pm === "deno" - ? "deno run -A ./src/main.ts" - : pm === "bun" - ? "bun run ./src/main.ts" - : "dotenvx run -- node --import tsx ./src/main.ts", - }, + tasks: TASKS[pmToRt(pm)], instruction: getInstruction(pm, 8000), }), }; export default bareBonesDescription; + +const TASKS = { + deno: { + dev: "deno run -A --watch ./src/main.ts", + prod: "deno run -A ./src/main.ts", + }, + bun: { + dev: "bun run --hot ./src/main.ts", + prod: "bun run ./src/main.ts", + }, + node: { + dev: "dotenvx run -- tsx watch ./src/main.ts", + prod: "dotenvx run -- node --import tsx ./src/main.ts", + }, +}; diff --git a/packages/init/src/webframeworks/elysia.ts b/packages/init/src/webframeworks/elysia.ts index 5f93d0991..703e5bb20 100644 --- a/packages/init/src/webframeworks/elysia.ts +++ b/packages/init/src/webframeworks/elysia.ts @@ -3,7 +3,7 @@ import deps from "../json/deps.json" with { type: "json" }; import { PACKAGE_VERSION, readTemplate } from "../lib.ts"; import type { WebFrameworkDescription } from "../types.ts"; import { defaultDenoDependencies, defaultDevDependencies } from "./const.ts"; -import { getInstruction, packageManagerToRuntime } from "./utils.ts"; +import { getInstruction, pmToRt } from "./utils.ts"; const elysiaDescription: WebFrameworkDescription = { label: "ElysiaJS", @@ -43,13 +43,11 @@ const elysiaDescription: WebFrameworkDescription = { loggingFile: "src/logging.ts", files: { "src/index.ts": (await readTemplate( - `elysia/index/${packageManagerToRuntime(pm)}.ts`, + `elysia/index/${pmToRt(pm)}.ts`, )).replace(/\/\* logger \*\//, projectName), - ...(pm !== "deno" - ? { - "eslint.config.ts": await readTemplate("defaults/eslint.config.ts"), - } - : {}), + ...(pm !== "deno" && { + "eslint.config.ts": await readTemplate("defaults/eslint.config.ts"), + }), }, compilerOptions: pm === "deno" || pm === "bun" ? undefined : { "lib": ["ESNext", "DOM"], @@ -61,24 +59,28 @@ const elysiaDescription: WebFrameworkDescription = { "noEmit": true, "strict": true, }, - tasks: { - "dev": pm === "deno" - ? "deno serve --allow-env --allow-net --watch ./src/index.ts" - : pm === "bun" - ? "bun run --hot ./src/index.ts" - : "tsx watch src/index.ts", - ...(pm === "deno" - ? { "prod": "deno serve --allow-env --allow-net ./src/index.ts" } - : pm === "bun" - ? { "prod": "bun run ./src/index.ts" } - : { - "build": "tsc src/index.ts --outDir dist", - "start": "NODE_ENV=production node dist/index.js", - }), - ...(pm !== "deno" ? { "lint": "eslint ." } : {}), - }, + tasks: TASKS[pmToRt(pm)], instruction: getInstruction(pm, 3000), }), }; export default elysiaDescription; + +const TASKS = { + deno: { + dev: + "deno serve --allow-read --allow-env --allow-net --watch ./src/index.ts", + prod: "deno serve --allow-read --allow-env --allow-net ./src/index.ts", + }, + bun: { + dev: "bun run --hot ./src/index.ts", + prod: "bun run ./src/index.ts", + lint: "eslint .", + }, + node: { + dev: "dotenvx run -- tsx watch src/index.ts", + build: "tsc src/index.ts --outDir dist", + start: "NODE_ENV=production node dist/index.js", + lint: "eslint .", + }, +}; diff --git a/packages/init/src/webframeworks/express.ts b/packages/init/src/webframeworks/express.ts index 7d54ed08f..a01891207 100644 --- a/packages/init/src/webframeworks/express.ts +++ b/packages/init/src/webframeworks/express.ts @@ -3,7 +3,7 @@ import deps from "../json/deps.json" with { type: "json" }; import { PACKAGE_VERSION, readTemplate } from "../lib.ts"; import type { WebFrameworkDescription } from "../types.ts"; import { defaultDenoDependencies, defaultDevDependencies } from "./const.ts"; -import { getInstruction } from "./utils.ts"; +import { getInstruction, pmToRt } from "./utils.ts"; const expressDescription: WebFrameworkDescription = { label: "Express", @@ -48,21 +48,28 @@ const expressDescription: WebFrameworkDescription = { "noEmit": true, "strict": true, }, - tasks: { - "dev": pm === "bun" - ? "bun run --hot ./src/index.ts" - : pm === "deno" - ? "deno run --allow-net --allow-env --allow-sys --watch ./src/index.ts" - : "dotenvx run -- tsx watch ./src/index.ts", - "prod": pm === "bun" - ? "bun run ./src/index.ts" - : 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 ." } : {}), - }, + tasks: TASKS[pmToRt(pm)], instruction: getInstruction(pm, 8000), }), }; export default expressDescription; + +const TASKS = { + deno: { + dev: + "deno run --allow-read --allow-net --allow-env --allow-sys --watch ./src/index.ts", + prod: + "deno run --allow-read --allow-net --allow-env --allow-sys ./src/index.ts", + }, + bun: { + dev: "bun run --hot ./src/index.ts", + prod: "bun run ./src/index.ts", + lint: "eslint .", + }, + node: { + dev: "dotenvx run -- tsx watch ./src/index.ts", + prod: "dotenvx run -- node --import tsx ./src/index.ts", + lint: "eslint .", + }, +}; diff --git a/packages/init/src/webframeworks/hono.ts b/packages/init/src/webframeworks/hono.ts index 9f74ee8b3..43f5fdbe1 100644 --- a/packages/init/src/webframeworks/hono.ts +++ b/packages/init/src/webframeworks/hono.ts @@ -5,7 +5,7 @@ import { PACKAGE_VERSION, readTemplate } from "../lib.ts"; import type { WebFrameworkDescription } from "../types.ts"; import { replace } from "../utils.ts"; import { defaultDenoDependencies, defaultDevDependencies } from "./const.ts"; -import { getInstruction, packageManagerToRuntime } from "./utils.ts"; +import { getInstruction, pmToRt } from "./utils.ts"; const honoDescription: WebFrameworkDescription = { label: "Hono", @@ -46,7 +46,7 @@ const honoDescription: WebFrameworkDescription = { replace(/\/\* logger \*\//, projectName), ), "src/index.ts": await readTemplate( - `hono/index/${packageManagerToRuntime(pm)}.ts`, + `hono/index/${pmToRt(pm)}.ts`, ), ...(pm !== "deno" ? { @@ -66,21 +66,26 @@ const honoDescription: WebFrameworkDescription = { "jsx": "react-jsx", "jsxImportSource": "hono/jsx", }, - tasks: { - "dev": pm === "deno" - ? "deno run -A --watch ./src/index.ts" - : pm === "bun" - ? "bun run --hot ./src/index.ts" - : "dotenvx run -- tsx watch ./src/index.ts", - "prod": pm === "deno" - ? "deno run -A ./src/index.ts" - : pm === "bun" - ? "bun run ./src/index.ts" - : "dotenvx run -- node --import tsx ./src/index.ts", - ...(pm !== "deno" ? { "lint": "eslint ." } : {}), - }, + tasks: TASKS[pmToRt(pm)], instruction: getInstruction(pm, 8000), }), }; export default honoDescription; + +const TASKS = { + deno: { + dev: "deno run -A --watch ./src/index.ts", + prod: "deno run -A ./src/index.ts", + }, + bun: { + dev: "bun run --hot ./src/index.ts", + prod: "bun run ./src/index.ts", + lint: "eslint .", + }, + node: { + dev: "dotenvx run -- tsx watch ./src/index.ts", + prod: "dotenvx run -- node --import tsx ./src/index.ts", + lint: "eslint .", + }, +}; diff --git a/packages/init/src/webframeworks/next.ts b/packages/init/src/webframeworks/next.ts index 0d9062b57..ceeb6e9f7 100644 --- a/packages/init/src/webframeworks/next.ts +++ b/packages/init/src/webframeworks/next.ts @@ -29,9 +29,7 @@ const nextDescription: WebFrameworkDescription = { } : {}), }, - tasks: { - ...(pm !== "deno" ? { "lint": "eslint ." } : {}), - }, + tasks: pm !== "deno" ? { "lint": "eslint ." } : {}, instruction: getInstruction(pm, 3000), }), }; diff --git a/packages/init/src/webframeworks/nitro.ts b/packages/init/src/webframeworks/nitro.ts index 59607bf4a..dd73e02e3 100644 --- a/packages/init/src/webframeworks/nitro.ts +++ b/packages/init/src/webframeworks/nitro.ts @@ -12,7 +12,7 @@ const nitroDescription: WebFrameworkDescription = { command: getNitroInitCommand(pm), dependencies: { "@fedify/h3": PACKAGE_VERSION, - ...(pm === "deno" ? defaultDenoDependencies : {}), + ...(pm === "deno" && defaultDenoDependencies), }, devDependencies: defaultDevDependencies, federationFile: "server/federation.ts", @@ -24,11 +24,9 @@ const nitroDescription: WebFrameworkDescription = { ), "server/error.ts": await readTemplate("nitro/server/error.ts"), "nitro.config.ts": await readTemplate("nitro/nitro.config.ts"), - ...(pm !== "deno" - ? { - "eslint.config.ts": await readTemplate("defaults/eslint.config.ts"), - } - : {}), + ...(pm !== "deno" && { + "eslint.config.ts": await readTemplate("defaults/eslint.config.ts"), + }), }, tasks: pm !== "deno" ? { "lint": "eslint ." } : {} as { lint?: string }, instruction: getInstruction(pm, 3000), diff --git a/packages/init/src/webframeworks/solidstart.ts b/packages/init/src/webframeworks/solidstart.ts index 3187074ef..361e68b5c 100644 --- a/packages/init/src/webframeworks/solidstart.ts +++ b/packages/init/src/webframeworks/solidstart.ts @@ -3,7 +3,7 @@ import deps from "../json/deps.json" with { type: "json" }; import { PACKAGE_VERSION, readTemplate } from "../lib.ts"; import type { WebFrameworkDescription } from "../types.ts"; import { defaultDenoDependencies, defaultDevDependencies } from "./const.ts"; -import { getInstruction } from "./utils.ts"; +import { getInstruction, pmToRt } from "./utils.ts"; const NPM_SOLIDSTART = `npm:@solidjs/start@${deps["npm:@solidjs/start"]}`; const solidstartDescription: WebFrameworkDescription = { @@ -76,26 +76,29 @@ const solidstartDescription: WebFrameworkDescription = { noEmit: true, skipLibCheck: true, }, - tasks: { - dev: pm === "deno" - ? "deno run -A npm:vinxi dev" - : pm === "bun" - ? "bunx vinxi dev" - : "vinxi dev", - build: pm === "deno" - ? "deno run -A npm:vinxi build" - : pm === "bun" - ? "bunx vinxi build" - : "vinxi build", - start: pm === "deno" - ? "deno run -A npm:vinxi start" - : pm === "bun" - ? "bunx vinxi start" - : "vinxi start", - ...(pm !== "deno" ? { lint: "eslint ." } : {}), - }, + tasks: TASKS[pmToRt(pm)], instruction: getInstruction(pm, 3000), }), }; export default solidstartDescription; + +const TASKS = { + deno: { + dev: "deno run -A npm:vinxi dev", + build: "deno run -A npm:vinxi build", + start: "deno run -A npm:vinxi start", + }, + bun: { + dev: "bunx vinxi dev", + build: "bunx vinxi build", + start: "bunx vinxi start", + lint: "eslint .", + }, + node: { + dev: "vinxi dev", + build: "vinxi build", + start: "vinxi start", + lint: "eslint .", + }, +}; diff --git a/packages/init/src/webframeworks/utils.ts b/packages/init/src/webframeworks/utils.ts index 1e36c03bf..12a9a1229 100644 --- a/packages/init/src/webframeworks/utils.ts +++ b/packages/init/src/webframeworks/utils.ts @@ -31,7 +31,5 @@ Then, try to look up an actor from your server: * @param pm - The package manager (deno, bun, npm, yarn, pnpm) * @returns The runtime name (deno, bun, or node) */ -export const packageManagerToRuntime = ( - pm: PackageManager, -): "deno" | "bun" | "node" => - pm === "deno" ? "deno" : pm === "bun" ? "bun" : "node"; +export const pmToRt = (pm: PackageManager): "deno" | "bun" | "node" => + (pm !== "deno" && pm !== "bun") ? "node" : pm; From d424a14d89250d43651ffeba880fe81e85dcbc30 Mon Sep 17 00:00:00 2001 From: ChanHaeng Lee <61987505+2chanhaeng@users.noreply.github.com> Date: Fri, 3 Apr 2026 14:40:07 +0900 Subject: [PATCH 05/15] Fix typo in comments Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- packages/init/src/types.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/init/src/types.ts b/packages/init/src/types.ts index 13cd9ab1c..0fbf97dc8 100644 --- a/packages/init/src/types.ts +++ b/packages/init/src/types.ts @@ -40,7 +40,6 @@ export type Runtimes = Record; * whether it is installed. */ export interface RuntimeDescription { - /** Human-readable name of the runtime (e.g., `"Deno"`, `"Node.js", "Bun"`). */ label: string; /** Shell command to run for checking availability (e.g., `["deno", "--version"]`). */ checkCommand: [string, ...string[]]; From aaf8d9efed6562905ee44a4f0efb32dd8d4f2268 Mon Sep 17 00:00:00 2001 From: ChanHaeng Lee <2chanhaeng@gmail.com> Date: Fri, 3 Apr 2026 07:13:52 +0000 Subject: [PATCH 06/15] Add `lint` task for bare-bones https://github.com/fedify-dev/fedify/pull/656#discussion_r3031605378 --- packages/init/src/webframeworks/bare-bones.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/init/src/webframeworks/bare-bones.ts b/packages/init/src/webframeworks/bare-bones.ts index cef71ec6d..90db4f060 100644 --- a/packages/init/src/webframeworks/bare-bones.ts +++ b/packages/init/src/webframeworks/bare-bones.ts @@ -72,9 +72,11 @@ const TASKS = { bun: { dev: "bun run --hot ./src/main.ts", prod: "bun run ./src/main.ts", + lint: "eslint .", }, node: { dev: "dotenvx run -- tsx watch ./src/main.ts", prod: "dotenvx run -- node --import tsx ./src/main.ts", + lint: "eslint .", }, }; From 4e42c84b839cf81e17f438bf7047a296f9fca559 Mon Sep 17 00:00:00 2001 From: ChanHaeng Lee <2chanhaeng@gmail.com> Date: Fri, 3 Apr 2026 07:15:30 +0000 Subject: [PATCH 07/15] Add `dotenvx` for `start` task of init Elysia https://github.com/fedify-dev/fedify/pull/656#discussion_r3031605386 --- .agents/skills/add-integration-package/SKILL.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .agents/skills/add-integration-package/SKILL.md diff --git a/.agents/skills/add-integration-package/SKILL.md b/.agents/skills/add-integration-package/SKILL.md new file mode 100644 index 000000000..afedafdb0 --- /dev/null +++ b/.agents/skills/add-integration-package/SKILL.md @@ -0,0 +1,13 @@ +--- +name: add-integration-package +description: >- + This skill is utilized when adding a web framework integration package. + After examining the given framework, a feasibility assessment is conducted + regarding the creation of an integration package. + If implementation is feasible, the package is generated; + if it is not possible, the rationale is provided to the user. +argument-hint: "Provide the name of the web framework you want to integrate with." +version: 1.0.0 +--- + +# Adding an Integration Package to a Web Framework From 639823758b7bc96bf5619121ae16c93ab8781ba4 Mon Sep 17 00:00:00 2001 From: ChanHaeng Lee <2chanhaeng@gmail.com> Date: Fri, 3 Apr 2026 16:16:58 +0900 Subject: [PATCH 08/15] Revert "Add `dotenvx` for `start` task of init Elysia" This reverts commit 15f56a8ecd04e3982f40ac358eab68ae6193d4ac. --- .agents/skills/add-integration-package/SKILL.md | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 .agents/skills/add-integration-package/SKILL.md diff --git a/.agents/skills/add-integration-package/SKILL.md b/.agents/skills/add-integration-package/SKILL.md deleted file mode 100644 index afedafdb0..000000000 --- a/.agents/skills/add-integration-package/SKILL.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -name: add-integration-package -description: >- - This skill is utilized when adding a web framework integration package. - After examining the given framework, a feasibility assessment is conducted - regarding the creation of an integration package. - If implementation is feasible, the package is generated; - if it is not possible, the rationale is provided to the user. -argument-hint: "Provide the name of the web framework you want to integrate with." -version: 1.0.0 ---- - -# Adding an Integration Package to a Web Framework From b180e7b8040bd581e443b498eeaf58f4f04edb12 Mon Sep 17 00:00:00 2001 From: ChanHaeng Lee <2chanhaeng@gmail.com> Date: Fri, 3 Apr 2026 07:17:08 +0000 Subject: [PATCH 09/15] Add `dotenvx` for `start` task of init Elysia https://github.com/fedify-dev/fedify/pull/656#discussion_r3031605386 --- packages/init/src/webframeworks/elysia.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/init/src/webframeworks/elysia.ts b/packages/init/src/webframeworks/elysia.ts index 703e5bb20..8089e7d97 100644 --- a/packages/init/src/webframeworks/elysia.ts +++ b/packages/init/src/webframeworks/elysia.ts @@ -80,7 +80,7 @@ const TASKS = { node: { dev: "dotenvx run -- tsx watch src/index.ts", build: "tsc src/index.ts --outDir dist", - start: "NODE_ENV=production node dist/index.js", + start: "NODE_ENV=production dotenvx run -- node dist/index.js", lint: "eslint .", }, }; From 29b9e7e671794df2b26ade82b1708103a5fafd71 Mon Sep 17 00:00:00 2001 From: ChanHaeng Lee <2chanhaeng@gmail.com> Date: Sun, 5 Apr 2026 09:33:04 +0000 Subject: [PATCH 10/15] Refactor adding `@std/dotenv` dependency logic --- packages/init/src/action/deps.ts | 4 ++-- packages/init/src/action/templates.ts | 11 ++++++----- packages/init/src/action/utils.ts | 8 ++++++++ packages/init/src/webframeworks/bare-bones.ts | 1 - 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/packages/init/src/action/deps.ts b/packages/init/src/action/deps.ts index 7166e3006..bf36516cd 100644 --- a/packages/init/src/action/deps.ts +++ b/packages/init/src/action/deps.ts @@ -13,7 +13,7 @@ import { PACKAGE_VERSION } from "../lib.ts"; import type { InitCommandData, PackageManager } from "../types.ts"; import { merge, replace } from "../utils.ts"; import { getPackagesPath } from "./const.ts"; -import { isDeno } from "./utils.ts"; +import { isDeno, needsDenoDotenv } from "./utils.ts"; type Deps = Record; @@ -36,7 +36,7 @@ export const getDependencies = ( "@fedify/fedify": PACKAGE_VERSION, "@fedify/vocab": PACKAGE_VERSION, "@logtape/logtape": deps["@logtape/logtape"], - ...(packageManager === "deno" && Object.keys(env).length > 0 && + ...(needsDenoDotenv({ packageManager, env }) && { "@std/dotenv": deps["@std/dotenv"] }), }, merge(initializer.dependencies), diff --git a/packages/init/src/action/templates.ts b/packages/init/src/action/templates.ts index 099823f19..47fd74d05 100644 --- a/packages/init/src/action/templates.ts +++ b/packages/init/src/action/templates.ts @@ -1,8 +1,9 @@ -import { entries, join, map, pipe } from "@fxts/core"; +import { concat, entries, join, map, pipe, when } from "@fxts/core"; import { toMerged } from "es-toolkit"; import { readTemplate } from "../lib.ts"; import type { InitCommandData, PackageManager } from "../types.ts"; import { replace } from "../utils.ts"; +import { needsDenoDotenv } from "./utils.ts"; /** * Loads the federation configuration file content from template. @@ -61,11 +62,11 @@ export const getImports = ({ kv, mq, packageManager, env }: InitCommandData) => .join(", ") } from ${JSON.stringify(module)};` ), + when( + () => needsDenoDotenv({ packageManager, env }), + concat('import "@std/dotenv/load";'), + ), join("\n"), - (imports) => - packageManager === "deno" && Object.keys(env).length > 0 - ? `import "@std/dotenv/load";\n${imports}` - : imports, ); /** diff --git a/packages/init/src/action/utils.ts b/packages/init/src/action/utils.ts index 32aab88b4..019dddb71 100644 --- a/packages/init/src/action/utils.ts +++ b/packages/init/src/action/utils.ts @@ -13,6 +13,14 @@ export const isDeno = ( { packageManager }: Pick, ) => packageManager === "deno"; +/** + * Returns `true` when the `@std/dotenv` dependency should be included: + * the project uses Deno and has at least one environment variable to load. + */ +export const needsDenoDotenv = ( + { packageManager, env }: Pick, +) => packageManager === "deno" && Object.keys(env).length > 0; + /** * Returns a function that prepends the project directory to a * `[filename, content]` tuple, resolving the filename into an absolute path. diff --git a/packages/init/src/webframeworks/bare-bones.ts b/packages/init/src/webframeworks/bare-bones.ts index 90db4f060..7b92e50ad 100644 --- a/packages/init/src/webframeworks/bare-bones.ts +++ b/packages/init/src/webframeworks/bare-bones.ts @@ -13,7 +13,6 @@ const bareBonesDescription: WebFrameworkDescription = { dependencies: pm === "deno" ? { ...defaultDenoDependencies, - "@std/dotenv": deps["@std/dotenv"], "@hongminhee/x-forwarded-fetch": deps["@hongminhee/x-forwarded-fetch"], } : pm === "bun" From e94e00d431ab6ac28ecdd6ae0495072efbabad8e Mon Sep 17 00:00:00 2001 From: ChanHaeng Lee <2chanhaeng@gmail.com> Date: Sun, 5 Apr 2026 10:22:44 +0000 Subject: [PATCH 11/15] Add `dotenvx` dependency for solidstart --- packages/init/src/webframeworks/solidstart.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/init/src/webframeworks/solidstart.ts b/packages/init/src/webframeworks/solidstart.ts index 361e68b5c..a8f0ca811 100644 --- a/packages/init/src/webframeworks/solidstart.ts +++ b/packages/init/src/webframeworks/solidstart.ts @@ -26,6 +26,7 @@ const solidstartDescription: WebFrameworkDescription = { "@fedify/solidstart": PACKAGE_VERSION, } : { + "@dotenvx/dotenvx": deps["npm:@dotenvx/dotenvx"], "@solidjs/router": deps["npm:@solidjs/router"], "@solidjs/start": deps["npm:@solidjs/start"], "solid-js": deps["npm:solid-js"], @@ -98,7 +99,7 @@ const TASKS = { node: { dev: "vinxi dev", build: "vinxi build", - start: "vinxi start", + start: "dotenvx run -- vinxi start", lint: "eslint .", }, }; From 58f5605764050173d8787d3d90afed5efeccb3ff Mon Sep 17 00:00:00 2001 From: ChanHaeng Lee <2chanhaeng@gmail.com> Date: Sun, 5 Apr 2026 10:35:57 +0000 Subject: [PATCH 12/15] Fix `concat` argument type --- packages/init/src/action/templates.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/init/src/action/templates.ts b/packages/init/src/action/templates.ts index 47fd74d05..3eab8a647 100644 --- a/packages/init/src/action/templates.ts +++ b/packages/init/src/action/templates.ts @@ -64,7 +64,7 @@ export const getImports = ({ kv, mq, packageManager, env }: InitCommandData) => ), when( () => needsDenoDotenv({ packageManager, env }), - concat('import "@std/dotenv/load";'), + concat(['import "@std/dotenv/load";']), ), join("\n"), ); From 1689329075dab3a9b44720e3053157a653f78426 Mon Sep 17 00:00:00 2001 From: ChanHaeng Lee <2chanhaeng@gmail.com> Date: Sun, 5 Apr 2026 10:43:58 +0000 Subject: [PATCH 13/15] Fix JSDocs --- packages/init/src/action/templates.ts | 32 +++++++++++++++++++-------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/packages/init/src/action/templates.ts b/packages/init/src/action/templates.ts index 3eab8a647..8e489c11e 100644 --- a/packages/init/src/action/templates.ts +++ b/packages/init/src/action/templates.ts @@ -7,9 +7,11 @@ import { needsDenoDotenv } from "./utils.ts"; /** * Loads the federation configuration file content from template. - * Reads the default federation template and replaces placeholders with actual configuration values. + * Reads the default federation template and replaces placeholders with actual + * configuration values. * - * @param param0 - Configuration object containing imports, project name, KV store, message queue, and package manager + * @param param0 - Configuration object containing imports, project name, + * KV store, message queue, and package manager * @returns The complete federation configuration file content as a string */ export const loadFederation = async ( @@ -44,9 +46,16 @@ export const loadLogging = async ({ projectName }: InitCommandData) => /** * Generates import statements for KV store and message queue dependencies. - * Merges imports from both KV and MQ configurations and creates proper ES module import syntax. + * Merges imports from both KV and MQ configurations and creates proper + * ES module import syntax. * - * @param param0 - Destructured object containing kv and mq configurations + * Destructured parameters: + * - kv: KV store configuration, including module import mappings + * - mq: Message queue configuration, including module import mappings + * - packageManager: Package manager used for environment-specific handling + * - env: Environment variable setup used to determine loading requirements + * + * @param param0 - InitCommandData containing kv, mq, packageManager, and env * @returns A multi-line string containing all necessary import statements */ export const getImports = ({ kv, mq, packageManager, env }: InitCommandData) => @@ -71,7 +80,8 @@ export const getImports = ({ kv, mq, packageManager, env }: InitCommandData) => /** * Converts import mappings to named import string with aliases. - * Creates proper ES module named import syntax, using aliases when the import name differs from the local name. + * Creates proper ES module named import syntax, using aliases when the import + * name differs from the local name. * * @param imports - A record mapping import names to their local aliases * @returns A comma-separated string of named imports with aliases where needed @@ -86,12 +96,16 @@ export const getAlias = (imports: Record) => const ENV_REG_EXP = /process\.env\.(\w+)/g; /** - * Converts Node.js environment variable access to Deno-compatible syntax when needed. - * Transforms `process.env.VAR_NAME` to `Deno.env.get("VAR_NAME")` for Deno projects. + * Converts Node.js environment variable access to Deno-compatible syntax when + * needed. + * Transforms `process.env.VAR_NAME` to `Deno.env.get("VAR_NAME")` for Deno + * projects. * - * @param obj - The object string containing potential environment variable references + * @param obj - The object string containing potential environment variable + * references * @param pm - The package manager (runtime) being used - * @returns The converted object string with appropriate environment variable access syntax + * @returns The converted object string with appropriate environment variable + * access syntax */ export const convertEnv = (obj: string, pm: PackageManager) => pm === "deno" && ENV_REG_EXP.test(obj) From 55ae65a01458bbb3a49cc08d1d168f44012b3bb7 Mon Sep 17 00:00:00 2001 From: ChanHaeng Lee <2chanhaeng@gmail.com> Date: Tue, 7 Apr 2026 15:40:28 +0000 Subject: [PATCH 14/15] Remove `object` type --- cspell.json | 2 + packages/init/src/types.ts | 8 +-- packages/init/src/webframeworks/astro.ts | 2 +- packages/init/src/webframeworks/bare-bones.ts | 41 +++++++------ packages/init/src/webframeworks/elysia.ts | 2 + packages/init/src/webframeworks/express.ts | 12 ++-- packages/init/src/webframeworks/hono.ts | 45 +++++++------- packages/init/src/webframeworks/next.ts | 12 ++-- packages/init/src/webframeworks/nitro.ts | 6 +- packages/init/src/webframeworks/solidstart.ts | 59 ++++++++++--------- 10 files changed, 100 insertions(+), 89 deletions(-) diff --git a/cspell.json b/cspell.json index 10cfad56a..1f22276ec 100644 --- a/cspell.json +++ b/cspell.json @@ -28,9 +28,11 @@ "dereferenceable", "discoverability", "docloader", + "dotenvx", "draft-cavage", "eddsa", "elysia", + "elysiajs", "fanout", "federatable", "Federatable", diff --git a/packages/init/src/types.ts b/packages/init/src/types.ts index 0fbf97dc8..fb46c9f0e 100644 --- a/packages/init/src/types.ts +++ b/packages/init/src/types.ts @@ -71,9 +71,9 @@ export interface WebFrameworkInitializer { /** Optional shell command to run before scaffolding (e.g., `create-next-app`). */ command?: string[]; /** Runtime dependencies to install (package name to version). */ - dependencies?: object; + dependencies?: Record; /** Development-only dependencies to install (package name to version). */ - devDependencies?: object; + devDependencies?: Record; /** Relative path where the federation configuration file will be created. */ federationFile: string; /** Relative path where the logging configuration file will be created. */ @@ -87,11 +87,11 @@ export interface WebFrameworkInitializer { /** Environment variables required by this framework, keyed by name to * default value. Merged together with KV store and message queue env vars * into the generated `.env` file. */ - env?: object; + env?: Record; /** TypeScript compiler options to include in `tsconfig.json`. */ compilerOptions?: Record; /** Task scripts keyed by task name (e.g., `"dev"`, `"prod"`, `"lint"`). */ - tasks?: object; + tasks?: Record; /** Instructions shown to the user after project initialization is complete. */ instruction: Message; } diff --git a/packages/init/src/webframeworks/astro.ts b/packages/init/src/webframeworks/astro.ts index 8aab497d4..063297209 100644 --- a/packages/init/src/webframeworks/astro.ts +++ b/packages/init/src/webframeworks/astro.ts @@ -37,7 +37,7 @@ const astroDescription: WebFrameworkDescription = { federationFile: "src/federation.ts", loggingFile: "src/logging.ts", files: { - [`astro.config.ts`]: await readTemplate( + "astro.config.ts": await readTemplate( `astro/astro.config.${pm === "deno" ? "deno" : "node"}.ts`, ), "src/middleware.ts": await readTemplate("astro/src/middleware.ts"), diff --git a/packages/init/src/webframeworks/bare-bones.ts b/packages/init/src/webframeworks/bare-bones.ts index 7b92e50ad..03cc2409b 100644 --- a/packages/init/src/webframeworks/bare-bones.ts +++ b/packages/init/src/webframeworks/bare-bones.ts @@ -10,19 +10,7 @@ const bareBonesDescription: WebFrameworkDescription = { packageManagers: PACKAGE_MANAGER, defaultPort: 8000, init: async ({ packageManager: pm }) => ({ - dependencies: pm === "deno" - ? { - ...defaultDenoDependencies, - "@hongminhee/x-forwarded-fetch": deps["@hongminhee/x-forwarded-fetch"], - } - : pm === "bun" - ? { "npm:x-forwarded-fetch": deps["npm:x-forwarded-fetch"] } - : { - "npm:@dotenvx/dotenvx": deps["npm:@dotenvx/dotenvx"], - "npm:@hono/node-server": deps["npm:@hono/node-server"], - "npm:tsx": deps["npm:tsx"], - "npm:x-forwarded-fetch": deps["npm:x-forwarded-fetch"], - }, + dependencies: getDependencies(pm), devDependencies: { ...defaultDevDependencies, ...(pm === "bun" @@ -32,14 +20,10 @@ const bareBonesDescription: WebFrameworkDescription = { federationFile: "src/federation.ts", loggingFile: "src/logging.ts", files: { - "src/main.ts": await readTemplate( - `bare-bones/main/${pmToRt(pm)}.ts`, - ), - ...(pm !== "deno" - ? { - "eslint.config.ts": await readTemplate("defaults/eslint.config.ts"), - } - : {}), + "src/main.ts": await readTemplate(`bare-bones/main/${pmToRt(pm)}.ts`), + ...(pm !== "deno" && { + "eslint.config.ts": await readTemplate("defaults/eslint.config.ts"), + }), }, compilerOptions: (pm === "deno" ? { @@ -63,6 +47,21 @@ const bareBonesDescription: WebFrameworkDescription = { export default bareBonesDescription; +const getDependencies = (pm: string): Record => + pm === "deno" + ? { + ...defaultDenoDependencies, + "@hongminhee/x-forwarded-fetch": deps["@hongminhee/x-forwarded-fetch"], + } + : pm === "bun" + ? { "npm:x-forwarded-fetch": deps["npm:x-forwarded-fetch"] } + : { + "npm:@dotenvx/dotenvx": deps["npm:@dotenvx/dotenvx"], + "npm:@hono/node-server": deps["npm:@hono/node-server"], + "npm:tsx": deps["npm:tsx"], + "npm:x-forwarded-fetch": deps["npm:x-forwarded-fetch"], + }; + const TASKS = { deno: { dev: "deno run -A --watch ./src/main.ts", diff --git a/packages/init/src/webframeworks/elysia.ts b/packages/init/src/webframeworks/elysia.ts index 8089e7d97..6c4e4b408 100644 --- a/packages/init/src/webframeworks/elysia.ts +++ b/packages/init/src/webframeworks/elysia.ts @@ -84,3 +84,5 @@ const TASKS = { lint: "eslint .", }, }; + +// cspell: ignore typebox diff --git a/packages/init/src/webframeworks/express.ts b/packages/init/src/webframeworks/express.ts index a01891207..101c03a8b 100644 --- a/packages/init/src/webframeworks/express.ts +++ b/packages/init/src/webframeworks/express.ts @@ -13,13 +13,11 @@ const expressDescription: WebFrameworkDescription = { dependencies: { "npm:express": deps["npm:express"], "@fedify/express": PACKAGE_VERSION, - ...(pm !== "deno" && pm !== "bun" - ? { - "@dotenvx/dotenvx": deps["npm:@dotenvx/dotenvx"], - tsx: deps["npm:tsx"], - } - : {}), - ...(pm === "deno" ? defaultDenoDependencies : {}), + ...(pmToRt(pm) === "node" && { + "@dotenvx/dotenvx": deps["npm:@dotenvx/dotenvx"], + tsx: deps["npm:tsx"], + }), + ...(pm === "deno" && defaultDenoDependencies), }, devDependencies: { "@types/express": deps["npm:@types/express"], diff --git a/packages/init/src/webframeworks/hono.ts b/packages/init/src/webframeworks/hono.ts index 43f5fdbe1..a9b867a27 100644 --- a/packages/init/src/webframeworks/hono.ts +++ b/packages/init/src/webframeworks/hono.ts @@ -12,27 +12,7 @@ const honoDescription: WebFrameworkDescription = { packageManagers: PACKAGE_MANAGER, defaultPort: 8000, init: async ({ projectName, packageManager: pm }) => ({ - dependencies: pm === "deno" - ? { - ...defaultDenoDependencies, - "@hono/hono": deps["@hono/hono"], - "@hongminhee/x-forwarded-fetch": deps["@hongminhee/x-forwarded-fetch"], - "@fedify/hono": PACKAGE_VERSION, - } - : pm === "bun" - ? { - hono: deps["npm:hono"], - "x-forwarded-fetch": deps["npm:x-forwarded-fetch"], - "@fedify/hono": PACKAGE_VERSION, - } - : { - "@dotenvx/dotenvx": deps["npm:@dotenvx/dotenvx"], - hono: deps["npm:hono"], - "@hono/node-server": deps["npm:@hono/node-server"], - tsx: deps["npm:tsx"], - "x-forwarded-fetch": deps["npm:x-forwarded-fetch"], - "@fedify/hono": PACKAGE_VERSION, - }, + dependencies: getDependencies(pm), devDependencies: { ...defaultDevDependencies, ...(pm === "bun" ? { "@types/bun": deps["npm:@types/bun"] } : {}), @@ -73,6 +53,29 @@ const honoDescription: WebFrameworkDescription = { export default honoDescription; +const getDependencies = (pm: string): Record => + pm === "deno" + ? { + ...defaultDenoDependencies, + "@hono/hono": deps["@hono/hono"], + "@hongminhee/x-forwarded-fetch": deps["@hongminhee/x-forwarded-fetch"], + "@fedify/hono": PACKAGE_VERSION, + } + : pm === "bun" + ? { + hono: deps["npm:hono"], + "x-forwarded-fetch": deps["npm:x-forwarded-fetch"], + "@fedify/hono": PACKAGE_VERSION, + } + : { + "@dotenvx/dotenvx": deps["npm:@dotenvx/dotenvx"], + hono: deps["npm:hono"], + "@hono/node-server": deps["npm:@hono/node-server"], + tsx: deps["npm:tsx"], + "x-forwarded-fetch": deps["npm:x-forwarded-fetch"], + "@fedify/hono": PACKAGE_VERSION, + }; + const TASKS = { deno: { dev: "deno run -A --watch ./src/index.ts", diff --git a/packages/init/src/webframeworks/next.ts b/packages/init/src/webframeworks/next.ts index ceeb6e9f7..cf7bcc00d 100644 --- a/packages/init/src/webframeworks/next.ts +++ b/packages/init/src/webframeworks/next.ts @@ -23,13 +23,13 @@ const nextDescription: WebFrameworkDescription = { loggingFile: "logging.ts", files: { "middleware.ts": await readTemplate("next/middleware.ts"), - ...(pm !== "deno" - ? { - "eslint.config.ts": await readTemplate("defaults/eslint.config.ts"), - } - : {}), + ...(pm !== "deno" && { + "eslint.config.ts": await readTemplate("defaults/eslint.config.ts"), + }), }, - tasks: pm !== "deno" ? { "lint": "eslint ." } : {}, + tasks: pm !== "deno" + ? { "lint": "eslint ." } + : {} as Record, instruction: getInstruction(pm, 3000), }), }; diff --git a/packages/init/src/webframeworks/nitro.ts b/packages/init/src/webframeworks/nitro.ts index dd73e02e3..5bfd5eadd 100644 --- a/packages/init/src/webframeworks/nitro.ts +++ b/packages/init/src/webframeworks/nitro.ts @@ -17,7 +17,7 @@ const nitroDescription: WebFrameworkDescription = { devDependencies: defaultDevDependencies, federationFile: "server/federation.ts", loggingFile: "server/logging.ts", - env: testMode ? { HOST: "127.0.0.1" } : {}, + env: testMode ? { HOST: "127.0.0.1" } : {} as Record, files: { "server/middleware/federation.ts": await readTemplate( "nitro/server/middleware/federation.ts", @@ -28,7 +28,9 @@ const nitroDescription: WebFrameworkDescription = { "eslint.config.ts": await readTemplate("defaults/eslint.config.ts"), }), }, - tasks: pm !== "deno" ? { "lint": "eslint ." } : {} as { lint?: string }, + tasks: pm !== "deno" + ? { "lint": "eslint ." } + : {} as Record, instruction: getInstruction(pm, 3000), }), }; diff --git a/packages/init/src/webframeworks/solidstart.ts b/packages/init/src/webframeworks/solidstart.ts index a8f0ca811..dee370003 100644 --- a/packages/init/src/webframeworks/solidstart.ts +++ b/packages/init/src/webframeworks/solidstart.ts @@ -11,28 +11,7 @@ const solidstartDescription: WebFrameworkDescription = { packageManagers: PACKAGE_MANAGER, defaultPort: 3000, init: async ({ packageManager: pm }) => ({ - dependencies: pm === "deno" - ? { - ...defaultDenoDependencies, - "@solidjs/router": `npm:@solidjs/router@${deps["npm:@solidjs/router"]}`, - "@solidjs/start": `${NPM_SOLIDSTART}`, - "@solidjs/start/client": `${NPM_SOLIDSTART}/client`, - "@solidjs/start/config": `${NPM_SOLIDSTART}/config`, - "@solidjs/start/middleware": `${NPM_SOLIDSTART}/middleware`, - "@solidjs/start/router": `${NPM_SOLIDSTART}/router`, - "@solidjs/start/server": `${NPM_SOLIDSTART}/server`, - "solid-js": `npm:solid-js@${deps["npm:solid-js"]}`, - vinxi: `npm:vinxi@${deps["npm:vinxi"]}`, - "@fedify/solidstart": PACKAGE_VERSION, - } - : { - "@dotenvx/dotenvx": deps["npm:@dotenvx/dotenvx"], - "@solidjs/router": deps["npm:@solidjs/router"], - "@solidjs/start": deps["npm:@solidjs/start"], - "solid-js": deps["npm:solid-js"], - vinxi: deps["npm:vinxi"], - "@fedify/solidstart": PACKAGE_VERSION, - }, + dependencies: getDependencies(pm), devDependencies: { ...defaultDevDependencies, typescript: deps["npm:typescript"], @@ -59,11 +38,9 @@ const solidstartDescription: WebFrameworkDescription = { "src/middleware/index.ts": await readTemplate( "solidstart/src/middleware/index.ts", ), - ...(pm !== "deno" - ? { - "eslint.config.ts": await readTemplate("defaults/eslint.config.ts"), - } - : {}), + ...(pm !== "deno" && { + "eslint.config.ts": await readTemplate("defaults/eslint.config.ts"), + }), }, compilerOptions: pm === "deno" ? undefined : { target: "ESNext", @@ -84,6 +61,34 @@ const solidstartDescription: WebFrameworkDescription = { export default solidstartDescription; +const getDependencies = (pm: string): Record => + pm === "deno" + ? { + ...defaultDenoDependencies, + "@solidjs/router": `npm:@solidjs/router@${deps["npm:@solidjs/router"]}`, + "@solidjs/start": NPM_SOLIDSTART, + ...DENO_SOLIDSTART, + "solid-js": `npm:solid-js@${deps["npm:solid-js"]}`, + vinxi: `npm:vinxi@${deps["npm:vinxi"]}`, + "@fedify/solidstart": PACKAGE_VERSION, + } + : { + "@dotenvx/dotenvx": deps["npm:@dotenvx/dotenvx"], + "@solidjs/router": deps["npm:@solidjs/router"], + "@solidjs/start": deps["npm:@solidjs/start"], + "solid-js": deps["npm:solid-js"], + vinxi: deps["npm:vinxi"], + "@fedify/solidstart": PACKAGE_VERSION, + }; + +const DENO_SOLIDSTART = Object.fromEntries([ + "client", + "config", + "middleware", + "router", + "server", +].map((pkg) => [`@solidjs/start/${pkg}`, `${NPM_SOLIDSTART}/${pkg}`])); + const TASKS = { deno: { dev: "deno run -A npm:vinxi dev", From bef0b7f19c2e6639b3fcc84a35de6fad676fb09d Mon Sep 17 00:00:00 2001 From: ChanHaeng Lee <61987505+2chanhaeng@users.noreply.github.com> Date: Wed, 8 Apr 2026 01:56:36 +0900 Subject: [PATCH 15/15] Update CHANGES.md for @fedify/init fixes --- CHANGES.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index e225318e4..13158e9a2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -47,6 +47,17 @@ To be released. [#601]: https://github.com/fedify-dev/fedify/pull/601 [#652]: https://github.com/fedify-dev/fedify/pull/652 +### @fedify/init + + - Fixed errors when using `fedify init` with certain web framework + integration packages (Astro, ElysiaJS, Nitro) alongside `@fedify/mysql`. + Environment variables are now properly loaded at runtime, resolving the + `TypeError: Cannot read properties of undefined` from `mysql2`. + [[#649], [#656] by ChanHaeng Lee] + +[#649]: https://github.com/fedify-dev/fedify/issues/649 +[#656]: https://github.com/fedify-dev/fedify/pull/656 + Version 2.1.4 -------------