diff --git a/.github/actions/setup-deno/action.yaml b/.github/actions/setup-deno/action.yaml index 2cf9a04a..6c6f30b6 100644 --- a/.github/actions/setup-deno/action.yaml +++ b/.github/actions/setup-deno/action.yaml @@ -6,4 +6,4 @@ runs: steps: - uses: denoland/setup-deno@v2 with: - deno-version: 2.6.10 # Keep in sync with mise.toml + deno-version: 2.7.1 # Keep in sync with mise.toml diff --git a/CHANGES.md b/CHANGES.md index 407739b5..6189ca7a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,6 +8,14 @@ Version 2.1.0 To be released. +### @fedify/init + + - Changed `fedify init` to add `"temporal"` to `deno.json`'s `"unstable"` + field only when the installed Deno version is earlier than 2.7.0. + On Deno 2.7.0 or later, it is no longer added. + - `fedify init` now omits the `"unstable"` field entirely when no unstable + feature is required for the generated Deno project. + ### @fedify/vocab - Fixed `Endpoints.toJsonLd()` to no longer emit invalid diff --git a/deno.json b/deno.json index c2d6e0ae..cb95879e 100644 --- a/deno.json +++ b/deno.json @@ -67,8 +67,7 @@ "unstable": [ "fs", "kv", - "process", - "temporal" + "process" ], "exclude": [ "**/pnpm-lock.yaml", @@ -98,7 +97,7 @@ "codegen": "deno task -f @fedify/vocab compile", "check-versions": "deno run --allow-read --allow-write scripts/check_versions.ts", "check-all": { - "command": "deno fmt --check && deno lint && deno check --unstable-temporal $(deno eval 'import m from \"./deno.json\" with { type: \"json\" }; for (let p of m.workspace) console.log(p)')", + "command": "deno fmt --check && deno lint && deno check $(deno eval 'import m from \"./deno.json\" with { type: \"json\" }; for (let p of m.workspace) console.log(p)')", "dependencies": [ "check-versions", "codegen" diff --git a/docs/cli.md b/docs/cli.md index fd26d7e5..fd604877 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -69,7 +69,7 @@ command: ~~~~ sh [Linux] deno install \ -gA \ - --unstable-fs --unstable-kv --unstable-temporal \ + --unstable-fs --unstable-kv \ -n fedify \ jsr:@fedify/cli ~~~~ @@ -77,7 +77,7 @@ deno install \ ~~~~ sh [macOS] deno install \ -gA \ - --unstable-fs --unstable-kv --unstable-temporal \ + --unstable-fs --unstable-kv \ -n fedify \ jsr:@fedify/cli ~~~~ @@ -85,13 +85,16 @@ deno install \ ~~~~ powershell [Windows] deno install ` -gA ` - --unstable-fs --unstable-kv --unstable-temporal ` + --unstable-fs --unstable-kv ` -n fedify ` jsr:@fedify/cli ~~~~ ::: +On Deno versions earlier than 2.7.0, add `--unstable-temporal` to the install +command above. + [Deno]: https://deno.com/ ### Downloading the executable diff --git a/docs/install.md b/docs/install.md index b06c43bc..fb905fe0 100644 --- a/docs/install.md +++ b/docs/install.md @@ -36,11 +36,14 @@ bun install -g @fedify/cli ~~~~ ~~~~ sh [Deno] -deno install -gA --unstable-fs --unstable-kv --unstable-temporal -n fedify jsr:@fedify/cli +deno install -gA --unstable-fs --unstable-kv -n fedify jsr:@fedify/cli ~~~~ ::: +If you use Deno earlier than 2.7.0, add `--unstable-temporal` to the Deno +installation command above. + There are other ways to install the `fedify` command. Please refer to the [*Installation* section](./cli.md#installation) in the *CLI toolchain* docs. @@ -115,9 +118,9 @@ via the following command: deno add jsr:@fedify/fedify ~~~~ -Since Fedify requires [`Temporal`] API, which is an unstable feature in Deno as -of November 2024, you need to add the `"temporal"` to the `"unstable"` field of -the *deno.json* file: +Fedify requires the [`Temporal`] API. On Deno 2.7.0 or later, it is stable and +no extra setting is needed. On Deno versions earlier than 2.7.0, add +`"temporal"` to the `"unstable"` field in *deno.json*: ~~~~ json{5} { diff --git a/docs/tutorial/basics.md b/docs/tutorial/basics.md index 503d9eaa..64b85f6a 100644 --- a/docs/tutorial/basics.md +++ b/docs/tutorial/basics.md @@ -55,7 +55,7 @@ Let's create a new project directory and initialize a new project: ~~~~ sh [Deno] mkdir follow-server cd follow-server/ -echo '{ "unstable": ["kv", "temporal"] }' > deno.json +echo '{ "unstable": ["kv"] }' > deno.json deno add jsr:@fedify/fedify ~~~~ @@ -84,7 +84,7 @@ The above commands will create a *deno.json* (in case of Deno) or *package.json* ~~~~ json [Deno] { - "unstable": ["kv", "temporal"], + "unstable": ["kv"], "imports": { "@fedify/fedify": "jsr:@fedify/fedify@^1.1.0" } @@ -120,10 +120,9 @@ The above commands will create a *deno.json* (in case of Deno) or *package.json* ::: > [!NOTE] -> The [`"unstable"`] field in the *deno.json* file is required because Fedify -> uses [`Temporal`] API, which is an unstable feature in Deno as of November -> 2024. By adding `"temporal"` to the `"unstable"` field, you can use the -> Fedify framework without any issues. +> If you use Deno 2.7.0 or later, you do not need any extra setting for +> [`Temporal`]. If you use Deno earlier than 2.7.0, add `"temporal"` to the +> [`"unstable"`] field in *deno.json*. > [!NOTE] > In Bun and Node.js, we recommend adding [`"type": "module"`] to the @@ -143,8 +142,8 @@ The above commands will create a *deno.json* (in case of Deno) or *package.json* [Deno]: https://deno.com/ [Bun]: https://bun.sh/ [Node.js]: https://nodejs.org/ -[`"unstable"`]: https://docs.deno.com/runtime/manual/tools/unstable_flags/#configuring-flags-in-deno.json [`Temporal`]: https://tc39.es/proposal-temporal/docs/ +[`"unstable"`]: https://docs.deno.com/runtime/manual/tools/unstable_flags/#configuring-flags-in-deno.json [`"type": "module"`]: https://nodejs.org/api/packages.html#type [ESM]: https://nodejs.org/api/esm.html [CommonJS]: https://nodejs.org/docs/latest/api/modules.html diff --git a/mise.toml b/mise.toml index 6ff79338..f23e49f0 100644 --- a/mise.toml +++ b/mise.toml @@ -1,6 +1,6 @@ [tools] bun = "1.2.22" -deno = "2.6.10" +deno = "2.7.1" node = "22" pnpm = "10.28.0" @@ -59,7 +59,7 @@ run = "deno lint" [tasks."check:types"] description = "Check TypeScript types" -run = "deno check --unstable-temporal $(deno eval 'import m from \"./deno.json\" with { type: \"json\" }; for (let p of m.workspace) console.log(p)')" +run = "deno check $(deno eval 'import m from \"./deno.json\" with { type: \"json\" }; for (let p of m.workspace) console.log(p)')" [tasks."check:md"] description = "Check Markdown formatting" diff --git a/packages/cli/README.md b/packages/cli/README.md index ea24cbc5..b731eb26 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -52,7 +52,7 @@ command: # Linux/macOS deno install \ -A \ - --unstable-fs --unstable-kv --unstable-temporal \ + --unstable-fs --unstable-kv \ -n fedify \ jsr:@fedify/cli ~~~~ @@ -61,11 +61,14 @@ deno install \ # Windows deno install ` -A ` - --unstable-fs --unstable-kv --unstable-temporal ` + --unstable-fs --unstable-kv ` -n fedify ` jsr:@fedify/cli ~~~~ +On Deno versions earlier than 2.7.0, add `--unstable-temporal` to the install +command above. + [Deno]: https://deno.com/ ### Downloading the executable diff --git a/packages/init/src/action/configs.ts b/packages/init/src/action/configs.ts index 2d58b87b..9726852c 100644 --- a/packages/init/src/action/configs.ts +++ b/packages/init/src/action/configs.ts @@ -7,7 +7,9 @@ import { pipe, toArray, } from "@fxts/core/index.js"; +import { getLogger } from "@logtape/logtape"; import { uniq } from "es-toolkit"; +import { execFileSync } from "node:child_process"; import { realpathSync } from "node:fs"; import { join as joinPath, relative } from "node:path"; import biome from "../json/biome.json" with { type: "json" }; @@ -22,6 +24,8 @@ import { merge } from "../utils.ts"; import { PACKAGES_PATH } from "./const.ts"; import { getDependencies, getDevDependencies, joinDepsReg } from "./deps.ts"; +const logger = getLogger(["fedify", "init", "action", "configs"]); + /** * Loads Deno configuration object with compiler options, unstable features, and tasks. * Combines unstable features required by KV store and message queue with framework-specific options. @@ -31,30 +35,81 @@ import { getDependencies, getDevDependencies, joinDepsReg } from "./deps.ts"; */ export const loadDenoConfig = ( data: InitCommandData, -) => ({ - path: "deno.json", - data: { - ...pick(["compilerOptions", "tasks"], data.initializer), - unstable: getUnstable(data), - nodeModulesDir: "auto", - imports: joinDepsReg("deno")(getDependencies(data)), - lint: { plugins: ["jsr:@fedify/lint"] }, - ...(data.testMode ? { links: getLinks(data) } : {}), - }, -}); +) => { + const unstable = getUnstable(data); + return { + path: "deno.json", + data: { + ...pick(["compilerOptions", "tasks"], data.initializer), + ...(unstable.length > 0 ? { unstable } : {}), + nodeModulesDir: "auto", + imports: joinDepsReg("deno")(getDependencies(data)), + lint: { plugins: ["jsr:@fedify/lint"] }, + ...(data.testMode ? { links: getLinks(data) } : {}), + }, + }; +}; const getUnstable = >({ kv: { denoUnstable: kv = [] }, mq: { denoUnstable: mq = [] }, }: T) => pipe( - ["temporal"], + needsUnstableTemporal() ? ["temporal"] : [], concat(kv), concat(mq), toArray, uniq, ); +const TEMPORAL_STABLE_FROM = [2, 7, 0] as const; + +const needsUnstableTemporal = (): boolean => { + const version = getInstalledDenoVersion(); + if (version == null) return true; + return compareVersions(version, TEMPORAL_STABLE_FROM) < 0; +}; + +const getInstalledDenoVersion = (): [number, number, number] | null => { + const deno = getDenoVersionFromRuntime(); + if (deno != null) return deno; + try { + const output = execFileSync("deno", ["--version"], { + encoding: "utf8", + stdio: ["ignore", "pipe", "ignore"], + }); + const version = output.match(/^deno\s+(\d+)\.(\d+)\.(\d+)/m); + if (version == null) return null; + return [Number(version[1]), Number(version[2]), Number(version[3])]; + } catch (error) { + logger.debug( + "Failed to get Deno version by executing `deno --version`: {error}", + { error }, + ); + return null; + } +}; + +const getDenoVersionFromRuntime = (): [number, number, number] | null => { + const deno = (globalThis as { Deno?: { version?: { deno?: string } } }).Deno + ?.version?.deno; + if (deno == null) return null; + const version = deno.match(/^(\d+)\.(\d+)\.(\d+)/); + if (version == null) return null; + return [Number(version[1]), Number(version[2]), Number(version[3])]; +}; + +const compareVersions = ( + a: readonly [number, number, number], + b: readonly [number, number, number], +): number => { + for (let i = 0; i < a.length; i++) { + if (a[i] < b[i]) return -1; + if (a[i] > b[i]) return 1; + } + return 0; +}; + const getLinks = < T extends Pick, >({ kv, mq, initializer, dir }: T) =>