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, "primary_tag": Any, "published_at": null, + "reading_time": 0, "slug": "lexical-test", "status": "draft", "tags": Any, @@ -335,7 +337,7 @@ exports[`Posts API Create Can create a post with lexical 2: [headers] 1`] = ` Object { "access-control-allow-origin": "http://127.0.0.1:2369", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "3737", + "content-length": "3743", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/posts\\\\/\\[a-f0-9\\]\\{24\\}\\\\//, @@ -370,6 +372,7 @@ Object { "feature_image_caption": null, "featured": false, "frontmatter": null, + "html": "

Testing post creation with mobiledoc

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, "lexical": null, "meta_description": null, @@ -382,6 +385,7 @@ Object { "primary_author": Any, "primary_tag": Any, "published_at": null, + "reading_time": 0, "slug": "mobiledoc-test", "status": "draft", "tags": Any, @@ -403,7 +407,7 @@ exports[`Posts API Create Can create a post with mobiledoc 2: [headers] 1`] = ` Object { "access-control-allow-origin": "http://127.0.0.1:2369", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "3489", + "content-length": "3559", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/posts\\\\/\\[a-f0-9\\]\\{24\\}\\\\//, @@ -442,6 +446,36 @@ Object { } `; +exports[`Posts API Create Errors with an invalid lexical state object 1: [body] 1`] = ` +Object { + "errors": Array [ + Object { + "code": null, + "context": "Invalid lexical structure.", + "details": null, + "ghostErrorCode": null, + "help": "https://ghost.org/docs/publishing/", + "id": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, + "message": "Validation error, cannot save post.", + "property": "lexical", + "type": "ValidationError", + }, + ], +} +`; + +exports[`Posts API Create Errors with an invalid lexical state object 2: [headers] 1`] = ` +Object { + "access-control-allow-origin": "http://127.0.0.1:2369", + "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", + "content-length": "284", + "content-type": "application/json; charset=utf-8", + "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, + "vary": "Origin, Accept-Encoding", + "x-powered-by": "Express", +} +`; + exports[`Posts API Delete Can destroy a post 1: [headers] 1`] = ` Object { "access-control-allow-origin": "http://127.0.0.1:2369", diff --git a/ghost/core/test/e2e-api/admin/pages.test.js b/ghost/core/test/e2e-api/admin/pages.test.js index fda89c439a..4b17283ea4 100644 --- a/ghost/core/test/e2e-api/admin/pages.test.js +++ b/ghost/core/test/e2e-api/admin/pages.test.js @@ -133,42 +133,37 @@ describe('Pages API', function () { const page = { title: 'Lexical test', lexical: JSON.stringify({ - 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' + root: { + children: [ + { + children: [ + { + detail: 0, + format: 0, + mode: 'normal', + style: '', + text: 'Testing page 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 + } }) }; - const res = await request.post(localUtils.API.getApiQuery('pages/?formats=mobiledoc,lexical')) + const res = await request.post(localUtils.API.getApiQuery('pages/?formats=mobiledoc,lexical,html')) .set('Origin', config.get('url')) .send({pages: [page]}) .expect('Content-Type', /json/) @@ -178,11 +173,12 @@ describe('Pages API', function () { res.body.pages.length.should.eql(1); const [returnedPage] = res.body.pages; - const additionalProperties = ['lexical']; + const additionalProperties = ['lexical', 'html', 'reading_time']; localUtils.API.checkResponse(returnedPage, 'page', additionalProperties); should.equal(returnedPage.mobiledoc, null); should.equal(returnedPage.lexical, page.lexical); + should.equal(returnedPage.html, '

Testing page creation with lexical

'); }); it('Can\'t add a page with both mobiledoc and lexical', async function () { diff --git a/ghost/core/test/e2e-api/admin/posts.test.js b/ghost/core/test/e2e-api/admin/posts.test.js index c07100e891..21468d194a 100644 --- a/ghost/core/test/e2e-api/admin/posts.test.js +++ b/ghost/core/test/e2e-api/admin/posts.test.js @@ -72,7 +72,7 @@ describe('Posts API', function () { }; await agent - .post('/posts/?formats=mobiledoc,lexical') + .post('/posts/?formats=mobiledoc,lexical,html') .body({posts: [post]}) .expectStatus(201) .matchBodySnapshot({ @@ -89,43 +89,38 @@ describe('Posts API', function () { title: 'Lexical test', mobiledoc: null, lexical: JSON.stringify({ - 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' + 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 + } }) }; await agent - .post('/posts/?formats=mobiledoc,lexical') + .post('/posts/?formats=mobiledoc,lexical,html') .body({posts: [post]}) .expectStatus(201) .matchBodySnapshot({ @@ -153,38 +148,33 @@ describe('Posts API', function () { ] }), lexical: JSON.stringify({ - 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' + 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 + } }) }; @@ -201,6 +191,28 @@ describe('Posts API', function () { etag: anyEtag }); }); + + it('Errors with an invalid lexical state object', async function () { + const post = { + title: 'Invalid lexical state', + lexical: JSON.stringify({ + notLexical: true + }) + }; + + await agent + .post('/posts/?formats=mobiledoc,lexical,html') + .body({posts: [post]}) + .expectStatus(422) + .matchBodySnapshot({ + errors: [{ + id: anyErrorId + }] + }) + .matchHeaderSnapshot({ + etag: anyEtag + }); + }); }); describe('Delete', function () { diff --git a/ghost/core/test/unit/server/lib/lexical.test.js b/ghost/core/test/unit/server/lib/lexical.test.js new file mode 100644 index 0000000000..16d7d74ba9 --- /dev/null +++ b/ghost/core/test/unit/server/lib/lexical.test.js @@ -0,0 +1,13 @@ +const should = require('should'); +const lexicalLib = require('../../../../core/server/lib/lexical'); + +describe('lib/lexical', function () { + describe('lexicalHtmlRenderer', function () { + it('renders', function () { + const lexical = `{"root":{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Lexical is ","type":"text","version":1},{"detail":0,"format":3,"mode":"normal","style":"","text":"rendering.","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1}],"direction":"ltr","format":"","indent":0,"type":"root","version":1}}`; + + lexicalLib.lexicalHtmlRenderer.render(lexical) + .should.eql('

Lexical is rendering.

'); + }); + }); +}); diff --git a/yarn.lock b/yarn.lock index 56d7021ef8..5b8eac333b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2777,6 +2777,163 @@ "@keyvhq/core" "^1.6.14" mimic-fn "~3.0.0" +"@lexical/clipboard@0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@lexical/clipboard/-/clipboard-0.4.1.tgz#67811f940756ba17b52538718db9650b44d433a9" + integrity sha512-/BHeh+LaYhNbBbzNcVPcYgSZtNp6HT5z/iHFKJMfhikE+4KKFf3DbLDO5b92AUDyT3s8QQC04Na5ZeRUbK/Jzw== + dependencies: + "@lexical/html" "0.4.1" + "@lexical/list" "0.4.1" + "@lexical/selection" "0.4.1" + "@lexical/utils" "0.4.1" + +"@lexical/code@0.4.1", "@lexical/code@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@lexical/code/-/code-0.4.1.tgz#4ba0ec1a471ae4b7cf24ee1a8c87aeabcea01102" + integrity sha512-Bf6MiPrP7rrbYIXOOFKN/+HuZbdSWt0Dy/1gLJ3cLl4CTNiOUPJo2+iKvr3T0LCiSkQLHW+u4ygDs9G0eeDjdg== + dependencies: + "@lexical/utils" "0.4.1" + prismjs "^1.27.0" + +"@lexical/dragon@0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@lexical/dragon/-/dragon-0.4.1.tgz#47de7e45aa059f352dc2b8c1f56b8c385c7bd2ae" + integrity sha512-RDOrxiSQ8c7PQoEiopjxX0z0DudFle8Dn/0Ptb1f8xm8jZFG573NTw9oJPRDg3bhyKdt8Fh4EoPYf0AMdMDJKg== + +"@lexical/hashtag@0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@lexical/hashtag/-/hashtag-0.4.1.tgz#6538d4a1895d10dc252a87c67198a68e4640062e" + integrity sha512-qQQppeW91Kual4+m+KAAX4JgXwxIH9/LHuSpbycaQZiSd29R2PrCYyVx+gb+E3L1dum0r2Uddz2Wa/WLycLeDQ== + dependencies: + "@lexical/utils" "0.4.1" + +"@lexical/headless@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@lexical/headless/-/headless-0.4.1.tgz#b8d1dc5476c7fcdae672140fe3bdc273c51e3112" + integrity sha512-1US/sub+8YQizmx1JE5U0TbrjmkvIjkNQK3rRzCdkRSuKjfxkvwfOg0R38yGXozMLVJTg6zvaDiNtdt3hXXHTA== + +"@lexical/history@0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@lexical/history/-/history-0.4.1.tgz#7b9991b16841ab082658a1abf898b5ac46b22148" + integrity sha512-n7KUYqCN0jj+NmMrT0bGjbSwn5x7EyDYhw9eghm69/fgHI/qHSJDQUQXa2ep4/0y4kPzYafVZSMrBDYXBxWqmw== + dependencies: + "@lexical/utils" "0.4.1" + +"@lexical/html@0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@lexical/html/-/html-0.4.1.tgz#a99b3b7c2b0ee4a555fe8478c6eef43754de35bb" + integrity sha512-nErgYUtghdrVTPZLB7Ad7U1m3SBxEZvPeW4FH4COFLCeTIVMEG4dg0PhURCk28xNAkEHszd4kQ3XFDRB5muOiQ== + dependencies: + "@lexical/selection" "0.4.1" + +"@lexical/link@0.4.1", "@lexical/link@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@lexical/link/-/link-0.4.1.tgz#af3744942dd79bc6af34d9502fecd5cd4c635b3b" + integrity sha512-566lQymmuBe3Y7UDyaaTs+VDlElbu1WhnjT9lVDk0BXag7MA8tv/f60XptWnTK1pv/Dobm/CyLmyLae55OuflQ== + dependencies: + "@lexical/utils" "0.4.1" + +"@lexical/list@0.4.1", "@lexical/list@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@lexical/list/-/list-0.4.1.tgz#5d764d7ed82cd3e710bc42619e1134e1968ec617" + integrity sha512-dP6i18qm1UhUoG6FvqLMn7hoj4htE0jYcmMPYqFyH+f1ir9Ybvr87pV3aFhqH6hdHjQ5tMyM0kURSL1t89GesQ== + dependencies: + "@lexical/utils" "0.4.1" + +"@lexical/mark@0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@lexical/mark/-/mark-0.4.1.tgz#fa20af940d16aabb076c66714e88fef5ca082c91" + integrity sha512-2aW26JvDYPZof8HyZ6WQjIbLSlYYncteJlTYyU5QnFubPjvUwxBps9X2lLf2lJm9CCVGT4T7gdBMkYBeckXSxQ== + dependencies: + "@lexical/utils" "0.4.1" + +"@lexical/markdown@0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@lexical/markdown/-/markdown-0.4.1.tgz#e0f28a585dec51ff51745e46b9d9db5ff49afc4c" + integrity sha512-MEGfQ31WDeb6xGn4zNqHVzG4YH0cpE4zGPHnzE6uNxNolb2md9inaKxziW5/ydpf+E/fur5XfLaDggiEi/ww4A== + dependencies: + "@lexical/code" "0.4.1" + "@lexical/link" "0.4.1" + "@lexical/list" "0.4.1" + "@lexical/rich-text" "0.4.1" + "@lexical/text" "0.4.1" + "@lexical/utils" "0.4.1" + +"@lexical/offset@0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@lexical/offset/-/offset-0.4.1.tgz#6a071a4cf6f6f6dcbafdb8b5b8ef133a82773304" + integrity sha512-MCFoWKsw12fcTuqxUBmC4PEip0ckv6KvrJGx4o6yI1gtkO47r3qb0YGaOShFivE/IQcbJOBXidXBjBk6jZRHEw== + +"@lexical/overflow@0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@lexical/overflow/-/overflow-0.4.1.tgz#38097eb02c4098a8e8b8d7df9bb50616432eb788" + integrity sha512-lOACcyDSIP/HF8ZEMJuz5bTG83WQJGHr+qYIi80giVw/tK8XkFMpCED5YhsOsvWsBqFTP9fxN8rpaF/N+rW4MQ== + +"@lexical/plain-text@0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@lexical/plain-text/-/plain-text-0.4.1.tgz#3b0598ba4f9d9ee5d5852cf0848f4f3f9cd87dbb" + integrity sha512-cNFLXhOfR0coUFGA6aPGcHr6a+Y9ZrkETMM78+XV9J8iVsKij4/katFhsqACQDna4vSfXuqjTitCRtiFaDevDg== + +"@lexical/react@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@lexical/react/-/react-0.4.1.tgz#d7639c08b3b2c0e003cc1570aceadaa6ca78084d" + integrity sha512-kA9ivSjziYNZILQy8k+l/PmaoPJBkgOQFZ/NTdS5tyM8HVwj5GPwsRKkR+isy2X/C2om7vQ0xYKe+LLiEGzQfg== + dependencies: + "@lexical/clipboard" "0.4.1" + "@lexical/code" "0.4.1" + "@lexical/dragon" "0.4.1" + "@lexical/hashtag" "0.4.1" + "@lexical/history" "0.4.1" + "@lexical/link" "0.4.1" + "@lexical/list" "0.4.1" + "@lexical/mark" "0.4.1" + "@lexical/markdown" "0.4.1" + "@lexical/overflow" "0.4.1" + "@lexical/plain-text" "0.4.1" + "@lexical/rich-text" "0.4.1" + "@lexical/selection" "0.4.1" + "@lexical/table" "0.4.1" + "@lexical/text" "0.4.1" + "@lexical/utils" "0.4.1" + "@lexical/yjs" "0.4.1" + +"@lexical/rich-text@0.4.1", "@lexical/rich-text@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@lexical/rich-text/-/rich-text-0.4.1.tgz#3a980917f994f31befa40bb50a4f6dd679fe0a36" + integrity sha512-EI4ul3y1hqMp0VS/4D8aOyR41ysz1KaYgkm6PyrRXEMyK8uKmVubJP83RkOU2fWkTVtdrMjM6aeT1qX849LetA== + +"@lexical/selection@0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@lexical/selection/-/selection-0.4.1.tgz#88c709a2b9813077dd91b89990646f1d9b80e16c" + integrity sha512-lrPaBtdWYp5FGpLA/mw8vVxQx8XM/GGVXFGam68B7mgMbY9xxKy3/MtvS94J+oRPtzNHEW3SyhkDEm9356r73g== + +"@lexical/table@0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@lexical/table/-/table-0.4.1.tgz#08545cdfde52a90a5dfd02a16cece699ccdaf6e2" + integrity sha512-1BRLB+cYpqXSk0IHk3uHI1Fa6H4rnQ5pySDm+7HSyShjAzQSMBt0BWoX9B0TW/vXysl3p5+vcnU8mGjGyCA0Lg== + dependencies: + "@lexical/utils" "0.4.1" + +"@lexical/text@0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@lexical/text/-/text-0.4.1.tgz#832ce70f26efc41fdc0041c0b732bb1c99c7d287" + integrity sha512-sfGESzElf8fYSi21+y/OCS00xbBsWUPoRLJ6D2EzvwRByqsPFYR5o9ouYMejQE/UFmkhgzX0Nv+VenFujkGk6Q== + +"@lexical/utils@0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@lexical/utils/-/utils-0.4.1.tgz#caf4cdbb5df0a785685cf7939c774bcb155f38c4" + integrity sha512-OUYS7qpu1oHv9vhUGhL60ow5gbyqLG449XGFZrzaKUGnz1iWU/rXP8nEljoqdtai3rg4t0Ahxcbe9cVIubK3hQ== + dependencies: + "@lexical/list" "0.4.1" + "@lexical/table" "0.4.1" + +"@lexical/yjs@0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@lexical/yjs/-/yjs-0.4.1.tgz#6db5fbf629518dc0ac6cecb08ccc83f6fc37325e" + integrity sha512-0VSq+rknDaixR7G3HMLYjAQk20EAsg7SaN0Qx/71+TxKKKniUgQSyAbmMw1ZLvW6Xlkqavj827dZCb/E3AbtEw== + dependencies: + "@lexical/offset" "0.4.1" + "@lint-todo/utils@^13.0.3": version "13.0.3" resolved "https://registry.yarnpkg.com/@lint-todo/utils/-/utils-13.0.3.tgz#ca222f38738b43eb43384d56e7292ba9cab3e891" @@ -3462,6 +3619,20 @@ lodash "^4.17.21" luxon "^2.1.1" +"@tryghost/kg-lexical-html-renderer@0.0.2": + version "0.0.2" + resolved "https://registry.yarnpkg.com/@tryghost/kg-lexical-html-renderer/-/kg-lexical-html-renderer-0.0.2.tgz#4d36225afe75d570f87a0edc2affbc8959a992cc" + integrity sha512-YB6yMP9zZhIeKhKRZ5Rpc1K73w2ZxY8GzxfomWsM087ahdQijOUDGnGzzoKa9vmE452i7f2auNNOK8ryS6nxuw== + dependencies: + "@lexical/code" "^0.4.1" + "@lexical/headless" "^0.4.1" + "@lexical/link" "^0.4.1" + "@lexical/list" "^0.4.1" + "@lexical/react" "^0.4.1" + "@lexical/rich-text" "^0.4.1" + jsdom "^20.0.0" + lexical "^0.4.1" + "@tryghost/kg-markdown-html-renderer@^5.1.7": version "5.1.7" resolved "https://registry.yarnpkg.com/@tryghost/kg-markdown-html-renderer/-/kg-markdown-html-renderer-5.1.7.tgz#267654bf72767c0118d26c20f3567dbc7cd4a164" @@ -14693,7 +14864,7 @@ jsdom@^18.0.0: ws "^8.2.3" xml-name-validator "^4.0.0" -jsdom@~20.0.0: +jsdom@^20.0.0, jsdom@~20.0.0: version "20.0.0" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-20.0.0.tgz#882825ac9cc5e5bbee704ba16143e1fa78361ebf" integrity sha512-x4a6CKCgx00uCmP+QakBDFXwjAJ69IkkIWHmtmjd3wvXPcdOS44hfX2vqkOQrVrq8l9DhNNADZRXaCEWvgXtVA== @@ -15148,6 +15319,11 @@ levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" +lexical@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/lexical/-/lexical-0.4.1.tgz#6780f949f8478590b33cc6e8374368bbffbd6180" + integrity sha512-EXcLh/6LbEuwqXlnPkk8MXZXO16yBzznFqklKva91DF+KU8utX2PLoeRSOtWC8P2YbRdiGQewwKjUVeXqxxOaQ== + liftoff@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/liftoff/-/liftoff-3.1.0.tgz#c9ba6081f908670607ee79062d700df062c52ed3" @@ -19062,6 +19238,11 @@ printf@^0.6.1: resolved "https://registry.yarnpkg.com/printf/-/printf-0.6.1.tgz#b9afa3d3b55b7f2e8b1715272479fc756ed88650" integrity sha512-is0ctgGdPJ5951KulgfzvHGwJtZ5ck8l042vRkV6jrkpBzTmb/lueTqguWHy2JfVA+RY6gFVlaZgUS0j7S/dsw== +prismjs@^1.27.0: + version "1.29.0" + resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.29.0.tgz#f113555a8fa9b57c35e637bba27509dcf802dd12" + integrity sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q== + private@^0.1.6, private@^0.1.8: version "0.1.8" resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"