2
1
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2023-12-13 21:00:40 +01:00

Updated mobiledoc renderer to insert begin/end HTML comments around cards

no issue

We're creating tooling to convert HTML to Ghost flavoured mobiledoc, however we have cards that allow arbitrary content without a wrapper element which means that we're unable to do a 1:1 mapping of mobiledoc->html->mobiledoc. To work around this problem we now output HTML comments before/after the output of each card so that our converter can extract card content correctly when parsing HTML.

- added `createCard` method which wraps a card's `render()` method to add begin/end comments and updated all cards to use it
- only takes affect for newly added or re-saved posts/pages
This commit is contained in:
Kevin Ansfield 2019-02-25 18:04:49 +07:00
parent 23d54803e6
commit 4f9e687f62
17 changed files with 91 additions and 42 deletions

View file

@ -1,5 +1,8 @@
// this card is just an alias of the `markdown` card which is necessary because // this card is just an alias of the `markdown` card which is necessary because
// our markdown-only editor was using the `card-markdown` card name // our markdown-only editor was using the `card-markdown` card name
const markdownCard = require('./markdown'); const markdownCard = require('./markdown');
const createCard = require('../create-card');
module.exports = Object.assign({}, markdownCard, {name: 'card-markdown'}); module.exports = createCard(
Object.assign({}, markdownCard, {name: 'card-markdown'})
);

View file

