diff --git a/lib/internal/webstorage.js b/lib/internal/webstorage.js index 989ccb2dd40eea..28b5df3f3da835 100644 --- a/lib/internal/webstorage.js +++ b/lib/internal/webstorage.js @@ -1,9 +1,9 @@ 'use strict'; const { ObjectDefineProperties, + Proxy, } = primordials; const { getOptionValue } = require('internal/options'); -const { lazyDOMException } = require('internal/util'); const { kConstructorKey, Storage } = internalBinding('webstorage'); const { getValidatedPath } = require('internal/fs/utils'); const kInMemoryPath = ':memory:'; @@ -21,17 +21,34 @@ ObjectDefineProperties(module.exports, { enumerable: true, get() { if (lazyLocalStorage === undefined) { - // For consistency with the web specification, throw from the accessor - // if the local storage path is not provided. const location = getOptionValue('--localstorage-file'); + if (location === '') { - throw lazyDOMException( - 'Cannot initialize local storage without a `--localstorage-file` path', - 'SecurityError', - ); - } + let warningEmitted = false; + const handler = { + __proto__: null, + get(target, prop) { + if (!warningEmitted) { + process.emitWarning('`--localstorage-file` was provided without a valid path'); + warningEmitted = true; + } + + return undefined; + }, + set(target, prop, value) { + if (!warningEmitted) { + process.emitWarning('`--localstorage-file` was provided without a valid path'); + warningEmitted = true; + } - lazyLocalStorage = new Storage(kConstructorKey, getValidatedPath(location)); + return false; + }, + }; + + lazyLocalStorage = new Proxy({}, handler); + } else { + lazyLocalStorage = new Storage(kConstructorKey, getValidatedPath(location)); + } } return lazyLocalStorage; diff --git a/test/common/index.js b/test/common/index.js index 3f612afbff5ef9..11465d7b5d77be 100755 --- a/test/common/index.js +++ b/test/common/index.js @@ -59,14 +59,6 @@ const hasSQLite = Boolean(process.versions.sqlite); const hasQuic = hasCrypto && !!process.features.quic; -const hasLocalStorage = (() => { - try { - return hasSQLite && globalThis.localStorage !== undefined; - } catch { - return false; - } -})(); - /** * Parse test metadata from the specified file. * @param {string} filename - The name of the file to parse. @@ -359,6 +351,7 @@ const knownGlobals = new Set([ 'CompressionStream', 'DecompressionStream', 'Storage', + 'localStorage', 'sessionStorage', ].forEach((i) => { if (globalThis[i] !== undefined) { @@ -373,10 +366,6 @@ if (hasCrypto) { knownGlobals.add(globalThis.SubtleCrypto); } -if (hasLocalStorage) { - knownGlobals.add(globalThis.localStorage); -} - const { Worker } = require('node:worker_threads'); knownGlobals.add(Worker); @@ -401,11 +390,6 @@ if (process.env.NODE_TEST_KNOWN_GLOBALS !== '0') { if (val === 'crypto' && !hasCrypto) { continue; } - // globalThis.localStorage is a getter that throws if Node.js was - // executed without a --localstorage-file path. - if (val === 'localStorage' && !hasLocalStorage) { - continue; - } if (!knownGlobals.has(globalThis[val])) { leaked.push(val); } @@ -956,7 +940,6 @@ const common = { hasQuic, hasInspector, hasSQLite, - hasLocalStorage, invalidArgTypeHelper, isAlive, isASan, diff --git a/test/common/index.mjs b/test/common/index.mjs index 7516eb68a53b7a..471d58b7dbbfe0 100644 --- a/test/common/index.mjs +++ b/test/common/index.mjs @@ -19,7 +19,6 @@ const { hasQuic, hasInspector, hasSQLite, - hasLocalStorage, hasIntl, hasIPv6, isAIX, @@ -73,7 +72,6 @@ export { hasQuic, hasInspector, hasSQLite, - hasLocalStorage, hasIntl, hasIPv6, isAIX, diff --git a/test/parallel/test-assert-checktag.js b/test/parallel/test-assert-checktag.js index b1c3660ab4a693..b86a1bde7f096d 100644 --- a/test/parallel/test-assert-checktag.js +++ b/test/parallel/test-assert-checktag.js @@ -1,5 +1,5 @@ 'use strict'; -const { hasCrypto, hasLocalStorage } = require('../common'); +const { hasCrypto } = require('../common'); const { test } = require('node:test'); const assert = require('assert'); @@ -12,7 +12,7 @@ const assert = require('assert'); if (process.stdout.isTTY) process.env.NODE_DISABLE_COLORS = '1'; -test({ skip: !hasCrypto || !hasLocalStorage }, () => { +test('', { skip: !hasCrypto }, () => { // See https://github.com/nodejs/node/issues/10258 { const date = new Date('2016'); diff --git a/test/parallel/test-webstorage.js b/test/parallel/test-webstorage.js index cc5f0366f7116e..bc48c9643773b5 100644 --- a/test/parallel/test-webstorage.js +++ b/test/parallel/test-webstorage.js @@ -41,13 +41,13 @@ test('sessionStorage is not persisted', async () => { assert.strictEqual((await readdir(tmpdir.path)).length, 0); }); -test('localStorage throws without --localstorage-file', async () => { +test('localStorage emits a warning when used without --localstorage-file ', async () => { const cp = await spawnPromisified(process.execPath, [ - '-e', 'localStorage', + '-pe', 'localStorage.length', ]); - assert.strictEqual(cp.code, 1); + assert.strictEqual(cp.code, 0); assert.strictEqual(cp.signal, null); - assert.match(cp.stderr, /SecurityError:/); + assert.match(cp.stderr, /Warning: `--localstorage-file` was provided without a valid path/); }); test('localStorage is not persisted if it is unused', async () => {