diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 106830f75682..621b86243425 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,8 +19,8 @@ jobs: fail-fast: false matrix: deno: - - v1.x - - v2.x + - lts + - vx.x.x - canary os: - ubuntu-latest diff --git a/README.md b/README.md index affd5f7d43b3..90122b8465bc 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,8 @@ [![ci](https://github.com/denoland/std/actions/workflows/ci.yml/badge.svg)](https://github.com/denoland/std/actions/workflows/ci.yml) High-quality APIs for [Deno](https://deno.com/) and the web. Use fearlessly. +Supports up to the +[current Deno LTS version](https://docs.deno.com/runtime/fundamentals/stability_and_releases/#long-term-support-(lts)). > [!IMPORTANT] > Newer versions of the Standard Library are now hosted on diff --git a/_tools/lint_plugin_test.ts b/_tools/lint_plugin_test.ts index 2d98cd797963..12e490118a0a 100644 --- a/_tools/lint_plugin_test.ts +++ b/_tools/lint_plugin_test.ts @@ -2,6 +2,7 @@ // @ts-nocheck Deno.lint namespace does not pass type checking in Deno 1.x import { assertEquals } from "@std/assert/equals"; +import { IS_LINT_PLUGIN_SUPPORTED } from "../internal/support.ts"; import lintPlugin from "./lint_plugin.ts"; function assertLintPluginDiagnostics( @@ -18,7 +19,7 @@ function assertLintPluginDiagnostics( } Deno.test("deno-style-guide/prefer-private-field", { - ignore: !Deno.version.deno.startsWith("2"), + ignore: !IS_LINT_PLUGIN_SUPPORTED, }, () => { // Good assertLintPluginDiagnostics( @@ -57,7 +58,7 @@ class MyClass { }); Deno.test("deno-style-guide/no-top-level-arrow-syntax", { - ignore: !Deno.version.deno.startsWith("2"), + ignore: !IS_LINT_PLUGIN_SUPPORTED, }, () => { // Bad assertLintPluginDiagnostics( @@ -98,7 +99,7 @@ function foo() { }); Deno.test("deno-style-guide/no-external-code", { - ignore: !Deno.version.deno.startsWith("2"), + ignore: !IS_LINT_PLUGIN_SUPPORTED, }, () => { // Good assertLintPluginDiagnostics('import { walk } from "@std/fs/walk";', []); @@ -128,7 +129,7 @@ import { bad } from "jsr:@malicious-muffin/bad"; }); Deno.test("deno-style-guide/naming-convention", { - ignore: !Deno.version.deno.startsWith("2"), + ignore: !IS_LINT_PLUGIN_SUPPORTED, }, () => { // Good assertLintPluginDiagnostics( @@ -243,7 +244,7 @@ enum enumName { }); Deno.test("deno-style-guide/error-message", { - ignore: !Deno.version.deno.startsWith("2"), + ignore: !IS_LINT_PLUGIN_SUPPORTED, }, () => { // Good assertLintPluginDiagnostics( @@ -312,7 +313,7 @@ new CustomError("Can't parse input"); }); Deno.test("deno-style-guide/exported-function-args-maximum", { - ignore: !Deno.version.deno.startsWith("2"), + ignore: !IS_LINT_PLUGIN_SUPPORTED, }, () => { // Good assertLintPluginDiagnostics( diff --git a/dotenv/mod.ts b/dotenv/mod.ts index c59e2e156bf7..4704441f7a6f 100644 --- a/dotenv/mod.ts +++ b/dotenv/mod.ts @@ -58,6 +58,8 @@ export interface LoadOptions { * * @param options Options for loading the environment variables. * @returns The parsed environment variables. + * @throws {Deno.errors.NotCapable} If the required `--allow-env` permission is + * not granted. */ export function loadSync( options: LoadOptions = {}, diff --git a/dotenv/mod_test.ts b/dotenv/mod_test.ts index c17eaa1fa73e..7f779c3cc1f1 100644 --- a/dotenv/mod_test.ts +++ b/dotenv/mod_test.ts @@ -8,7 +8,6 @@ import { } from "@std/assert"; import { load, type LoadOptions, loadSync } from "./mod.ts"; import * as path from "@std/path"; -import { IS_DENO_2 } from "../internal/_is_deno_2.ts"; const moduleDir = path.dirname(path.fromFileUrl(import.meta.url)); const testdataDir = path.resolve(moduleDir, "testdata"); @@ -174,11 +173,7 @@ Deno.test( }; assertThrows( () => loadSync(loadOptions), - IS_DENO_2 - // TODO(iuioiua): Just use `Deno.errors.NotCapable` once Deno 2 is released. - // deno-lint-ignore no-explicit-any - ? (Deno as any).errors.NotCapable - : Deno.errors.PermissionDenied, + Deno.errors.NotCapable, `Requires env access to "EMPTY", run again with the --allow-env flag`, ); }, diff --git a/fs/ensure_dir.ts b/fs/ensure_dir.ts index 9990020475a0..bbfa1749443c 100644 --- a/fs/ensure_dir.ts +++ b/fs/ensure_dir.ts @@ -23,6 +23,8 @@ import { getFileInfoType } from "./_get_file_info_type.ts"; * * await ensureDir("./bar"); * ``` + * @throws {Deno.errors.NotCapable} If the required `--allow-write` permission + * is not granted. */ export async function ensureDir(dir: string | URL) { try { @@ -71,6 +73,9 @@ export async function ensureDir(dir: string | URL) { * * ensureDirSync("./bar"); * ``` + * + * @throws {Deno.errors.NotCapable} If the required `--allow-write` permission + * is not granted. */ export function ensureDirSync(dir: string | URL) { try { diff --git a/fs/ensure_dir_test.ts b/fs/ensure_dir_test.ts index e1f92e5ab3aa..7f0863424200 100644 --- a/fs/ensure_dir_test.ts +++ b/fs/ensure_dir_test.ts @@ -3,7 +3,6 @@ import { assertEquals, assertRejects, assertThrows } from "@std/assert"; import * as path from "@std/path"; import { ensureDir, ensureDirSync } from "./ensure_dir.ts"; import { ensureFile, ensureFileSync } from "./ensure_file.ts"; -import { IS_DENO_2 } from "../internal/_is_deno_2.ts"; const moduleDir = path.dirname(path.fromFileUrl(import.meta.url)); const testdataDir = path.resolve(moduleDir, "testdata", "ensure_dir"); @@ -183,12 +182,8 @@ Deno.test({ // ensureDir fails because this test doesn't have write permissions, // but don't swallow that error. await assertRejects( - async () => await ensureDir(baseDir), - IS_DENO_2 - // TODO(iuioiua): Just use `Deno.errors.NotCapable` once Deno 2 is released. - // deno-lint-ignore no-explicit-any - ? (Deno as any).errors.NotCapable - : Deno.errors.PermissionDenied, + () => ensureDir(baseDir), + Deno.errors.NotCapable, ); }, }); @@ -206,11 +201,7 @@ Deno.test({ // but don't swallow that error. assertThrows( () => ensureDirSync(baseDir), - IS_DENO_2 - // TODO(iuioiua): Just use `Deno.errors.NotCapable` once Deno 2 is released. - // deno-lint-ignore no-explicit-any - ? (Deno as any).errors.NotCapable - : Deno.errors.PermissionDenied, + Deno.errors.NotCapable, ); }, }); diff --git a/fs/ensure_file.ts b/fs/ensure_file.ts index bb7ddd703eaf..1e72a57fdecb 100644 --- a/fs/ensure_file.ts +++ b/fs/ensure_file.ts @@ -25,6 +25,9 @@ import { toPathString } from "./_to_path_string.ts"; * * await ensureFile("./folder/targetFile.dat"); * ``` + * + * @throws {Deno.errors.NotCapable} If the required `--allow-write` permission + * is not granted. */ export async function ensureFile(filePath: string | URL): Promise { try { @@ -72,6 +75,9 @@ export async function ensureFile(filePath: string | URL): Promise { * * ensureFileSync("./folder/targetFile.dat"); * ``` + * + * @throws {Deno.errors.NotCapable} If the required `--allow-write` permission + * is not granted. */ export function ensureFileSync(filePath: string | URL): void { try { diff --git a/fs/ensure_file_test.ts b/fs/ensure_file_test.ts index e54d9c9d404d..22ca057a3e92 100644 --- a/fs/ensure_file_test.ts +++ b/fs/ensure_file_test.ts @@ -2,7 +2,6 @@ import { assertRejects, assertThrows } from "@std/assert"; import * as path from "@std/path"; import { ensureFile, ensureFileSync } from "./ensure_file.ts"; -import { IS_DENO_2 } from "../internal/_is_deno_2.ts"; const moduleDir = path.dirname(path.fromFileUrl(import.meta.url)); const testdataDir = path.resolve(moduleDir, "testdata"); @@ -133,12 +132,8 @@ Deno.test({ // ensureFile fails because this test doesn't have write permissions, // but don't swallow that error. await assertRejects( - async () => await ensureFile(testFile), - IS_DENO_2 - // TODO(iuioiua): Just use `Deno.errors.NotCapable` once Deno 2 is released. - // deno-lint-ignore no-explicit-any - ? (Deno as any).errors.NotCapable - : Deno.errors.PermissionDenied, + () => ensureFile(testFile), + Deno.errors.NotCapable, ); }, }); @@ -154,11 +149,7 @@ Deno.test({ // but don't swallow that error. assertThrows( () => ensureFileSync(testFile), - IS_DENO_2 - // TODO(iuioiua): Just use `Deno.errors.NotCapable` once Deno 2 is released. - // deno-lint-ignore no-explicit-any - ? (Deno as any).errors.NotCapable - : Deno.errors.PermissionDenied, + Deno.errors.NotCapable, ); }, }); diff --git a/fs/ensure_symlink.ts b/fs/ensure_symlink.ts index 046534fd9090..9c0fef46958f 100644 --- a/fs/ensure_symlink.ts +++ b/fs/ensure_symlink.ts @@ -56,6 +56,9 @@ function getSymlinkOption( * // Ensures the link `./folder/targetFile.link.dat` exists and points to `./folder/targetFile.dat` * await ensureSymlink("./targetFile.dat", "./folder/targetFile.link.dat"); * ``` + * + * @throws {Deno.errors.NotCapable} If the required `--allow-write` permission + * is not granted. */ export async function ensureSymlink( target: string | URL, @@ -134,6 +137,9 @@ export async function ensureSymlink( * // Ensures the link `./folder/targetFile.link.dat` exists and points to `./folder/targetFile.dat` * ensureSymlinkSync("./targetFile.dat", "./folder/targetFile.link.dat"); * ``` + * + * @throws {Deno.errors.NotCapable} If the required `--allow-write` permission + * is not granted. */ export function ensureSymlinkSync( target: string | URL, diff --git a/fs/ensure_symlink_test.ts b/fs/ensure_symlink_test.ts index e642892c6f8a..f62bf29f8735 100644 --- a/fs/ensure_symlink_test.ts +++ b/fs/ensure_symlink_test.ts @@ -9,7 +9,6 @@ import { } from "@std/assert"; import * as path from "@std/path"; import { ensureSymlink, ensureSymlinkSync } from "./ensure_symlink.ts"; -import { IS_DENO_2 } from "../internal/_is_deno_2.ts"; const moduleDir = path.dirname(path.fromFileUrl(import.meta.url)); const testdataDir = path.resolve(moduleDir, "testdata"); @@ -354,14 +353,8 @@ Deno.test( const linkFile = path.join(testdataDir, "link.ts"); await assertRejects( - async () => { - await ensureSymlink(testFile, linkFile); - }, - IS_DENO_2 - // TODO(iuioiua): Just use `Deno.errors.NotCapable` once Deno 2 is released. - // deno-lint-ignore no-explicit-any - ? (Deno as any).errors.NotCapable - : Deno.errors.PermissionDenied, + () => ensureSymlink(testFile, linkFile), + Deno.errors.NotCapable, ); }, ); @@ -374,14 +367,8 @@ Deno.test( const linkFile = path.join(testdataDir, "link.ts"); assertThrows( - () => { - ensureSymlinkSync(testFile, linkFile); - }, - IS_DENO_2 - // TODO(iuioiua): Just use `Deno.errors.NotCapable` once Deno 2 is released. - // deno-lint-ignore no-explicit-any - ? (Deno as any).errors.NotCapable - : Deno.errors.PermissionDenied, + () => ensureSymlinkSync(testFile, linkFile), + Deno.errors.NotCapable, ); }, ); diff --git a/fs/expand_glob.ts b/fs/expand_glob.ts index 8c7fba39129a..64bace0c91f6 100644 --- a/fs/expand_glob.ts +++ b/fs/expand_glob.ts @@ -109,6 +109,9 @@ function comparePath(a: WalkEntry, b: WalkEntry): number { * @returns An async iterator that yields each walk entry matching the glob * pattern. * + * @throws {Deno.errors.NotCapable} If the required `--allow-read` permission + * is not granted. + * * @example Basic usage * * File structure: diff --git a/fs/expand_glob_test.ts b/fs/expand_glob_test.ts index 6a65e16043b9..db43c13672c5 100644 --- a/fs/expand_glob_test.ts +++ b/fs/expand_glob_test.ts @@ -12,7 +12,6 @@ import { type ExpandGlobOptions, expandGlobSync, } from "./expand_glob.ts"; -import { IS_DENO_2 } from "../internal/_is_deno_2.ts"; async function expandGlobArray( globString: string, @@ -114,28 +113,16 @@ Deno.test( async function () { { await assertRejects( - async () => { - await expandGlobArray("*", EG_OPTIONS); - }, - IS_DENO_2 - // TODO(iuioiua): Just use `Deno.errors.NotCapable` once Deno 2 is released. - // deno-lint-ignore no-explicit-any - ? (Deno as any).errors.NotCapable - : Deno.errors.PermissionDenied, + () => expandGlobArray("*", EG_OPTIONS), + Deno.errors.NotCapable, "run again with the --allow-read flag", ); } { assertThrows( - () => { - expandGlobSyncArray("*", EG_OPTIONS); - }, - IS_DENO_2 - // TODO(iuioiua): Just use `Deno.errors.NotCapable` once Deno 2 is released. - // deno-lint-ignore no-explicit-any - ? (Deno as any).errors.NotCapable - : Deno.errors.PermissionDenied, + () => expandGlobSyncArray("*", EG_OPTIONS), + Deno.errors.NotCapable, "run again with the --allow-read flag", ); } @@ -307,11 +294,7 @@ Deno.test( assert(!success); assertEquals(code, 1); assertEquals(decoder.decode(stdout), ""); - assertStringIncludes( - decoder.decode(stderr), - // TODO(iuioiua): Just use `Deno.errors.NotCapable` once Deno 2 is released. - IS_DENO_2 ? "NotCapable" : "PermissionDenied", - ); + assertStringIncludes(decoder.decode(stderr), "NotCapable"); }, ); diff --git a/fs/unstable_rename_test.ts b/fs/unstable_rename_test.ts index e4b6478dc18b..7408b57f7e34 100644 --- a/fs/unstable_rename_test.ts +++ b/fs/unstable_rename_test.ts @@ -1,7 +1,6 @@ // Copyright 2018-2025 the Deno authors. MIT license. import { assert, assertRejects, assertThrows } from "@std/assert"; -import { lessOrEqual, parse as parseSemver } from "@std/semver"; import { rename, renameSync } from "./unstable_rename.ts"; import { NotFound } from "./unstable_errors.js"; import { mkdir, mkdtemp, open, rm, stat, symlink } from "node:fs/promises"; @@ -17,11 +16,11 @@ import { statSync, symlinkSync, } from "node:fs"; +import { isDenoVersionGreaterOrEqual } from "../internal/support.ts"; // In Deno 2.2.2 or earlier, the `rename` function has an issue on Windows. const RENAME_HAS_ISSUE = Deno.version && - parseSemver(Deno.version.deno).build?.length === 0 && // not canary - lessOrEqual(parseSemver(Deno.version.deno), parseSemver("2.2.2")) && + !isDenoVersionGreaterOrEqual("2.2.3") && platform() === "win32"; /** Tests if the original file/directory is missing since the file is renamed. diff --git a/http/file_server_test.ts b/http/file_server_test.ts index d14a2c097cb5..6a1e876113ab 100644 --- a/http/file_server_test.ts +++ b/http/file_server_test.ts @@ -22,7 +22,7 @@ import denoConfig from "./deno.json" with { type: "json" }; import { MINUTE } from "@std/datetime/constants"; import { getAvailablePort } from "@std/net/get-available-port"; import { concat } from "@std/bytes/concat"; -import { lessThan, parse as parseSemver } from "@std/semver"; +import { isDenoVersionGreaterOrEqual } from "../internal/support.ts"; const moduleDir = dirname(fromFileUrl(import.meta.url)); const testdataDir = resolve(moduleDir, "testdata"); @@ -33,11 +33,9 @@ const serveDirOptions: ServeDirOptions = { showDotfiles: true, enableCors: true, }; -const denoVersion = parseSemver(Deno.version.deno); -const isCanary = denoVersion.build ? denoVersion.build.length > 0 : false; // FileInfo.mode is not available on Windows before Deno 2.1.0 const fsModeUnavailable = Deno.build.os === "windows" && - lessThan(denoVersion, parseSemver("2.1.0")) && !isCanary; + !isDenoVersionGreaterOrEqual("2.1.0"); const TEST_FILE_PATH = join(testdataDir, "test_file.txt"); const TEST_FILE_STAT = await Deno.stat(TEST_FILE_PATH); diff --git a/internal/_is_deno_2.ts b/internal/_is_deno_2.ts deleted file mode 100644 index 041d49876ba0..000000000000 --- a/internal/_is_deno_2.ts +++ /dev/null @@ -1,3 +0,0 @@ -// Copyright 2018-2025 the Deno authors. MIT license. -// TODO(iuioiua): Remove this file when Deno 2 is released. -export const IS_DENO_2 = Deno.version.deno.startsWith("2."); diff --git a/internal/deno.json b/internal/deno.json index faf57cb9278b..ded4a7b17b77 100644 --- a/internal/deno.json +++ b/internal/deno.json @@ -9,6 +9,7 @@ "./diff": "./diff.ts", "./format": "./format.ts", "./styles": "./styles.ts", - "./types": "./types.ts" + "./types": "./types.ts", + "./support": "./support.ts" } } diff --git a/internal/mod.ts b/internal/mod.ts index a9fad6e96dcc..39e5ed991c56 100644 --- a/internal/mod.ts +++ b/internal/mod.ts @@ -1,5 +1,4 @@ // Copyright 2018-2025 the Deno authors. MIT license. -// This module is browser compatible. /** * Internal utilities for the public API of the Deno Standard Library. * @@ -43,3 +42,4 @@ export * from "./diff_str.ts"; export * from "./format.ts"; export * from "./styles.ts"; export * from "./types.ts"; +export * from "./support.ts"; diff --git a/internal/support.ts b/internal/support.ts new file mode 100644 index 000000000000..6dd4baaccc34 --- /dev/null +++ b/internal/support.ts @@ -0,0 +1,36 @@ +// Copyright 2018-2025 the Deno authors. MIT license. +import { greaterOrEqual } from "../semver/greater_or_equal.ts"; +import { parse } from "../semver/parse.ts"; + +/** + * Checks if the current Deno version is greater than or equal to the specified + * minimum version. + * + * @param minVersion Minimum version to check against. + * @returns `true` if the current Deno version is greater than or equal to the + * specified minimum version, `false` otherwise. + * + * @example Basic usage + * ```ts no-assert + * import { isDenoVersionGreaterOrEqual } from "@std/internal/support"; + * + * if (isDenoVersionGreaterOrEqual("1.0.0")) { + * console.log("Deno version is greater than or equal to 1.0.0"); + * } else { + * console.log("Deno version is less than 1.0.0"); + * } + * ``` + */ +export function isDenoVersionGreaterOrEqual(minVersion: string): boolean { + // deno-lint-ignore no-explicit-any + return (Deno.version as any) && + greaterOrEqual(parse(Deno.version.deno), parse(minVersion)); +} + +/** + * Whether the current Deno version supports the + * {@link https://docs.deno.com/runtime/reference/lint_plugins/ | lint plugin API}. + */ +export const IS_LINT_PLUGIN_SUPPORTED: boolean = isDenoVersionGreaterOrEqual( + "2.2.0", +); diff --git a/testing/_snapshot_utils.ts b/testing/_snapshot_utils.ts index 7f70de10082f..88967b797989 100644 --- a/testing/_snapshot_utils.ts +++ b/testing/_snapshot_utils.ts @@ -99,6 +99,3 @@ export function getSnapshotNotMatchMessage( `Snapshot does not match:\n${diffMsg}\nTo update snapshots, run\n deno test --allow-read --allow-write [files]... -- --update\n`; return getErrorMessage(message, options); } - -// TODO (WWRS): Remove this when we drop support for Deno 1.x -export const LINT_SUPPORTED = !Deno.version.deno.startsWith("1."); diff --git a/testing/unstable_snapshot.ts b/testing/unstable_snapshot.ts index 4e8fd1998d12..57d7552e8885 100644 --- a/testing/unstable_snapshot.ts +++ b/testing/unstable_snapshot.ts @@ -9,9 +9,9 @@ import { getIsUpdate, getOptions, getSnapshotNotMatchMessage, - LINT_SUPPORTED, serialize, } from "./_snapshot_utils.ts"; +import { IS_LINT_PLUGIN_SUPPORTED } from "@std/internal/support"; /** * The options for {@linkcode assertInlineSnapshot}. @@ -95,7 +95,7 @@ const updateRequests: SnapshotUpdateRequest[] = []; function updateSnapshots() { if (updateRequests.length === 0) return; - if (!LINT_SUPPORTED) { + if (!IS_LINT_PLUGIN_SUPPORTED) { throw new Error( "Deno versions before 2.2.0 do not support Deno.lint, which is required to update inline snapshots", ); diff --git a/testing/unstable_snapshot_test.ts b/testing/unstable_snapshot_test.ts index 993b25b18a51..54acbc691b59 100644 --- a/testing/unstable_snapshot_test.ts +++ b/testing/unstable_snapshot_test.ts @@ -6,7 +6,7 @@ import { assertInlineSnapshot, createAssertInlineSnapshot, } from "./unstable_snapshot.ts"; -import { LINT_SUPPORTED } from "./_snapshot_utils.ts"; +import { IS_LINT_PLUGIN_SUPPORTED } from "../internal/support.ts"; const SNAPSHOT_MODULE_URL = toFileUrl(join( dirname(fromFileUrl(import.meta.url)), @@ -80,7 +80,7 @@ Deno.test("assertInlineSnapshot()", () => { }); Deno.test("assertInlineSnapshot() formats", async () => { - if (!LINT_SUPPORTED) return; + if (!IS_LINT_PLUGIN_SUPPORTED) return; const fileContents = `import { assertInlineSnapshot } from "${SNAPSHOT_MODULE_URL}"; @@ -143,7 +143,7 @@ Deno.test("format test", async () => { }); Deno.test("assertInlineSnapshot() counts lines and columns like V8", async () => { - if (!LINT_SUPPORTED) return; + if (!IS_LINT_PLUGIN_SUPPORTED) return; const tempDir = await Deno.makeTempDir(); const countTestFile = join(tempDir, "count_test.ts");