From d5b4d347dc2964e41b4e8482f1ca082e4c583675 Mon Sep 17 00:00:00 2001 From: "C. Spencer Beggs" Date: Thu, 10 Sep 2026 00:27:46 -0400 Subject: [PATCH] fix(vfs): don't read localStorage during feature detection in Node Node 26 exposes `localStorage` as a global accessor that emits an ExperimentalWarning when read - `typeof` included - unless the process was started with `--localstorage-file`. The existing feature detect reads it at module evaluation, so simply importing `@typescript/vfs` prints a warning on Node 26 before any API of the package is called. The surrounding try/catch does not help: the accessor warns and returns undefined rather than throwing. Skip the probe entirely in a bare Node process. The only thing the value is used for is looking up a `DEBUG` key, and in Node `process.env.DEBUG` already covers that. Hosts that expose both `process` and a DOM (Electron renderers) still probe as before, as do browsers. Also folds the `typeof localStorage.getItem === 'function'` guard added in #3450 into `hasLocalStorage`, where it belongs. Signed-off-by: C. Spencer Beggs Claude-Session: https://claude.ai/code/session_01TTDH2nJgCerAnT3DQPSTew --- .changeset/olive-tigers-hammer.md | 5 +++++ packages/typescript-vfs/src/index.ts | 22 +++++++++++++++++----- 2 files changed, 22 insertions(+), 5 deletions(-) create mode 100644 .changeset/olive-tigers-hammer.md diff --git a/.changeset/olive-tigers-hammer.md b/.changeset/olive-tigers-hammer.md new file mode 100644 index 000000000000..f718444cc40d --- /dev/null +++ b/.changeset/olive-tigers-hammer.md @@ -0,0 +1,5 @@ +--- +"@typescript/vfs": patch +--- + +Skip the localStorage feature detect in Node so importing the package no longer emits an ExperimentalWarning on Node 26 diff --git a/packages/typescript-vfs/src/index.ts b/packages/typescript-vfs/src/index.ts index 9dfeec90f1b7..cf1b4decebb6 100755 --- a/packages/typescript-vfs/src/index.ts +++ b/packages/typescript-vfs/src/index.ts @@ -17,13 +17,25 @@ interface LocalStorageLike { declare var localStorage: LocalStorageLike | undefined; declare var fetch: FetchLike | undefined; +const hasProcess = typeof process !== `undefined` + +// Node >= 26 exposes `localStorage` as a global accessor which emits an +// ExperimentalWarning when it is read - including via `typeof` - unless the process +// was started with `--localstorage-file`. Probing it here would print that warning at +// module evaluation for every consumer, so skip the probe entirely in a bare Node +// process, where `process.env.DEBUG` below is the only reachable way to opt in anyway. +// DOM-bearing hosts that also expose `process` (Electron renderers) still get probed. +const isBareNodeProcess = + hasProcess && typeof process.versions?.node === `string` && !(`window` in globalThis) + let hasLocalStorage = false -try { - hasLocalStorage = typeof localStorage !== `undefined` -} catch (error) { } +if (!isBareNodeProcess) { + try { + hasLocalStorage = typeof localStorage !== `undefined` && typeof localStorage.getItem === `function` + } catch (error) { } +} -const hasProcess = typeof process !== `undefined` -const shouldDebug = (hasLocalStorage && typeof localStorage!.getItem === 'function' && localStorage!.getItem("DEBUG")) || (hasProcess && process.env.DEBUG) +const shouldDebug = (hasLocalStorage && localStorage!.getItem("DEBUG")) || (hasProcess && process.env.DEBUG) const debugLog = shouldDebug ? console.log : (_message?: any, ..._optionalParams: any[]) => "" export interface VirtualTypeScriptEnvironment {