@atlaskit/editor-ssr-renderer 2.1.7 → 2.2.1
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/CHANGELOG.md +18 -0
- package/dist/cjs/editor-ssr-renderer/EditorSSRRenderer.js +168 -144
- package/dist/es2019/editor-ssr-renderer/EditorSSRRenderer.js +141 -118
- package/dist/esm/editor-ssr-renderer/EditorSSRRenderer.js +167 -144
- package/dist/types/editor-ssr-renderer/EditorSSRRenderer.d.ts +6 -1
- package/dist/types-ts4.5/editor-ssr-renderer/EditorSSRRenderer.d.ts +6 -1
- package/package.json +8 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @atlaskit/editor-ssr-renderer
|
|
2
2
|
|
|
3
|
+
## 2.2.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies
|
|
8
|
+
|
|
9
|
+
## 2.2.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- [`cbc1403b3cae1`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/cbc1403b3cae1) -
|
|
14
|
+
[EDITOR-5094](https://hello.jira.atlassian.cloud/browse/EDITOR-5094) - add PerfPortal segments for
|
|
15
|
+
editor SSR logic
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- Updated dependencies
|
|
20
|
+
|
|
3
21
|
## 2.1.7
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
|
@@ -21,12 +21,16 @@ var _model = require("@atlaskit/editor-prosemirror/model");
|
|
|
21
21
|
var _eventDispatcher = require("@atlaskit/editor-common/event-dispatcher");
|
|
22
22
|
var _providerFactory = require("@atlaskit/editor-common/provider-factory");
|
|
23
23
|
var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
|
|
24
|
-
var
|
|
24
|
+
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
|
|
25
|
+
var _ssrMeasures = require("@atlaskit/editor-common/performance/ssr-measures");
|
|
26
|
+
var _excluded = ["plugins", "schema", "doc", "portalProviderAPI", "intl", "onSSRMeasure", "onEditorStateChanged"];
|
|
25
27
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
|
|
26
28
|
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; }
|
|
27
29
|
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) { (0, _defineProperty2.default)(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; }
|
|
28
30
|
function _callSuper(t, o, e) { return o = (0, _getPrototypeOf2.default)(o), (0, _possibleConstructorReturn2.default)(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], (0, _getPrototypeOf2.default)(t).constructor) : o.apply(t, e)); }
|
|
29
31
|
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
|
|
32
|
+
var SSR_TRACE_SEGMENT_NAME = 'reactEditorView/editorSSRRenderer';
|
|
33
|
+
|
|
30
34
|
// The copy of type from prosemirror-view.
|
|
31
35
|
// Probably, we need to fix this package exports and add `NodeViewConstructor` and `MarkViewConstructor` types here.
|
|
32
36
|
/**
|
|
@@ -179,43 +183,50 @@ function EditorSSRRenderer(_ref) {
|
|
|
179
183
|
doc = _ref.doc,
|
|
180
184
|
portalProviderAPI = _ref.portalProviderAPI,
|
|
181
185
|
intl = _ref.intl,
|
|
186
|
+
onSSRMeasure = _ref.onSSRMeasure,
|
|
182
187
|
onEditorStateChanged = _ref.onEditorStateChanged,
|
|
183
188
|
divProps = (0, _objectWithoutProperties2.default)(_ref, _excluded);
|
|
189
|
+
// Should be always the first statement in the component
|
|
190
|
+
var firstRenderStartTimestampRef = (0, _react.useRef)(performance.now());
|
|
191
|
+
|
|
184
192
|
// PMPluginFactoryParams use `getIntl` function to get current intl instance,
|
|
185
193
|
// so we don't need to add `intl` as a dependency to `useMemo`.
|
|
186
194
|
// We will store intl in ref and access to it dynamically in `getIntl` function call.
|
|
187
195
|
var intlRef = (0, _react.useRef)(intl);
|
|
188
196
|
intlRef.current = intl;
|
|
189
197
|
var pmPlugins = (0, _react.useMemo)(function () {
|
|
190
|
-
var
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
198
|
+
var createPMPlugins = function createPMPlugins() {
|
|
199
|
+
var eventDispatcher = new SSREventDispatcher();
|
|
200
|
+
var providerFactory = new _providerFactory.ProviderFactory();
|
|
201
|
+
var pmPluginFactoryParams = {
|
|
202
|
+
dispatch: (0, _eventDispatcher.createDispatch)(eventDispatcher),
|
|
203
|
+
dispatchAnalyticsEvent: function dispatchAnalyticsEvent() {},
|
|
204
|
+
eventDispatcher: eventDispatcher,
|
|
205
|
+
featureFlags: {},
|
|
206
|
+
getIntl: function getIntl() {
|
|
207
|
+
return intlRef.current;
|
|
208
|
+
},
|
|
209
|
+
nodeViewPortalProviderAPI: portalProviderAPI,
|
|
210
|
+
portalProviderAPI: portalProviderAPI,
|
|
211
|
+
providerFactory: providerFactory,
|
|
212
|
+
schema: schema
|
|
213
|
+
};
|
|
214
|
+
return plugins.reduce(function (acc, editorPlugin) {
|
|
215
|
+
var _editorPlugin$pmPlugi;
|
|
216
|
+
(_editorPlugin$pmPlugi = editorPlugin.pmPlugins) === null || _editorPlugin$pmPlugi === void 0 || _editorPlugin$pmPlugi.call(editorPlugin).forEach(function (_ref2) {
|
|
217
|
+
var plugin = _ref2.plugin;
|
|
218
|
+
try {
|
|
219
|
+
var pmPlugin = plugin(pmPluginFactoryParams);
|
|
220
|
+
if (pmPlugin) {
|
|
221
|
+
acc.push(pmPlugin);
|
|
222
|
+
}
|
|
223
|
+
} catch (_unused) {}
|
|
224
|
+
});
|
|
225
|
+
return acc;
|
|
226
|
+
}, []);
|
|
204
227
|
};
|
|
205
|
-
return
|
|
206
|
-
|
|
207
|
-
(_editorPlugin$pmPlugi = editorPlugin.pmPlugins) === null || _editorPlugin$pmPlugi === void 0 || _editorPlugin$pmPlugi.call(editorPlugin).forEach(function (_ref2) {
|
|
208
|
-
var plugin = _ref2.plugin;
|
|
209
|
-
try {
|
|
210
|
-
var pmPlugin = plugin(pmPluginFactoryParams);
|
|
211
|
-
if (pmPlugin) {
|
|
212
|
-
acc.push(pmPlugin);
|
|
213
|
-
}
|
|
214
|
-
} catch (_unused) {}
|
|
215
|
-
});
|
|
216
|
-
return acc;
|
|
217
|
-
}, []);
|
|
218
|
-
}, [plugins, portalProviderAPI, schema]);
|
|
228
|
+
return (0, _platformFeatureFlags.fg)('platform_editor_better_editor_ssr_spans') ? (0, _ssrMeasures.profileSSROperation)("".concat(SSR_TRACE_SEGMENT_NAME, "/createPMPlugins"), createPMPlugins, onSSRMeasure) : createPMPlugins();
|
|
229
|
+
}, [plugins, portalProviderAPI, schema, onSSRMeasure]);
|
|
219
230
|
var nodeViews = (0, _react.useMemo)(function () {
|
|
220
231
|
return pmPlugins.reduce(function (acc, plugin) {
|
|
221
232
|
return Object.assign(acc, plugin.props.nodeViews);
|
|
@@ -227,12 +238,15 @@ function EditorSSRRenderer(_ref) {
|
|
|
227
238
|
}, {});
|
|
228
239
|
}, [pmPlugins]);
|
|
229
240
|
var editorState = (0, _react.useMemo)(function () {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
241
|
+
var createEditorState = function createEditorState() {
|
|
242
|
+
return _state.EditorState.create({
|
|
243
|
+
doc: doc,
|
|
244
|
+
schema: schema,
|
|
245
|
+
plugins: pmPlugins
|
|
246
|
+
});
|
|
247
|
+
};
|
|
248
|
+
return (0, _platformFeatureFlags.fg)('platform_editor_better_editor_ssr_spans') ? (0, _ssrMeasures.profileSSROperation)("".concat(SSR_TRACE_SEGMENT_NAME, "/createEditorState"), createEditorState, onSSRMeasure) : createEditorState();
|
|
249
|
+
}, [doc, pmPlugins, schema, onSSRMeasure]);
|
|
236
250
|
|
|
237
251
|
// In React 19 could be replaced by `useEffectEvent` hook.
|
|
238
252
|
var onEditorStateChangedRef = (0, _react.useRef)(onEditorStateChanged);
|
|
@@ -247,127 +261,133 @@ function EditorSSRRenderer(_ref) {
|
|
|
247
261
|
});
|
|
248
262
|
}, [editorState]);
|
|
249
263
|
var _useMemo = (0, _react.useMemo)(function () {
|
|
250
|
-
var
|
|
264
|
+
var createSerializerAndNodePositions = function createSerializerAndNodePositions() {
|
|
265
|
+
var nodePositions = new WeakMap();
|
|
251
266
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
267
|
+
// ProseMirror View adds <br class="ProseMirror-trailingBreak" /> to empty nodes. Because we are using
|
|
268
|
+
// DOMSerializer, we should simulate the same behaviour to get the same HTML document.
|
|
269
|
+
//
|
|
270
|
+
// There are a lot of conditions that check for adding `<br />` but we could implement only the case when we
|
|
271
|
+
// are adding `<br />` to empty texblock, because if we add `<br />` in other cases it will change order of DOM nodes inside
|
|
272
|
+
// this node (`<br />`) will be the first, after will be other nodes. It's because we are adding `<br />` to root node before
|
|
273
|
+
// we are rendering child node.
|
|
274
|
+
//
|
|
275
|
+
// See: https://discuss.prosemirror.net/t/where-can-i-read-about-prosemirror-trailingbreak/6665
|
|
276
|
+
// See: https://github.com/ProseMirror/prosemirror-view/blob/76c7c47f03730b18397b94bd269ece8a9cb7f486/src/viewdesc.ts#L803
|
|
277
|
+
// See: https://github.com/ProseMirror/prosemirror-view/blob/76c7c47f03730b18397b94bd269ece8a9cb7f486/src/viewdesc.ts#L1365
|
|
278
|
+
var addTrailingBreakIfNeeded = function addTrailingBreakIfNeeded(node, contentDOM) {
|
|
279
|
+
if (contentDOM && node.isTextblock && !node.lastChild) {
|
|
280
|
+
var br = document.createElement('br');
|
|
281
|
+
br.classList.add('ProseMirror-trailingBreak');
|
|
282
|
+
contentDOM.appendChild(br);
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
var toDomNodeRenderers = Object.fromEntries(Object.entries(schema.nodes).map(function (_ref3) {
|
|
286
|
+
var _ref4 = (0, _slicedToArray2.default)(_ref3, 2),
|
|
287
|
+
nodeName = _ref4[0],
|
|
288
|
+
nodeType = _ref4[1];
|
|
289
|
+
return [nodeName, nodeType.spec.toDOM];
|
|
290
|
+
}).filter(function (_ref5) {
|
|
291
|
+
var _ref6 = (0, _slicedToArray2.default)(_ref5, 2),
|
|
292
|
+
toDOM = _ref6[1];
|
|
293
|
+
return !!toDOM;
|
|
294
|
+
}));
|
|
295
|
+
var toDomMarkRenderers = Object.fromEntries(Object.entries(schema.marks).map(function (_ref7) {
|
|
296
|
+
var _ref8 = (0, _slicedToArray2.default)(_ref7, 2),
|
|
297
|
+
markName = _ref8[0],
|
|
298
|
+
markType = _ref8[1];
|
|
299
|
+
return [markName, markType.spec.toDOM];
|
|
300
|
+
}).filter(function (_ref9) {
|
|
301
|
+
var _ref0 = (0, _slicedToArray2.default)(_ref9, 2),
|
|
302
|
+
toDOM = _ref0[1];
|
|
303
|
+
return !!toDOM;
|
|
304
|
+
}));
|
|
305
|
+
var nodeViewRenderers = Object.fromEntries(Object.entries(nodeViews).map(function (_ref1) {
|
|
306
|
+
var _ref10 = (0, _slicedToArray2.default)(_ref1, 2),
|
|
307
|
+
nodeName = _ref10[0],
|
|
308
|
+
nodeViewFactory = _ref10[1];
|
|
309
|
+
return [nodeName, function (node) {
|
|
310
|
+
var nodeViewInstance = nodeViewFactory(node, editorView, function () {
|
|
311
|
+
var _nodePositions$get;
|
|
312
|
+
return (_nodePositions$get = nodePositions.get(node)) !== null && _nodePositions$get !== void 0 ? _nodePositions$get : 0;
|
|
313
|
+
}, [], _view.DecorationSet.create(node, []));
|
|
314
|
+
addTrailingBreakIfNeeded(node, nodeViewInstance.contentDOM);
|
|
315
|
+
return {
|
|
316
|
+
dom: nodeViewInstance.dom,
|
|
317
|
+
// Leaf nodes have no content, ProseMirror will throw an error if we pass contentDOM
|
|
318
|
+
contentDOM: node.isLeaf ? undefined : nodeViewInstance.contentDOM
|
|
319
|
+
};
|
|
320
|
+
}];
|
|
321
|
+
}));
|
|
307
322
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
}
|
|
323
|
-
return [nodeName, function (node) {
|
|
324
|
-
if (!node.lastChild) {
|
|
325
|
-
var result = _model.DOMSerializer.renderSpec(document, toDOM(node));
|
|
326
|
-
addTrailingBreakIfNeeded(node, result.contentDOM);
|
|
327
|
-
return result;
|
|
323
|
+
// Create renderers for textblock nodes that don't have custom NodeViews (e.g. paragraph, heading)
|
|
324
|
+
var textblockRenderers = Object.fromEntries(Object.entries(schema.nodes).filter(function (_ref11) {
|
|
325
|
+
var _ref12 = (0, _slicedToArray2.default)(_ref11, 2),
|
|
326
|
+
nodeName = _ref12[0],
|
|
327
|
+
nodeType = _ref12[1];
|
|
328
|
+
// Only handle textblock nodes
|
|
329
|
+
return nodeType.spec.toDOM && nodeType.isTextblock && !nodeViews[nodeName];
|
|
330
|
+
}).map(function (_ref13) {
|
|
331
|
+
var _ref14 = (0, _slicedToArray2.default)(_ref13, 2),
|
|
332
|
+
nodeName = _ref14[0],
|
|
333
|
+
nodeType = _ref14[1];
|
|
334
|
+
var toDOM = nodeType.spec.toDOM;
|
|
335
|
+
if (!toDOM) {
|
|
336
|
+
return [nodeName, undefined];
|
|
328
337
|
}
|
|
329
|
-
return
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
338
|
+
return [nodeName, function (node) {
|
|
339
|
+
if (!node.lastChild) {
|
|
340
|
+
var result = _model.DOMSerializer.renderSpec(document, toDOM(node));
|
|
341
|
+
addTrailingBreakIfNeeded(node, result.contentDOM);
|
|
342
|
+
return result;
|
|
343
|
+
}
|
|
344
|
+
return toDOM(node);
|
|
345
|
+
}];
|
|
346
|
+
}).filter(function (_ref15) {
|
|
347
|
+
var _ref16 = (0, _slicedToArray2.default)(_ref15, 2),
|
|
348
|
+
renderer = _ref16[1];
|
|
349
|
+
return !!renderer;
|
|
350
|
+
}));
|
|
351
|
+
var markViewRenderers = Object.fromEntries(Object.entries(markViews).map(function (_ref17) {
|
|
352
|
+
var _ref18 = (0, _slicedToArray2.default)(_ref17, 2),
|
|
353
|
+
markName = _ref18[0],
|
|
354
|
+
markViewFactory = _ref18[1];
|
|
355
|
+
return [markName, function (mark) {
|
|
356
|
+
var markViewInstance = markViewFactory(mark, editorView, false);
|
|
357
|
+
return {
|
|
358
|
+
dom: markViewInstance.dom,
|
|
359
|
+
contentDOM: markViewInstance.contentDOM
|
|
360
|
+
};
|
|
361
|
+
}];
|
|
362
|
+
}));
|
|
363
|
+
var serializer = new _model.DOMSerializer(_objectSpread(_objectSpread(_objectSpread(_objectSpread({}, toDomNodeRenderers), textblockRenderers), nodeViewRenderers), {}, {
|
|
364
|
+
text: renderText
|
|
365
|
+
}), _objectSpread(_objectSpread({}, toDomMarkRenderers), markViewRenderers));
|
|
366
|
+
return {
|
|
367
|
+
serializer: serializer,
|
|
368
|
+
nodePositions: nodePositions
|
|
369
|
+
};
|
|
354
370
|
};
|
|
355
|
-
|
|
371
|
+
return (0, _platformFeatureFlags.fg)('platform_editor_better_editor_ssr_spans') ? (0, _ssrMeasures.profileSSROperation)("".concat(SSR_TRACE_SEGMENT_NAME, "/createSerializerAndNodePositions"), createSerializerAndNodePositions, onSSRMeasure) : createSerializerAndNodePositions();
|
|
372
|
+
}, [editorView, markViews, nodeViews, schema.marks, schema.nodes, onSSRMeasure]),
|
|
356
373
|
serializer = _useMemo.serializer,
|
|
357
374
|
nodePositions = _useMemo.nodePositions;
|
|
358
375
|
var editorHTML = (0, _react.useMemo)(function () {
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
376
|
+
var serializeFragment = function serializeFragment() {
|
|
377
|
+
if (!doc) {
|
|
378
|
+
return undefined;
|
|
379
|
+
}
|
|
363
380
|
doc.descendants(function (node, pos) {
|
|
364
381
|
nodePositions.set(node, pos);
|
|
365
382
|
});
|
|
366
383
|
return serializer.serializeFragment(doc.content);
|
|
384
|
+
};
|
|
385
|
+
try {
|
|
386
|
+
return (0, _platformFeatureFlags.fg)('platform_editor_better_editor_ssr_spans') ? (0, _ssrMeasures.profileSSROperation)("".concat(SSR_TRACE_SEGMENT_NAME, "/serializeFragment"), serializeFragment, onSSRMeasure) : serializeFragment();
|
|
367
387
|
} catch (_unused2) {
|
|
368
388
|
return undefined;
|
|
369
389
|
}
|
|
370
|
-
}, [doc, serializer, nodePositions]);
|
|
390
|
+
}, [doc, serializer, nodePositions, onSSRMeasure]);
|
|
371
391
|
var containerRef = (0, _react.useRef)(null);
|
|
372
392
|
(0, _react.useLayoutEffect)(function () {
|
|
373
393
|
if (containerRef.current && editorHTML) {
|
|
@@ -375,7 +395,11 @@ function EditorSSRRenderer(_ref) {
|
|
|
375
395
|
containerRef.current.appendChild(editorHTML);
|
|
376
396
|
}
|
|
377
397
|
}, [editorHTML]);
|
|
378
|
-
return /*#__PURE__*/_react.default.createElement(
|
|
398
|
+
return /*#__PURE__*/_react.default.createElement(_ssrMeasures.SSRRenderMeasure, {
|
|
399
|
+
segmentName: SSR_TRACE_SEGMENT_NAME,
|
|
400
|
+
startTimestampRef: firstRenderStartTimestampRef,
|
|
401
|
+
onSSRMeasure: (0, _platformFeatureFlags.fg)('platform_editor_better_editor_ssr_spans') ? onSSRMeasure : undefined
|
|
402
|
+
}, /*#__PURE__*/_react.default.createElement("div", {
|
|
379
403
|
ref: containerRef,
|
|
380
404
|
id: divProps.id
|
|
381
405
|
// For some reason on SSR, the result `class` has a trailing space, that broke UFO,
|
|
@@ -396,7 +420,7 @@ function EditorSSRRenderer(_ref) {
|
|
|
396
420
|
contenteditable: "true",
|
|
397
421
|
"data-gramm": "false",
|
|
398
422
|
translate: "no"
|
|
399
|
-
});
|
|
423
|
+
}));
|
|
400
424
|
}
|
|
401
425
|
function renderText(node) {
|
|
402
426
|
return node.text || '';
|
|
@@ -5,6 +5,9 @@ import { DOMSerializer } from '@atlaskit/editor-prosemirror/model';
|
|
|
5
5
|
import { EventDispatcher, createDispatch } from '@atlaskit/editor-common/event-dispatcher';
|
|
6
6
|
import { ProviderFactory } from '@atlaskit/editor-common/provider-factory';
|
|
7
7
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
8
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
9
|
+
import { profileSSROperation, SSRRenderMeasure } from '@atlaskit/editor-common/performance/ssr-measures';
|
|
10
|
+
const SSR_TRACE_SEGMENT_NAME = 'reactEditorView/editorSSRRenderer';
|
|
8
11
|
|
|
9
12
|
// The copy of type from prosemirror-view.
|
|
10
13
|
// Probably, we need to fix this package exports and add `NodeViewConstructor` and `MarkViewConstructor` types here.
|
|
@@ -111,43 +114,50 @@ export function EditorSSRRenderer({
|
|
|
111
114
|
doc,
|
|
112
115
|
portalProviderAPI,
|
|
113
116
|
intl,
|
|
117
|
+
onSSRMeasure,
|
|
114
118
|
onEditorStateChanged,
|
|
115
119
|
...divProps
|
|
116
120
|
}) {
|
|
121
|
+
// Should be always the first statement in the component
|
|
122
|
+
const firstRenderStartTimestampRef = useRef(performance.now());
|
|
123
|
+
|
|
117
124
|
// PMPluginFactoryParams use `getIntl` function to get current intl instance,
|
|
118
125
|
// so we don't need to add `intl` as a dependency to `useMemo`.
|
|
119
126
|
// We will store intl in ref and access to it dynamically in `getIntl` function call.
|
|
120
127
|
const intlRef = useRef(intl);
|
|
121
128
|
intlRef.current = intl;
|
|
122
129
|
const pmPlugins = useMemo(() => {
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
130
|
+
const createPMPlugins = () => {
|
|
131
|
+
const eventDispatcher = new SSREventDispatcher();
|
|
132
|
+
const providerFactory = new ProviderFactory();
|
|
133
|
+
const pmPluginFactoryParams = {
|
|
134
|
+
dispatch: createDispatch(eventDispatcher),
|
|
135
|
+
dispatchAnalyticsEvent: () => {},
|
|
136
|
+
eventDispatcher,
|
|
137
|
+
featureFlags: {},
|
|
138
|
+
getIntl: () => intlRef.current,
|
|
139
|
+
nodeViewPortalProviderAPI: portalProviderAPI,
|
|
140
|
+
portalProviderAPI: portalProviderAPI,
|
|
141
|
+
providerFactory,
|
|
142
|
+
schema
|
|
143
|
+
};
|
|
144
|
+
return plugins.reduce((acc, editorPlugin) => {
|
|
145
|
+
var _editorPlugin$pmPlugi;
|
|
146
|
+
(_editorPlugin$pmPlugi = editorPlugin.pmPlugins) === null || _editorPlugin$pmPlugi === void 0 ? void 0 : _editorPlugin$pmPlugi.call(editorPlugin).forEach(({
|
|
147
|
+
plugin
|
|
148
|
+
}) => {
|
|
149
|
+
try {
|
|
150
|
+
const pmPlugin = plugin(pmPluginFactoryParams);
|
|
151
|
+
if (pmPlugin) {
|
|
152
|
+
acc.push(pmPlugin);
|
|
153
|
+
}
|
|
154
|
+
} catch {}
|
|
155
|
+
});
|
|
156
|
+
return acc;
|
|
157
|
+
}, []);
|
|
135
158
|
};
|
|
136
|
-
return
|
|
137
|
-
|
|
138
|
-
(_editorPlugin$pmPlugi = editorPlugin.pmPlugins) === null || _editorPlugin$pmPlugi === void 0 ? void 0 : _editorPlugin$pmPlugi.call(editorPlugin).forEach(({
|
|
139
|
-
plugin
|
|
140
|
-
}) => {
|
|
141
|
-
try {
|
|
142
|
-
const pmPlugin = plugin(pmPluginFactoryParams);
|
|
143
|
-
if (pmPlugin) {
|
|
144
|
-
acc.push(pmPlugin);
|
|
145
|
-
}
|
|
146
|
-
} catch {}
|
|
147
|
-
});
|
|
148
|
-
return acc;
|
|
149
|
-
}, []);
|
|
150
|
-
}, [plugins, portalProviderAPI, schema]);
|
|
159
|
+
return fg('platform_editor_better_editor_ssr_spans') ? profileSSROperation(`${SSR_TRACE_SEGMENT_NAME}/createPMPlugins`, createPMPlugins, onSSRMeasure) : createPMPlugins();
|
|
160
|
+
}, [plugins, portalProviderAPI, schema, onSSRMeasure]);
|
|
151
161
|
const nodeViews = useMemo(() => {
|
|
152
162
|
return pmPlugins.reduce((acc, plugin) => {
|
|
153
163
|
return Object.assign(acc, plugin.props.nodeViews);
|
|
@@ -159,12 +169,15 @@ export function EditorSSRRenderer({
|
|
|
159
169
|
}, {});
|
|
160
170
|
}, [pmPlugins]);
|
|
161
171
|
const editorState = useMemo(() => {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
172
|
+
const createEditorState = () => {
|
|
173
|
+
return EditorState.create({
|
|
174
|
+
doc,
|
|
175
|
+
schema,
|
|
176
|
+
plugins: pmPlugins
|
|
177
|
+
});
|
|
178
|
+
};
|
|
179
|
+
return fg('platform_editor_better_editor_ssr_spans') ? profileSSROperation(`${SSR_TRACE_SEGMENT_NAME}/createEditorState`, createEditorState, onSSRMeasure) : createEditorState();
|
|
180
|
+
}, [doc, pmPlugins, schema, onSSRMeasure]);
|
|
168
181
|
|
|
169
182
|
// In React 19 could be replaced by `useEffectEvent` hook.
|
|
170
183
|
const onEditorStateChangedRef = useRef(onEditorStateChanged);
|
|
@@ -182,101 +195,107 @@ export function EditorSSRRenderer({
|
|
|
182
195
|
serializer,
|
|
183
196
|
nodePositions
|
|
184
197
|
} = useMemo(() => {
|
|
185
|
-
const
|
|
198
|
+
const createSerializerAndNodePositions = () => {
|
|
199
|
+
const nodePositions = new WeakMap();
|
|
186
200
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
201
|
+
// ProseMirror View adds <br class="ProseMirror-trailingBreak" /> to empty nodes. Because we are using
|
|
202
|
+
// DOMSerializer, we should simulate the same behaviour to get the same HTML document.
|
|
203
|
+
//
|
|
204
|
+
// There are a lot of conditions that check for adding `<br />` but we could implement only the case when we
|
|
205
|
+
// are adding `<br />` to empty texblock, because if we add `<br />` in other cases it will change order of DOM nodes inside
|
|
206
|
+
// this node (`<br />`) will be the first, after will be other nodes. It's because we are adding `<br />` to root node before
|
|
207
|
+
// we are rendering child node.
|
|
208
|
+
//
|
|
209
|
+
// See: https://discuss.prosemirror.net/t/where-can-i-read-about-prosemirror-trailingbreak/6665
|
|
210
|
+
// See: https://github.com/ProseMirror/prosemirror-view/blob/76c7c47f03730b18397b94bd269ece8a9cb7f486/src/viewdesc.ts#L803
|
|
211
|
+
// See: https://github.com/ProseMirror/prosemirror-view/blob/76c7c47f03730b18397b94bd269ece8a9cb7f486/src/viewdesc.ts#L1365
|
|
212
|
+
const addTrailingBreakIfNeeded = (node, contentDOM) => {
|
|
213
|
+
if (contentDOM && node.isTextblock && !node.lastChild) {
|
|
214
|
+
const br = document.createElement('br');
|
|
215
|
+
br.classList.add('ProseMirror-trailingBreak');
|
|
216
|
+
contentDOM.appendChild(br);
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
const toDomNodeRenderers = Object.fromEntries(Object.entries(schema.nodes).map(([nodeName, nodeType]) => {
|
|
220
|
+
return [nodeName, nodeType.spec.toDOM];
|
|
221
|
+
}).filter(([, toDOM]) => !!toDOM));
|
|
222
|
+
const toDomMarkRenderers = Object.fromEntries(Object.entries(schema.marks).map(([markName, markType]) => {
|
|
223
|
+
return [markName, markType.spec.toDOM];
|
|
224
|
+
}).filter(([, toDOM]) => !!toDOM));
|
|
225
|
+
const nodeViewRenderers = Object.fromEntries(Object.entries(nodeViews).map(([nodeName, nodeViewFactory]) => {
|
|
226
|
+
return [nodeName, node => {
|
|
227
|
+
const nodeViewInstance = nodeViewFactory(node, editorView, () => {
|
|
228
|
+
var _nodePositions$get;
|
|
229
|
+
return (_nodePositions$get = nodePositions.get(node)) !== null && _nodePositions$get !== void 0 ? _nodePositions$get : 0;
|
|
230
|
+
}, [], DecorationSet.create(node, []));
|
|
231
|
+
addTrailingBreakIfNeeded(node, nodeViewInstance.contentDOM);
|
|
232
|
+
return {
|
|
233
|
+
dom: nodeViewInstance.dom,
|
|
234
|
+
// Leaf nodes have no content, ProseMirror will throw an error if we pass contentDOM
|
|
235
|
+
contentDOM: node.isLeaf ? undefined : nodeViewInstance.contentDOM
|
|
236
|
+
};
|
|
237
|
+
}];
|
|
238
|
+
}));
|
|
225
239
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
}
|
|
235
|
-
return [nodeName, node => {
|
|
236
|
-
if (!node.lastChild) {
|
|
237
|
-
const result = DOMSerializer.renderSpec(document, toDOM(node));
|
|
238
|
-
addTrailingBreakIfNeeded(node, result.contentDOM);
|
|
239
|
-
return result;
|
|
240
|
+
// Create renderers for textblock nodes that don't have custom NodeViews (e.g. paragraph, heading)
|
|
241
|
+
const textblockRenderers = Object.fromEntries(Object.entries(schema.nodes).filter(([nodeName, nodeType]) => {
|
|
242
|
+
// Only handle textblock nodes
|
|
243
|
+
return nodeType.spec.toDOM && nodeType.isTextblock && !nodeViews[nodeName];
|
|
244
|
+
}).map(([nodeName, nodeType]) => {
|
|
245
|
+
const toDOM = nodeType.spec.toDOM;
|
|
246
|
+
if (!toDOM) {
|
|
247
|
+
return [nodeName, undefined];
|
|
240
248
|
}
|
|
241
|
-
return
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
249
|
+
return [nodeName, node => {
|
|
250
|
+
if (!node.lastChild) {
|
|
251
|
+
const result = DOMSerializer.renderSpec(document, toDOM(node));
|
|
252
|
+
addTrailingBreakIfNeeded(node, result.contentDOM);
|
|
253
|
+
return result;
|
|
254
|
+
}
|
|
255
|
+
return toDOM(node);
|
|
256
|
+
}];
|
|
257
|
+
}).filter(([, renderer]) => !!renderer));
|
|
258
|
+
const markViewRenderers = Object.fromEntries(Object.entries(markViews).map(([markName, markViewFactory]) => {
|
|
259
|
+
return [markName, mark => {
|
|
260
|
+
const markViewInstance = markViewFactory(mark, editorView, false);
|
|
261
|
+
return {
|
|
262
|
+
dom: markViewInstance.dom,
|
|
263
|
+
contentDOM: markViewInstance.contentDOM
|
|
264
|
+
};
|
|
265
|
+
}];
|
|
266
|
+
}));
|
|
267
|
+
const serializer = new DOMSerializer({
|
|
268
|
+
...toDomNodeRenderers,
|
|
269
|
+
...textblockRenderers,
|
|
270
|
+
...nodeViewRenderers,
|
|
271
|
+
text: renderText
|
|
272
|
+
}, {
|
|
273
|
+
...toDomMarkRenderers,
|
|
274
|
+
...markViewRenderers
|
|
275
|
+
});
|
|
276
|
+
return {
|
|
277
|
+
serializer,
|
|
278
|
+
nodePositions
|
|
279
|
+
};
|
|
265
280
|
};
|
|
266
|
-
|
|
281
|
+
return fg('platform_editor_better_editor_ssr_spans') ? profileSSROperation(`${SSR_TRACE_SEGMENT_NAME}/createSerializerAndNodePositions`, createSerializerAndNodePositions, onSSRMeasure) : createSerializerAndNodePositions();
|
|
282
|
+
}, [editorView, markViews, nodeViews, schema.marks, schema.nodes, onSSRMeasure]);
|
|
267
283
|
const editorHTML = useMemo(() => {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
284
|
+
const serializeFragment = () => {
|
|
285
|
+
if (!doc) {
|
|
286
|
+
return undefined;
|
|
287
|
+
}
|
|
272
288
|
doc.descendants((node, pos) => {
|
|
273
289
|
nodePositions.set(node, pos);
|
|
274
290
|
});
|
|
275
291
|
return serializer.serializeFragment(doc.content);
|
|
292
|
+
};
|
|
293
|
+
try {
|
|
294
|
+
return fg('platform_editor_better_editor_ssr_spans') ? profileSSROperation(`${SSR_TRACE_SEGMENT_NAME}/serializeFragment`, serializeFragment, onSSRMeasure) : serializeFragment();
|
|
276
295
|
} catch {
|
|
277
296
|
return undefined;
|
|
278
297
|
}
|
|
279
|
-
}, [doc, serializer, nodePositions]);
|
|
298
|
+
}, [doc, serializer, nodePositions, onSSRMeasure]);
|
|
280
299
|
const containerRef = useRef(null);
|
|
281
300
|
useLayoutEffect(() => {
|
|
282
301
|
if (containerRef.current && editorHTML) {
|
|
@@ -284,7 +303,11 @@ export function EditorSSRRenderer({
|
|
|
284
303
|
containerRef.current.appendChild(editorHTML);
|
|
285
304
|
}
|
|
286
305
|
}, [editorHTML]);
|
|
287
|
-
return /*#__PURE__*/React.createElement(
|
|
306
|
+
return /*#__PURE__*/React.createElement(SSRRenderMeasure, {
|
|
307
|
+
segmentName: SSR_TRACE_SEGMENT_NAME,
|
|
308
|
+
startTimestampRef: firstRenderStartTimestampRef,
|
|
309
|
+
onSSRMeasure: fg('platform_editor_better_editor_ssr_spans') ? onSSRMeasure : undefined
|
|
310
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
288
311
|
ref: containerRef,
|
|
289
312
|
id: divProps.id
|
|
290
313
|
// For some reason on SSR, the result `class` has a trailing space, that broke UFO,
|
|
@@ -305,7 +328,7 @@ export function EditorSSRRenderer({
|
|
|
305
328
|
contenteditable: "true",
|
|
306
329
|
"data-gramm": "false",
|
|
307
330
|
translate: "no"
|
|
308
|
-
});
|
|
331
|
+
}));
|
|
309
332
|
}
|
|
310
333
|
function renderText(node) {
|
|
311
334
|
return node.text || '';
|
|
@@ -6,7 +6,7 @@ import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf";
|
|
|
6
6
|
import _inherits from "@babel/runtime/helpers/inherits";
|
|
7
7
|
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
|
|
8
8
|
import _createClass from "@babel/runtime/helpers/createClass";
|
|
9
|
-
var _excluded = ["plugins", "schema", "doc", "portalProviderAPI", "intl", "onEditorStateChanged"];
|
|
9
|
+
var _excluded = ["plugins", "schema", "doc", "portalProviderAPI", "intl", "onSSRMeasure", "onEditorStateChanged"];
|
|
10
10
|
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; }
|
|
11
11
|
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; }
|
|
12
12
|
function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); }
|
|
@@ -18,6 +18,9 @@ import { DOMSerializer } from '@atlaskit/editor-prosemirror/model';
|
|
|
18
18
|
import { EventDispatcher, createDispatch } from '@atlaskit/editor-common/event-dispatcher';
|
|
19
19
|
import { ProviderFactory } from '@atlaskit/editor-common/provider-factory';
|
|
20
20
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
21
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
22
|
+
import { profileSSROperation, SSRRenderMeasure } from '@atlaskit/editor-common/performance/ssr-measures';
|
|
23
|
+
var SSR_TRACE_SEGMENT_NAME = 'reactEditorView/editorSSRRenderer';
|
|
21
24
|
|
|
22
25
|
// The copy of type from prosemirror-view.
|
|
23
26
|
// Probably, we need to fix this package exports and add `NodeViewConstructor` and `MarkViewConstructor` types here.
|
|
@@ -171,43 +174,50 @@ export function EditorSSRRenderer(_ref) {
|
|
|
171
174
|
doc = _ref.doc,
|
|
172
175
|
portalProviderAPI = _ref.portalProviderAPI,
|
|
173
176
|
intl = _ref.intl,
|
|
177
|
+
onSSRMeasure = _ref.onSSRMeasure,
|
|
174
178
|
onEditorStateChanged = _ref.onEditorStateChanged,
|
|
175
179
|
divProps = _objectWithoutProperties(_ref, _excluded);
|
|
180
|
+
// Should be always the first statement in the component
|
|
181
|
+
var firstRenderStartTimestampRef = useRef(performance.now());
|
|
182
|
+
|
|
176
183
|
// PMPluginFactoryParams use `getIntl` function to get current intl instance,
|
|
177
184
|
// so we don't need to add `intl` as a dependency to `useMemo`.
|
|
178
185
|
// We will store intl in ref and access to it dynamically in `getIntl` function call.
|
|
179
186
|
var intlRef = useRef(intl);
|
|
180
187
|
intlRef.current = intl;
|
|
181
188
|
var pmPlugins = useMemo(function () {
|
|
182
|
-
var
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
189
|
+
var createPMPlugins = function createPMPlugins() {
|
|
190
|
+
var eventDispatcher = new SSREventDispatcher();
|
|
191
|
+
var providerFactory = new ProviderFactory();
|
|
192
|
+
var pmPluginFactoryParams = {
|
|
193
|
+
dispatch: createDispatch(eventDispatcher),
|
|
194
|
+
dispatchAnalyticsEvent: function dispatchAnalyticsEvent() {},
|
|
195
|
+
eventDispatcher: eventDispatcher,
|
|
196
|
+
featureFlags: {},
|
|
197
|
+
getIntl: function getIntl() {
|
|
198
|
+
return intlRef.current;
|
|
199
|
+
},
|
|
200
|
+
nodeViewPortalProviderAPI: portalProviderAPI,
|
|
201
|
+
portalProviderAPI: portalProviderAPI,
|
|
202
|
+
providerFactory: providerFactory,
|
|
203
|
+
schema: schema
|
|
204
|
+
};
|
|
205
|
+
return plugins.reduce(function (acc, editorPlugin) {
|
|
206
|
+
var _editorPlugin$pmPlugi;
|
|
207
|
+
(_editorPlugin$pmPlugi = editorPlugin.pmPlugins) === null || _editorPlugin$pmPlugi === void 0 || _editorPlugin$pmPlugi.call(editorPlugin).forEach(function (_ref2) {
|
|
208
|
+
var plugin = _ref2.plugin;
|
|
209
|
+
try {
|
|
210
|
+
var pmPlugin = plugin(pmPluginFactoryParams);
|
|
211
|
+
if (pmPlugin) {
|
|
212
|
+
acc.push(pmPlugin);
|
|
213
|
+
}
|
|
214
|
+
} catch (_unused) {}
|
|
215
|
+
});
|
|
216
|
+
return acc;
|
|
217
|
+
}, []);
|
|
196
218
|
};
|
|
197
|
-
return
|
|
198
|
-
|
|
199
|
-
(_editorPlugin$pmPlugi = editorPlugin.pmPlugins) === null || _editorPlugin$pmPlugi === void 0 || _editorPlugin$pmPlugi.call(editorPlugin).forEach(function (_ref2) {
|
|
200
|
-
var plugin = _ref2.plugin;
|
|
201
|
-
try {
|
|
202
|
-
var pmPlugin = plugin(pmPluginFactoryParams);
|
|
203
|
-
if (pmPlugin) {
|
|
204
|
-
acc.push(pmPlugin);
|
|
205
|
-
}
|
|
206
|
-
} catch (_unused) {}
|
|
207
|
-
});
|
|
208
|
-
return acc;
|
|
209
|
-
}, []);
|
|
210
|
-
}, [plugins, portalProviderAPI, schema]);
|
|
219
|
+
return fg('platform_editor_better_editor_ssr_spans') ? profileSSROperation("".concat(SSR_TRACE_SEGMENT_NAME, "/createPMPlugins"), createPMPlugins, onSSRMeasure) : createPMPlugins();
|
|
220
|
+
}, [plugins, portalProviderAPI, schema, onSSRMeasure]);
|
|
211
221
|
var nodeViews = useMemo(function () {
|
|
212
222
|
return pmPlugins.reduce(function (acc, plugin) {
|
|
213
223
|
return Object.assign(acc, plugin.props.nodeViews);
|
|
@@ -219,12 +229,15 @@ export function EditorSSRRenderer(_ref) {
|
|
|
219
229
|
}, {});
|
|
220
230
|
}, [pmPlugins]);
|
|
221
231
|
var editorState = useMemo(function () {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
232
|
+
var createEditorState = function createEditorState() {
|
|
233
|
+
return EditorState.create({
|
|
234
|
+
doc: doc,
|
|
235
|
+
schema: schema,
|
|
236
|
+
plugins: pmPlugins
|
|
237
|
+
});
|
|
238
|
+
};
|
|
239
|
+
return fg('platform_editor_better_editor_ssr_spans') ? profileSSROperation("".concat(SSR_TRACE_SEGMENT_NAME, "/createEditorState"), createEditorState, onSSRMeasure) : createEditorState();
|
|
240
|
+
}, [doc, pmPlugins, schema, onSSRMeasure]);
|
|
228
241
|
|
|
229
242
|
// In React 19 could be replaced by `useEffectEvent` hook.
|
|
230
243
|
var onEditorStateChangedRef = useRef(onEditorStateChanged);
|
|
@@ -239,127 +252,133 @@ export function EditorSSRRenderer(_ref) {
|
|
|
239
252
|
});
|
|
240
253
|
}, [editorState]);
|
|
241
254
|
var _useMemo = useMemo(function () {
|
|
242
|
-
var
|
|
255
|
+
var createSerializerAndNodePositions = function createSerializerAndNodePositions() {
|
|
256
|
+
var nodePositions = new WeakMap();
|
|
243
257
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
258
|
+
// ProseMirror View adds <br class="ProseMirror-trailingBreak" /> to empty nodes. Because we are using
|
|
259
|
+
// DOMSerializer, we should simulate the same behaviour to get the same HTML document.
|
|
260
|
+
//
|
|
261
|
+
// There are a lot of conditions that check for adding `<br />` but we could implement only the case when we
|
|
262
|
+
// are adding `<br />` to empty texblock, because if we add `<br />` in other cases it will change order of DOM nodes inside
|
|
263
|
+
// this node (`<br />`) will be the first, after will be other nodes. It's because we are adding `<br />` to root node before
|
|
264
|
+
// we are rendering child node.
|
|
265
|
+
//
|
|
266
|
+
// See: https://discuss.prosemirror.net/t/where-can-i-read-about-prosemirror-trailingbreak/6665
|
|
267
|
+
// See: https://github.com/ProseMirror/prosemirror-view/blob/76c7c47f03730b18397b94bd269ece8a9cb7f486/src/viewdesc.ts#L803
|
|
268
|
+
// See: https://github.com/ProseMirror/prosemirror-view/blob/76c7c47f03730b18397b94bd269ece8a9cb7f486/src/viewdesc.ts#L1365
|
|
269
|
+
var addTrailingBreakIfNeeded = function addTrailingBreakIfNeeded(node, contentDOM) {
|
|
270
|
+
if (contentDOM && node.isTextblock && !node.lastChild) {
|
|
271
|
+
var br = document.createElement('br');
|
|
272
|
+
br.classList.add('ProseMirror-trailingBreak');
|
|
273
|
+
contentDOM.appendChild(br);
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
var toDomNodeRenderers = Object.fromEntries(Object.entries(schema.nodes).map(function (_ref3) {
|
|
277
|
+
var _ref4 = _slicedToArray(_ref3, 2),
|
|
278
|
+
nodeName = _ref4[0],
|
|
279
|
+
nodeType = _ref4[1];
|
|
280
|
+
return [nodeName, nodeType.spec.toDOM];
|
|
281
|
+
}).filter(function (_ref5) {
|
|
282
|
+
var _ref6 = _slicedToArray(_ref5, 2),
|
|
283
|
+
toDOM = _ref6[1];
|
|
284
|
+
return !!toDOM;
|
|
285
|
+
}));
|
|
286
|
+
var toDomMarkRenderers = Object.fromEntries(Object.entries(schema.marks).map(function (_ref7) {
|
|
287
|
+
var _ref8 = _slicedToArray(_ref7, 2),
|
|
288
|
+
markName = _ref8[0],
|
|
289
|
+
markType = _ref8[1];
|
|
290
|
+
return [markName, markType.spec.toDOM];
|
|
291
|
+
}).filter(function (_ref9) {
|
|
292
|
+
var _ref0 = _slicedToArray(_ref9, 2),
|
|
293
|
+
toDOM = _ref0[1];
|
|
294
|
+
return !!toDOM;
|
|
295
|
+
}));
|
|
296
|
+
var nodeViewRenderers = Object.fromEntries(Object.entries(nodeViews).map(function (_ref1) {
|
|
297
|
+
var _ref10 = _slicedToArray(_ref1, 2),
|
|
298
|
+
nodeName = _ref10[0],
|
|
299
|
+
nodeViewFactory = _ref10[1];
|
|
300
|
+
return [nodeName, function (node) {
|
|
301
|
+
var nodeViewInstance = nodeViewFactory(node, editorView, function () {
|
|
302
|
+
var _nodePositions$get;
|
|
303
|
+
return (_nodePositions$get = nodePositions.get(node)) !== null && _nodePositions$get !== void 0 ? _nodePositions$get : 0;
|
|
304
|
+
}, [], DecorationSet.create(node, []));
|
|
305
|
+
addTrailingBreakIfNeeded(node, nodeViewInstance.contentDOM);
|
|
306
|
+
return {
|
|
307
|
+
dom: nodeViewInstance.dom,
|
|
308
|
+
// Leaf nodes have no content, ProseMirror will throw an error if we pass contentDOM
|
|
309
|
+
contentDOM: node.isLeaf ? undefined : nodeViewInstance.contentDOM
|
|
310
|
+
};
|
|
311
|
+
}];
|
|
312
|
+
}));
|
|
299
313
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
}
|
|
315
|
-
return [nodeName, function (node) {
|
|
316
|
-
if (!node.lastChild) {
|
|
317
|
-
var result = DOMSerializer.renderSpec(document, toDOM(node));
|
|
318
|
-
addTrailingBreakIfNeeded(node, result.contentDOM);
|
|
319
|
-
return result;
|
|
314
|
+
// Create renderers for textblock nodes that don't have custom NodeViews (e.g. paragraph, heading)
|
|
315
|
+
var textblockRenderers = Object.fromEntries(Object.entries(schema.nodes).filter(function (_ref11) {
|
|
316
|
+
var _ref12 = _slicedToArray(_ref11, 2),
|
|
317
|
+
nodeName = _ref12[0],
|
|
318
|
+
nodeType = _ref12[1];
|
|
319
|
+
// Only handle textblock nodes
|
|
320
|
+
return nodeType.spec.toDOM && nodeType.isTextblock && !nodeViews[nodeName];
|
|
321
|
+
}).map(function (_ref13) {
|
|
322
|
+
var _ref14 = _slicedToArray(_ref13, 2),
|
|
323
|
+
nodeName = _ref14[0],
|
|
324
|
+
nodeType = _ref14[1];
|
|
325
|
+
var toDOM = nodeType.spec.toDOM;
|
|
326
|
+
if (!toDOM) {
|
|
327
|
+
return [nodeName, undefined];
|
|
320
328
|
}
|
|
321
|
-
return
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
329
|
+
return [nodeName, function (node) {
|
|
330
|
+
if (!node.lastChild) {
|
|
331
|
+
var result = DOMSerializer.renderSpec(document, toDOM(node));
|
|
332
|
+
addTrailingBreakIfNeeded(node, result.contentDOM);
|
|
333
|
+
return result;
|
|
334
|
+
}
|
|
335
|
+
return toDOM(node);
|
|
336
|
+
}];
|
|
337
|
+
}).filter(function (_ref15) {
|
|
338
|
+
var _ref16 = _slicedToArray(_ref15, 2),
|
|
339
|
+
renderer = _ref16[1];
|
|
340
|
+
return !!renderer;
|
|
341
|
+
}));
|
|
342
|
+
var markViewRenderers = Object.fromEntries(Object.entries(markViews).map(function (_ref17) {
|
|
343
|
+
var _ref18 = _slicedToArray(_ref17, 2),
|
|
344
|
+
markName = _ref18[0],
|
|
345
|
+
markViewFactory = _ref18[1];
|
|
346
|
+
return [markName, function (mark) {
|
|
347
|
+
var markViewInstance = markViewFactory(mark, editorView, false);
|
|
348
|
+
return {
|
|
349
|
+
dom: markViewInstance.dom,
|
|
350
|
+
contentDOM: markViewInstance.contentDOM
|
|
351
|
+
};
|
|
352
|
+
}];
|
|
353
|
+
}));
|
|
354
|
+
var serializer = new DOMSerializer(_objectSpread(_objectSpread(_objectSpread(_objectSpread({}, toDomNodeRenderers), textblockRenderers), nodeViewRenderers), {}, {
|
|
355
|
+
text: renderText
|
|
356
|
+
}), _objectSpread(_objectSpread({}, toDomMarkRenderers), markViewRenderers));
|
|
357
|
+
return {
|
|
358
|
+
serializer: serializer,
|
|
359
|
+
nodePositions: nodePositions
|
|
360
|
+
};
|
|
346
361
|
};
|
|
347
|
-
|
|
362
|
+
return fg('platform_editor_better_editor_ssr_spans') ? profileSSROperation("".concat(SSR_TRACE_SEGMENT_NAME, "/createSerializerAndNodePositions"), createSerializerAndNodePositions, onSSRMeasure) : createSerializerAndNodePositions();
|
|
363
|
+
}, [editorView, markViews, nodeViews, schema.marks, schema.nodes, onSSRMeasure]),
|
|
348
364
|
serializer = _useMemo.serializer,
|
|
349
365
|
nodePositions = _useMemo.nodePositions;
|
|
350
366
|
var editorHTML = useMemo(function () {
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
367
|
+
var serializeFragment = function serializeFragment() {
|
|
368
|
+
if (!doc) {
|
|
369
|
+
return undefined;
|
|
370
|
+
}
|
|
355
371
|
doc.descendants(function (node, pos) {
|
|
356
372
|
nodePositions.set(node, pos);
|
|
357
373
|
});
|
|
358
374
|
return serializer.serializeFragment(doc.content);
|
|
375
|
+
};
|
|
376
|
+
try {
|
|
377
|
+
return fg('platform_editor_better_editor_ssr_spans') ? profileSSROperation("".concat(SSR_TRACE_SEGMENT_NAME, "/serializeFragment"), serializeFragment, onSSRMeasure) : serializeFragment();
|
|
359
378
|
} catch (_unused2) {
|
|
360
379
|
return undefined;
|
|
361
380
|
}
|
|
362
|
-
}, [doc, serializer, nodePositions]);
|
|
381
|
+
}, [doc, serializer, nodePositions, onSSRMeasure]);
|
|
363
382
|
var containerRef = useRef(null);
|
|
364
383
|
useLayoutEffect(function () {
|
|
365
384
|
if (containerRef.current && editorHTML) {
|
|
@@ -367,7 +386,11 @@ export function EditorSSRRenderer(_ref) {
|
|
|
367
386
|
containerRef.current.appendChild(editorHTML);
|
|
368
387
|
}
|
|
369
388
|
}, [editorHTML]);
|
|
370
|
-
return /*#__PURE__*/React.createElement(
|
|
389
|
+
return /*#__PURE__*/React.createElement(SSRRenderMeasure, {
|
|
390
|
+
segmentName: SSR_TRACE_SEGMENT_NAME,
|
|
391
|
+
startTimestampRef: firstRenderStartTimestampRef,
|
|
392
|
+
onSSRMeasure: fg('platform_editor_better_editor_ssr_spans') ? onSSRMeasure : undefined
|
|
393
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
371
394
|
ref: containerRef,
|
|
372
395
|
id: divProps.id
|
|
373
396
|
// For some reason on SSR, the result `class` has a trailing space, that broke UFO,
|
|
@@ -388,7 +411,7 @@ export function EditorSSRRenderer(_ref) {
|
|
|
388
411
|
contenteditable: "true",
|
|
389
412
|
"data-gramm": "false",
|
|
390
413
|
translate: "no"
|
|
391
|
-
});
|
|
414
|
+
}));
|
|
392
415
|
}
|
|
393
416
|
function renderText(node) {
|
|
394
417
|
return node.text || '';
|
|
@@ -14,9 +14,14 @@ interface Props {
|
|
|
14
14
|
id: string;
|
|
15
15
|
intl: IntlShape;
|
|
16
16
|
onEditorStateChanged?: (state: EditorState) => void;
|
|
17
|
+
onSSRMeasure?: (measure: {
|
|
18
|
+
endTimestamp: number;
|
|
19
|
+
segmentName: string;
|
|
20
|
+
startTimestamp: number;
|
|
21
|
+
}) => void;
|
|
17
22
|
plugins: EditorPlugin[];
|
|
18
23
|
portalProviderAPI: PortalProviderAPI;
|
|
19
24
|
schema: Schema;
|
|
20
25
|
}
|
|
21
|
-
export declare function EditorSSRRenderer({ plugins, schema, doc, portalProviderAPI, intl, onEditorStateChanged, ...divProps }: Props): React.JSX.Element;
|
|
26
|
+
export declare function EditorSSRRenderer({ plugins, schema, doc, portalProviderAPI, intl, onSSRMeasure, onEditorStateChanged, ...divProps }: Props): React.JSX.Element;
|
|
22
27
|
export {};
|
|
@@ -14,9 +14,14 @@ interface Props {
|
|
|
14
14
|
id: string;
|
|
15
15
|
intl: IntlShape;
|
|
16
16
|
onEditorStateChanged?: (state: EditorState) => void;
|
|
17
|
+
onSSRMeasure?: (measure: {
|
|
18
|
+
endTimestamp: number;
|
|
19
|
+
segmentName: string;
|
|
20
|
+
startTimestamp: number;
|
|
21
|
+
}) => void;
|
|
17
22
|
plugins: EditorPlugin[];
|
|
18
23
|
portalProviderAPI: PortalProviderAPI;
|
|
19
24
|
schema: Schema;
|
|
20
25
|
}
|
|
21
|
-
export declare function EditorSSRRenderer({ plugins, schema, doc, portalProviderAPI, intl, onEditorStateChanged, ...divProps }: Props): React.JSX.Element;
|
|
26
|
+
export declare function EditorSSRRenderer({ plugins, schema, doc, portalProviderAPI, intl, onSSRMeasure, onEditorStateChanged, ...divProps }: Props): React.JSX.Element;
|
|
22
27
|
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/editor-ssr-renderer",
|
|
3
|
-
"version": "2.1
|
|
3
|
+
"version": "2.2.1",
|
|
4
4
|
"description": "SSR Renderer based on Editor",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"atlassian": {
|
|
@@ -29,12 +29,18 @@
|
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@atlaskit/adf-schema": "^51.5.0",
|
|
31
31
|
"@atlaskit/editor-prosemirror": "^7.3.0",
|
|
32
|
-
"@atlaskit/
|
|
32
|
+
"@atlaskit/platform-feature-flags": "^1.1.0",
|
|
33
|
+
"@atlaskit/tmp-editor-statsig": "^27.1.0",
|
|
33
34
|
"@babel/runtime": "^7.0.0",
|
|
34
35
|
"react-intl-next": "npm:react-intl@^5.18.1"
|
|
35
36
|
},
|
|
36
37
|
"peerDependencies": {
|
|
37
38
|
"@atlaskit/editor-common": "^111.16.0",
|
|
38
39
|
"react": "^18.2.0"
|
|
40
|
+
},
|
|
41
|
+
"platform-feature-flags": {
|
|
42
|
+
"platform_editor_better_editor_ssr_spans": {
|
|
43
|
+
"type": "boolean"
|
|
44
|
+
}
|
|
39
45
|
}
|
|
40
46
|
}
|