diff --git a/ghost/core/core/server/lib/lexical.js b/ghost/core/core/server/lib/lexical.js
new file mode 100644
index 0000000000..6171b52c30
--- /dev/null
+++ b/ghost/core/core/server/lib/lexical.js
@@ -0,0 +1,12 @@
+let lexicalHtmlRenderer;
+
+module.exports = {
+ get lexicalHtmlRenderer() {
+ if (!lexicalHtmlRenderer) {
+ const LexicalHtmlRenderer = require('@tryghost/kg-lexical-html-renderer');
+ lexicalHtmlRenderer = new LexicalHtmlRenderer();
+ }
+
+ return lexicalHtmlRenderer;
+ }
+};
diff --git a/ghost/core/core/server/models/post.js b/ghost/core/core/server/models/post.js
index 628aefd260..a7be83855b 100644
--- a/ghost/core/core/server/models/post.js
+++ b/ghost/core/core/server/models/post.js
@@ -13,6 +13,7 @@ const config = require('../../shared/config');
const settingsCache = require('../../shared/settings-cache');
const limitService = require('../services/limits');
const mobiledocLib = require('../lib/mobiledoc');
+const lexicalLib = require('../lib/lexical');
const relations = require('./relations');
const urlUtils = require('../../shared/url-utils');
const {Tag} = require('./tag');
@@ -610,9 +611,12 @@ Post = ghostBookshelf.Model.extend({
// CASE: ?force_rerender=true passed via Admin API
// CASE: html is null, but mobiledoc exists (only important for migrations & importing)
if (
- (this.hasChanged('mobiledoc') && !this.get('lexical'))
- || options.force_rerender
- || (!this.get('html') && (options.migrating || options.importing))
+ !this.get('lexical') &&
+ (
+ this.hasChanged('mobiledoc')
+ || options.force_rerender
+ || (!this.get('html') && (options.migrating || options.importing))
+ )
) {
try {
this.set('html', mobiledocLib.mobiledocHtmlRenderer.render(JSON.parse(this.get('mobiledoc'))));
@@ -624,6 +628,28 @@ Post = ghostBookshelf.Model.extend({
}
}
+ // CASE: lexical has changed, generate html
+ // CASE: ?force_rerender=true passed via Admin API
+ // CASE: html is null, but lexical exists (only important for migrations & importing)
+ if (
+ !this.get('mobiledoc') &&
+ (
+ this.hasChanged('lexical')
+ || options.force_rerender
+ || (!this.get('html') && (options.migrating || options.importing))
+ )
+ ) {
+ try {
+ this.set('html', lexicalLib.lexicalHtmlRenderer.render(this.get('lexical')));
+ } catch (err) {
+ throw new errors.ValidationError({
+ message: 'Invalid lexical structure.',
+ help: 'https://ghost.org/docs/publishing/',
+ property: 'lexical'
+ });
+ }
+ }
+
if (this.hasChanged('html') || !this.get('plaintext')) {
let plaintext;
diff --git a/ghost/core/package.json b/ghost/core/package.json
index f034bbcbee..5210c8e3d1 100644
--- a/ghost/core/package.json
+++ b/ghost/core/package.json
@@ -79,6 +79,7 @@
"@tryghost/kg-card-factory": "3.1.5",
"@tryghost/kg-default-atoms": "3.1.4",
"@tryghost/kg-default-cards": "5.18.0",
+ "@tryghost/kg-lexical-html-renderer": "0.0.2",
"@tryghost/kg-mobiledoc-html-renderer": "5.3.7",
"@tryghost/limit-service": "1.2.3",
"@tryghost/link-redirects": "0.0.0",
diff --git a/ghost/core/test/e2e-api/admin/__snapshots__/posts.test.js.snap b/ghost/core/test/e2e-api/admin/__snapshots__/posts.test.js.snap
index 874a79e30d..cf3f5499fd 100644
--- a/ghost/core/test/e2e-api/admin/__snapshots__/posts.test.js.snap
+++ b/ghost/core/test/e2e-api/admin/__snapshots__/posts.test.js.snap
@@ -296,14 +296,15 @@ Object {
"email_only": false,
"email_segment": "all",
"email_subject": null,
- "excerpt": null,
+ "excerpt": "Testing post creation with lexical",
"feature_image": null,
"feature_image_alt": null,
"feature_image_caption": null,
"featured": false,
"frontmatter": null,
+ "html": "
Testing post creation with lexical
",
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
- "lexical": "{\\"editorState\\":{\\"root\\":{\\"children\\":[{\\"children\\":[{\\"detail\\":0,\\"format\\":0,\\"mode\\":\\"normal\\",\\"style\\":\\"\\",\\"text\\":\\"Testing post creation with lexical\\",\\"type\\":\\"text\\",\\"version\\":1}],\\"direction\\":\\"ltr\\",\\"format\\":\\"\\",\\"indent\\":0,\\"type\\":\\"paragraph\\",\\"version\\":1}],\\"direction\\":\\"ltr\\",\\"format\\":\\"\\",\\"indent\\":0,\\"type\\":\\"root\\",\\"version\\":1}},\\"lastSaved\\":1663081361393,\\"source\\":\\"Playground\\",\\"version\\":\\"0.4.1\\"}",
+ "lexical": "{\\"root\\":{\\"children\\":[{\\"children\\":[{\\"detail\\":0,\\"format\\":0,\\"mode\\":\\"normal\\",\\"style\\":\\"\\",\\"text\\":\\"Testing post creation with lexical\\",\\"type\\":\\"text\\",\\"version\\":1}],\\"direction\\":\\"ltr\\",\\"format\\":\\"\\",\\"indent\\":0,\\"type\\":\\"paragraph\\",\\"version\\":1}],\\"direction\\":\\"ltr\\",\\"format\\":\\"\\",\\"indent\\":0,\\"type\\":\\"root\\",\\"version\\":1}}",
"meta_description": null,
"meta_title": null,
"mobiledoc": null,
@@ -314,6 +315,7 @@ Object {
"primary_author": Any