| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 7698480 commit fa70327
5 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -404,8 +404,26 @@ function setupWebStorage() { | |||
| 404 | 404 | ||
| 405 | 405 | // https://html.spec.whatwg.org/multipage/webstorage.html#webstorage | |
| 406 | 406 | exposeLazyInterfaces(globalThis, 'internal/webstorage', ['Storage']); | |
| 407 | + | ||
| 408 | + // localStorage is non-enumerable when --localstorage-file is not provided | ||
| 409 | + // to avoid breaking {...globalThis} operations. | ||
| 410 | + const localStorageFile = getOptionValue('--localstorage-file'); | ||
| 411 | + let lazyLocalStorage; | ||
| 412 | + ObjectDefineProperty(globalThis, 'localStorage', { | ||
| 413 | + __proto__: null, | ||
| 414 | + enumerable: localStorageFile !== '', | ||
| 415 | + configurable: true, | ||
| 416 | + get() { | ||
| 417 | + lazyLocalStorage ??= require('internal/webstorage').localStorage; | ||
| 418 | + return lazyLocalStorage; | ||
| 419 | + }, | ||
| 420 | + set(value) { | ||
| 421 | + lazyLocalStorage = value; | ||
| 422 | + }, | ||
| 423 | + }); | ||
| 424 | + | ||
| 407 | 425 | defineReplaceableLazyAttribute(globalThis, 'internal/webstorage', [ | |
| 408 | - 'localStorage', 'sessionStorage', | ||
| 426 | + 'sessionStorage', | ||
| 409 | 427 | ]); | |
| 410 | 428 | } | |
| 411 | 429 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3,7 +3,6 @@ const { | |||
| 3 | 3 | ObjectDefineProperties, | |
| 4 | 4 | } = primordials; | |
| 5 | 5 | const { getOptionValue } = require('internal/options'); | |
| 6 | - const { lazyDOMException } = require('internal/util'); | ||
| 7 | 6 | const { kConstructorKey, Storage } = internalBinding('webstorage'); | |
| 8 | 7 | const { getValidatedPath } = require('internal/fs/utils'); | |
| 9 | 8 | const kInMemoryPath = ':memory:'; | |
@@ -12,26 +11,32 @@ module.exports = { Storage }; | |||
| 12 | 11 | ||
| 13 | 12 | let lazyLocalStorage; | |
| 14 | 13 | let lazySessionStorage; | |
| 14 | + let localStorageWarned = false; | ||
| 15 | + | ||
| 16 | + // Check at load time if localStorage file is provided to determine enumerability. | ||
| 17 | + // If not provided, localStorage is non-enumerable to avoid breaking {...globalThis}. | ||
| 18 | + const localStorageLocation = getOptionValue('--localstorage-file'); | ||
| 15 | 19 | ||
| 16 | 20 | ObjectDefineProperties(module.exports, { | |
| 17 | 21 | __proto__: null, | |
| 18 | 22 | localStorage: { | |
| 19 | 23 | __proto__: null, | |
| 20 | 24 | configurable: true, | |
| 21 | - enumerable: true, | ||
| 25 | + enumerable: localStorageLocation !== '', | ||
| 22 | 26 | get() { | |
| 23 | 27 | if (lazyLocalStorage === undefined) { | |
| 24 | - // For consistency with the web specification, throw from the accessor | ||
| 25 | - // if the local storage path is not provided. | ||
| 26 | - const location = getOptionValue('--localstorage-file'); | ||
| 27 | - if (location === '') { | ||
| 28 | - throw lazyDOMException( | ||
| 29 | - 'Cannot initialize local storage without a `--localstorage-file` path', | ||
| 30 | - 'SecurityError', | ||
| 31 | - ); | ||
| 28 | + if (localStorageLocation === '') { | ||
| 29 | + if (!localStorageWarned) { | ||
| 30 | + localStorageWarned = true; | ||
| 31 | + process.emitWarning( | ||
| 32 | + 'localStorage is not available because --localstorage-file was not provided.', | ||
| 33 | + 'ExperimentalWarning', | ||
| 34 | + ); | ||
| 35 | + } | ||
| 36 | + return undefined; | ||
| 32 | 37 | } | |
| 33 | 38 | ||
| 34 | - lazyLocalStorage = new Storage(kConstructorKey, getValidatedPath(location)); | ||
| 39 | + lazyLocalStorage = new Storage(kConstructorKey, getValidatedPath(localStorageLocation)); | ||
| 35 | 40 | } | |
| 36 | 41 | ||
| 37 | 42 | return lazyLocalStorage; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -71,11 +71,10 @@ const hasSQLite = Boolean(process.versions.sqlite); | |||
| 71 | 71 | const hasQuic = hasCrypto && !!process.features.quic; | |
| 72 | 72 | ||
| 73 | 73 | const hasLocalStorage = (() => { | |
| 74 | - try { | ||
| 75 | - return hasSQLite && globalThis.localStorage !== undefined; | ||
| 76 | - } catch { | ||
| 77 | - return false; | ||
| 78 | - } | ||
| 74 | + // Check enumerable property to avoid triggering the getter which emits a warning. | ||
| 75 | + // localStorage is enumerable only when --localstorage-file is provided. | ||
| 76 | + const desc = Object.getOwnPropertyDescriptor(globalThis, 'localStorage'); | ||
| 77 | + return hasSQLite && desc?.enumerable === true; | ||
| 79 | 78 | })(); | |
| 80 | 79 | ||
| 81 | 80 | /** | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -61,7 +61,12 @@ for (const moduleName of builtinModules) { | |||
| 61 | 61 | 'navigator', | |
| 62 | 62 | ]; | |
| 63 | 63 | if (common.hasSQLite) { | |
| 64 | - expected.push('localStorage', 'sessionStorage'); | ||
| 64 | + // sessionStorage is always enumerable when SQLite is available. | ||
| 65 | + // localStorage is only enumerable when --localstorage-file is provided. | ||
| 66 | + expected.push('sessionStorage'); | ||
| 67 | + if (common.hasLocalStorage) { | ||
| 68 | + expected.push('localStorage'); | ||
| 69 | + } | ||
| 65 | 70 | } | |
| 66 | 71 | assert.deepStrictEqual(new Set(Object.keys(globalThis)), new Set(expected)); | |
| 67 | 72 | expected.forEach((value) => { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -41,13 +41,22 @@ test('sessionStorage is not persisted', async () => { | |||
| 41 | 41 | assert.strictEqual((await readdir(tmpdir.path)).length, 0); | |
| 42 | 42 | }); | |
| 43 | 43 | ||
| 44 | - test('localStorage throws without --localstorage-file', async () => { | ||
| 44 | + test('localStorage returns undefined and warns without --localstorage-file', async () => { | ||
| 45 | 45 | const cp = await spawnPromisified(process.execPath, [ | |
| 46 | - '-e', 'localStorage', | ||
| 46 | + '-pe', 'localStorage', | ||
| 47 | 47 | ]); | |
| 48 | - assert.strictEqual(cp.code, 1); | ||
| 48 | + assert.strictEqual(cp.code, 0); | ||
| 49 | 49 | assert.strictEqual(cp.signal, null); | |
| 50 | - assert.match(cp.stderr, /SecurityError:/); | ||
| 50 | + assert.match(cp.stdout, /undefined/); | ||
| 51 | + assert.match(cp.stderr, /ExperimentalWarning:.*localStorage is not available/); | ||
| 52 | + }); | ||
| 53 | + | ||
| 54 | + test('localStorage is not enumerable without --localstorage-file', async () => { | ||
| 55 | + const cp = await spawnPromisified(process.execPath, [ | ||
| 56 | + '-pe', 'Object.keys(globalThis).includes("localStorage")', | ||
| 57 | + ]); | ||
| 58 | + assert.strictEqual(cp.code, 0); | ||
| 59 | + assert.match(cp.stdout, /false/); | ||
| 51 | 60 | }); | |
| 52 | 61 | ||
| 53 | 62 | test('localStorage is not persisted if it is unused', async () => { | |
| Back | FazBrowse Home | New Git URL |
0 commit comments