@ -1,4 +1,6 @@
module.exports = { const createCard = require('../create-card');
module.exports = createCard({
name: 'code', name: 'code',
type: 'dom', type: 'dom',
render(opts) { render(opts) {
@ -21,4 +23,4 @@ module.exports = {
return pre; return pre;
} }
}; });

View file

@ -1,4 +1,6 @@
module.exports = { const createCard = require('../create-card');
module.exports = createCard({
name: 'embed', name: 'embed',
type: 'dom', type: 'dom',
render(opts) { render(opts) {
@ -23,4 +25,4 @@ module.exports = {
return figure; return figure;
} }
}; });

View file

@ -1,5 +1,3 @@
const MAX_IMG_PER_ROW = 3;
/** /**
* <figure class="kg-gallery-card kg-width-wide"> * <figure class="kg-gallery-card kg-width-wide">
* <div class="kg-gallery-container> * <div class="kg-gallery-container>
@ -17,7 +15,11 @@ const MAX_IMG_PER_ROW = 3;
* </figure> * </figure>
*/ */
module.exports = { const createCard = require('../create-card');
const MAX_IMG_PER_ROW = 3;
module.exports = createCard({
name: 'gallery', name: 'gallery',
type: 'dom', type: 'dom',
render(opts) { render(opts) {
@ -107,4 +109,4 @@ module.exports = {
return figure; return figure;
} }
}; });

View file

@ -1,7 +1,9 @@
module.exports = { const createCard = require('../create-card');
module.exports = createCard({
name: 'hr', name: 'hr',
type: 'dom', type: 'dom',
render(opts) { render(opts) {
return opts.env.dom.createElement('hr'); return opts.env.dom.createElement('hr');
} }
}; });

View file

@ -1,4 +1,6 @@
module.exports = { const createCard = require('../create-card');
module.exports = createCard({
name: 'html', name: 'html',
type: 'dom', type: 'dom',
render(opts) { render(opts) {
@ -10,4 +12,4 @@ module.exports = {
// avoids parsing/rendering of potentially broken or unsupported HTML // avoids parsing/rendering of potentially broken or unsupported HTML
return opts.env.dom.createRawHTMLSection(opts.payload.html); return opts.env.dom.createRawHTMLSection(opts.payload.html);
} }
}; });

View file

@ -1,4 +1,6 @@
module.exports = { const createCard = require('../create-card');
module.exports = createCard({
name: 'image', name: 'image',
type: 'dom', type: 'dom',
render(opts) { render(opts) {
@ -38,4 +40,4 @@ module.exports = {
return figure; return figure;
} }
}; });

View file

@ -1,4 +1,6 @@
module.exports = { const createCard = require('../create-card');
module.exports = createCard({
name: 'markdown', name: 'markdown',
type: 'dom', type: 'dom',
render: function (opts) { render: function (opts) {
@ -8,6 +10,10 @@ module.exports = {
// convert markdown to HTML ready for insertion into dom // convert markdown to HTML ready for insertion into dom
let html = converters.markdownConverter.render(payload.markdown || ''); let html = converters.markdownConverter.render(payload.markdown || '');
if (!html) {
return '';
}
/** /**
* @deprecated Ghost 1.0's markdown-only renderer wrapped cards. Remove in Ghost 3.0 * @deprecated Ghost 1.0's markdown-only renderer wrapped cards. Remove in Ghost 3.0
*/ */
@ -19,4 +25,4 @@ module.exports = {
// avoids parsing/rendering of potentially broken or unsupported HTML // avoids parsing/rendering of potentially broken or unsupported HTML
return opts.env.dom.createRawHTMLSection(html); return opts.env.dom.createRawHTMLSection(html);
} }
}; });

View file

@ -0,0 +1,28 @@
module.exports = function createCard(card) {
const {name, type} = card;
return {
name,
type,
render({env, payload, options}) {
const {dom} = env;
const cleanName = name.replace(/^card-/, '');
const cardOutput = card.render({env, payload, options});
if (!cardOutput) {
return cardOutput;
}
const beginComment = dom.createComment(`kg-card-begin: ${cleanName}`);
const endComment = dom.createComment(`kg-card-end: ${cleanName}`);
const fragment = dom.createDocumentFragment();
fragment.appendChild(beginComment);
fragment.appendChild(cardOutput);
fragment.appendChild(endComment);
return fragment;
}
};
};

View file

@ -14,7 +14,7 @@ describe('Code card', function () {
} }
}; };
serializer.serialize(card.render(opts)).should.match('<pre><code>&lt;p&gt;Test&lt;/p&gt;</code></pre>'); serializer.serialize(card.render(opts)).should.match('<!--kg-card-begin: code--><pre><code>&lt;p&gt;Test&lt;/p&gt;</code></pre><!--kg-card-end: code-->');
}); });
it('Renders language class if provided', function () { it('Renders language class if provided', function () {
@ -28,7 +28,7 @@ describe('Code card', function () {
} }
}; };
serializer.serialize(card.render(opts)).should.match('<pre><code class="language-html">&lt;p&gt;Test&lt;/p&gt;</code></pre>'); serializer.serialize(card.render(opts)).should.match('<!--kg-card-begin: code--><pre><code class="language-html">&lt;p&gt;Test&lt;/p&gt;</code></pre><!--kg-card-end: code-->');
}); });
it('Renders nothing when payload is undefined', function () { it('Renders nothing when payload is undefined', function () {

View file

@ -14,7 +14,7 @@ describe('Embed card', function () {
} }
}; };
serializer.serialize(card.render(opts)).should.match('<figure class="kg-card kg-embed-card"><h1>HEADING</h1><p>PARAGRAPH</p></figure>'); serializer.serialize(card.render(opts)).should.match('<!--kg-card-begin: embed--><figure class="kg-card kg-embed-card"><h1>HEADING</h1><p>PARAGRAPH</p></figure><!--kg-card-end: embed-->');
}); });
it('Plain content renders', function () { it('Plain content renders', function () {
@ -27,7 +27,7 @@ describe('Embed card', function () {
} }
}; };
serializer.serialize(card.render(opts)).should.match('<figure class="kg-card kg-embed-card">CONTENT</figure>'); serializer.serialize(card.render(opts)).should.match('<!--kg-card-begin: embed--><figure class="kg-card kg-embed-card">CONTENT</figure><!--kg-card-end: embed-->');
}); });
it('Invalid HTML returns', function () { it('Invalid HTML returns', function () {
@ -40,7 +40,7 @@ describe('Embed card', function () {
} }
}; };
serializer.serialize(card.render(opts)).should.match('<figure class="kg-card kg-embed-card"><h1>HEADING<</figure>'); serializer.serialize(card.render(opts)).should.match('<!--kg-card-begin: embed--><figure class="kg-card kg-embed-card"><h1>HEADING<</figure><!--kg-card-end: embed-->');
}); });
it('Renders nothing when payload is undefined', function () { it('Renders nothing when payload is undefined', function () {
@ -67,6 +67,6 @@ describe('Embed card', function () {
} }
}; };
serializer.serialize(card.render(opts)).should.match('<figure class="kg-card kg-embed-card kg-card-hascaption">Testing<figcaption><strong>Caption</strong></figcaption></figure>'); serializer.serialize(card.render(opts)).should.match('<!--kg-card-begin: embed--><figure class="kg-card kg-embed-card kg-card-hascaption">Testing<figcaption><strong>Caption</strong></figcaption></figure><!--kg-card-end: embed-->');
}); });
}); });

