From e14bf5b25038572a0fba10b630533c5e2a14cd82 Mon Sep 17 00:00:00 2001 From: Kirill Rogovoy Date: Tue, 2 Apr 2019 20:22:04 +0300 Subject: [PATCH] =?UTF-8?q?Add=20Markdown=20support=20=E2=80=94=20v2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/core/integration-tests/package.json | 2 + .../markdown-config/index.fixture.html | 2 + .../test/integration/markdown-config/index.md | 3 + .../markdown-config/local-config.fixture.html | 2 + .../markdown-config/local-config.md | 7 + .../markdown-config/marked.config.js | 3 + .../markdown-templating/index.fixture.html | 4 + .../integration/markdown-templating/index.md | 7 + .../markdown-templating/template.html | 3 + .../markdown/frontmatter.fixture.html | 1 + .../test/integration/markdown/frontmatter.md | 4 + .../integration/markdown/index.fixture.html | 3 + .../core/integration-tests/test/markdown.js | 157 +++++++++++++++++- .../src/assets/MarkdownAsset.js | 79 ++++++++- yarn.lock | 7 + 15 files changed, 274 insertions(+), 10 deletions(-) create mode 100644 packages/core/integration-tests/test/integration/markdown-config/index.fixture.html create mode 100644 packages/core/integration-tests/test/integration/markdown-config/index.md create mode 100644 packages/core/integration-tests/test/integration/markdown-config/local-config.fixture.html create mode 100644 packages/core/integration-tests/test/integration/markdown-config/local-config.md create mode 100644 packages/core/integration-tests/test/integration/markdown-config/marked.config.js create mode 100644 packages/core/integration-tests/test/integration/markdown-templating/index.fixture.html create mode 100644 packages/core/integration-tests/test/integration/markdown-templating/index.md create mode 100644 packages/core/integration-tests/test/integration/markdown-templating/template.html create mode 100644 packages/core/integration-tests/test/integration/markdown/frontmatter.fixture.html create mode 100644 packages/core/integration-tests/test/integration/markdown/frontmatter.md create mode 100644 packages/core/integration-tests/test/integration/markdown/index.fixture.html diff --git a/packages/core/integration-tests/package.json b/packages/core/integration-tests/package.json index 2f88079ef1c..2a949cd3cd6 100644 --- a/packages/core/integration-tests/package.json +++ b/packages/core/integration-tests/package.json @@ -18,6 +18,7 @@ "@parcel/test-utils": "^1.12.0", "codecov": "^3.0.0", "command-exists": "^1.2.6", + "front-matter": "^3.0.1", "graphql-tag": "^2.6.0", "json5": "^1.0.1", "kotlin": "^1.3.11", @@ -25,6 +26,7 @@ "mocha": "^5.1.1", "mocha-junit-reporter": "^1.18.0", "mocha-multi-reporters": "^1.1.7", + "mustache": "^3.0.1", "ncp": "^2.0.0", "nyc": "^11.1.0", "parcel-bundler": "^1.12.3", diff --git a/packages/core/integration-tests/test/integration/markdown-config/index.fixture.html b/packages/core/integration-tests/test/integration/markdown-config/index.fixture.html new file mode 100644 index 00000000000..91bc1ccfb82 --- /dev/null +++ b/packages/core/integration-tests/test/integration/markdown-config/index.fixture.html @@ -0,0 +1,2 @@ +

heading1

+

content content content

diff --git a/packages/core/integration-tests/test/integration/markdown-config/index.md b/packages/core/integration-tests/test/integration/markdown-config/index.md new file mode 100644 index 00000000000..c641f4265c9 --- /dev/null +++ b/packages/core/integration-tests/test/integration/markdown-config/index.md @@ -0,0 +1,3 @@ +# heading1 + +content content content diff --git a/packages/core/integration-tests/test/integration/markdown-config/local-config.fixture.html b/packages/core/integration-tests/test/integration/markdown-config/local-config.fixture.html new file mode 100644 index 00000000000..d431e05efb8 --- /dev/null +++ b/packages/core/integration-tests/test/integration/markdown-config/local-config.fixture.html @@ -0,0 +1,2 @@ +

heading1

+

content content content

diff --git a/packages/core/integration-tests/test/integration/markdown-config/local-config.md b/packages/core/integration-tests/test/integration/markdown-config/local-config.md new file mode 100644 index 00000000000..ec89adfcb2f --- /dev/null +++ b/packages/core/integration-tests/test/integration/markdown-config/local-config.md @@ -0,0 +1,7 @@ +--- +markedConfig: + headerPrefix: myPrefixFromFrontMatter_ +--- +# heading1 + +content content content diff --git a/packages/core/integration-tests/test/integration/markdown-config/marked.config.js b/packages/core/integration-tests/test/integration/markdown-config/marked.config.js new file mode 100644 index 00000000000..85c5476e4cf --- /dev/null +++ b/packages/core/integration-tests/test/integration/markdown-config/marked.config.js @@ -0,0 +1,3 @@ +module.exports = { + headerPrefix: 'myPrefix_' +} diff --git a/packages/core/integration-tests/test/integration/markdown-templating/index.fixture.html b/packages/core/integration-tests/test/integration/markdown-templating/index.fixture.html new file mode 100644 index 00000000000..1d6edb879ad --- /dev/null +++ b/packages/core/integration-tests/test/integration/markdown-templating/index.fixture.html @@ -0,0 +1,4 @@ +
+

