mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-25 11:55:03 +03:00
Switched to extracted @tryghost/mobiledoc-dom-renderer
package
no issue - moved `mobiledoc.renderers.mobiledocHtmlRenderer` to `mobiledoc.mobiledocHtmlRenderer` so that it's easier for the getter to access the parent objects getters - removed all tests and dependencies that now live in @tryghost/mobiledoc-dom-renderer - kept the `mobiledocHtmlRenderer` test because that's testing that we've correctly wired up our cards and atoms and the output is what we expect
This commit is contained in:
parent
35e3e0708c
commit
b37ac8ef1f
@ -216,7 +216,7 @@ class PostsImporter extends BaseImporter {
|
|||||||
});
|
});
|
||||||
|
|
||||||
model.mobiledoc = JSON.stringify(mobiledoc);
|
model.mobiledoc = JSON.stringify(mobiledoc);
|
||||||
model.html = mobiledocLib.renderers.mobiledocHtmlRenderer.render(JSON.parse(model.mobiledoc));
|
model.html = mobiledocLib.mobiledocHtmlRenderer.render(JSON.parse(model.mobiledoc));
|
||||||
}
|
}
|
||||||
this.sanitizePostsMeta(model);
|
this.sanitizePostsMeta(model);
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const Promise = require('bluebird');
|
const Promise = require('bluebird');
|
||||||
const common = require('../../../../lib/common');
|
const common = require('../../../../lib/common');
|
||||||
const renderers = require('../../../../lib/mobiledoc/renderers');
|
const mobiledocLib = require('../../../../lib/mobiledoc');
|
||||||
const models = require('../../../../models');
|
const models = require('../../../../models');
|
||||||
const message1 = 'Migrating Koenig beta post\'s mobiledoc/HTML to 2.0 format';
|
const message1 = 'Migrating Koenig beta post\'s mobiledoc/HTML to 2.0 format';
|
||||||
const message2 = 'Migrated Koenig beta post\'s mobiledoc/HTML to 2.0 format';
|
const message2 = 'Migrated Koenig beta post\'s mobiledoc/HTML to 2.0 format';
|
||||||
@ -54,7 +54,7 @@ module.exports.up = function regenerateKoenigBetaHTML(options) {
|
|||||||
|
|
||||||
// re-render the html to remove .kg-post wrapper and adjust image classes
|
// re-render the html to remove .kg-post wrapper and adjust image classes
|
||||||
let version = 2;
|
let version = 2;
|
||||||
let html = renderers.mobiledocHtmlRenderer.render(mobiledoc, version);
|
let html = mobiledocLib.mobiledocHtmlRenderer.render(mobiledoc, version);
|
||||||
|
|
||||||
return models.Post.edit({
|
return models.Post.edit({
|
||||||
html,
|
html,
|
||||||
|
@ -61,7 +61,7 @@ module.exports.up = (options) => {
|
|||||||
// CASE: if mobiledoc field is null, we auto set a blank structure in the model layer
|
// CASE: if mobiledoc field is null, we auto set a blank structure in the model layer
|
||||||
// CASE: if html field is null, we auto generate the html in the model layer
|
// CASE: if html field is null, we auto generate the html in the model layer
|
||||||
if (mobiledoc && post.html && post.html.match(/^<div class="kg-card-markdown">/)) {
|
if (mobiledoc && post.html && post.html.match(/^<div class="kg-card-markdown">/)) {
|
||||||
html = mobiledocLib.renderers.mobiledocHtmlRenderer.render(mobiledoc);
|
html = mobiledocLib.mobiledocHtmlRenderer.render(mobiledoc);
|
||||||
}
|
}
|
||||||
return localOptions
|
return localOptions
|
||||||
.transacting('posts')
|
.transacting('posts')
|
||||||
@ -101,7 +101,7 @@ module.exports.down = (options) => {
|
|||||||
|
|
||||||
// CASE: revert: all new editor posts to the old editor format
|
// CASE: revert: all new editor posts to the old editor format
|
||||||
if (mobiledoc && post.html) {
|
if (mobiledoc && post.html) {
|
||||||
html = mobiledocLib.renderers.mobiledocHtmlRenderer.render(mobiledoc, version);
|
html = mobiledocLib.mobiledocHtmlRenderer.render(mobiledoc, version);
|
||||||
}
|
}
|
||||||
|
|
||||||
return localOptions
|
return localOptions
|
||||||
|
@ -2,7 +2,7 @@ const _ = require('lodash');
|
|||||||
const Promise = require('bluebird');
|
const Promise = require('bluebird');
|
||||||
const htmlToText = require('html-to-text');
|
const htmlToText = require('html-to-text');
|
||||||
const common = require('../../../../lib/common');
|
const common = require('../../../../lib/common');
|
||||||
const renderers = require('../../../../lib/mobiledoc/renderers');
|
const mobiledocLib = require('../../../../lib/mobiledoc');
|
||||||
|
|
||||||
module.exports.config = {
|
module.exports.config = {
|
||||||
transaction: true
|
transaction: true
|
||||||
@ -36,7 +36,7 @@ module.exports.up = (options) => {
|
|||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
const html = renderers.mobiledocHtmlRenderer.render(mobiledoc);
|
const html = mobiledocLib.mobiledocHtmlRenderer.render(mobiledoc);
|
||||||
|
|
||||||
const updatedAttrs = {
|
const updatedAttrs = {
|
||||||
html: html
|
html: html
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
const common = require('../common');
|
const common = require('../common');
|
||||||
const config = require('../../config');
|
const config = require('../../config');
|
||||||
|
|
||||||
let cardFactory, cards;
|
let cardFactory, cards, mobiledocHtmlRenderer;
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
get blankDocument() {
|
get blankDocument() {
|
||||||
@ -19,21 +19,19 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
get cards() {
|
get cards() {
|
||||||
if (cards) {
|
if (!cards) {
|
||||||
return cards;
|
const CardFactory = require('@tryghost/kg-card-factory');
|
||||||
|
const defaultCards = require('@tryghost/kg-default-cards');
|
||||||
|
|
||||||
|
cardFactory = new CardFactory({
|
||||||
|
siteUrl: config.get('url')
|
||||||
|
});
|
||||||
|
|
||||||
|
cards = defaultCards.map((card) => {
|
||||||
|
return cardFactory.createCard(card);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const CardFactory = require('@tryghost/kg-card-factory');
|
|
||||||
const defaultCards = require('@tryghost/kg-default-cards');
|
|
||||||
|
|
||||||
cardFactory = new CardFactory({
|
|
||||||
siteUrl: config.get('url')
|
|
||||||
});
|
|
||||||
|
|
||||||
cards = defaultCards.map((card) => {
|
|
||||||
return cardFactory.createCard(card);
|
|
||||||
});
|
|
||||||
|
|
||||||
return cards;
|
return cards;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -41,8 +39,22 @@ module.exports = {
|
|||||||
return require('@tryghost/kg-default-atoms');
|
return require('@tryghost/kg-default-atoms');
|
||||||
},
|
},
|
||||||
|
|
||||||
get renderers() {
|
get mobiledocHtmlRenderer() {
|
||||||
return require('./renderers');
|
if (!mobiledocHtmlRenderer) {
|
||||||
|
const MobiledocHtmlRenderer = require('@tryghost/kg-mobiledoc-html-renderer');
|
||||||
|
|
||||||
|
mobiledocHtmlRenderer = new MobiledocHtmlRenderer({
|
||||||
|
cards: this.cards,
|
||||||
|
atoms: this.atoms,
|
||||||
|
unknownCardHandler(args) {
|
||||||
|
common.logging.error(new common.errors.InternalServerError({
|
||||||
|
message: 'Mobiledoc card \'' + args.env.name + '\' not found.'
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return mobiledocHtmlRenderer;
|
||||||
},
|
},
|
||||||
|
|
||||||
get htmlToMobiledocConverter() {
|
get htmlToMobiledocConverter() {
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
get mobiledocHtmlRenderer() {
|
|
||||||
return require('./mobiledoc-html-renderer');
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,115 +0,0 @@
|
|||||||
const SimpleDom = require('simple-dom');
|
|
||||||
const Renderer = require('mobiledoc-dom-renderer').default;
|
|
||||||
const common = require('../../common');
|
|
||||||
const mobiledoc = require('../');
|
|
||||||
const options = {
|
|
||||||
dom: new SimpleDom.Document(),
|
|
||||||
cards: mobiledoc.cards,
|
|
||||||
atoms: mobiledoc.atoms,
|
|
||||||
unknownCardHandler: function (args) {
|
|
||||||
common.logging.error(new common.errors.InternalServerError({
|
|
||||||
message: 'Mobiledoc card \'' + args.env.name + '\' not found.'
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const walkDom = function (node, func) {
|
|
||||||
func(node);
|
|
||||||
node = node.firstChild;
|
|
||||||
|
|
||||||
while (node) {
|
|
||||||
walkDom(node, func);
|
|
||||||
node = node.nextSibling;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const nodeTextContent = function (node) {
|
|
||||||
let textContent = '';
|
|
||||||
|
|
||||||
walkDom(node, (node) => {
|
|
||||||
if (node.nodeType === 3) {
|
|
||||||
textContent += node.nodeValue;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return textContent;
|
|
||||||
};
|
|
||||||
|
|
||||||
// used to walk the rendered SimpleDOM output and modify elements before
|
|
||||||
// serializing to HTML. Saves having a large HTML parsing dependency such as
|
|
||||||
// jsdom that may break on malformed HTML in MD or HTML cards
|
|
||||||
class DomModifier {
|
|
||||||
constructor() {
|
|
||||||
this.usedIds = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
addHeadingId(node) {
|
|
||||||
if (!node.firstChild || node.getAttribute('id')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let text = nodeTextContent(node);
|
|
||||||
let id = text
|
|
||||||
.replace(/[<>&"?]/g, '')
|
|
||||||
.trim()
|
|
||||||
.replace(/[^\w]/g, '-')
|
|
||||||
.replace(/-{2,}/g, '-')
|
|
||||||
.toLowerCase();
|
|
||||||
|
|
||||||
if (this.usedIds[id] !== undefined) {
|
|
||||||
this.usedIds[id] += 1;
|
|
||||||
id += `-${this.usedIds[id]}`;
|
|
||||||
} else {
|
|
||||||
this.usedIds[id] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
node.setAttribute('id', id);
|
|
||||||
}
|
|
||||||
|
|
||||||
modifyChildren(node) {
|
|
||||||
walkDom(node, this.modify.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
modify(node) {
|
|
||||||
// add id attributes to H* tags
|
|
||||||
if (node.nodeType === 1 && node.nodeName.match(/^h\d$/i)) {
|
|
||||||
this.addHeadingId(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
render(mobiledoc, version) {
|
|
||||||
/**
|
|
||||||
* @deprecated: version 1 === Ghost 1.0 markdown-only mobiledoc
|
|
||||||
* We keep the version 1 logic till Ghost 3.0 to be able to rollback posts.
|
|
||||||
*
|
|
||||||
* version 2 (latest) === Ghost 2.0 full mobiledoc
|
|
||||||
*/
|
|
||||||
version = version || 2;
|
|
||||||
|
|
||||||
const versionedOptions = Object.assign({}, options, {
|
|
||||||
cardOptions: {version}
|
|
||||||
});
|
|
||||||
|
|
||||||
const renderer = new Renderer(versionedOptions);
|
|
||||||
const rendered = renderer.render(mobiledoc);
|
|
||||||
const serializer = new SimpleDom.HTMLSerializer(SimpleDom.voidMap);
|
|
||||||
|
|
||||||
// Koenig keeps a blank paragraph at the end of a doc but we want to
|
|
||||||
// make sure it doesn't get rendered
|
|
||||||
const lastChild = rendered.result.lastChild;
|
|
||||||
if (lastChild && lastChild.tagName === 'P') {
|
|
||||||
if (!nodeTextContent(lastChild)) {
|
|
||||||
rendered.result.removeChild(lastChild);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Walk the DOM output and modify nodes as needed
|
|
||||||
// eg. to add ID attributes to heading elements
|
|
||||||
const modifier = new DomModifier();
|
|
||||||
modifier.modifyChildren(rendered.result);
|
|
||||||
|
|
||||||
return serializer.serializeChildren(rendered.result);
|
|
||||||
}
|
|
||||||
};
|
|
@ -405,7 +405,7 @@ Post = ghostBookshelf.Model.extend({
|
|||||||
// CASE: html is null, but mobiledoc exists (only important for migrations & importing)
|
// CASE: html is null, but mobiledoc exists (only important for migrations & importing)
|
||||||
if (this.hasChanged('mobiledoc') || (!this.get('html') && (options.migrating || options.importing))) {
|
if (this.hasChanged('mobiledoc') || (!this.get('html') && (options.migrating || options.importing))) {
|
||||||
try {
|
try {
|
||||||
this.set('html', mobiledocLib.renderers.mobiledocHtmlRenderer.render(JSON.parse(this.get('mobiledoc'))));
|
this.set('html', mobiledocLib.mobiledocHtmlRenderer.render(JSON.parse(this.get('mobiledoc'))));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new common.errors.ValidationError({
|
throw new common.errors.ValidationError({
|
||||||
message: 'Invalid mobiledoc structure.',
|
message: 'Invalid mobiledoc structure.',
|
||||||
|
@ -49,6 +49,7 @@
|
|||||||
"@tryghost/kg-default-atoms": "1.0.0",
|
"@tryghost/kg-default-atoms": "1.0.0",
|
||||||
"@tryghost/kg-default-cards": "1.0.1",
|
"@tryghost/kg-default-cards": "1.0.1",
|
||||||
"@tryghost/kg-markdown-html-renderer": "1.0.2",
|
"@tryghost/kg-markdown-html-renderer": "1.0.2",
|
||||||
|
"@tryghost/kg-mobiledoc-html-renderer": "^1.0.0",
|
||||||
"@tryghost/members-api": "0.18.0",
|
"@tryghost/members-api": "0.18.0",
|
||||||
"@tryghost/members-ssr": "0.7.4",
|
"@tryghost/members-ssr": "0.7.4",
|
||||||
"@tryghost/mw-session-from-token": "0.1.0",
|
"@tryghost/mw-session-from-token": "0.1.0",
|
||||||
@ -109,7 +110,6 @@
|
|||||||
"metascraper-publisher": "5.11.8",
|
"metascraper-publisher": "5.11.8",
|
||||||
"metascraper-title": "5.11.8",
|
"metascraper-title": "5.11.8",
|
||||||
"metascraper-url": "5.11.8",
|
"metascraper-url": "5.11.8",
|
||||||
"mobiledoc-dom-renderer": "0.7.0",
|
|
||||||
"moment": "2.24.0",
|
"moment": "2.24.0",
|
||||||
"moment-timezone": "0.5.23",
|
"moment-timezone": "0.5.23",
|
||||||
"multer": "1.4.2",
|
"multer": "1.4.2",
|
||||||
@ -124,7 +124,6 @@
|
|||||||
"rss": "1.2.2",
|
"rss": "1.2.2",
|
||||||
"sanitize-html": "1.22.1",
|
"sanitize-html": "1.22.1",
|
||||||
"semver": "7.2.1",
|
"semver": "7.2.1",
|
||||||
"simple-dom": "0.3.2",
|
|
||||||
"uuid": "7.0.3",
|
"uuid": "7.0.3",
|
||||||
"validator": "6.3.0",
|
"validator": "6.3.0",
|
||||||
"xml": "1.0.1"
|
"xml": "1.0.1"
|
||||||
|
@ -1,215 +0,0 @@
|
|||||||
const should = require('should');
|
|
||||||
const converter = require('../../../../../core/server/lib/mobiledoc/renderers/mobiledoc-html-renderer');
|
|
||||||
|
|
||||||
describe('Mobiledoc HTML renderer', function () {
|
|
||||||
describe('default', function () {
|
|
||||||
it('renders all default cards and atoms', function () {
|
|
||||||
let mobiledoc = {
|
|
||||||
version: '0.3.1',
|
|
||||||
atoms: [
|
|
||||||
['soft-return', '', {}]
|
|
||||||
],
|
|
||||||
cards: [
|
|
||||||
['markdown', {
|
|
||||||
markdown: '# Markdown card\nSome markdown'
|
|
||||||
}],
|
|
||||||
['hr', {}],
|
|
||||||
['image', {
|
|
||||||
cardWidth: 'wide',
|
|
||||||
src: '/content/images/2018/04/NatGeo06.jpg',
|
|
||||||
caption: 'Birdies'
|
|
||||||
}],
|
|
||||||
['html', {
|
|
||||||
html: '<h2>HTML card</h2>\n<div><p>Some HTML</p></div>'
|
|
||||||
}],
|
|
||||||
['embed', {
|
|
||||||
html: '<h2>Embed card</h2>'
|
|
||||||
}],
|
|
||||||
['gallery', {
|
|
||||||
images: [{
|
|
||||||
fileName: 'test.png',
|
|
||||||
src: '/test.png',
|
|
||||||
width: 1000,
|
|
||||||
height: 500
|
|
||||||
}]
|
|
||||||
}]
|
|
||||||
],
|
|
||||||
markups: [],
|
|
||||||
sections: [
|
|
||||||
[1, 'p', [
|
|
||||||
[0, [], 0, 'One'],
|
|
||||||
[1, [], 0, 0],
|
|
||||||
[0, [], 0, 'Two']
|
|
||||||
]],
|
|
||||||
[10, 0],
|
|
||||||
[1, 'p', [
|
|
||||||
[0, [], 0, 'Three']
|
|
||||||
]],
|
|
||||||
[10, 1],
|
|
||||||
[10, 2],
|
|
||||||
[1, 'p', [
|
|
||||||
[0, [], 0, 'Four']
|
|
||||||
]],
|
|
||||||
[10, 3],
|
|
||||||
[10, 4],
|
|
||||||
[10, 5],
|
|
||||||
[1, 'p', []]
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
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><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><!--kg-card-begin: html--><h2>HTML card</h2>\n<div><p>Some HTML</p></div><!--kg-card-end: html--><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>');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('removes final blank paragraph', function () {
|
|
||||||
let mobiledoc = {
|
|
||||||
version: '0.3.1',
|
|
||||||
atoms: [],
|
|
||||||
cards: [],
|
|
||||||
markups: [],
|
|
||||||
sections: [
|
|
||||||
[1, 'p', [
|
|
||||||
[0, [], 0, 'Test']
|
|
||||||
]],
|
|
||||||
[1, 'p', []]
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
converter.render(mobiledoc, 2).should.eql('<p>Test</p>');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('removes single blank paragraph', function () {
|
|
||||||
let mobiledoc = {
|
|
||||||
version: '0.3.1',
|
|
||||||
atoms: [],
|
|
||||||
cards: [],
|
|
||||||
markups: [],
|
|
||||||
sections: [
|
|
||||||
[1, 'p', []]
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
converter.render(mobiledoc, 2).should.eql('');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('removes single blank paragraph with empty content', function () {
|
|
||||||
let mobiledoc = {
|
|
||||||
version: '0.3.1',
|
|
||||||
markups: [],
|
|
||||||
atoms: [],
|
|
||||||
cards: [],
|
|
||||||
sections: [
|
|
||||||
[1, 'p', [
|
|
||||||
[0, [], 0, '']
|
|
||||||
]]
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
converter.render(mobiledoc, 2).should.eql('');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('doesn\'t remove last paragraph if it has markups', function () {
|
|
||||||
let mobiledoc = {
|
|
||||||
version: '0.3.1',
|
|
||||||
markups: [['em']],
|
|
||||||
atoms: [],
|
|
||||||
cards: [],
|
|
||||||
sections: [
|
|
||||||
[1, 'p', [
|
|
||||||
[0, [0], 1, 'This should be kept']
|
|
||||||
]]
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
converter.render(mobiledoc, 2).should.eql('<p><em>This should be kept</em></p>');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('adds id attributes to headings', function () {
|
|
||||||
let mobiledoc = {
|
|
||||||
version: '0.3.1',
|
|
||||||
atoms: [],
|
|
||||||
cards: [],
|
|
||||||
markups: [
|
|
||||||
['a', ['href', 'http://example.com']]
|
|
||||||
],
|
|
||||||
sections: [
|
|
||||||
[1, 'h1', [
|
|
||||||
[0, [], 0, 'Heading One']
|
|
||||||
]],
|
|
||||||
[1, 'h2', [
|
|
||||||
[0, [], 0, 'Heading Two']
|
|
||||||
]],
|
|
||||||
[1, 'h3', [
|
|
||||||
[0, [], 0, 'Heading Three']
|
|
||||||
]],
|
|
||||||
[1, 'h4', [
|
|
||||||
[0, [], 0, 'Heading Four']
|
|
||||||
]],
|
|
||||||
[1, 'h5', [
|
|
||||||
[0, [], 0, 'Heading Five']
|
|
||||||
]],
|
|
||||||
[1, 'h6', [
|
|
||||||
[0, [], 0, 'Heading Six']
|
|
||||||
]],
|
|
||||||
// duplicate text
|
|
||||||
[1, 'h1', [
|
|
||||||
[0, [], 0, 'Heading One']
|
|
||||||
]],
|
|
||||||
[1, 'h3', [
|
|
||||||
[0, [], 0, 'Heading One']
|
|
||||||
]],
|
|
||||||
// invalid attr chars
|
|
||||||
[1, 'h1', [
|
|
||||||
[0, [], 0, '< left < arrow <']
|
|
||||||
]],
|
|
||||||
[1, 'h1', [
|
|
||||||
[0, [], 0, '> right > arrow >']
|
|
||||||
]],
|
|
||||||
[1, 'h1', [
|
|
||||||
[0, [], 0, '"quote" "test"']
|
|
||||||
]],
|
|
||||||
[1, 'h1', [
|
|
||||||
[0, [], 0, '? question?']
|
|
||||||
]],
|
|
||||||
[1, 'h1', [
|
|
||||||
[0, [], 0, '& ampersand&']
|
|
||||||
]],
|
|
||||||
// trailing link
|
|
||||||
[1, 'h1', [
|
|
||||||
[0, [], 0, 'trailing '],
|
|
||||||
[0, [0], 1, 'link']
|
|
||||||
]],
|
|
||||||
// preceding link
|
|
||||||
[1, 'h1', [
|
|
||||||
[0, [0], 1, 'preceding'],
|
|
||||||
[0, [], 0, ' link']
|
|
||||||
]]
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
let output = converter.render(mobiledoc, 2);
|
|
||||||
|
|
||||||
// normal headings
|
|
||||||
output.should.match(/<h1 id="heading-one">Heading One<\/h1>/);
|
|
||||||
output.should.match(/<h2 id="heading-two">Heading Two<\/h2>/);
|
|
||||||
output.should.match(/<h3 id="heading-three">Heading Three<\/h3>/);
|
|
||||||
output.should.match(/<h4 id="heading-four">Heading Four<\/h4>/);
|
|
||||||
output.should.match(/<h5 id="heading-five">Heading Five<\/h5>/);
|
|
||||||
output.should.match(/<h6 id="heading-six">Heading Six<\/h6>/);
|
|
||||||
|
|
||||||
// duplicate heading text
|
|
||||||
output.should.match(/<h1 id="heading-one-1">Heading One<\/h1>/);
|
|
||||||
output.should.match(/<h3 id="heading-one-2">Heading One<\/h3>/);
|
|
||||||
|
|
||||||
// invalid ID/hash-url chars
|
|
||||||
output.should.match(/<h1 id="left-arrow">< left < arrow <<\/h1>/);
|
|
||||||
output.should.match(/<h1 id="right-arrow">> right > arrow ><\/h1>/);
|
|
||||||
output.should.match(/<h1 id="quote-test">"quote" "test"<\/h1>/);
|
|
||||||
output.should.match(/<h1 id="question">\? question\?<\/h1>/);
|
|
||||||
output.should.match(/<h1 id="ampersand">& ampersand&<\/h1>/);
|
|
||||||
|
|
||||||
// heading with link
|
|
||||||
output.should.match(/<h1 id="trailing-link">trailing <a href="http:\/\/example\.com">link<\/a><\/h1>/);
|
|
||||||
output.should.match(/<h1 id="preceding-link"><a href="http:\/\/example\.com">preceding<\/a> link<\/h1>/);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
73
test/unit/lib/mobiledoc_spec.js
Normal file
73
test/unit/lib/mobiledoc_spec.js
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
const should = require('should');
|
||||||
|
const configUtils = require('../../utils/configUtils');
|
||||||
|
const mobiledocLib = require('../../../core/server/lib/mobiledoc');
|
||||||
|
|
||||||
|
describe('lib/mobiledoc', function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
configUtils.set('url', 'https://example.com');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
configUtils.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('mobiledocHtmlRenderer', function () {
|
||||||
|
it('renders all default cards and atoms', function () {
|
||||||
|
let mobiledoc = {
|
||||||
|
version: '0.3.1',
|
||||||
|
atoms: [
|
||||||
|
['soft-return', '', {}]
|
||||||
|
],
|
||||||
|
cards: [
|
||||||
|
['markdown', {
|
||||||
|
markdown: '# Markdown card\nSome markdown'
|
||||||
|
}],
|
||||||
|
['hr', {}],
|
||||||
|
['image', {
|
||||||
|
cardWidth: 'wide',
|
||||||
|
src: '/content/images/2018/04/NatGeo06.jpg',
|
||||||
|
caption: 'Birdies'
|
||||||
|
}],
|
||||||
|
['html', {
|
||||||
|
html: '<h2>HTML card</h2>\n<div><p>Some HTML</p></div>'
|
||||||
|
}],
|
||||||
|
['embed', {
|
||||||
|
html: '<h2>Embed card</h2>'
|
||||||
|
}],
|
||||||
|
['gallery', {
|
||||||
|
images: [{
|
||||||
|
fileName: 'test.png',
|
||||||
|
src: '/test.png',
|
||||||
|
width: 1000,
|
||||||
|
height: 500
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
],
|
||||||
|
markups: [],
|
||||||
|
sections: [
|
||||||
|
[1, 'p', [
|
||||||
|
[0, [], 0, 'One'],
|
||||||
|
[1, [], 0, 0],
|
||||||
|
[0, [], 0, 'Two']
|
||||||
|
]],
|
||||||
|
[10, 0],
|
||||||
|
[1, 'p', [
|
||||||
|
[0, [], 0, 'Three']
|
||||||
|
]],
|
||||||
|
[10, 1],
|
||||||
|
[10, 2],
|
||||||
|
[1, 'p', [
|
||||||
|
[0, [], 0, 'Four']
|
||||||
|
]],
|
||||||
|
[10, 3],
|
||||||
|
[10, 4],
|
||||||
|
[10, 5],
|
||||||
|
[1, 'p', []]
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
mobiledocLib.mobiledocHtmlRenderer.render(mobiledoc)
|
||||||
|
.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><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><!--kg-card-begin: html--><h2>HTML card</h2>\n<div><p>Some HTML</p></div><!--kg-card-end: html--><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>');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
55
yarn.lock
55
yarn.lock
@ -224,6 +224,37 @@
|
|||||||
"@sentry/types" "5.15.4"
|
"@sentry/types" "5.15.4"
|
||||||
tslib "^1.9.3"
|
tslib "^1.9.3"
|
||||||
|
|
||||||
|
"@simple-dom/document@^1.4.0":
|
||||||
|
version "1.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@simple-dom/document/-/document-1.4.0.tgz#af60855f957f284d436983798ef1006cca1a1678"
|
||||||
|
integrity sha512-/RUeVH4kuD3rzo5/91+h4Z1meLSLP66eXqpVAw/4aZmYozkeqUkMprq0znL4psX/adEed5cBgiNJcfMz/eKZLg==
|
||||||
|
dependencies:
|
||||||
|
"@simple-dom/interface" "^1.4.0"
|
||||||
|
|
||||||
|
"@simple-dom/interface@^1.4.0":
|
||||||
|
version "1.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@simple-dom/interface/-/interface-1.4.0.tgz#e8feea579232017f89b0138e2726facda6fbb71f"
|
||||||
|
integrity sha512-l5qumKFWU0S+4ZzMaLXFU8tQZsicHEMEyAxI5kDFGhJsRqDwe0a7/iPA/GdxlGyDKseQQAgIz5kzU7eXTrlSpA==
|
||||||
|
|
||||||
|
"@simple-dom/parser@^1.4.0":
|
||||||
|
version "1.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@simple-dom/parser/-/parser-1.4.0.tgz#b1fee1a23f48a37d6bdd98f5242db0cab5b67abc"
|
||||||
|
integrity sha512-TNjDkOehueRIKr1df416qk9ELj+qWuVVJNIT25y1aZg3pQvxv4UPGrgaDFte7dsWBTbF3V8NYPNQ5FDUZQ8Wlg==
|
||||||
|
dependencies:
|
||||||
|
"@simple-dom/interface" "^1.4.0"
|
||||||
|
|
||||||
|
"@simple-dom/serializer@^1.4.0":
|
||||||
|
version "1.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@simple-dom/serializer/-/serializer-1.4.0.tgz#98470f357f418d72b1a1ec78d68191e60aefe215"
|
||||||
|
integrity sha512-mI1yRahsVs8atXLiQksineDsFEFqeG7RHwnnBTDOK6inbzl4tZQgjR+Z7edjgIJq5j5RhZvwPI6EuCji9B3eQw==
|
||||||
|
dependencies:
|
||||||
|
"@simple-dom/interface" "^1.4.0"
|
||||||
|
|
||||||
|
"@simple-dom/void-map@^1.4.0":
|
||||||
|
version "1.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@simple-dom/void-map/-/void-map-1.4.0.tgz#f15f07568fe1076740407266aa5e6eac249bc78c"
|
||||||
|
integrity sha512-VDhLEyVCbuhOBBgHol9ShzIv9O8UCzdXeH4FoXu2DOcu/nnvTjLTck+BgXsCLv5ynDiUdoqsREEVFnoyPpFKVw==
|
||||||
|
|
||||||
"@sindresorhus/is@^0.14.0":
|
"@sindresorhus/is@^0.14.0":
|
||||||
version "0.14.0"
|
version "0.14.0"
|
||||||
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea"
|
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea"
|
||||||
@ -400,6 +431,14 @@
|
|||||||
markdown-it-lazy-headers "^0.1.3"
|
markdown-it-lazy-headers "^0.1.3"
|
||||||
markdown-it-mark "^3.0.0"
|
markdown-it-mark "^3.0.0"
|
||||||
|
|
||||||
|
"@tryghost/kg-mobiledoc-html-renderer@^1.0.0":
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@tryghost/kg-mobiledoc-html-renderer/-/kg-mobiledoc-html-renderer-1.0.0.tgz#7892ead37a7d709dd3407b32e264f76df946bd71"
|
||||||
|
integrity sha512-/py2bsGEM+L98Tmurbom8Hc2GbcOXd1bPx9C/7PNALlXD92RiNzzrxXnTrb8XnJKAeIF5o1XxBSsx0YvwJcxCQ==
|
||||||
|
dependencies:
|
||||||
|
mobiledoc-dom-renderer "^0.7.0"
|
||||||
|
simple-dom "^1.4.0"
|
||||||
|
|
||||||
"@tryghost/kg-parser-plugins@0.9.2":
|
"@tryghost/kg-parser-plugins@0.9.2":
|
||||||
version "0.9.2"
|
version "0.9.2"
|
||||||
resolved "https://registry.yarnpkg.com/@tryghost/kg-parser-plugins/-/kg-parser-plugins-0.9.2.tgz#ef7335dfa54446505e85b24bca2ff093f10a82a2"
|
resolved "https://registry.yarnpkg.com/@tryghost/kg-parser-plugins/-/kg-parser-plugins-0.9.2.tgz#ef7335dfa54446505e85b24bca2ff093f10a82a2"
|
||||||
@ -5992,7 +6031,7 @@ mkdirp@^1.0.3, mkdirp@~1.0.3:
|
|||||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.3.tgz#4cf2e30ad45959dddea53ad97d518b6c8205e1ea"
|
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.3.tgz#4cf2e30ad45959dddea53ad97d518b6c8205e1ea"
|
||||||
integrity sha512-6uCP4Qc0sWsgMLy1EOqqS/3rjDHOEnsStVr/4vtAIK2Y5i2kA7lFFejYrpIyiN9w0pYf4ckeCYT9f1r1P9KX5g==
|
integrity sha512-6uCP4Qc0sWsgMLy1EOqqS/3rjDHOEnsStVr/4vtAIK2Y5i2kA7lFFejYrpIyiN9w0pYf4ckeCYT9f1r1P9KX5g==
|
||||||
|
|
||||||
mobiledoc-dom-renderer@0.7.0:
|
mobiledoc-dom-renderer@0.7.0, mobiledoc-dom-renderer@^0.7.0:
|
||||||
version "0.7.0"
|
version "0.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/mobiledoc-dom-renderer/-/mobiledoc-dom-renderer-0.7.0.tgz#53ab5f14dd612b16f03513390e5cbcc2b89f6979"
|
resolved "https://registry.yarnpkg.com/mobiledoc-dom-renderer/-/mobiledoc-dom-renderer-0.7.0.tgz#53ab5f14dd612b16f03513390e5cbcc2b89f6979"
|
||||||
integrity sha512-A+gT6D4Ru3DKY7ZYOBRORmwhRJ7rDj2vy75D2dWuZS5NgX0mCmGs0yN7qs48YlxvfCif8RFpYsaaPg6Kc3MdJg==
|
integrity sha512-A+gT6D4Ru3DKY7ZYOBRORmwhRJ7rDj2vy75D2dWuZS5NgX0mCmGs0yN7qs48YlxvfCif8RFpYsaaPg6Kc3MdJg==
|
||||||
@ -8199,10 +8238,16 @@ simple-concat@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.0.tgz#7344cbb8b6e26fb27d66b2fc86f9f6d5997521c6"
|
resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.0.tgz#7344cbb8b6e26fb27d66b2fc86f9f6d5997521c6"
|
||||||
integrity sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=
|
integrity sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=
|
||||||
|
|
||||||
simple-dom@0.3.2:
|
simple-dom@^1.4.0:
|
||||||
version "0.3.2"
|
version "1.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/simple-dom/-/simple-dom-0.3.2.tgz#0663d10f1556f1500551d518f56e3aba0871371d"
|
resolved "https://registry.yarnpkg.com/simple-dom/-/simple-dom-1.4.0.tgz#78ad1f41b8b70d16f82b7e0d458441c9262565b7"
|
||||||
integrity sha1-BmPRDxVW8VAFUdUY9W46ughxNx0=
|
integrity sha512-TnBPkmOyjdaOqyBMb4ick+n8c0Xv9Iwg1PykFV7hz9Se3UCiacTbRb+25cPmvozFNJLBUNvUzX/KsPfXF14ivA==
|
||||||
|
dependencies:
|
||||||
|
"@simple-dom/document" "^1.4.0"
|
||||||
|
"@simple-dom/interface" "^1.4.0"
|
||||||
|
"@simple-dom/parser" "^1.4.0"
|
||||||
|
"@simple-dom/serializer" "^1.4.0"
|
||||||
|
"@simple-dom/void-map" "^1.4.0"
|
||||||
|
|
||||||
simple-get@^3.0.3, simple-get@^3.1.0:
|
simple-get@^3.0.3, simple-get@^3.1.0:
|
||||||
version "3.1.0"
|
version "3.1.0"
|
||||||
|
Loading…
Reference in New Issue
Block a user