Added URL transform for image cards in Lexical documents (#15890)

refs https://github.com/TryGhost/Team/issues/2225

- updated the `formatOnWrite` transform map for posts to include the new `nodes` and `transformMap` options used by `urlUtils` for transforming node payload data
- added `nodes` to the `lexicalLib` module that pulls in our default nodes to be passed in to the URL transform utilities
- added `urlTransformMap` to the `lexicalLib` module that maps transform type and data type to URL transform utility functions that accept a single URL argument
This commit is contained in:
Kevin Ansfield 2022-11-29 16:57:01 +00:00 committed by GitHub
parent 86abab4f1d
commit 457c672c6a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 168 additions and 11 deletions

View File

@ -1,4 +1,8 @@
const urlUtils = require('../../shared/url-utils');
let nodes;
let lexicalHtmlRenderer;
let urlTransformMap;
module.exports = {
get lexicalHtmlRenderer() {
@ -8,5 +12,38 @@ module.exports = {
}
return lexicalHtmlRenderer;
},
get nodes() {
if (!nodes) {
const {DEFAULT_NODES} = require('@tryghost/kg-default-nodes');
nodes = DEFAULT_NODES;
}
return nodes;
},
get urlTransformMap() {
if (!urlTransformMap) {
urlTransformMap = {
absoluteToRelative: {
url: urlUtils.absoluteToRelative.bind(urlUtils),
html: urlUtils.htmlAbsoluteToRelative.bind(urlUtils),
markdown: urlUtils.markdownAbsoluteToRelative.bind(urlUtils)
},
relativeToAbsolute: {
url: urlUtils.relativeToAbsolute.bind(urlUtils),
html: urlUtils.htmlRelativeToAbsolute.bind(urlUtils),
markdown: urlUtils.markdownRelativeToAbsolute.bind(urlUtils)
},
toTransformReady: {
url: urlUtils.toTransformReady.bind(urlUtils),
html: urlUtils.htmlToTransformReady.bind(urlUtils),
markdown: urlUtils.markdownToTransformReady.bind(urlUtils)
}
};
}
return urlTransformMap;
}
};

View File

@ -180,7 +180,13 @@ Post = ghostBookshelf.Model.extend({
cardTransformers: mobiledocLib.cards
}
},
lexical: 'lexicalToTransformReady',
lexical: {
method: 'lexicalToTransformReady',
options: {
nodes: lexicalLib.nodes,
transformMap: lexicalLib.urlTransformMap
}
},
html: 'htmlToTransformReady',
plaintext: 'plaintextToTransformReady',
custom_excerpt: 'htmlToTransformReady',

View File

@ -86,6 +86,7 @@
"@tryghost/kg-card-factory": "3.1.7",
"@tryghost/kg-default-atoms": "3.1.4",
"@tryghost/kg-default-cards": "5.18.5",
"@tryghost/kg-default-nodes": "0.0.1",
"@tryghost/kg-lexical-html-renderer": "0.0.11",
"@tryghost/kg-mobiledoc-html-renderer": "5.3.9",
"@tryghost/limit-service": "1.2.3",
@ -128,7 +129,7 @@
"@tryghost/tiers": "0.0.0",
"@tryghost/tpl": "0.1.19",
"@tryghost/update-check-service": "0.0.0",
"@tryghost/url-utils": "4.2.0",
"@tryghost/url-utils": "4.3.0",
"@tryghost/validator": "0.1.29",
"@tryghost/verification-trigger": "0.0.0",
"@tryghost/version": "0.1.16",

View File

@ -1190,15 +1190,15 @@ describe('Post Model', function () {
it('stores lexical as transform-ready and reads as absolute', async function () {
const post = {
title: 'Absolute->Transform-ready Lexical URL Transform Test',
lexical: `{"root":{"children":[{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"local link","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"url":"http://127.0.0.1:2369/local"}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1},{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"external link","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"url":"https://example.com/external"}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1}],"direction":"ltr","format":"","indent":0,"type":"root","version":1}}`
lexical: `{"root":{"children":[{"type":"image","src":"http://127.0.0.1:2369/content/images/card.jpg"},{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"local link","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"url":"http://127.0.0.1:2369/local"}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1},{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"external link","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"url":"https://example.com/external"}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1}],"direction":"ltr","format":"","indent":0,"type":"root","version":1}}`
};
const createdPost = await models.Post.add(post, context);
createdPost.get('lexical').should.equal(`{"root":{"children":[{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"local link","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"url":"http://127.0.0.1:2369/local"}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1},{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"external link","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"url":"https://example.com/external"}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1}],"direction":"ltr","format":"","indent":0,"type":"root","version":1}}`, 'Post.add result');
createdPost.get('lexical').should.equal(`{"root":{"children":[{"type":"image","src":"http://127.0.0.1:2369/content/images/card.jpg"},{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"local link","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"url":"http://127.0.0.1:2369/local"}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1},{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"external link","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"url":"https://example.com/external"}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1}],"direction":"ltr","format":"","indent":0,"type":"root","version":1}}`, 'Post.add result');
const knexResult = await db.knex('posts').where({id: createdPost.id});
const [knexPost] = knexResult;
knexPost.lexical.should.equal('{"root":{"children":[{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"local link","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"url":"__GHOST_URL__/local"}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1},{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"external link","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"url":"https://example.com/external"}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1}],"direction":"ltr","format":"","indent":0,"type":"root","version":1}}', 'knex result');
knexPost.lexical.should.equal('{"root":{"children":[{"type":"image","src":"__GHOST_URL__/content/images/card.jpg"},{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"local link","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"url":"__GHOST_URL__/local"}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1},{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"external link","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"url":"https://example.com/external"}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1}],"direction":"ltr","format":"","indent":0,"type":"root","version":1}}', 'knex result');
});
});

View File

@ -9,5 +9,37 @@ describe('lib/lexical', function () {
lexicalLib.lexicalHtmlRenderer.render(lexical)
.should.eql('<p>Lexical is <strong><em>rendering.</em></strong></p>');
});
it('renders all default cards', function () {
const lexicalState = JSON.stringify({
root: {
children: [
{
type: 'image',
cardWidth: 'wide',
src: '/content/images/2018/04/NatGeo06.jpg',
width: 4000,
height: 2000,
caption: 'Birdies'
}
],
direction: null,
format: '',
indent: 0,
type: 'root',
version: 1
}
});
lexicalLib.lexicalHtmlRenderer.render(lexicalState)
.should.eql(`
<figure class="kg-card kg-image-card kg-width-wide">
<img src="/content/images/2018/04/NatGeo06.jpg" alt="" />
<figcaption>
Birdies
</figcaption>
</figure>
`);
});
});
});

