diff --git a/core/server/data/importer/importers/data/posts.js b/core/server/data/importer/importers/data/posts.js index 61703dffe3..a53185f082 100644 --- a/core/server/data/importer/importers/data/posts.js +++ b/core/server/data/importer/importers/data/posts.js @@ -216,7 +216,7 @@ class PostsImporter extends BaseImporter { }); 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); }); diff --git a/core/server/data/migrations/versions/1.25/1-update-koenig-beta-html.js b/core/server/data/migrations/versions/1.25/1-update-koenig-beta-html.js index c4d23e534d..bc813322d0 100644 --- a/core/server/data/migrations/versions/1.25/1-update-koenig-beta-html.js +++ b/core/server/data/migrations/versions/1.25/1-update-koenig-beta-html.js @@ -1,7 +1,7 @@ const _ = require('lodash'); const Promise = require('bluebird'); const common = require('../../../../lib/common'); -const renderers = require('../../../../lib/mobiledoc/renderers'); +const mobiledocLib = require('../../../../lib/mobiledoc'); const models = require('../../../../models'); 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'; @@ -54,7 +54,7 @@ module.exports.up = function regenerateKoenigBetaHTML(options) { // re-render the html to remove .kg-post wrapper and adjust image classes let version = 2; - let html = renderers.mobiledocHtmlRenderer.render(mobiledoc, version); + let html = mobiledocLib.mobiledocHtmlRenderer.render(mobiledoc, version); return models.Post.edit({ html, diff --git a/core/server/data/migrations/versions/2.0/2-update-posts.js b/core/server/data/migrations/versions/2.0/2-update-posts.js index 067e870e40..85ff782ea5 100644 --- a/core/server/data/migrations/versions/2.0/2-update-posts.js +++ b/core/server/data/migrations/versions/2.0/2-update-posts.js @@ -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 html field is null, we auto generate the html in the model layer if (mobiledoc && post.html && post.html.match(/^
/)) { - html = mobiledocLib.renderers.mobiledocHtmlRenderer.render(mobiledoc); + html = mobiledocLib.mobiledocHtmlRenderer.render(mobiledoc); } return localOptions .transacting('posts') @@ -101,7 +101,7 @@ module.exports.down = (options) => { // CASE: revert: all new editor posts to the old editor format if (mobiledoc && post.html) { - html = mobiledocLib.renderers.mobiledocHtmlRenderer.render(mobiledoc, version); + html = mobiledocLib.mobiledocHtmlRenderer.render(mobiledoc, version); } return localOptions diff --git a/core/server/data/migrations/versions/3.0/11-update-posts-html.js b/core/server/data/migrations/versions/3.0/11-update-posts-html.js index 99b6527e0b..8f77a05d5b 100644 --- a/core/server/data/migrations/versions/3.0/11-update-posts-html.js +++ b/core/server/data/migrations/versions/3.0/11-update-posts-html.js @@ -2,7 +2,7 @@ const _ = require('lodash'); const Promise = require('bluebird'); const htmlToText = require('html-to-text'); const common = require('../../../../lib/common'); -const renderers = require('../../../../lib/mobiledoc/renderers'); +const mobiledocLib = require('../../../../lib/mobiledoc'); module.exports.config = { transaction: true @@ -36,7 +36,7 @@ module.exports.up = (options) => { return Promise.resolve(); } - const html = renderers.mobiledocHtmlRenderer.render(mobiledoc); + const html = mobiledocLib.mobiledocHtmlRenderer.render(mobiledoc); const updatedAttrs = { html: html diff --git a/core/server/lib/mobiledoc/index.js b/core/server/lib/mobiledoc/index.js index 631464718c..ebd426d9a6 100644 --- a/core/server/lib/mobiledoc/index.js +++ b/core/server/lib/mobiledoc/index.js @@ -1,7 +1,7 @@ const common = require('../common'); const config = require('../../config'); -let cardFactory, cards; +let cardFactory, cards, mobiledocHtmlRenderer; module.exports = { get blankDocument() { @@ -19,21 +19,19 @@ module.exports = { }, get cards() { - if (cards) { - return cards; + if (!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; }, @@ -41,8 +39,22 @@ module.exports = { return require('@tryghost/kg-default-atoms'); }, - get renderers() { - return require('./renderers'); + get mobiledocHtmlRenderer() { + 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() { diff --git a/core/server/lib/mobiledoc/renderers/index.js b/core/server/lib/mobiledoc/renderers/index.js deleted file mode 100644 index 1d3fcbe4bc..0000000000 --- a/core/server/lib/mobiledoc/renderers/index.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - get mobiledocHtmlRenderer() { - return require('./mobiledoc-html-renderer'); - } -}; diff --git a/core/server/lib/mobiledoc/renderers/mobiledoc-html-renderer.js b/core/server/lib/mobiledoc/renderers/mobiledoc-html-renderer.js deleted file mode 100644 index 6e3b4895d3..0000000000 --- a/core/server/lib/mobiledoc/renderers/mobiledoc-html-renderer.js +++ /dev/null @@ -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); - } -}; diff --git a/core/server/models/post.js b/core/server/models/post.js index 67dee5150e..a83eefe9f0 100644 --- a/core/server/models/post.js +++ b/core/server/models/post.js @@ -405,7 +405,7 @@ Post = ghostBookshelf.Model.extend({ // CASE: html is null, but mobiledoc exists (only important for migrations & importing) if (this.hasChanged('mobiledoc') || (!this.get('html') && (options.migrating || options.importing))) { 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) { throw new common.errors.ValidationError({ message: 'Invalid mobiledoc structure.', diff --git a/package.json b/package.json index 7d47084b8b..04dee7e95f 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "@tryghost/kg-default-atoms": "1.0.0", "@tryghost/kg-default-cards": "1.0.1", "@tryghost/kg-markdown-html-renderer": "1.0.2", + "@tryghost/kg-mobiledoc-html-renderer": "^1.0.0", "@tryghost/members-api": "0.18.0", "@tryghost/members-ssr": "0.7.4", "@tryghost/mw-session-from-token": "0.1.0", @@ -109,7 +110,6 @@ "metascraper-publisher": "5.11.8", "metascraper-title": "5.11.8", "metascraper-url": "5.11.8", - "mobiledoc-dom-renderer": "0.7.0", "moment": "2.24.0", "moment-timezone": "0.5.23", "multer": "1.4.2", @@ -124,7 +124,6 @@ "rss": "1.2.2", "sanitize-html": "1.22.1", "semver": "7.2.1", - "simple-dom": "0.3.2", "uuid": "7.0.3", "validator": "6.3.0", "xml": "1.0.1" diff --git a/test/unit/lib/mobiledoc/renderers/mobiledoc-html-renderer_spec.js b/test/unit/lib/mobiledoc/renderers/mobiledoc-html-renderer_spec.js deleted file mode 100644 index b4f7547b9d..0000000000 --- a/test/unit/lib/mobiledoc/renderers/mobiledoc-html-renderer_spec.js +++ /dev/null @@ -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: '

HTML card

\n

Some HTML

' - }], - ['embed', { - html: '

Embed card

' - }], - ['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('

One
Two

Markdown card

\n

Some markdown

\n

Three


Birdies

Four

HTML card

\n

Some HTML

Embed card

'); - }); - - 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('

Test

'); - }); - - 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('

This should be kept

'); - }); - - 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(/

Heading One<\/h1>/); - output.should.match(/

Heading Two<\/h2>/); - output.should.match(/

Heading Three<\/h3>/); - output.should.match(/

Heading Four<\/h4>/); - output.should.match(/

Heading Five<\/h5>/); - output.should.match(/
Heading Six<\/h6>/); - - // duplicate heading text - output.should.match(/

Heading One<\/h1>/); - output.should.match(/

Heading One<\/h3>/); - - // invalid ID/hash-url chars - output.should.match(/

