Skip to content

Commit

Permalink
Updated mobiledoc renderer to insert begin/end HTML comments around c…
Browse files Browse the repository at this point in the history
…ards

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
  • Loading branch information
kevinansfield committed Feb 25, 2019
1 parent 23d5480 commit 4f9e687
Show file tree
Hide file tree
Showing 17 changed files with 91 additions and 42 deletions.
5 changes: 4 additions & 1 deletion core/server/lib/mobiledoc/cards/card-markdown.js
@@ -1,5 +1,8 @@
// 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
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'})
);
6 changes: 4 additions & 2 deletions core/server/lib/mobiledoc/cards/code.js
@@ -1,4 +1,6 @@
module.exports = {
const createCard = require('../create-card');

module.exports = createCard({
name: 'code',
type: 'dom',
render(opts) {
Expand All @@ -21,4 +23,4 @@ module.exports = {

return pre;
}
};
});
6 changes: 4 additions & 2 deletions core/server/lib/mobiledoc/cards/embed.js
@@ -1,4 +1,6 @@
module.exports = {
const createCard = require('../create-card');

module.exports = createCard({
name: 'embed',
type: 'dom',
render(opts) {
Expand All @@ -23,4 +25,4 @@ module.exports = {

return figure;
}
};
});
10 changes: 6 additions & 4 deletions core/server/lib/mobiledoc/cards/gallery.js
@@ -1,5 +1,3 @@
const MAX_IMG_PER_ROW = 3;

/**
* <figure class="kg-gallery-card kg-width-wide">
* <div class="kg-gallery-container>
Expand All @@ -17,7 +15,11 @@ const MAX_IMG_PER_ROW = 3;
* </figure>
*/

module.exports = {
const createCard = require('../create-card');

const MAX_IMG_PER_ROW = 3;

module.exports = createCard({
name: 'gallery',
type: 'dom',
render(opts) {
Expand Down Expand Up @@ -107,4 +109,4 @@ module.exports = {

return figure;
}
};
});
6 changes: 4 additions & 2 deletions core/server/lib/mobiledoc/cards/hr.js
@@ -1,7 +1,9 @@
module.exports = {
const createCard = require('../create-card');

module.exports = createCard({
name: 'hr',
type: 'dom',
render(opts) {
return opts.env.dom.createElement('hr');
}
};
});
6 changes: 4 additions & 2 deletions core/server/lib/mobiledoc/cards/html.js
@@ -1,4 +1,6 @@
module.exports = {
const createCard = require('../create-card');

module.exports = createCard({
name: 'html',
type: 'dom',
render(opts) {
Expand All @@ -10,4 +12,4 @@ module.exports = {
// avoids parsing/rendering of potentially broken or unsupported HTML
return opts.env.dom.createRawHTMLSection(opts.payload.html);
}
};
});
6 changes: 4 additions & 2 deletions core/server/lib/mobiledoc/cards/image.js
@@ -1,4 +1,6 @@
module.exports = {
const createCard = require('../create-card');

module.exports = createCard({
name: 'image',
type: 'dom',
render(opts) {
Expand Down Expand Up @@ -38,4 +40,4 @@ module.exports = {

return figure;
}
};
});
10 changes: 8 additions & 2 deletions core/server/lib/mobiledoc/cards/markdown.js
@@ -1,4 +1,6 @@
module.exports = {
const createCard = require('../create-card');

module.exports = createCard({
name: 'markdown',
type: 'dom',
render: function (opts) {
Expand All @@ -8,6 +10,10 @@ module.exports = {
// convert markdown to HTML ready for insertion into dom
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
*/
Expand All @@ -19,4 +25,4 @@ module.exports = {
// avoids parsing/rendering of potentially broken or unsupported HTML
return opts.env.dom.createRawHTMLSection(html);
}
};
});
28 changes: 28 additions & 0 deletions core/server/lib/mobiledoc/create-card.js
@@ -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;
}
};
};
4 changes: 2 additions & 2 deletions core/test/unit/lib/mobiledoc/cards/code_spec.js
Expand Up @@ -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 () {
Expand All @@ -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 () {
Expand Down
8 changes: 4 additions & 4 deletions core/test/unit/lib/mobiledoc/cards/embed_spec.js
Expand Up @@ -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 () {
Expand All @@ -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 () {
Expand All @@ -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 () {
Expand All @@ -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-->');
});
});
4 changes: 2 additions & 2 deletions core/test/unit/lib/mobiledoc/cards/gallery_spec.js
Expand Up @@ -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 () {
Expand Down Expand Up @@ -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-->');
});
});
2 changes: 1 addition & 1 deletion core/test/unit/lib/mobiledoc/cards/hr_spec.js
Expand Up @@ -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-->');
});
});
8 changes: 4 additions & 4 deletions core/test/unit/lib/mobiledoc/cards/html_spec.js
Expand Up @@ -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 () {
Expand All @@ -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 () {
Expand All @@ -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 () {
Expand All @@ -53,6 +53,6 @@ describe('HTML card', function () {
}
};

serializer.serialize(card.render(opts)).should.match('');
serializer.serialize(card.render(opts)).should.eql('');
});
});
14 changes: 7 additions & 7 deletions core/test/unit/lib/mobiledoc/cards/image_spec.js
Expand Up @@ -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 () {
Expand All @@ -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 () {
Expand All @@ -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 () {
Expand All @@ -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 () {
Expand Down Expand Up @@ -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 () {
Expand All @@ -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 () {
Expand All @@ -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-->');
});
});
});
8 changes: 4 additions & 4 deletions core/test/unit/lib/mobiledoc/cards/markdown_spec.js
Expand Up @@ -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 () {
Expand All @@ -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 () {
Expand All @@ -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 () {
Expand All @@ -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-->');
});
});
});
Expand Up @@ -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 () {
Expand Down

0 comments on commit 4f9e687

Please sign in to comment.