@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,230 @@
|
|
|
1
|
+
import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
|
|
2
|
+
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
|
|
3
|
+
import _createClass from "@babel/runtime/helpers/createClass";
|
|
4
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
5
|
+
import _regeneratorRuntime from "@babel/runtime/regenerator";
|
|
6
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
7
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
8
|
+
// re exported to avoid type issues
|
|
9
|
+
// this type is only used in jsdoc comments in this file.
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* This is the base class for creating a node data provider for an editor plugin.
|
|
13
|
+
*
|
|
14
|
+
* ## Usage
|
|
15
|
+
*
|
|
16
|
+
* ### Create a provider
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* class EmojiNodeDataProvider extends NodeDataProvider<
|
|
21
|
+
* { attrs: EmojiAttributes },
|
|
22
|
+
* { resolvedData: string }
|
|
23
|
+
* > {
|
|
24
|
+
* constructor({ existingCache }?: { existingCache: Record<string, { resolvedData: string }> }) {
|
|
25
|
+
* super({ existingCache, nodeName: 'emoji' });
|
|
26
|
+
* }
|
|
27
|
+
* nodeToKey(node: { attrs: EmojiAttributes }): string {
|
|
28
|
+
* return `${node.attrs.shortName}-${node.attrs.text}-${node.attrs.id}`;
|
|
29
|
+
* }
|
|
30
|
+
* resolve(node: { attrs: EmojiAttributes }, _?: { signal: AbortSignal }) {
|
|
31
|
+
* return Promise.resolve({ resolvedData: 'resolved' });
|
|
32
|
+
* }
|
|
33
|
+
* }
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* ### Use the provider
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* const emojiNodeDataProvider = new EmojiNodeDataProvider();
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* ### Caching
|
|
44
|
+
*
|
|
45
|
+
* @see {@link buildCaches} for more information on building caches.
|
|
46
|
+
*
|
|
47
|
+
* #### Load an existing provider with a cache
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```
|
|
51
|
+
* await buildCaches({
|
|
52
|
+
* adf: docFromSomewhere,
|
|
53
|
+
* nodeDataProviders: [emojiNodeDataProvider],
|
|
54
|
+
* signal: AbortSignal.timeout(5000),
|
|
55
|
+
* });
|
|
56
|
+
* emojiNodeDataProvider // { 'key': 'value' }
|
|
57
|
+
* ```
|
|
58
|
+
*
|
|
59
|
+
* ### Load an new provider with an existing cache
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```
|
|
63
|
+
* const provider1 = new ExampleNodeDataProvider();
|
|
64
|
+
* await buildCaches({adf, nodeDataProviders: [provider1]})
|
|
65
|
+
* provider1.cache // { 'key': 'value' }
|
|
66
|
+
*
|
|
67
|
+
* const provider2 = new ExampleNodeDataProvider({existingCache: provider1.cache});
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
export var NodeDataProvider = /*#__PURE__*/function () {
|
|
71
|
+
/**
|
|
72
|
+
* This takes a node and returns a key that can be used to cache the result of the resolve function.
|
|
73
|
+
*/
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* This returns the information required to render a node.
|
|
77
|
+
*
|
|
78
|
+
* If unresolvable, this method will throw an error.
|
|
79
|
+
*
|
|
80
|
+
* If signal is aborted, this method will return undefined.
|
|
81
|
+
*/
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* The adf node name this provider is responsible for.
|
|
85
|
+
*/
|
|
86
|
+
|
|
87
|
+
function NodeDataProvider(_ref) {
|
|
88
|
+
var _ref$existingCache = _ref.existingCache,
|
|
89
|
+
existingCache = _ref$existingCache === void 0 ? {} : _ref$existingCache,
|
|
90
|
+
nodeName = _ref.nodeName,
|
|
91
|
+
nodeToKey = _ref.nodeToKey,
|
|
92
|
+
resolve = _ref.resolve;
|
|
93
|
+
_classCallCheck(this, NodeDataProvider);
|
|
94
|
+
/**
|
|
95
|
+
* This is added to ease building types
|
|
96
|
+
*/
|
|
97
|
+
// @ts-ignore
|
|
98
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
99
|
+
_defineProperty(this, "__node", {});
|
|
100
|
+
_defineProperty(this, "pending", {});
|
|
101
|
+
this.__cache = existingCache;
|
|
102
|
+
this.nodeName = nodeName;
|
|
103
|
+
this.nodeToKey = nodeToKey;
|
|
104
|
+
this.resolve = resolve;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Updates the providers cache.
|
|
109
|
+
*
|
|
110
|
+
* Useful in scenarios such as SSR where the cache is built on the server and then passed to the client.
|
|
111
|
+
*
|
|
112
|
+
* Avoids the need to provide the cache to the constructor (to allow decoupling creation of node data providers from cache building),
|
|
113
|
+
* and allow for caching to be managed at a group level across multiple providers.
|
|
114
|
+
*
|
|
115
|
+
* This is not expected to be used by consumers, for internal consumption examples;
|
|
116
|
+
* @see {@link buildCaches}
|
|
117
|
+
*
|
|
118
|
+
*/
|
|
119
|
+
_createClass(NodeDataProvider, [{
|
|
120
|
+
key: "updateCache",
|
|
121
|
+
value: function updateCache(cache, options) {
|
|
122
|
+
switch (options.strategy) {
|
|
123
|
+
case 'merge-override':
|
|
124
|
+
this.__cache = _objectSpread(_objectSpread({}, this.__cache), cache);
|
|
125
|
+
return;
|
|
126
|
+
case 'replace':
|
|
127
|
+
this.__cache = cache;
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* This is the cache for the provider.
|
|
134
|
+
*/
|
|
135
|
+
}, {
|
|
136
|
+
key: "cache",
|
|
137
|
+
get: function get() {
|
|
138
|
+
return this.__cache;
|
|
139
|
+
}
|
|
140
|
+
}, {
|
|
141
|
+
key: "get",
|
|
142
|
+
value: function get(node, _) {
|
|
143
|
+
var _this = this;
|
|
144
|
+
var key = this.nodeToKey(node);
|
|
145
|
+
var cached = this.cache[key];
|
|
146
|
+
if (cached) {
|
|
147
|
+
return cached;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Get could be called from a variety of sources;
|
|
151
|
+
// - a Node Data Provider
|
|
152
|
+
// - a cache build
|
|
153
|
+
// - something else
|
|
154
|
+
//
|
|
155
|
+
// We want to avoid triggering multiple resolves for the same node -- so we keep track of pending requests
|
|
156
|
+
// and share them across any overlapping gets.
|
|
157
|
+
//
|
|
158
|
+
// When a get is cancelled -- we only want to cancel the shared resolve if all signals are aborted
|
|
159
|
+
// so we keep track of all signals that are not aborted.
|
|
160
|
+
|
|
161
|
+
var originalSignal = (_ === null || _ === void 0 ? void 0 : _.signal) || new AbortController().signal;
|
|
162
|
+
if (!this.pending[key]) {
|
|
163
|
+
var abortController = new AbortController();
|
|
164
|
+
this.pending[key] = {
|
|
165
|
+
resolving: new Promise( /*#__PURE__*/function () {
|
|
166
|
+
var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(res, rej) {
|
|
167
|
+
var result;
|
|
168
|
+
return _regeneratorRuntime.wrap(function _callee$(_context) {
|
|
169
|
+
while (1) switch (_context.prev = _context.next) {
|
|
170
|
+
case 0:
|
|
171
|
+
_context.prev = 0;
|
|
172
|
+
_context.next = 3;
|
|
173
|
+
return _this.resolve(node, {
|
|
174
|
+
signal: abortController.signal
|
|
175
|
+
}).catch(function (res) {
|
|
176
|
+
return res(undefined);
|
|
177
|
+
});
|
|
178
|
+
case 3:
|
|
179
|
+
result = _context.sent;
|
|
180
|
+
res(result);
|
|
181
|
+
_context.next = 10;
|
|
182
|
+
break;
|
|
183
|
+
case 7:
|
|
184
|
+
_context.prev = 7;
|
|
185
|
+
_context.t0 = _context["catch"](0);
|
|
186
|
+
res(undefined);
|
|
187
|
+
case 10:
|
|
188
|
+
case "end":
|
|
189
|
+
return _context.stop();
|
|
190
|
+
}
|
|
191
|
+
}, _callee, null, [[0, 7]]);
|
|
192
|
+
}));
|
|
193
|
+
return function (_x, _x2) {
|
|
194
|
+
return _ref2.apply(this, arguments);
|
|
195
|
+
};
|
|
196
|
+
}()),
|
|
197
|
+
abortController: abortController,
|
|
198
|
+
activeSignals: [originalSignal]
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
var handleAbort = function handleAbort() {
|
|
202
|
+
_this.pending[key].activeSignals = _this.pending[key].activeSignals.filter(function (activeSignal) {
|
|
203
|
+
return activeSignal !== originalSignal;
|
|
204
|
+
});
|
|
205
|
+
if (_this.pending[key].activeSignals.length === 0) {
|
|
206
|
+
// abort the resolution if all signals are aborted
|
|
207
|
+
_this.pending[key].abortController.abort();
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
// eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
|
|
212
|
+
originalSignal === null || originalSignal === void 0 || originalSignal.addEventListener('abort', handleAbort);
|
|
213
|
+
this.pending[key].resolving.then(function (resolvedValue) {
|
|
214
|
+
if (resolvedValue) {
|
|
215
|
+
_this.cache[key] = resolvedValue;
|
|
216
|
+
}
|
|
217
|
+
// eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
|
|
218
|
+
originalSignal === null || originalSignal === void 0 || originalSignal.removeEventListener('abort', handleAbort);
|
|
219
|
+
return resolvedValue;
|
|
220
|
+
});
|
|
221
|
+
return this.pending[key].resolving;
|
|
222
|
+
}
|
|
223
|
+
}]);
|
|
224
|
+
return NodeDataProvider;
|
|
225
|
+
}();
|
|
226
|
+
|
|
227
|
+
// The purpose of this type is to ensure that either a DocNode or a PMNode is passed in
|
|
228
|
+
// to the provider.
|
|
229
|
+
// It is not opinionated about which nodes are used, so `any` is used here to allow
|
|
230
|
+
// compatibility with both DocNodes and PMNodes.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import _typeof from "@babel/runtime/helpers/typeof";
|
|
2
|
+
import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
|
|
3
|
+
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
4
|
+
import _regeneratorRuntime from "@babel/runtime/regenerator";
|
|
5
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
6
|
+
/**
|
|
7
|
+
*
|
|
8
|
+
*
|
|
9
|
+
* This hook is intended to simplify accessing data via the one tick providers.
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* const value = useNodeDataProviderGet(emojiProvider, emojiNode);
|
|
13
|
+
*
|
|
14
|
+
* if (value.state === 'loading') {
|
|
15
|
+
* return <Loading />;
|
|
16
|
+
* }
|
|
17
|
+
* if (value.state === 'failed') {
|
|
18
|
+
* return <Fallback />;
|
|
19
|
+
* }
|
|
20
|
+
* return <Emoji properties=(value.result) />
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export function useNodeDataProviderGet(options) {
|
|
24
|
+
var getResult = useMemo(function () {
|
|
25
|
+
return options.provider.get(options.node);
|
|
26
|
+
}, [options.provider, options.node]);
|
|
27
|
+
var _useState = useState(getResult !== undefined && !isPromise(getResult) ? getResult : undefined),
|
|
28
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
29
|
+
resolved = _useState2[0],
|
|
30
|
+
setResolved = _useState2[1];
|
|
31
|
+
useEffect(function () {
|
|
32
|
+
if (!isPromise(getResult)) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
var cancelled = false;
|
|
36
|
+
_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
|
|
37
|
+
var _resolved;
|
|
38
|
+
return _regeneratorRuntime.wrap(function _callee$(_context) {
|
|
39
|
+
while (1) switch (_context.prev = _context.next) {
|
|
40
|
+
case 0:
|
|
41
|
+
_context.prev = 0;
|
|
42
|
+
_context.next = 3;
|
|
43
|
+
return getResult;
|
|
44
|
+
case 3:
|
|
45
|
+
_resolved = _context.sent;
|
|
46
|
+
if (!cancelled) {
|
|
47
|
+
if (_resolved === undefined) {
|
|
48
|
+
setResolved('error');
|
|
49
|
+
} else {
|
|
50
|
+
setResolved(_resolved);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
_context.next = 10;
|
|
54
|
+
break;
|
|
55
|
+
case 7:
|
|
56
|
+
_context.prev = 7;
|
|
57
|
+
_context.t0 = _context["catch"](0);
|
|
58
|
+
setResolved('error');
|
|
59
|
+
case 10:
|
|
60
|
+
case "end":
|
|
61
|
+
return _context.stop();
|
|
62
|
+
}
|
|
63
|
+
}, _callee, null, [[0, 7]]);
|
|
64
|
+
}))();
|
|
65
|
+
return function () {
|
|
66
|
+
cancelled = true;
|
|
67
|
+
};
|
|
68
|
+
}, [getResult]);
|
|
69
|
+
if (resolved === undefined) {
|
|
70
|
+
return {
|
|
71
|
+
state: 'loading',
|
|
72
|
+
result: undefined
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
if (resolved === 'error') {
|
|
76
|
+
return {
|
|
77
|
+
state: 'failed',
|
|
78
|
+
result: undefined
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
state: 'resolved',
|
|
83
|
+
result: resolved
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function isPromise(obj) {
|
|
87
|
+
return !!obj && (_typeof(obj) === 'object' || typeof obj === 'function') &&
|
|
88
|
+
// @ts-ignore
|
|
89
|
+
typeof obj.then === 'function';
|
|
90
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
|
|
2
|
+
import _regeneratorRuntime from "@babel/runtime/regenerator";
|
|
3
|
+
import { NodeDataProvider } from '../index';
|
|
4
|
+
export function createEmojiNodeDataProvider(_ref) {
|
|
5
|
+
var emojiProvider = _ref.emojiProvider,
|
|
6
|
+
existingCache = _ref.existingCache;
|
|
7
|
+
var emojiNodeDataProvider = new NodeDataProvider({
|
|
8
|
+
existingCache: existingCache,
|
|
9
|
+
nodeName: 'emoji',
|
|
10
|
+
nodeToKey: function nodeToKey(node) {
|
|
11
|
+
var key = "".concat(node.attrs.id, "-").concat(node.attrs.shortName, "-").concat(node.attrs.text);
|
|
12
|
+
return key;
|
|
13
|
+
},
|
|
14
|
+
resolve: function resolve(node, _resolveOptions) {
|
|
15
|
+
return _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
|
|
16
|
+
var emojiDescriptionWithVariations;
|
|
17
|
+
return _regeneratorRuntime.wrap(function _callee$(_context) {
|
|
18
|
+
while (1) switch (_context.prev = _context.next) {
|
|
19
|
+
case 0:
|
|
20
|
+
_context.next = 2;
|
|
21
|
+
return emojiProvider;
|
|
22
|
+
case 2:
|
|
23
|
+
_context.next = 4;
|
|
24
|
+
return _context.sent.fetchByEmojiId({
|
|
25
|
+
id: node.attrs.id,
|
|
26
|
+
shortName: node.attrs.shortName,
|
|
27
|
+
fallback: node.attrs.text
|
|
28
|
+
}, true);
|
|
29
|
+
case 4:
|
|
30
|
+
emojiDescriptionWithVariations = _context.sent;
|
|
31
|
+
if (emojiDescriptionWithVariations) {
|
|
32
|
+
_context.next = 7;
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
throw new Error('Could not resolve emoji');
|
|
36
|
+
case 7:
|
|
37
|
+
return _context.abrupt("return", emojiDescriptionWithVariations);
|
|
38
|
+
case 8:
|
|
39
|
+
case "end":
|
|
40
|
+
return _context.stop();
|
|
41
|
+
}
|
|
42
|
+
}, _callee);
|
|
43
|
+
}))();
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
return emojiNodeDataProvider;
|
|
47
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { DocNode } from '@atlaskit/adf-schema';
|
|
2
|
+
import { type AnyNodeDataProvider } from './internal-types';
|
|
3
|
+
import { type EmojiNodeDataProvider } from './providers/emoji';
|
|
4
|
+
export type NodeDataProvidersCache = {
|
|
5
|
+
[nodeName: string]: Record<string, any>;
|
|
6
|
+
};
|
|
7
|
+
export type NodeDataProviders = {
|
|
8
|
+
emoji: EmojiNodeDataProvider;
|
|
9
|
+
[nodeName: string]: AnyNodeDataProvider;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Builds {@link NodeDataProvider}s caches for a document.
|
|
13
|
+
*
|
|
14
|
+
* It will traverse the document and call the resolve method for each node.
|
|
15
|
+
* When all promises are resolved, NodeDataProviders will have their caches populated.
|
|
16
|
+
*
|
|
17
|
+
* The providers will then be ready for use with an Editor.
|
|
18
|
+
*
|
|
19
|
+
* To limit the time spent building the cache, a signal can be provided to abort the request.
|
|
20
|
+
*
|
|
21
|
+
* ## Usage
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* buildCaches({
|
|
26
|
+
* adf: doc,
|
|
27
|
+
* nodeDataProviders: { emoji: emojiNodeDataProvider, ... },
|
|
28
|
+
* signal: AbortSignal.timeout(5000),
|
|
29
|
+
* });
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* ### Using caches
|
|
33
|
+
*
|
|
34
|
+
* To make use of a cache in another provider (ie. for a cache created on the server), you can retrieve the cache
|
|
35
|
+
* from the provider and pass it to the new provider.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```tsx
|
|
39
|
+
* // SSR env
|
|
40
|
+
* const ssrProvidersCaches = await buildCaches({adf, nodeDataProviders: { emoji }})
|
|
41
|
+
*
|
|
42
|
+
* // Client env (providersCaches is the cache from the server)
|
|
43
|
+
* <ContentNodeDataProviders ... existingProvidersCache={ssrProvidersCaches} />
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* *Note:* On the client - buildCache is not expected to be used directly.
|
|
47
|
+
*
|
|
48
|
+
* @see {@link ContentNodeDataProviders} for expected client usage.
|
|
49
|
+
*/
|
|
50
|
+
export declare function buildCaches({ adf, nodeDataProviders, signal, existingProvidersCache, }: {
|
|
51
|
+
adf?: DocNode;
|
|
52
|
+
/**
|
|
53
|
+
* Providers to build caches for
|
|
54
|
+
*/
|
|
55
|
+
nodeDataProviders: NodeDataProviders;
|
|
56
|
+
/**
|
|
57
|
+
* Signal to abort cache building -- the caches will be built up to the point of abort.
|
|
58
|
+
*/
|
|
59
|
+
signal?: AbortSignal;
|
|
60
|
+
existingProvidersCache?: NodeDataProvidersCache;
|
|
61
|
+
}): Promise<NodeDataProvidersCache>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type NodeDataProviders } from '../cache';
|
|
2
|
+
import { type LRUCache } from './_lru-cache';
|
|
3
|
+
type GlobalNdpCachesContextValue = {
|
|
4
|
+
[contentType: string]: LRUCache<NodeDataProviders>;
|
|
5
|
+
};
|
|
6
|
+
export declare function useGlobalNdpCachesContext(): GlobalNdpCachesContextValue;
|
|
7
|
+
export declare function _resetGlobalNdpCachesContext(): void;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type DocNode } from '@atlaskit/adf-schema';
|
|
2
|
+
import { type NodeDataProviders, type NodeDataProvidersCache } from '../cache';
|
|
3
|
+
/**
|
|
4
|
+
* Sets up nodeview data providers for a content node.
|
|
5
|
+
*
|
|
6
|
+
* This will return the cached node data providers if they exist, otherwise it will call the provided getNodeDataProviders function.
|
|
7
|
+
*
|
|
8
|
+
* Note: Calling this has side effects where caches for the nodeview data providers will continue to be built
|
|
9
|
+
* in the background after this resolves to a set of nodeview data providers that can be used.
|
|
10
|
+
*/
|
|
11
|
+
export declare function useContentNodeDataProvidersSetup(content: {
|
|
12
|
+
/**
|
|
13
|
+
* The type of content.
|
|
14
|
+
*/
|
|
15
|
+
contentType: 'page';
|
|
16
|
+
contentId: string;
|
|
17
|
+
},
|
|
18
|
+
/**
|
|
19
|
+
* Note: changes to this object will not be reflected in the cache.
|
|
20
|
+
*/
|
|
21
|
+
setupOptions: {
|
|
22
|
+
/**
|
|
23
|
+
* Note: this will only be used if no existing NodeDataProviders are found for the content.
|
|
24
|
+
*/
|
|
25
|
+
adfToUpdateWith?: DocNode;
|
|
26
|
+
existingProvidersCache?: NodeDataProvidersCache;
|
|
27
|
+
getNodeDataProviders: () => NodeDataProviders;
|
|
28
|
+
onCacheWarmed?: (_: {
|
|
29
|
+
warmedNodeDataProvidersCache: NodeDataProvidersCache;
|
|
30
|
+
nodeDataProviders: NodeDataProviders;
|
|
31
|
+
}) => void;
|
|
32
|
+
}): NodeDataProviders | undefined;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { type DocNode } from '@atlaskit/adf-schema';
|
|
3
|
+
import { type NodeDataProviders, type NodeDataProvidersCache } from '../cache';
|
|
4
|
+
import { _resetGlobalNdpCachesContext } from './_global-ndp-caches';
|
|
5
|
+
/**
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```tsx
|
|
9
|
+
* <ContentNodeDataProviders
|
|
10
|
+
* contentType="page" contentId="9001"
|
|
11
|
+
* adf={doc}
|
|
12
|
+
* placeholder={<Spinner />}
|
|
13
|
+
* existingProvidersCache={ssrProvidersCache}
|
|
14
|
+
* getNodeDataProviders={getPageNodeDataProviders}
|
|
15
|
+
* onCacheWarmed={trackCacheWarmed}
|
|
16
|
+
* >
|
|
17
|
+
* <Editor />
|
|
18
|
+
* </ContentNodeDataProviders>
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export declare function ContentNodeDataProviders(props: {
|
|
22
|
+
/**
|
|
23
|
+
* The type of content, this is used to group the resulting providers cache
|
|
24
|
+
*
|
|
25
|
+
* Note: Providers Caches are stored in an internal LRU cache, are grouped by content type.
|
|
26
|
+
*/
|
|
27
|
+
contentType: 'page';
|
|
28
|
+
/**
|
|
29
|
+
* The type of content, this is used to group the resulting providers cache
|
|
30
|
+
*
|
|
31
|
+
*/
|
|
32
|
+
contentId: string;
|
|
33
|
+
/**
|
|
34
|
+
* This is optional - when passed the caches in the data providers will be warmed
|
|
35
|
+
* based on this document.
|
|
36
|
+
*/
|
|
37
|
+
adf?: DocNode;
|
|
38
|
+
/**
|
|
39
|
+
* This is optional, and supports passing in an existing providers cache.
|
|
40
|
+
*
|
|
41
|
+
* An example of this is when you have a server-side rendered providers cache that you want to use on the client.
|
|
42
|
+
*/
|
|
43
|
+
existingProvidersCache?: NodeDataProvidersCache;
|
|
44
|
+
/**
|
|
45
|
+
* Returns a set of `NodeDataProviders` which are put in context for use when creating an editor or renderer.
|
|
46
|
+
* These will be pre-warmed if adf is passed in.
|
|
47
|
+
*/
|
|
48
|
+
getNodeDataProviders: () => NodeDataProviders;
|
|
49
|
+
/**
|
|
50
|
+
* Called when the cache is warmed.
|
|
51
|
+
*/
|
|
52
|
+
onCacheWarmed?: (_: {
|
|
53
|
+
warmedNodeDataProvidersCache: NodeDataProvidersCache;
|
|
54
|
+
nodeDataProviders: NodeDataProviders;
|
|
55
|
+
}) => void;
|
|
56
|
+
children: React.ReactNode;
|
|
57
|
+
}): JSX.Element;
|
|
58
|
+
export declare function useContentNodeDataProviders(): NodeDataProviders | undefined;
|
|
59
|
+
export {
|
|
60
|
+
/**
|
|
61
|
+
* Exported for testing purposes only.
|
|
62
|
+
*
|
|
63
|
+
* This API will change.
|
|
64
|
+
*/
|
|
65
|
+
_resetGlobalNdpCachesContext as __testOnly_resetGlobalNdpCachesContext, };
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import type { buildCaches } from './cache';
|
|
2
|
+
export type { buildCaches as __doNotUseThisType };
|
|
3
|
+
/**
|
|
4
|
+
* This is the base class for creating a node data provider for an editor plugin.
|
|
5
|
+
*
|
|
6
|
+
* ## Usage
|
|
7
|
+
*
|
|
8
|
+
* ### Create a provider
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* class EmojiNodeDataProvider extends NodeDataProvider<
|
|
13
|
+
* { attrs: EmojiAttributes },
|
|
14
|
+
* { resolvedData: string }
|
|
15
|
+
* > {
|
|
16
|
+
* constructor({ existingCache }?: { existingCache: Record<string, { resolvedData: string }> }) {
|
|
17
|
+
* super({ existingCache, nodeName: 'emoji' });
|
|
18
|
+
* }
|
|
19
|
+
* nodeToKey(node: { attrs: EmojiAttributes }): string {
|
|
20
|
+
* return `${node.attrs.shortName}-${node.attrs.text}-${node.attrs.id}`;
|
|
21
|
+
* }
|
|
22
|
+
* resolve(node: { attrs: EmojiAttributes }, _?: { signal: AbortSignal }) {
|
|
23
|
+
* return Promise.resolve({ resolvedData: 'resolved' });
|
|
24
|
+
* }
|
|
25
|
+
* }
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* ### Use the provider
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```ts
|
|
32
|
+
* const emojiNodeDataProvider = new EmojiNodeDataProvider();
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* ### Caching
|
|
36
|
+
*
|
|
37
|
+
* @see {@link buildCaches} for more information on building caches.
|
|
38
|
+
*
|
|
39
|
+
* #### Load an existing provider with a cache
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```
|
|
43
|
+
* await buildCaches({
|
|
44
|
+
* adf: docFromSomewhere,
|
|
45
|
+
* nodeDataProviders: [emojiNodeDataProvider],
|
|
46
|
+
* signal: AbortSignal.timeout(5000),
|
|
47
|
+
* });
|
|
48
|
+
* emojiNodeDataProvider // { 'key': 'value' }
|
|
49
|
+
* ```
|
|
50
|
+
*
|
|
51
|
+
* ### Load an new provider with an existing cache
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```
|
|
55
|
+
* const provider1 = new ExampleNodeDataProvider();
|
|
56
|
+
* await buildCaches({adf, nodeDataProviders: [provider1]})
|
|
57
|
+
* provider1.cache // { 'key': 'value' }
|
|
58
|
+
*
|
|
59
|
+
* const provider2 = new ExampleNodeDataProvider({existingCache: provider1.cache});
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
export declare class NodeDataProvider<INode extends ReceivableNode, Result extends unknown> {
|
|
63
|
+
/**
|
|
64
|
+
* This is added to ease building types
|
|
65
|
+
*/
|
|
66
|
+
__node: INode;
|
|
67
|
+
private __cache;
|
|
68
|
+
/**
|
|
69
|
+
* This takes a node and returns a key that can be used to cache the result of the resolve function.
|
|
70
|
+
*/
|
|
71
|
+
nodeToKey: (node: INode) => string;
|
|
72
|
+
/**
|
|
73
|
+
* This returns the information required to render a node.
|
|
74
|
+
*
|
|
75
|
+
* If unresolvable, this method will throw an error.
|
|
76
|
+
*
|
|
77
|
+
* If signal is aborted, this method will return undefined.
|
|
78
|
+
*/
|
|
79
|
+
resolve: (node: INode, _?: {
|
|
80
|
+
signal?: AbortSignal;
|
|
81
|
+
}) => Promise<Result | undefined>;
|
|
82
|
+
/**
|
|
83
|
+
* The adf node name this provider is responsible for.
|
|
84
|
+
*/
|
|
85
|
+
nodeName: string;
|
|
86
|
+
constructor({ existingCache, nodeName, nodeToKey, resolve, }: {
|
|
87
|
+
/**
|
|
88
|
+
* A cache to load the provider with.
|
|
89
|
+
*
|
|
90
|
+
* @see {@link buildCaches} for more information on building caches.
|
|
91
|
+
*/
|
|
92
|
+
existingCache?: Record<string, Result>;
|
|
93
|
+
/**
|
|
94
|
+
* The adf node name this provider is responsible for.
|
|
95
|
+
*
|
|
96
|
+
* It is used for;
|
|
97
|
+
* - determining if the traverser should use this provider to resolve a node when building caches
|
|
98
|
+
* - identifying the node when submitting analytics events via the helper react hooks
|
|
99
|
+
*/
|
|
100
|
+
nodeName: string;
|
|
101
|
+
nodeToKey: typeof NodeDataProvider.prototype.nodeToKey;
|
|
102
|
+
resolve: typeof NodeDataProvider.prototype.resolve;
|
|
103
|
+
});
|
|
104
|
+
/**
|
|
105
|
+
* Updates the providers cache.
|
|
106
|
+
*
|
|
107
|
+
* Useful in scenarios such as SSR where the cache is built on the server and then passed to the client.
|
|
108
|
+
*
|
|
109
|
+
* Avoids the need to provide the cache to the constructor (to allow decoupling creation of node data providers from cache building),
|
|
110
|
+
* and allow for caching to be managed at a group level across multiple providers.
|
|
111
|
+
*
|
|
112
|
+
* This is not expected to be used by consumers, for internal consumption examples;
|
|
113
|
+
* @see {@link buildCaches}
|
|
114
|
+
*
|
|
115
|
+
*/
|
|
116
|
+
updateCache(cache: Record<string, Result>, options: {
|
|
117
|
+
strategy: 'merge-override' | 'replace';
|
|
118
|
+
}): void;
|
|
119
|
+
/**
|
|
120
|
+
* This is the cache for the provider.
|
|
121
|
+
*/
|
|
122
|
+
get cache(): Record<string, Result>;
|
|
123
|
+
private pending;
|
|
124
|
+
get(node: INode, _?: {
|
|
125
|
+
signal: AbortSignal;
|
|
126
|
+
}): Promise<Result | undefined> | Result;
|
|
127
|
+
}
|
|
128
|
+
export type ReceivableNode = {
|
|
129
|
+
[key: string]: any;
|
|
130
|
+
attrs: {
|
|
131
|
+
[key: string]: any;
|
|
132
|
+
};
|
|
133
|
+
};
|
|
134
|
+
export type ResolveOptions = {
|
|
135
|
+
signal: AbortSignal;
|
|
136
|
+
};
|