View file

@ -74,7 +74,7 @@ describe('Gallery card', function () {
} }
}; };
serializer.serialize(card.render(opts)).should.eql('<figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="/content/images/2018/08/NatGeo01-9.jpg" width="3200" height="1600"></div><div class="kg-gallery-image"><img src="/content/images/2018/08/NatGeo02-10.jpg" width="3200" height="1600"></div><div class="kg-gallery-image"><img src="/content/images/2018/08/NatGeo03-6.jpg" width="3200" height="1600"></div></div><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="/content/images/2018/08/NatGeo04-7.jpg" width="3200" height="1600" alt="Alt test"></div><div class="kg-gallery-image"><img src="/content/images/2018/08/NatGeo05-4.jpg" width="3200" height="1600" title="Title test"></div><div class="kg-gallery-image"><img src="/content/images/2018/08/NatGeo06-6.jpg" width="3200" height="1600"></div></div><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="/content/images/2018/08/NatGeo07-5.jpg" width="3200" height="1600"></div><div class="kg-gallery-image"><img src="/content/images/2018/08/NatGeo09-8.jpg" width="3200" height="1600"></div></div></div><figcaption>Test caption</figcaption></figure>'); serializer.serialize(card.render(opts)).should.eql('<!--kg-card-begin: gallery--><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="/content/images/2018/08/NatGeo01-9.jpg" width="3200" height="1600"></div><div class="kg-gallery-image"><img src="/content/images/2018/08/NatGeo02-10.jpg" width="3200" height="1600"></div><div class="kg-gallery-image"><img src="/content/images/2018/08/NatGeo03-6.jpg" width="3200" height="1600"></div></div><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="/content/images/2018/08/NatGeo04-7.jpg" width="3200" height="1600" alt="Alt test"></div><div class="kg-gallery-image"><img src="/content/images/2018/08/NatGeo05-4.jpg" width="3200" height="1600" title="Title test"></div><div class="kg-gallery-image"><img src="/content/images/2018/08/NatGeo06-6.jpg" width="3200" height="1600"></div></div><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="/content/images/2018/08/NatGeo07-5.jpg" width="3200" height="1600"></div><div class="kg-gallery-image"><img src="/content/images/2018/08/NatGeo09-8.jpg" width="3200" height="1600"></div></div></div><figcaption>Test caption</figcaption></figure><!--kg-card-end: gallery-->');
}); });
it('renders nothing with no images', function () { it('renders nothing with no images', function () {
@ -136,6 +136,6 @@ describe('Gallery card', function () {
} }
}; };
serializer.serialize(card.render(opts)).should.eql('<figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="/content/images/2018/08/NatGeo01-9.jpg" width="3200" height="1600"></div><div class="kg-gallery-image"><img src="/content/images/2018/08/NatGeo03-6.jpg" width="3200" height="1600"></div></div></div><figcaption>Test caption</figcaption></figure>'); serializer.serialize(card.render(opts)).should.eql('<!--kg-card-begin: gallery--><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="/content/images/2018/08/NatGeo01-9.jpg" width="3200" height="1600"></div><div class="kg-gallery-image"><img src="/content/images/2018/08/NatGeo03-6.jpg" width="3200" height="1600"></div></div></div><figcaption>Test caption</figcaption></figure><!--kg-card-end: gallery-->');
}); });
}); });

View file

@ -11,6 +11,6 @@ describe('HR card', function () {
} }
}; };
serializer.serialize(card.render(opts)).should.match('<hr>'); serializer.serialize(card.render(opts)).should.match('<!--kg-card-begin: hr--><hr><!--kg-card-end: hr-->');
}); });
}); });

View file