< left < arrow <<\/h1>/); - output.should.match(/

> right > arrow ><\/h1>/); - output.should.match(/

"quote" "test"<\/h1>/); - output.should.match(/

\? question\?<\/h1>/); - output.should.match(/

& ampersand&<\/h1>/); - - // heading with link - output.should.match(/

trailing link<\/a><\/h1>/); - output.should.match(/

preceding<\/a> link<\/h1>/); - }); - }); -}); diff --git a/test/unit/lib/mobiledoc_spec.js b/test/unit/lib/mobiledoc_spec.js new file mode 100644 index 0000000000..14f82b73cd --- /dev/null +++ b/test/unit/lib/mobiledoc_spec.js @@ -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: '

HTML card

\n

Some HTML

' + }], + ['embed', { + html: '

Embed card

' + }], + ['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('

One
Two

Markdown card

\n

Some markdown

\n

Three


Birdies

Four

HTML card

\n

Some HTML

Embed card

'); + }); + }); +}); diff --git a/yarn.lock b/yarn.lock index 8f7b4f4262..ce75807c89 100644 --- a/yarn.lock +++ b/yarn.lock @@ -224,6 +224,37 @@ "@sentry/types" "5.15.4" 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": version "0.14.0" 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-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": version "0.9.2" 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" 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" resolved "https://registry.yarnpkg.com/mobiledoc-dom-renderer/-/mobiledoc-dom-renderer-0.7.0.tgz#53ab5f14dd612b16f03513390e5cbcc2b89f6979" 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" integrity sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY= -simple-dom@0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/simple-dom/-/simple-dom-0.3.2.tgz#0663d10f1556f1500551d518f56e3aba0871371d" - integrity sha1-BmPRDxVW8VAFUdUY9W46ughxNx0= +simple-dom@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/simple-dom/-/simple-dom-1.4.0.tgz#78ad1f41b8b70d16f82b7e0d458441c9262565b7" + 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: version "3.1.0"