-
Notifications
You must be signed in to change notification settings - Fork 271
/
storageUtils.ts
158 lines (146 loc) · 4.36 KB
/
storageUtils.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const StorageTypes = ['localStorage', 'sessionStorage', 'none'] as const;
export type StorageType = (typeof StorageTypes)[number];
const DefaultStorageType: StorageType = 'localStorage';
/**
* Will return `null` if browser storage is unavailable (like running Docusaurus
* in an iframe). This should NOT be called in SSR.
*
* @see https://github.com/facebook/docusaurus/pull/4501
*/
function getBrowserStorage(
storageType: StorageType = DefaultStorageType,
): Storage | null {
if (typeof window === 'undefined') {
throw new Error(
'Browser storage is not available on Node.js/Docusaurus SSR process.',
);
}
if (storageType === 'none') {
return null;
}
try {
return window[storageType];
} catch (err) {
logOnceBrowserStorageNotAvailableWarning(err as Error);
return null;
}
}
let hasLoggedBrowserStorageNotAvailableWarning = false;
/**
* Poor man's memoization to avoid logging multiple times the same warning.
* Sometimes, `localStorage`/`sessionStorage` is unavailable due to browser
* policies.
*/
function logOnceBrowserStorageNotAvailableWarning(error: Error) {
if (!hasLoggedBrowserStorageNotAvailableWarning) {
console.warn(
`Docusaurus browser storage is not available.
Possible reasons: running Docusaurus in an iframe, in an incognito browser session, or using too strict browser privacy settings.`,
error,
);
hasLoggedBrowserStorageNotAvailableWarning = true;
}
}
// Convenient storage interface for a single storage key
export type StorageSlot = {
get: () => string | null;
set: (value: string) => void;
del: () => void;
};
const NoopStorageSlot: StorageSlot = {
get: () => null,
set: () => {
/* empty */
},
del: () => {
/* empty */
},
};
// Fail-fast, as storage APIs should not be used during the SSR process
function createServerStorageSlot(key: string): StorageSlot {
function throwError(): never {
throw new Error(`Illegal storage API usage for storage key "${key}".
Docusaurus storage APIs are not supposed to be called on the server-rendering process.
Please only call storage APIs in effects and event handlers.`);
}
return {
get: throwError,
set: throwError,
del: throwError,
};
}
/**
* Creates an interface to work on a particular key in the storage model.
* Note that this function only initializes the interface, but doesn't allocate
* anything by itself (i.e. no side-effects).
*
* The API is fail-safe, since usage of browser storage should be considered
* unreliable. Local storage might simply be unavailable (iframe + browser
* security) or operations might fail individually. Please assume that using
* this API can be a no-op. See also https://github.com/facebook/docusaurus/issues/6036
*/
export function createStorageSlot(
key: string,
options?: { persistence?: StorageType },
): StorageSlot {
if (typeof window === 'undefined') {
return createServerStorageSlot(key);
}
const browserStorage = getBrowserStorage(options?.persistence);
if (browserStorage === null) {
return NoopStorageSlot;
}
return {
get: () => {
try {
return browserStorage.getItem(key);
} catch (err) {
console.error(`Docusaurus storage error, can't get key=${key}`, err);
return null;
}
},
set: (value) => {
try {
browserStorage.setItem(key, value);
} catch (err) {
console.error(
`Docusaurus storage error, can't set ${key}=${value}`,
err,
);
}
},
del: () => {
try {
browserStorage.removeItem(key);
} catch (err) {
console.error(`Docusaurus storage error, can't delete key=${key}`, err);
}
},
};
}
/**
* Returns a list of all the keys currently stored in browser storage,
* or an empty list if browser storage can't be accessed.
*/
export function listStorageKeys(
storageType: StorageType = DefaultStorageType,
): string[] {
const browserStorage = getBrowserStorage(storageType);
if (!browserStorage) {
return [];
}
const keys: string[] = [];
for (let i = 0; i < browserStorage.length; i += 1) {
const key = browserStorage.key(i);
if (key !== null) {
keys.push(key);
}
}
return keys;
}