@atlaskit/node-data-provider 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +14 -0
- package/CHANGELOG.md +24 -0
- package/LICENSE.md +11 -0
- package/README.md +48 -0
- package/cache/package.json +15 -0
- package/content/package.json +15 -0
- package/dist/cjs/cache.js +145 -0
- package/dist/cjs/consumption/_global-ndp-caches.js +21 -0
- package/dist/cjs/consumption/_internal-context.js +77 -0
- package/dist/cjs/consumption/_lru-cache.js +44 -0
- package/dist/cjs/consumption/content.js +53 -0
- package/dist/cjs/get-providers/confluence-page.js +15 -0
- package/dist/cjs/index.js +234 -0
- package/dist/cjs/internal-types.js +5 -0
- package/dist/cjs/plugin-hooks.js +97 -0
- package/dist/cjs/providers/emoji.js +54 -0
- package/dist/es2019/cache.js +96 -0
- package/dist/es2019/consumption/_global-ndp-caches.js +13 -0
- package/dist/es2019/consumption/_internal-context.js +71 -0
- package/dist/es2019/consumption/_lru-cache.js +28 -0
- package/dist/es2019/consumption/content.js +46 -0
- package/dist/es2019/get-providers/confluence-page.js +10 -0
- package/dist/es2019/index.js +193 -0
- package/dist/es2019/internal-types.js +1 -0
- package/dist/es2019/plugin-hooks.js +66 -0
- package/dist/es2019/providers/emoji.js +26 -0
- package/dist/esm/cache.js +141 -0
- package/dist/esm/consumption/_global-ndp-caches.js +13 -0
- package/dist/esm/consumption/_internal-context.js +71 -0
- package/dist/esm/consumption/_lru-cache.js +37 -0
- package/dist/esm/consumption/content.js +46 -0
- package/dist/esm/get-providers/confluence-page.js +9 -0
- package/dist/esm/index.js +230 -0
- package/dist/esm/internal-types.js +1 -0
- package/dist/esm/plugin-hooks.js +90 -0
- package/dist/esm/providers/emoji.js +47 -0
- package/dist/types/cache.d.ts +61 -0
- package/dist/types/consumption/_global-ndp-caches.d.ts +8 -0
- package/dist/types/consumption/_internal-context.d.ts +32 -0
- package/dist/types/consumption/_lru-cache.d.ts +7 -0
- package/dist/types/consumption/content.d.ts +65 -0
- package/dist/types/get-providers/confluence-page.d.ts +6 -0
- package/dist/types/index.d.ts +136 -0
- package/dist/types/internal-types.d.ts +2 -0
- package/dist/types/plugin-hooks.d.ts +32 -0
- package/dist/types/providers/emoji.d.ts +10 -0
- package/dist/types-ts4.5/cache.d.ts +61 -0
- package/dist/types-ts4.5/consumption/_global-ndp-caches.d.ts +8 -0
- package/dist/types-ts4.5/consumption/_internal-context.d.ts +32 -0
- package/dist/types-ts4.5/consumption/_lru-cache.d.ts +7 -0
- package/dist/types-ts4.5/consumption/content.d.ts +65 -0
- package/dist/types-ts4.5/get-providers/confluence-page.d.ts +6 -0
- package/dist/types-ts4.5/index.d.ts +136 -0
- package/dist/types-ts4.5/internal-types.d.ts +2 -0
- package/dist/types-ts4.5/plugin-hooks.d.ts +32 -0
- package/dist/types-ts4.5/providers/emoji.d.ts +10 -0
- package/emoji-provider/package.json +15 -0
- package/get-confluence-page-providers/package.json +15 -0
- package/package.json +73 -0
- package/plugin-hooks/package.json +15 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
// re exported to avoid type issues
|
|
3
|
+
// this type is only used in jsdoc comments in this file.
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* This is the base class for creating a node data provider for an editor plugin.
|
|
7
|
+
*
|
|
8
|
+
* ## Usage
|
|
9
|
+
*
|
|
10
|
+
* ### Create a provider
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* class EmojiNodeDataProvider extends NodeDataProvider<
|
|
15
|
+
* { attrs: EmojiAttributes },
|
|
16
|
+
* { resolvedData: string }
|
|
17
|
+
* > {
|
|
18
|
+
* constructor({ existingCache }?: { existingCache: Record<string, { resolvedData: string }> }) {
|
|
19
|
+
* super({ existingCache, nodeName: 'emoji' });
|
|
20
|
+
* }
|
|
21
|
+
* nodeToKey(node: { attrs: EmojiAttributes }): string {
|
|
22
|
+
* return `${node.attrs.shortName}-${node.attrs.text}-${node.attrs.id}`;
|
|
23
|
+
* }
|
|
24
|
+
* resolve(node: { attrs: EmojiAttributes }, _?: { signal: AbortSignal }) {
|
|
25
|
+
* return Promise.resolve({ resolvedData: 'resolved' });
|
|
26
|
+
* }
|
|
27
|
+
* }
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* ### Use the provider
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```ts
|
|
34
|
+
* const emojiNodeDataProvider = new EmojiNodeDataProvider();
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* ### Caching
|
|
38
|
+
*
|
|
39
|
+
* @see {@link buildCaches} for more information on building caches.
|
|
40
|
+
*
|
|
41
|
+
* #### Load an existing provider with a cache
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```
|
|
45
|
+
* await buildCaches({
|
|
46
|
+
* adf: docFromSomewhere,
|
|
47
|
+
* nodeDataProviders: [emojiNodeDataProvider],
|
|
48
|
+
* signal: AbortSignal.timeout(5000),
|
|
49
|
+
* });
|
|
50
|
+
* emojiNodeDataProvider // { 'key': 'value' }
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* ### Load an new provider with an existing cache
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```
|
|
57
|
+
* const provider1 = new ExampleNodeDataProvider();
|
|
58
|
+
* await buildCaches({adf, nodeDataProviders: [provider1]})
|
|
59
|
+
* provider1.cache // { 'key': 'value' }
|
|
60
|
+
*
|
|
61
|
+
* const provider2 = new ExampleNodeDataProvider({existingCache: provider1.cache});
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export class NodeDataProvider {
|
|
65
|
+
/**
|
|
66
|
+
* This takes a node and returns a key that can be used to cache the result of the resolve function.
|
|
67
|
+
*/
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* This returns the information required to render a node.
|
|
71
|
+
*
|
|
72
|
+
* If unresolvable, this method will throw an error.
|
|
73
|
+
*
|
|
74
|
+
* If signal is aborted, this method will return undefined.
|
|
75
|
+
*/
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* The adf node name this provider is responsible for.
|
|
79
|
+
*/
|
|
80
|
+
|
|
81
|
+
constructor({
|
|
82
|
+
existingCache = {},
|
|
83
|
+
nodeName,
|
|
84
|
+
nodeToKey,
|
|
85
|
+
resolve
|
|
86
|
+
}) {
|
|
87
|
+
/**
|
|
88
|
+
* This is added to ease building types
|
|
89
|
+
*/
|
|
90
|
+
// @ts-ignore
|
|
91
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
92
|
+
_defineProperty(this, "__node", {});
|
|
93
|
+
_defineProperty(this, "pending", {});
|
|
94
|
+
this.__cache = existingCache;
|
|
95
|
+
this.nodeName = nodeName;
|
|
96
|
+
this.nodeToKey = nodeToKey;
|
|
97
|
+
this.resolve = resolve;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Updates the providers cache.
|
|
102
|
+
*
|
|
103
|
+
* Useful in scenarios such as SSR where the cache is built on the server and then passed to the client.
|
|
104
|
+
*
|
|
105
|
+
* Avoids the need to provide the cache to the constructor (to allow decoupling creation of node data providers from cache building),
|
|
106
|
+
* and allow for caching to be managed at a group level across multiple providers.
|
|
107
|
+
*
|
|
108
|
+
* This is not expected to be used by consumers, for internal consumption examples;
|
|
109
|
+
* @see {@link buildCaches}
|
|
110
|
+
*
|
|
111
|
+
*/
|
|
112
|
+
updateCache(cache, options) {
|
|
113
|
+
switch (options.strategy) {
|
|
114
|
+
case 'merge-override':
|
|
115
|
+
this.__cache = {
|
|
116
|
+
...this.__cache,
|
|
117
|
+
...cache
|
|
118
|
+
};
|
|
119
|
+
return;
|
|
120
|
+
case 'replace':
|
|
121
|
+
this.__cache = cache;
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* This is the cache for the provider.
|
|
128
|
+
*/
|
|
129
|
+
get cache() {
|
|
130
|
+
return this.__cache;
|
|
131
|
+
}
|
|
132
|
+
get(node, _) {
|
|
133
|
+
const key = this.nodeToKey(node);
|
|
134
|
+
const cached = this.cache[key];
|
|
135
|
+
if (cached) {
|
|
136
|
+
return cached;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Get could be called from a variety of sources;
|
|
140
|
+
// - a Node Data Provider
|
|
141
|
+
// - a cache build
|
|
142
|
+
// - something else
|
|
143
|
+
//
|
|
144
|
+
// We want to avoid triggering multiple resolves for the same node -- so we keep track of pending requests
|
|
145
|
+
// and share them across any overlapping gets.
|
|
146
|
+
//
|
|
147
|
+
// When a get is cancelled -- we only want to cancel the shared resolve if all signals are aborted
|
|
148
|
+
// so we keep track of all signals that are not aborted.
|
|
149
|
+
|
|
150
|
+
let originalSignal = (_ === null || _ === void 0 ? void 0 : _.signal) || new AbortController().signal;
|
|
151
|
+
if (!this.pending[key]) {
|
|
152
|
+
const abortController = new AbortController();
|
|
153
|
+
this.pending[key] = {
|
|
154
|
+
resolving: new Promise(async (res, rej) => {
|
|
155
|
+
try {
|
|
156
|
+
const result = await this.resolve(node, {
|
|
157
|
+
signal: abortController.signal
|
|
158
|
+
}).catch(res => res(undefined));
|
|
159
|
+
res(result);
|
|
160
|
+
} catch {
|
|
161
|
+
res(undefined);
|
|
162
|
+
}
|
|
163
|
+
}),
|
|
164
|
+
abortController,
|
|
165
|
+
activeSignals: [originalSignal]
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
const handleAbort = () => {
|
|
169
|
+
this.pending[key].activeSignals = this.pending[key].activeSignals.filter(activeSignal => activeSignal !== originalSignal);
|
|
170
|
+
if (this.pending[key].activeSignals.length === 0) {
|
|
171
|
+
// abort the resolution if all signals are aborted
|
|
172
|
+
this.pending[key].abortController.abort();
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
// eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
|
|
177
|
+
originalSignal === null || originalSignal === void 0 ? void 0 : originalSignal.addEventListener('abort', handleAbort);
|
|
178
|
+
this.pending[key].resolving.then(resolvedValue => {
|
|
179
|
+
if (resolvedValue) {
|
|
180
|
+
this.cache[key] = resolvedValue;
|
|
181
|
+
}
|
|
182
|
+
// eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
|
|
183
|
+
originalSignal === null || originalSignal === void 0 ? void 0 : originalSignal.removeEventListener('abort', handleAbort);
|
|
184
|
+
return resolvedValue;
|
|
185
|
+
});
|
|
186
|
+
return this.pending[key].resolving;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// The purpose of this type is to ensure that either a DocNode or a PMNode is passed in
|
|
191
|
+
// to the provider.
|
|
192
|
+
// It is not opinionated about which nodes are used, so `any` is used here to allow
|
|
193
|
+
// compatibility with both DocNodes and PMNodes.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
*
|
|
4
|
+
*
|
|
5
|
+
* This hook is intended to simplify accessing data via the one tick providers.
|
|
6
|
+
*
|
|
7
|
+
* ```ts
|
|
8
|
+
* const value = useNodeDataProviderGet(emojiProvider, emojiNode);
|
|
9
|
+
*
|
|
10
|
+
* if (value.state === 'loading') {
|
|
11
|
+
* return <Loading />;
|
|
12
|
+
* }
|
|
13
|
+
* if (value.state === 'failed') {
|
|
14
|
+
* return <Fallback />;
|
|
15
|
+
* }
|
|
16
|
+
* return <Emoji properties=(value.result) />
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export function useNodeDataProviderGet(options) {
|
|
20
|
+
const getResult = useMemo(() => options.provider.get(options.node), [options.provider, options.node]);
|
|
21
|
+
let [resolved, setResolved] = useState(getResult !== undefined && !isPromise(getResult) ? getResult : undefined);
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if (!isPromise(getResult)) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
let cancelled = false;
|
|
27
|
+
(async function () {
|
|
28
|
+
try {
|
|
29
|
+
let resolved = await getResult;
|
|
30
|
+
if (!cancelled) {
|
|
31
|
+
if (resolved === undefined) {
|
|
32
|
+
setResolved('error');
|
|
33
|
+
} else {
|
|
34
|
+
setResolved(resolved);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
} catch {
|
|
38
|
+
setResolved('error');
|
|
39
|
+
}
|
|
40
|
+
})();
|
|
41
|
+
return () => {
|
|
42
|
+
cancelled = true;
|
|
43
|
+
};
|
|
44
|
+
}, [getResult]);
|
|
45
|
+
if (resolved === undefined) {
|
|
46
|
+
return {
|
|
47
|
+
state: 'loading',
|
|
48
|
+
result: undefined
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
if (resolved === 'error') {
|
|
52
|
+
return {
|
|
53
|
+
state: 'failed',
|
|
54
|
+
result: undefined
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
state: 'resolved',
|
|
59
|
+
result: resolved
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function isPromise(obj) {
|
|
63
|
+
return !!obj && (typeof obj === 'object' || typeof obj === 'function') &&
|
|
64
|
+
// @ts-ignore
|
|
65
|
+
typeof obj.then === 'function';
|
|
66
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { NodeDataProvider } from '../index';
|
|
2
|
+
export function createEmojiNodeDataProvider({
|
|
3
|
+
emojiProvider,
|
|
4
|
+
existingCache
|
|
5
|
+
}) {
|
|
6
|
+
const emojiNodeDataProvider = new NodeDataProvider({
|
|
7
|
+
existingCache,
|
|
8
|
+
nodeName: 'emoji',
|
|
9
|
+
nodeToKey: node => {
|
|
10
|
+
const key = `${node.attrs.id}-${node.attrs.shortName}-${node.attrs.text}`;
|
|
11
|
+
return key;
|
|
12
|
+
},
|
|
13
|
+
async resolve(node, _resolveOptions) {
|
|
14
|
+
const emojiDescriptionWithVariations = await (await emojiProvider).fetchByEmojiId({
|
|
15
|
+
id: node.attrs.id,
|
|
16
|
+
shortName: node.attrs.shortName,
|
|
17
|
+
fallback: node.attrs.text
|
|
18
|
+
}, true);
|
|
19
|
+
if (!emojiDescriptionWithVariations) {
|
|
20
|
+
throw new Error('Could not resolve emoji');
|
|
21
|
+
}
|
|
22
|
+
return emojiDescriptionWithVariations;
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
return emojiNodeDataProvider;
|
|
26
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import _typeof from "@babel/runtime/helpers/typeof";
|
|
2
|
+
import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
|
|
3
|
+
import _regeneratorRuntime from "@babel/runtime/regenerator";
|
|
4
|
+
import { traverse } from '@atlaskit/adf-utils/traverse';
|
|
5
|
+
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
7
|
+
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Builds {@link NodeDataProvider}s caches for a document.
|
|
12
|
+
*
|
|
13
|
+
* It will traverse the document and call the resolve method for each node.
|
|
14
|
+
* When all promises are resolved, NodeDataProviders will have their caches populated.
|
|
15
|
+
*
|
|
16
|
+
* The providers will then be ready for use with an Editor.
|
|
17
|
+
*
|
|
18
|
+
* To limit the time spent building the cache, a signal can be provided to abort the request.
|
|
19
|
+
*
|
|
20
|
+
* ## Usage
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* buildCaches({
|
|
25
|
+
* adf: doc,
|
|
26
|
+
* nodeDataProviders: { emoji: emojiNodeDataProvider, ... },
|
|
27
|
+
* signal: AbortSignal.timeout(5000),
|
|
28
|
+
* });
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* ### Using caches
|
|
32
|
+
*
|
|
33
|
+
* To make use of a cache in another provider (ie. for a cache created on the server), you can retrieve the cache
|
|
34
|
+
* from the provider and pass it to the new provider.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```tsx
|
|
38
|
+
* // SSR env
|
|
39
|
+
* const ssrProvidersCaches = await buildCaches({adf, nodeDataProviders: { emoji }})
|
|
40
|
+
*
|
|
41
|
+
* // Client env (providersCaches is the cache from the server)
|
|
42
|
+
* <ContentNodeDataProviders ... existingProvidersCache={ssrProvidersCaches} />
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* *Note:* On the client - buildCache is not expected to be used directly.
|
|
46
|
+
*
|
|
47
|
+
* @see {@link ContentNodeDataProviders} for expected client usage.
|
|
48
|
+
*/
|
|
49
|
+
export function buildCaches(_x) {
|
|
50
|
+
return _buildCaches.apply(this, arguments);
|
|
51
|
+
}
|
|
52
|
+
function _buildCaches() {
|
|
53
|
+
_buildCaches = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(_ref) {
|
|
54
|
+
var adf, nodeDataProviders, _ref$signal, signal, existingProvidersCache, visitors, promises, _loop, _i, _Object$values, caches, _i2, _Object$values2, nodeDataProvider;
|
|
55
|
+
return _regeneratorRuntime.wrap(function _callee$(_context2) {
|
|
56
|
+
while (1) switch (_context2.prev = _context2.next) {
|
|
57
|
+
case 0:
|
|
58
|
+
adf = _ref.adf, nodeDataProviders = _ref.nodeDataProviders, _ref$signal = _ref.signal, signal = _ref$signal === void 0 ? new AbortController().signal : _ref$signal, existingProvidersCache = _ref.existingProvidersCache;
|
|
59
|
+
visitors = {};
|
|
60
|
+
promises = [];
|
|
61
|
+
_loop = /*#__PURE__*/_regeneratorRuntime.mark(function _loop() {
|
|
62
|
+
var _nodeDataProvider, nodeDataProvider;
|
|
63
|
+
return _regeneratorRuntime.wrap(function _loop$(_context) {
|
|
64
|
+
while (1) switch (_context.prev = _context.next) {
|
|
65
|
+
case 0:
|
|
66
|
+
_nodeDataProvider = _Object$values[_i];
|
|
67
|
+
// widen type to avoid typescript errors with the specific node data provider types
|
|
68
|
+
nodeDataProvider = _nodeDataProvider;
|
|
69
|
+
if (existingProvidersCache && existingProvidersCache[nodeDataProvider.nodeName]) {
|
|
70
|
+
nodeDataProvider.updateCache(existingProvidersCache[nodeDataProvider.nodeName], {
|
|
71
|
+
strategy: 'merge-override'
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
visitors[nodeDataProvider.nodeName] = function (node) {
|
|
75
|
+
var result = nodeDataProvider.get(node, {
|
|
76
|
+
signal: signal
|
|
77
|
+
});
|
|
78
|
+
if (!isPromise(result)) {
|
|
79
|
+
nodeDataProvider.cache[nodeDataProvider.nodeToKey(node)] = result;
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
promises.push(result);
|
|
83
|
+
result.then(function (resolvedValue) {
|
|
84
|
+
var signalAborted = signal ? signal.aborted : false;
|
|
85
|
+
if (!signalAborted && resolvedValue !== undefined) {
|
|
86
|
+
nodeDataProvider.cache[nodeDataProvider.nodeToKey(node)] = resolvedValue;
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
return undefined;
|
|
90
|
+
};
|
|
91
|
+
case 4:
|
|
92
|
+
case "end":
|
|
93
|
+
return _context.stop();
|
|
94
|
+
}
|
|
95
|
+
}, _loop);
|
|
96
|
+
});
|
|
97
|
+
_i = 0, _Object$values = Object.values(nodeDataProviders);
|
|
98
|
+
case 5:
|
|
99
|
+
if (!(_i < _Object$values.length)) {
|
|
100
|
+
_context2.next = 10;
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
return _context2.delegateYield(_loop(), "t0", 7);
|
|
104
|
+
case 7:
|
|
105
|
+
_i++;
|
|
106
|
+
_context2.next = 5;
|
|
107
|
+
break;
|
|
108
|
+
case 10:
|
|
109
|
+
if (!adf) {
|
|
110
|
+
_context2.next = 16;
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
traverse(adf, visitors);
|
|
114
|
+
_context2.next = 14;
|
|
115
|
+
return Promise.all(promises);
|
|
116
|
+
case 14:
|
|
117
|
+
_context2.next = 18;
|
|
118
|
+
break;
|
|
119
|
+
case 16:
|
|
120
|
+
_context2.next = 18;
|
|
121
|
+
return Promise.resolve();
|
|
122
|
+
case 18:
|
|
123
|
+
caches = {};
|
|
124
|
+
for (_i2 = 0, _Object$values2 = Object.values(nodeDataProviders); _i2 < _Object$values2.length; _i2++) {
|
|
125
|
+
nodeDataProvider = _Object$values2[_i2];
|
|
126
|
+
caches[nodeDataProvider.nodeName] = nodeDataProvider.cache;
|
|
127
|
+
}
|
|
128
|
+
return _context2.abrupt("return", caches);
|
|
129
|
+
case 21:
|
|
130
|
+
case "end":
|
|
131
|
+
return _context2.stop();
|
|
132
|
+
}
|
|
133
|
+
}, _callee);
|
|
134
|
+
}));
|
|
135
|
+
return _buildCaches.apply(this, arguments);
|
|
136
|
+
}
|
|
137
|
+
function isPromise(obj) {
|
|
138
|
+
return !!obj && (_typeof(obj) === 'object' || typeof obj === 'function') &&
|
|
139
|
+
// @ts-ignore
|
|
140
|
+
typeof obj.then === 'function';
|
|
141
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
var GlobalNdpCachesContext = /*#__PURE__*/React.createContext({});
|
|
3
|
+
export function useGlobalNdpCachesContext() {
|
|
4
|
+
var globalNdpCachesContextValue = React.useContext(GlobalNdpCachesContext);
|
|
5
|
+
return globalNdpCachesContextValue;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// The ndp caches currently use module scope to store the caches. This is not ideal, and a global provider
|
|
9
|
+
// will avoid the need for this.
|
|
10
|
+
// This function is used to reset the global ndp caches context in tests.
|
|
11
|
+
export function _resetGlobalNdpCachesContext() {
|
|
12
|
+
GlobalNdpCachesContext = /*#__PURE__*/React.createContext({});
|
|
13
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { buildCaches } from '../cache';
|
|
3
|
+
import { useGlobalNdpCachesContext } from './_global-ndp-caches';
|
|
4
|
+
import { LRUCache } from './_lru-cache';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The settings for the data providers lru cache,
|
|
8
|
+
* increasing the value will increase the number of data providers that can be stored in the cache.
|
|
9
|
+
*/
|
|
10
|
+
var lruCacheSettings = {
|
|
11
|
+
page: 5,
|
|
12
|
+
default: 1
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Sets up nodeview data providers for a content node.
|
|
17
|
+
*
|
|
18
|
+
* This will return the cached node data providers if they exist, otherwise it will call the provided getNodeDataProviders function.
|
|
19
|
+
*
|
|
20
|
+
* Note: Calling this has side effects where caches for the nodeview data providers will continue to be built
|
|
21
|
+
* in the background after this resolves to a set of nodeview data providers that can be used.
|
|
22
|
+
*/
|
|
23
|
+
export function useContentNodeDataProvidersSetup(content,
|
|
24
|
+
/**
|
|
25
|
+
* Note: changes to this object will not be reflected in the cache.
|
|
26
|
+
*/
|
|
27
|
+
setupOptions) {
|
|
28
|
+
var _globalNdpCachesContextValue = useGlobalNdpCachesContext();
|
|
29
|
+
// Create a cache for the content type if it doesn't exist
|
|
30
|
+
// While this will not result in any existing context consumers getting the updated value.
|
|
31
|
+
// It will ensure that the cache is available for future consumers.
|
|
32
|
+
if (_globalNdpCachesContextValue[content.contentType] === undefined) {
|
|
33
|
+
var _lruCacheSettings$con;
|
|
34
|
+
_globalNdpCachesContextValue[content.contentType] = new LRUCache((_lruCacheSettings$con = lruCacheSettings[content.contentType]) !== null && _lruCacheSettings$con !== void 0 ? _lruCacheSettings$con : lruCacheSettings.default);
|
|
35
|
+
}
|
|
36
|
+
var contentTypeNdpCaches = _globalNdpCachesContextValue[content.contentType];
|
|
37
|
+
|
|
38
|
+
// The node data providers should only be rebuilt if the content changes
|
|
39
|
+
// to avoid unnecessary rebuilding of the cache.
|
|
40
|
+
// useRef is used over useMemo as use memo is not a guarantee that the value will be reused
|
|
41
|
+
// - in development it can be called twice
|
|
42
|
+
// - react have made clear that in future versions useMemo may add features that throw away the cache, and [recommend refs](https://react.dev/reference/react/useMemo#caveats) for this use case.
|
|
43
|
+
var currentContentKey = "".concat(content.contentType, "-").concat(content.contentId);
|
|
44
|
+
var contentKeyRef = React.useRef();
|
|
45
|
+
var nodeDataProvidersRef = React.useRef();
|
|
46
|
+
if (contentKeyRef.current !== currentContentKey) {
|
|
47
|
+
contentKeyRef.current = currentContentKey;
|
|
48
|
+
var cachedContentNdps = contentTypeNdpCaches === null || contentTypeNdpCaches === void 0 ? void 0 : contentTypeNdpCaches.get(content.contentId);
|
|
49
|
+
var nodeDataProviders = cachedContentNdps || setupOptions.getNodeDataProviders();
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Note: while this will remove old NodeDataProviders from the cache -- these are passed directly to consumers,
|
|
53
|
+
* so removing from the cache will not result in actively used NodeDataProviders being deleted in some way.
|
|
54
|
+
*
|
|
55
|
+
*/
|
|
56
|
+
contentTypeNdpCaches.set(content.contentId, nodeDataProviders);
|
|
57
|
+
buildCaches({
|
|
58
|
+
adf: setupOptions.adfToUpdateWith,
|
|
59
|
+
nodeDataProviders: nodeDataProviders,
|
|
60
|
+
existingProvidersCache: setupOptions.existingProvidersCache
|
|
61
|
+
}).then(function (warmedNodeDataProvidersCache) {
|
|
62
|
+
var _setupOptions$onCache;
|
|
63
|
+
(_setupOptions$onCache = setupOptions.onCacheWarmed) === null || _setupOptions$onCache === void 0 || _setupOptions$onCache.call(setupOptions, {
|
|
64
|
+
warmedNodeDataProvidersCache: warmedNodeDataProvidersCache,
|
|
65
|
+
nodeDataProviders: nodeDataProviders
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
nodeDataProvidersRef.current = nodeDataProviders;
|
|
69
|
+
}
|
|
70
|
+
return nodeDataProvidersRef.current;
|
|
71
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
|
|
2
|
+
import _createClass from "@babel/runtime/helpers/createClass";
|
|
3
|
+
export var LRUCache = /*#__PURE__*/function () {
|
|
4
|
+
function LRUCache(capacity) {
|
|
5
|
+
_classCallCheck(this, LRUCache);
|
|
6
|
+
this.capacity = capacity;
|
|
7
|
+
this.cache = new Map();
|
|
8
|
+
}
|
|
9
|
+
_createClass(LRUCache, [{
|
|
10
|
+
key: "get",
|
|
11
|
+
value: function get(key) {
|
|
12
|
+
if (!this.cache.has(key)) {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Move the used key to the end to mark it as most recently used
|
|
17
|
+
var value = this.cache.get(key);
|
|
18
|
+
this.cache.delete(key);
|
|
19
|
+
this.cache.set(key, value);
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
}, {
|
|
23
|
+
key: "set",
|
|
24
|
+
value: function set(key, value) {
|
|
25
|
+
// Check if the key already exists and delete it to update its position
|
|
26
|
+
if (this.cache.has(key)) {
|
|
27
|
+
this.cache.delete(key);
|
|
28
|
+
} else if (this.cache.size >= this.capacity) {
|
|
29
|
+
// Remove the first (least recently used) cache item if we're at capacity
|
|
30
|
+
var firstKey = this.cache.keys().next().value;
|
|
31
|
+
this.cache.delete(firstKey);
|
|
32
|
+
}
|
|
33
|
+
this.cache.set(key, value);
|
|
34
|
+
}
|
|
35
|
+
}]);
|
|
36
|
+
return LRUCache;
|
|
37
|
+
}();
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { _resetGlobalNdpCachesContext } from './_global-ndp-caches';
|
|
3
|
+
import { useContentNodeDataProvidersSetup } from './_internal-context';
|
|
4
|
+
var ContentNodeDataProvidersContext = /*#__PURE__*/React.createContext(undefined);
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```tsx
|
|
10
|
+
* <ContentNodeDataProviders
|
|
11
|
+
* contentType="page" contentId="9001"
|
|
12
|
+
* adf={doc}
|
|
13
|
+
* placeholder={<Spinner />}
|
|
14
|
+
* existingProvidersCache={ssrProvidersCache}
|
|
15
|
+
* getNodeDataProviders={getPageNodeDataProviders}
|
|
16
|
+
* onCacheWarmed={trackCacheWarmed}
|
|
17
|
+
* >
|
|
18
|
+
* <Editor />
|
|
19
|
+
* </ContentNodeDataProviders>
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export function ContentNodeDataProviders(props) {
|
|
23
|
+
var contentNodeDataProviders = useContentNodeDataProvidersSetup({
|
|
24
|
+
contentType: props.contentType,
|
|
25
|
+
contentId: props.contentId
|
|
26
|
+
}, {
|
|
27
|
+
adfToUpdateWith: props.adf,
|
|
28
|
+
existingProvidersCache: props.existingProvidersCache,
|
|
29
|
+
getNodeDataProviders: props.getNodeDataProviders,
|
|
30
|
+
onCacheWarmed: props.onCacheWarmed
|
|
31
|
+
});
|
|
32
|
+
return /*#__PURE__*/React.createElement(ContentNodeDataProvidersContext.Provider, {
|
|
33
|
+
value: contentNodeDataProviders
|
|
34
|
+
}, props.children);
|
|
35
|
+
}
|
|
36
|
+
export function useContentNodeDataProviders() {
|
|
37
|
+
var contentNodeDataProvidersContext = React.useContext(ContentNodeDataProvidersContext);
|
|
38
|
+
return contentNodeDataProvidersContext;
|
|
39
|
+
}
|
|
40
|
+
export {
|
|
41
|
+
/**
|
|
42
|
+
* Exported for testing purposes only.
|
|
43
|
+
*
|
|
44
|
+
* This API will change.
|
|
45
|
+
*/
|
|
46
|
+
_resetGlobalNdpCachesContext as __testOnly_resetGlobalNdpCachesContext };
|