@ -14,7 +14,7 @@ describe('HTML card', function () {
} }
}; };
serializer.serialize(card.render(opts)).should.match('<h1>HEADING</h1><p>PARAGRAPH</p>'); serializer.serialize(card.render(opts)).should.eql('<!--kg-card-begin: html--><h1>HEADING</h1><p>PARAGRAPH</p><!--kg-card-end: html-->');
}); });
it('Plain content renders', function () { it('Plain content renders', function () {
@ -27,7 +27,7 @@ describe('HTML card', function () {
} }
}; };
serializer.serialize(card.render(opts)).should.match('CONTENT'); serializer.serialize(card.render(opts)).should.eql('<!--kg-card-begin: html-->CONTENT<!--kg-card-end: html-->');
}); });
it('Invalid HTML returns', function () { it('Invalid HTML returns', function () {
@ -40,7 +40,7 @@ describe('HTML card', function () {
} }
}; };
serializer.serialize(card.render(opts)).should.match('<h1>HEADING<'); serializer.serialize(card.render(opts)).should.eql('<!--kg-card-begin: html--><h1>HEADING<<!--kg-card-end: html-->');
}); });
it('Renders nothing when payload is undefined', function () { it('Renders nothing when payload is undefined', function () {
@ -53,6 +53,6 @@ describe('HTML card', function () {
} }
}; };
serializer.serialize(card.render(opts)).should.match(''); serializer.serialize(card.render(opts)).should.eql('');
}); });
}); });

View file

@ -14,7 +14,7 @@ describe('Image card', function () {
} }
}; };
serializer.serialize(card.render(opts)).should.eql('<figure class="kg-card kg-image-card"><img src="https://www.ghost.org/image.png" class="kg-image"></figure>'); serializer.serialize(card.render(opts)).should.eql('<!--kg-card-begin: image--><figure class="kg-card kg-image-card"><img src="https://www.ghost.org/image.png" class="kg-image"></figure><!--kg-card-end: image-->');
}); });
it('renders an image with caption', function () { it('renders an image with caption', function () {
@ -28,7 +28,7 @@ describe('Image card', function () {
} }
}; };
serializer.serialize(card.render(opts)).should.eql('<figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.ghost.org/image.png" class="kg-image"><figcaption><b>Test caption</b></figcaption></figure>'); serializer.serialize(card.render(opts)).should.eql('<!--kg-card-begin: image--><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.ghost.org/image.png" class="kg-image"><figcaption><b>Test caption</b></figcaption></figure><!--kg-card-end: image-->');
}); });
it('renders an image with alt text', function () { it('renders an image with alt text', function () {
@ -42,7 +42,7 @@ describe('Image card', function () {
} }
}; };
serializer.serialize(card.render(opts)).should.eql('<figure class="kg-card kg-image-card"><img src="https://www.ghost.org/image.png" class="kg-image" alt="example image"></figure>'); serializer.serialize(card.render(opts)).should.eql('<!--kg-card-begin: image--><figure class="kg-card kg-image-card"><img src="https://www.ghost.org/image.png" class="kg-image" alt="example image"></figure><!--kg-card-end: image-->');
}); });
it('renders an image with title attribute', function () { it('renders an image with title attribute', function () {
@ -56,7 +56,7 @@ describe('Image card', function () {
} }
}; };
serializer.serialize(card.render(opts)).should.eql('<figure class="kg-card kg-image-card"><img src="https://www.ghost.org/image.png" class="kg-image" title="example image"></figure>'); serializer.serialize(card.render(opts)).should.eql('<!--kg-card-begin: image--><figure class="kg-card kg-image-card"><img src="https://www.ghost.org/image.png" class="kg-image" title="example image"></figure><!--kg-card-end: image-->');
}); });
it('renders nothing with no src', function () { it('renders nothing with no src', function () {
@ -85,7 +85,7 @@ describe('Image card', function () {
} }
}; };
serializer.serialize(card.render(opts)).should.eql('<figure class="kg-card kg-image-card"><img src="https://www.ghost.org/image.png" class="kg-image"></figure>'); serializer.serialize(card.render(opts)).should.eql('<!--kg-card-begin: image--><figure class="kg-card kg-image-card"><img src="https://www.ghost.org/image.png" class="kg-image"></figure><!--kg-card-end: image-->');
}); });
it('wide', function () { it('wide', function () {
@ -99,7 +99,7 @@ describe('Image card', function () {
} }
}; };
serializer.serialize(card.render(opts)).should.eql('<figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.ghost.org/image.png" class="kg-image"></figure>'); serializer.serialize(card.render(opts)).should.eql('<!--kg-card-begin: image--><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.ghost.org/image.png" class="kg-image"></figure><!--kg-card-end: image-->');
}); });
it('full', function () { it('full', function () {
@ -113,7 +113,7 @@ describe('Image card', function () {
} }
}; };
serializer.serialize(card.render(opts)).should.eql('<figure class="kg-card kg-image-card kg-width-full"><img src="https://www.ghost.org/image.png" class="kg-image"></figure>'); serializer.serialize(card.render(opts)).should.eql('<!--kg-card-begin: image--><figure class="kg-card kg-image-card kg-width-full"><img src="https://www.ghost.org/image.png" class="kg-image"></figure><!--kg-card-end: image-->');
}); });
}); });
}); });