heading1

+

content content content

+
diff --git a/packages/core/integration-tests/test/integration/markdown-templating/index.md b/packages/core/integration-tests/test/integration/markdown-templating/index.md new file mode 100644 index 00000000000..f71daf3e72a --- /dev/null +++ b/packages/core/integration-tests/test/integration/markdown-templating/index.md @@ -0,0 +1,7 @@ +--- +mustacheTemplate: ./template.html +attr1: val1 +--- +# heading1 + +content content content diff --git a/packages/core/integration-tests/test/integration/markdown-templating/template.html b/packages/core/integration-tests/test/integration/markdown-templating/template.html new file mode 100644 index 00000000000..9145b6438cb --- /dev/null +++ b/packages/core/integration-tests/test/integration/markdown-templating/template.html @@ -0,0 +1,3 @@ +
+{{{ contents }}} +
diff --git a/packages/core/integration-tests/test/integration/markdown/frontmatter.fixture.html b/packages/core/integration-tests/test/integration/markdown/frontmatter.fixture.html new file mode 100644 index 00000000000..42c8c784146 --- /dev/null +++ b/packages/core/integration-tests/test/integration/markdown/frontmatter.fixture.html @@ -0,0 +1 @@ +

content content content

diff --git a/packages/core/integration-tests/test/integration/markdown/frontmatter.md b/packages/core/integration-tests/test/integration/markdown/frontmatter.md new file mode 100644 index 00000000000..3c689b1781d --- /dev/null +++ b/packages/core/integration-tests/test/integration/markdown/frontmatter.md @@ -0,0 +1,4 @@ +--- +key1: val1 +--- +content content content diff --git a/packages/core/integration-tests/test/integration/markdown/index.fixture.html b/packages/core/integration-tests/test/integration/markdown/index.fixture.html new file mode 100644 index 00000000000..2eb7e4b2594 --- /dev/null +++ b/packages/core/integration-tests/test/integration/markdown/index.fixture.html @@ -0,0 +1,3 @@ +

heading1

+

content content content

+

image