View File

@ -4429,6 +4429,14 @@
lodash "^4.17.21"
luxon "^2.1.1"
"@tryghost/kg-default-nodes@0.0.1":
version "0.0.1"
resolved "https://registry.yarnpkg.com/@tryghost/kg-default-nodes/-/kg-default-nodes-0.0.1.tgz#1d0d3b05aaaf99d016d1560c5d6b49538c604bf0"
integrity sha512-u+UJ/wxEMhiNlvRXZBp92nHj5PTgqrcja73HoA1S5WDOu97+8dZvAmGxhRHHHEY5VNGHEA0cXxi8S7owL9FUMg==
dependencies:
jsdom "^20.0.3"
lexical "^0.6.0"
"@tryghost/kg-lexical-html-renderer@0.0.11":
version "0.0.11"
resolved "https://registry.yarnpkg.com/@tryghost/kg-lexical-html-renderer/-/kg-lexical-html-renderer-0.0.11.tgz#0f4636660be9866cf5f030cc6a435ad958e8804b"
@ -4444,11 +4452,11 @@
lexical "^0.5.0"
"@tryghost/kg-markdown-html-renderer@^5.1.9":
version "5.1.9"
resolved "https://registry.yarnpkg.com/@tryghost/kg-markdown-html-renderer/-/kg-markdown-html-renderer-5.1.9.tgz#cb043705fb3cb140aef8577f87237cc82277ee15"
integrity sha512-HPziXC+qdqzpXi8K00GGZg0U3suJAm1Z4mmiWDFDc5BkDO0Dlrl+whOXbmmev+XIsZgr6sOje/uMO9fbAefZuA==
version "5.1.11"
resolved "https://registry.yarnpkg.com/@tryghost/kg-markdown-html-renderer/-/kg-markdown-html-renderer-5.1.11.tgz#c0aa728bc2ec518cefa69353211bdb866f70090d"
integrity sha512-mMQmQbO5/l5vVfiKiqlq0Y81wPX83AdSXPEq5GcAklq8E9q1ITpVtIZKC0KGrnQNiuledGOTP6wf2t5tyjgTLg==
dependencies:
"@tryghost/kg-utils" "^1.0.6"
"@tryghost/kg-utils" "^1.0.8"
markdown-it "^12.2.0"
markdown-it-footnote "^3.0.3"
markdown-it-image-lazy-loading "^1.1.0"
@ -4479,6 +4487,13 @@
dependencies:
semver "^7.3.5"
"@tryghost/kg-utils@^1.0.8":
version "1.0.8"
resolved "https://registry.yarnpkg.com/@tryghost/kg-utils/-/kg-utils-1.0.8.tgz#305897a7199cc32062e5db1ef54f8ad33ba45755"
integrity sha512-uO8WDFG8Q2VQ6LCAa2oAryavxFLcewMeXAoQ2PCj+oJZ/MiUjmUeTPEHnQV9TjlHyoriWyhz51PBFvlegjSxRw==
dependencies:
semver "^7.3.5"
"@tryghost/limit-service@1.2.3":
version "1.2.3"
resolved "https://registry.yarnpkg.com/@tryghost/limit-service/-/limit-service-1.2.3.tgz#161acc62f180a47b2a326562a657f0b6c6dd96c0"
@ -4665,7 +4680,19 @@
dependencies:
lodash.template "^4.5.0"
"@tryghost/url-utils@4.2.0", "@tryghost/url-utils@^4.0.0":
"@tryghost/url-utils@4.3.0":
version "4.3.0"
resolved "https://registry.yarnpkg.com/@tryghost/url-utils/-/url-utils-4.3.0.tgz#f276ea0963f34547dca8f8f8a6c7bd4e6d4bb6e3"
integrity sha512-DtsCVFytzkB6pE3azrO8PVP4wzwLR6Mjljy0ckwgvC7hj7bKM1QeGpCIasPNIKF+mTo/U9dXYadSVs1oGod1rA==
dependencies:
cheerio "^0.22.0"
moment "^2.27.0"
moment-timezone "^0.5.31"
remark "^11.0.2"
remark-footnotes "^1.0.0"
unist-util-visit "^2.0.0"
"@tryghost/url-utils@^4.0.0":
version "4.2.0"
resolved "https://registry.yarnpkg.com/@tryghost/url-utils/-/url-utils-4.2.0.tgz#342c73c840dda4f2ba2316fc581167404e219554"
integrity sha512-ipeGBj6CJau/17J+M1t2vUJvdptoTnCPVgph2mbKGclOEdAMv9h3/S15cNMnxIqoSNURAvwMKD+QxkOboM/7zg==
@ -5741,6 +5768,11 @@ acorn@^8.1.0, acorn@^8.2.4, acorn@^8.5.0, acorn@^8.7.1, acorn@^8.8.0:
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8"
integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==
acorn@^8.8.1:
version "8.8.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.1.tgz#0a3f9cbecc4ec3bea6f0a80b66ae8dd2da250b73"
integrity sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==
address@^1.0.1, address@^1.1.2:
version "1.2.1"
resolved "https://registry.yarnpkg.com/address/-/address-1.2.1.tgz#25bb61095b7522d65b357baa11bc05492d4c8acd"
@ -10301,7 +10333,7 @@ decamelize@^4.0.0:
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837"
integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==
decimal.js@^10.2.1, decimal.js@^10.4.1:
decimal.js@^10.2.1, decimal.js@^10.4.1, decimal.js@^10.4.2:
version "10.4.2"
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.2.tgz#0341651d1d997d86065a2ce3a441fbd0d8e8b98e"
integrity sha512-ic1yEvwT6GuvaYwBLLY6/aFFgjZdySKTE8en/fkU3QICTmRtgtSlFn0u0BXN06InZwtfCelR7j8LRiDI/02iGA==
@ -17231,6 +17263,38 @@ jsdom@^20.0.0, jsdom@~20.0.0:
ws "^8.9.0"
xml-name-validator "^4.0.0"
jsdom@^20.0.3:
version "20.0.3"
resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-20.0.3.tgz#886a41ba1d4726f67a8858028c99489fed6ad4db"
integrity sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==
dependencies:
abab "^2.0.6"
acorn "^8.8.1"
acorn-globals "^7.0.0"
cssom "^0.5.0"
cssstyle "^2.3.0"
data-urls "^3.0.2"
decimal.js "^10.4.2"
domexception "^4.0.0"
escodegen "^2.0.0"
form-data "^4.0.0"
html-encoding-sniffer "^3.0.0"
http-proxy-agent "^5.0.0"
https-proxy-agent "^5.0.1"
is-potential-custom-element-name "^1.0.1"
nwsapi "^2.2.2"
parse5 "^7.1.1"
saxes "^6.0.0"
symbol-tree "^3.2.4"
tough-cookie "^4.1.2"
w3c-xmlserializer "^4.0.0"
webidl-conversions "^7.0.0"
whatwg-encoding "^2.0.0"
whatwg-mimetype "^3.0.0"
whatwg-url "^11.0.0"
ws "^8.11.0"
xml-name-validator "^4.0.0"
jsesc@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b"
@ -17654,6 +17718,11 @@ lexical@^0.5.0:
resolved "https://registry.yarnpkg.com/lexical/-/lexical-0.5.0.tgz#03a8abc816f6d922384ecd01bd14ed36a1ea4d63"
integrity sha512-J0cFuNPQQY5P9W5XW2/xgqp5W0eEQ2rxShLf8eevLvxFWsPSY3zjg3RCzTHyheSiGBBjKDIaM4gxtO8eNeJr2A==
lexical@^0.6.0:
version "0.6.4"
resolved "https://registry.yarnpkg.com/lexical/-/lexical-0.6.4.tgz#00f5070a967fd1410aaa7d4c928f6e61af73c604"
integrity sha512-QBJEowv2FRkanu9V4HxCiR/V0rECt98zUeHsaYcgUCphrxbZseQgk8AO5UbHJMgAD4iQ0CCKZWbiqbv7Z8tYnw==
liftoff@3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/liftoff/-/liftoff-3.1.0.tgz#c9ba6081f908670607ee79062d700df062c52ed3"
@ -26121,6 +26190,13 @@ w3c-xmlserializer@^3.0.0:
dependencies:
xml-name-validator "^4.0.0"
w3c-xmlserializer@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz#aebdc84920d806222936e3cdce408e32488a3073"
integrity sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==
dependencies:
xml-name-validator "^4.0.0"
walk-sync@3.0.0, walk-sync@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/walk-sync/-/walk-sync-3.0.0.tgz#67f882925021e20569a1edd560b8da31da8d171c"
@ -26823,6 +26899,11 @@ ws@^7.4.6:
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591"
integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==
ws@^8.11.0:
version "8.11.0"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143"
integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==
ws@^8.4.2, ws@^8.9.0:
version "8.9.0"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.9.0.tgz#2a994bb67144be1b53fe2d23c53c028adeb7f45e"