View file

@ -15,7 +15,7 @@ describe('Markdown card', function () {
} }
}; };
serializer.serialize(card.render(opts)).should.match('<h1 id="heading">HEADING</h1>\n<ul>\n<li>list</li>\n<li>items</li>\n</ul>\n'); serializer.serialize(card.render(opts)).should.eql('<!--kg-card-begin: markdown--><h1 id="heading">HEADING</h1>\n<ul>\n<li>list</li>\n<li>items</li>\n</ul>\n<!--kg-card-end: markdown-->');
}); });
it('Accepts invalid HTML in markdown', function () { it('Accepts invalid HTML in markdown', function () {
@ -28,7 +28,7 @@ describe('Markdown card', function () {
} }
}; };
serializer.serialize(card.render(opts)).should.match('<h1 id="heading">HEADING</h1>\n<h2>Heading 2>'); serializer.serialize(card.render(opts)).should.eql('<!--kg-card-begin: markdown--><h1 id="heading">HEADING</h1>\n<h2>Heading 2><!--kg-card-end: markdown-->');
}); });
it('Renders nothing when payload is undefined', function () { it('Renders nothing when payload is undefined', function () {
@ -41,7 +41,7 @@ describe('Markdown card', function () {
} }
}; };
serializer.serialize(card.render(opts)).should.match(''); serializer.serialize(card.render(opts)).should.eql('');
}); });
it('[deprecated] version 1', function () { it('[deprecated] version 1', function () {
@ -57,7 +57,7 @@ describe('Markdown card', function () {
} }
}; };
serializer.serialize(card.render(opts)).should.match('<div class="kg-card-markdown"><h1 id="heading">HEADING</h1>\n<ul>\n<li>list</li>\n<li>items</li>\n</ul>\n</div>'); serializer.serialize(card.render(opts)).should.eql('<!--kg-card-begin: markdown--><div class="kg-card-markdown"><h1 id="heading">HEADING</h1>\n<ul>\n<li>list</li>\n<li>items</li>\n</ul>\n</div><!--kg-card-end: markdown-->');
}); });
}); });
}); });

View file

@ -57,7 +57,7 @@ describe('Mobiledoc converter', function () {
] ]
}; };
converter.render(mobiledoc, 2).should.eql('<p>One<br>Two</p><h1 id="markdowncard">Markdown card</h1>\n<p>Some markdown</p>\n<p>Three</p><hr><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="/content/images/2018/04/NatGeo06.jpg" class="kg-image"><figcaption>Birdies</figcaption></figure><p>Four</p><h2>HTML card</h2>\n<div><p>Some HTML</p></div><figure class="kg-card kg-embed-card"><h2>Embed card</h2></figure><figure class="kg-card kg-gallery-card kg-width-wide"><div class="kg-gallery-container"></div></figure>'); converter.render(mobiledoc, 2).should.eql('<p>One<br>Two</p><!--kg-card-begin: markdown--><h1 id="markdowncard">Markdown card</h1>\n<p>Some markdown</p>\n<!--kg-card-end: markdown--><p>Three</p><!--kg-card-begin: hr--><hr><!--kg-card-end: hr--><!--kg-card-begin: image--><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="/content/images/2018/04/NatGeo06.jpg" class="kg-image"><figcaption>Birdies</figcaption></figure><!--kg-card-end: image--><p>Four</p><!--kg-card-begin: html--><h2>HTML card</h2>\n<div><p>Some HTML</p></div><!--kg-card-end: html--><!--kg-card-begin: embed--><figure class="kg-card kg-embed-card"><h2>Embed card</h2></figure><!--kg-card-end: embed--><!--kg-card-begin: gallery--><figure class="kg-card kg-gallery-card kg-width-wide"><div class="kg-gallery-container"></div></figure><!--kg-card-end: gallery-->');
}); });
it('removes final blank paragraph', function () { it('removes final blank paragraph', function () {