diff --git a/packages/core/integration-tests/test/markdown.js b/packages/core/integration-tests/test/markdown.js index 307b35d4b88..dd7b76d5e3d 100644 --- a/packages/core/integration-tests/test/markdown.js +++ b/packages/core/integration-tests/test/markdown.js @@ -17,17 +17,158 @@ describe('markdown', function() { type: 'png', assets: ['100x100.png'], childBundles: [] + }, + { + type: 'js', + assets: ['index.md'], + childBundles: [] + } + ] + }); + + let fixture = (await fs.readFile( + path.join(__dirname, '/integration/markdown/index.fixture.html') + )) + .toString() + .trim(); + let html = (await fs.readFile(path.join(__dirname, '/dist/index.html'))) + .toString() + .trim(); + let js = (await fs.readFile(path.join(__dirname, '/dist/index.js'))) + .toString() + .trim(); + + assert.equal(html, fixture); + assert(js.includes(JSON.stringify(fixture))); + }); + + it('should support front matter', async function() { + let b = await bundle( + path.join(__dirname, '/integration/markdown/frontmatter.md') + ); + + await assertBundleTree(b, { + name: 'frontmatter.html', + assets: ['frontmatter.md'], + childBundles: [ + { + type: 'js', + assets: ['frontmatter.md'], + childBundles: [] + } + ] + }); + + let fixture = (await fs.readFile( + path.join(__dirname, '/integration/markdown/frontmatter.fixture.html') + )) + .toString() + .trim(); + let html = (await fs.readFile( + path.join(__dirname, '/dist/frontmatter.html') + )) + .toString() + .trim(); + let js = (await fs.readFile(path.join(__dirname, '/dist/frontmatter.js'))) + .toString() + .trim(); + + assert.equal(html, fixture); + assert(js.includes(JSON.stringify({key1: 'val1'}))); + }); + + it('should support marked config', async function() { + let b = await bundle( + path.join(__dirname, '/integration/markdown-config/index.md') + ); + + await assertBundleTree(b, { + name: 'index.html', + assets: ['index.md'], + childBundles: [ + { + type: 'js', + assets: ['index.md'], + childBundles: [] } ] }); - let files = await fs.readdir(path.join(__dirname, '/dist')); - let html = await fs.readFile(path.join(__dirname, '/dist/index.html')); - for (let file of files) { - let ext = file.match(/\.([0-9a-z]+)(?:[?#]|$)/i)[0]; - if (file !== 'index.html' && ext !== '.map') { - assert(html.includes(file)); - } - } + let fixture = (await fs.readFile( + path.join(__dirname, '/integration/markdown-config/index.fixture.html') + )) + .toString() + .trim(); + let html = (await fs.readFile(path.join(__dirname, '/dist/index.html'))) + .toString() + .trim(); + + assert.equal(html, fixture); + }); + + it('should support marked config from frontmatter', async function() { + let b = await bundle( + path.join(__dirname, '/integration/markdown-config/local-config.md') + ); + + await assertBundleTree(b, { + name: 'local-config.html', + assets: ['local-config.md'], + childBundles: [ + { + type: 'js', + assets: ['local-config.md'], + childBundles: [] + } + ] + }); + + let fixture = (await fs.readFile( + path.join( + __dirname, + '/integration/markdown-config/local-config.fixture.html' + ) + )) + .toString() + .trim(); + let html = (await fs.readFile( + path.join(__dirname, '/dist/local-config.html') + )) + .toString() + .trim(); + + assert.equal(html, fixture); + }); + + it('should support mustache templating when the template exists', async function() { + let b = await bundle( + path.join(__dirname, '/integration/markdown-templating/index.md') + ); + + await assertBundleTree(b, { + name: 'index.html', + assets: ['index.md'], + childBundles: [ + { + type: 'js', + assets: ['index.md'], + childBundles: [] + } + ] + }); + + let fixture = (await fs.readFile( + path.join( + __dirname, + '/integration/markdown-templating/index.fixture.html' + ) + )) + .toString() + .trim(); + let html = (await fs.readFile(path.join(__dirname, '/dist/index.html'))) + .toString() + .trim(); + + assert.equal(html, fixture); }); }); diff --git a/packages/core/parcel-bundler/src/assets/MarkdownAsset.js b/packages/core/parcel-bundler/src/assets/MarkdownAsset.js index 076667c8405..4ff748bc8f6 100644 --- a/packages/core/parcel-bundler/src/assets/MarkdownAsset.js +++ b/packages/core/parcel-bundler/src/assets/MarkdownAsset.js @@ -1,5 +1,7 @@ const localRequire = require('../utils/localRequire'); const Asset = require('../Asset'); +const path = require('path'); +const fs = require('@parcel/fs'); class MarkdownAsset extends Asset { constructor(name, options) { @@ -8,8 +10,81 @@ class MarkdownAsset extends Asset { this.hmrPageReload = true; } async generate() { - let marked = await localRequire('marked', this.name); - return marked(this.contents); + const fm = await localRequire('front-matter', this.name); + const marked = await localRequire('marked', this.name); + + let markedOptions = {}; + + const markedConfig = await this.getConfig(['marked.config.js']); + if (markedConfig) { + markedOptions = { + markedOptions, + ...markedConfig + }; + } + + const doc = fm(this.contents); + + this.frontMatterAttributes = doc.attributes; + + const perFileConfig = doc.attributes['markedConfig']; + if (perFileConfig && typeof perFileConfig === 'object') { + markedOptions = { + markedOptions, + ...perFileConfig + }; + } + + // .trim() is important here – test asserts rely on trimmed strings for easy comparison + let contents = marked(doc.body, markedOptions).trim(); + + contents = await this.applyMustacheTemplate(contents, doc.attributes); + + return [ + { + type: 'html', + value: contents + } + ]; + } + + async postProcess(generated) { + const compiledMarkdown = generated.find(t => t.type === 'html').value; + + const jsModule = { + contents: compiledMarkdown, + attributes: this.frontMatterAttributes + }; + + generated.push({ + type: 'js', + value: `module.exports=${JSON.stringify(jsModule)}` + }); + return generated; + } + + async applyMustacheTemplate(contents, attributes) { + const templatePath = attributes['mustacheTemplate']; + if (!templatePath) { + return contents; + } + + if (typeof templatePath !== 'string') { + throw new Error(`${this.name}: mustacheTemplate should be a string`); + } + + const templatePathNormalized = path.join( + path.dirname(this.name), + templatePath + ); + + const template = (await fs.readFile(templatePathNormalized)).toString(); + + const mustache = await localRequire('mustache', this.name); + return mustache.render(template, { + contents, + ...attributes + }); } } module.exports = MarkdownAsset; diff --git a/yarn.lock b/yarn.lock index 4a0a44971f1..06aa42a7928 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4675,6 +4675,13 @@ from2@^2.1.0: inherits "^2.0.1" readable-stream "^2.0.0" +front-matter@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/front-matter/-/front-matter-3.0.1.tgz#9ed0a8e2771b1367c4d8809b2f91c528b9b04d0c" + integrity sha512-iCHZ7RZGE36uG58iIWp8zrhDi9BZjlDiRj7aRcGm45EIqrbK+u4KTAmRKLG3FOaVkFhZI5/29SUo7sMLzlQkcA== + dependencies: + js-yaml "^3.10.0" + fs-extra@^7.0.0: version "7.0.1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9"