From 854feec6af8d2561f701086ddc42e03cc7691003 Mon Sep 17 00:00:00 2001 From: Chris Manson Date: Mon, 22 Jul 2019 10:45:42 +0100 Subject: [PATCH 1/7] testing new travis structure --- .eslintrc.json | 8 +------- .travis.yml | 33 ++++++++++++++++++++------------- lib/construct-serializer.js | 2 +- lib/serialize-json-blobs.js | 6 +++--- 4 files changed, 25 insertions(+), 24 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 2b1ab37..5d79ec2 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -10,13 +10,7 @@ "no-underscore-dangle": [2, { "allow": ["__content"] }], - "comma-dangle": ["error", { - "arrays": "always-multiline", - "objects": "always-multiline", - "imports": "always", - "exports": "always", - "functions": "never" - }] + "linebreak-style": 0 }, "overrides": [{ "files": ["test/**/*.js"], diff --git a/.travis.yml b/.travis.yml index dfc74a3..2edaa8c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,15 +1,21 @@ language: node_js +node_js: + - "10" branches: only: - master + - refactor/smaller-broccoli-plugins + +jobs: + fail_fast: true -matrix: include: - - node_js: 8 - os: linux - - node_js: 10 - os: linux + - stage: "Tests" + name: "Tests" + script: + - npm run lint:js + - npm test before_script: - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter - chmod +x ./cc-test-reporter @@ -18,23 +24,24 @@ matrix: after_script: - npm run coverage - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT + - stage: "All OS and Node versions" + node_js: 8 + os: linux - node_js: 8 os: windows - script: npm test + - node_js: 10 + os: linux - node_js: 10 os: windows - script: npm test - + - node_js: 12 + os: linux + - node_js: 12 + os: windows env: global: - CC_TEST_REPORTER_ID=e86c6de11dd97ad1777b3212cddc0367b150816025083150839f687b7fbce7f9 -before_install: - - npm config --global set spin false - # if npm version is less than 5, upgrade to 6 - - if [[ $(npm -v | cut -d '.' -f 1) -lt 5 ]]; then npm i -g npm@6; fi - script: - npm run lint:js - npm test diff --git a/lib/construct-serializer.js b/lib/construct-serializer.js index c21b397..427e34d 100644 --- a/lib/construct-serializer.js +++ b/lib/construct-serializer.js @@ -20,7 +20,7 @@ module.exports = function constructSerializer(configuration, folder) { attributes: _.union( options.contentTypes, ['title'], - options.attributes + options.attributes, ), keyForAttribute: 'camelCase', }; diff --git a/lib/serialize-json-blobs.js b/lib/serialize-json-blobs.js index b67924a..0201adc 100644 --- a/lib/serialize-json-blobs.js +++ b/lib/serialize-json-blobs.js @@ -57,7 +57,7 @@ class SerializeJsonBlobs extends Plugin { const contentPages = preparePages( blobs, this.options.pageSize, - this.options.paginateSortFunction + this.options.paginateSortFunction, ); contentPages.forEach((pageData, index) => { const serializedPageData = this.contentSerializer.serialize(pageData); @@ -86,14 +86,14 @@ class SerializeJsonBlobs extends Plugin { } writeFileSync( join(this.outputPath, fileName), - JSON.stringify(serializedPageData) + JSON.stringify(serializedPageData), ); // also write the default collection name for the first page if (index === 0) { writeFileSync( join(this.outputPath, this.options.collationFileName), - JSON.stringify(serializedPageData) + JSON.stringify(serializedPageData), ); } }); From 1c7e6f9a237b545f61b175cde5ec339e44ada026 Mon Sep 17 00:00:00 2001 From: Chris Manson Date: Tue, 23 Jul 2019 09:44:48 +0100 Subject: [PATCH 2/7] remove intermediary step for json->json:api --- index.js | 6 +-- lib/construct-serializer.js | 36 ------------- lib/markdown-to-json.js | 51 ------------------ lib/markdown-to-jsonapi.js | 82 +++++++++++++++++++++++++++++ test/plugins/markdown-to-jsonapi.js | 68 ++++++++++++++++++++++++ 5 files changed, 152 insertions(+), 91 deletions(-) delete mode 100644 lib/construct-serializer.js delete mode 100644 lib/markdown-to-json.js create mode 100644 lib/markdown-to-jsonapi.js create mode 100644 test/plugins/markdown-to-jsonapi.js diff --git a/index.js b/index.js index 0610fe0..85c6ddd 100644 --- a/index.js +++ b/index.js @@ -2,8 +2,7 @@ const BroccoliMergeTrees = require('broccoli-merge-trees'); const BroccoliFunnel = require('broccoli-funnel'); const { mv } = require('broccoli-stew'); const TableOfContents = require('./lib/table-of-contents'); -const MarkdownToJsonApi = require('./lib/markdown-to-json'); -const CollateJsonApiBlobs = require('./lib/serialize-json-blobs'); +const MarkdownToJsonApi = require('./lib/markdown-to-jsonapi'); module.exports = function StaticSiteJson(folder, options = {}) { @@ -11,8 +10,7 @@ module.exports = function StaticSiteJson(folder, options = {}) { include: ['**/*.md', '**/*.markdown'], }); const pagesTree = new TableOfContents([folder], options); - const jsonTree = new MarkdownToJsonApi(cleanMarkdownFunnel, options); - const jsonApiTree = new CollateJsonApiBlobs(jsonTree, options); + const jsonApiTree = new MarkdownToJsonApi(cleanMarkdownFunnel, options); const compiledTrees = new BroccoliMergeTrees([jsonApiTree, pagesTree]); return mv(compiledTrees, (options.contentFolder || 'content')); }; diff --git a/lib/construct-serializer.js b/lib/construct-serializer.js deleted file mode 100644 index 427e34d..0000000 --- a/lib/construct-serializer.js +++ /dev/null @@ -1,36 +0,0 @@ -const { Serializer } = require('jsonapi-serializer'); -const _ = require('lodash'); - -module.exports = function constructSerializer(configuration, folder) { - const options = _.assign({}, { - contentFolder: 'content', - contentTypes: ['html', 'content'], - collationFileName: 'all.json', - pageSize: 10, - }, configuration); - - const supportedContentTypes = ['content', 'html', 'description']; - const unsupportedContentTypes = _.difference(options.contentTypes, supportedContentTypes); - - if (unsupportedContentTypes.length) { - throw new Error(`Unknown content type: ${unsupportedContentTypes[0]}`); - } - - const serializerOptions = { - attributes: _.union( - options.contentTypes, - ['title'], - options.attributes, - ), - keyForAttribute: 'camelCase', - }; - - if (options.references) { - options.references.forEach((reference) => { - serializerOptions[reference] = { ref: true }; - }); - - serializerOptions.attributes = _.union(serializerOptions.attributes, options.references); - } - return new Serializer(options.type || folder, serializerOptions); -}; diff --git a/lib/markdown-to-json.js b/lib/markdown-to-json.js deleted file mode 100644 index 1792860..0000000 --- a/lib/markdown-to-json.js +++ /dev/null @@ -1,51 +0,0 @@ -const PersistentFilter = require('broccoli-persistent-filter'); -const yamlFront = require('yaml-front-matter'); -const showdown = require('showdown'); -const _ = require('lodash'); -const h2p = require('html2plaintext'); - -let currentId = null; -class MarkDownToJsonApi extends PersistentFilter { - constructor(folder, options) { - super(folder, options); - this.extensions = ['md', 'markdown']; - this.targetExtension = 'json'; - this.options = options; - this.converter = new showdown.Converter(); - } - - processString(content, relativePath) { - const front = yamlFront.loadFront(content); - const markdown = front.__content.trim(); - const baseProperties = { - path: relativePath, - id: relativePath.replace(/.md$/, ''), - content: markdown, - html: this.converter.makeHtml(markdown), - }; - - const resultHash = { ...baseProperties, ...front }; - if (!resultHash.description && (this.options.contentTypes || []).includes('description')) { - const description = _.truncate(h2p(resultHash.html), { - length: 260, - separator: /,?\.* +/, - }); - resultHash.description = description; - } - - // dirty hack, funnel works serialy so should be safe - // https://github.com/broccolijs/broccoli-filter/blob/8ec98ed974119c7e7da19db76f37a78e0642b077/index.js#L63 - currentId = resultHash.id; - return JSON.stringify(resultHash); - } - - // eslint-disable-next-line class-methods-use-this - getDestFilePath(relativePath) { - if (relativePath.endsWith('.md') || relativePath.endsWith('.markdown')) { - return `${currentId}.json`; - } - return null; - } -} - -module.exports = MarkDownToJsonApi; diff --git a/lib/markdown-to-jsonapi.js b/lib/markdown-to-jsonapi.js new file mode 100644 index 0000000..a0193c1 --- /dev/null +++ b/lib/markdown-to-jsonapi.js @@ -0,0 +1,82 @@ +const PersistentFilter = require('broccoli-persistent-filter'); +const yamlFront = require('yaml-front-matter'); +const showdown = require('showdown'); +const _ = require('lodash'); +const h2p = require('html2plaintext'); +const { Serializer } = require('jsonapi-serializer'); + +const supportedContentTypes = ['content', 'html', 'description']; + +class MarkDownToJsonApi extends PersistentFilter { + constructor(folder, options) { + super(folder, options); + this.extensions = ['md', 'markdown']; + this.targetExtension = 'json'; + this.options = { + contentTypes: ['html', 'content'], + type: 'content', + attributes: [], + references: [], + ...options, + }; + + const unsupportedContentTypes = _.difference(this.options.contentTypes, supportedContentTypes); + + if (unsupportedContentTypes.length) { + throw new Error(`Unknown content type: ${unsupportedContentTypes[0]}`); + } + + this.converter = new showdown.Converter(); + + // build serialiser for jsonapi + const serializerOptions = { + attributes: _.union( + this.options.contentTypes, + this.options.attributes, + this.options.references, + ), + keyForAttribute: 'camelCase', + }; + + this.options.references.forEach((reference) => { + serializerOptions[reference] = { ref: true }; + }); + + this.serializer = new Serializer(this.options.type, serializerOptions); + } + + processString(content, relativePath) { + const front = yamlFront.loadFront(content); + const markdown = front.__content.trim(); + + const baseProperties = { + path: relativePath, + id: relativePath.replace(/\.(md|markdown)$/, ''), + content: markdown, + html: this.converter.makeHtml(markdown), + }; + + const resultHash = { ...baseProperties, ...front }; + + if (!resultHash.description && _.includes(this.options.contentTypes, 'description')) { + const description = _.truncate(h2p(resultHash.html), { + length: 260, + separator: /,?\.* +/, + }); + + resultHash.description = description; + } + + return JSON.stringify(this.serializer.serialize(resultHash)); + } + + // eslint-disable-next-line class-methods-use-this + getDestFilePath(relativePath) { + if (relativePath.endsWith('.md') || relativePath.endsWith('.markdown')) { + return `${relativePath.replace(/.(md|markdown)$/, '')}.json`; + } + return null; + } +} + +module.exports = MarkDownToJsonApi; diff --git a/test/plugins/markdown-to-jsonapi.js b/test/plugins/markdown-to-jsonapi.js new file mode 100644 index 0000000..0f4f910 --- /dev/null +++ b/test/plugins/markdown-to-jsonapi.js @@ -0,0 +1,68 @@ +/* eslint-disable no-useless-escape */ + +const { createBuilder, createTempDir } = require('broccoli-test-helper'); +const { expect } = require('chai'); +const MarkDownToJsonApi = require('../../lib/markdown-to-jsonapi'); + +describe('markdown-to-jsonapi', function () { + it('should build', async function () { + const input = await createTempDir(); + try { + const subject = new MarkDownToJsonApi(input.path(), { + type: 'things', + }); + const output = createBuilder(subject); + + try { + // INITIAL + input.write({ + 'something.md': '#hello', + lib: { + 'b.markdown': '##goodbye', + }, + }); + + await output.build(); + + expect(output.read()).to.deep.equal({ + 'something.json': '{"data":{"type":"things","id":"something","attributes":{"html":"

hello

","content":"#hello"}}}', + lib: { + 'b.json': '{"data":{"type":"things","id":"lib/b","attributes":{"html":"

goodbye

","content":"##goodbye"}}}', + }, + }); + + expect(output.changes()).to.deep.equal({ + 'something.json': 'create', + 'lib/': 'mkdir', + 'lib/b.json': 'create', + }); + + // UPDATE + input.write({ + 'something.md': '**AA!**', // change + lib: null, // rmdir + }); + await output.build(); + + expect(output.read()).to.deep.equal({ + 'something.json': '{"data":{"type":"things","id":"something","attributes":{"html":"

AA!

","content":"**AA!**"}}}', + }); + + expect(output.changes()).to.deep.equal({ + 'lib/b.json': 'unlink', + 'lib/': 'rmdir', + 'something.json': 'change', + }); + + // NOOP + await output.build(); + + expect(output.changes()).to.deep.equal({}); + } finally { + await output.dispose(); + } + } finally { + await input.dispose(); + } + }); +}); From aa81fbdc130c194a8fa99c7933e6f2d1c39097bc Mon Sep 17 00:00:00 2001 From: Chris Manson Date: Tue, 23 Jul 2019 09:45:58 +0100 Subject: [PATCH 3/7] remove implicit title attribute --- test/attributes.js | 14 +++++++++++++- test/collections.js | 2 ++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/test/attributes.js b/test/attributes.js index 4915590..b86436f 100644 --- a/test/attributes.js +++ b/test/attributes.js @@ -38,12 +38,24 @@ describe('JSONAPI attributes', () => { } }); - it('should include title if the frontmatter is included but no options passed', async () => { + it('should not include title if the frontmatter is included but no options passed', async () => { const result = await buildSingleFile(`--- title: a lovely title --- # Hello world`); + expect(result.attributes).not.have.property('title', 'a lovely title'); + expect(result.attributes).have.property('content', '# Hello world'); + }); + + it('should include title if the frontmatter is included and title is in attributes', async () => { + const result = await buildSingleFile(`--- +title: a lovely title +--- +# Hello world`, { + attributes: ['title'], + }); + expect(result.attributes).have.property('title', 'a lovely title'); expect(result.attributes).have.property('content', '# Hello world'); }); diff --git a/test/collections.js b/test/collections.js index 5a3a7e4..e5ce940 100644 --- a/test/collections.js +++ b/test/collections.js @@ -111,6 +111,7 @@ id: 3 it('should allow you to define a collection and for the specified content folder to be exported as an single JSONAPI array response', async () => { const subject = new StaticSiteJson(input.path(), { + attributes: ['title'], type: 'page', collate: true, }); @@ -167,6 +168,7 @@ title: more words it('should still generate the all.json even if there is only one input file', async () => { const subject = new StaticSiteJson(input.path(), { + attributes: ['title'], type: 'page', collate: true, }); From 0dcbff28dba55ac1966750e4289839a24de9e4ed Mon Sep 17 00:00:00 2001 From: Chris Manson Date: Tue, 23 Jul 2019 09:46:56 +0100 Subject: [PATCH 4/7] remove support for overriding id --- test/collections.js | 33 --------------------------------- test/core.js | 41 ----------------------------------------- 2 files changed, 74 deletions(-) diff --git a/test/collections.js b/test/collections.js index e5ce940..e9da952 100644 --- a/test/collections.js +++ b/test/collections.js @@ -76,39 +76,6 @@ title: more words expect(files['all.json'].find(obj => obj.id === 'double-word')).to.be.ok; }); - it('should name the files the same as the id if the id is specified', async () => { - const files = await buildFiles({ - 'index.md': `--- -title: a lovely title -id: 1 ---- -# Hello world`, - 'project.md': `--- -title: a less lovely title -id: 2 ---- -# Goodbye world`, - 'double-word.md': `--- -title: more words -id: 3 ---- -# When one word is not enough`, - }, { - collate: true, - }); - - expect(files).to.have.property('1.json'); - - expect(files['1.json']).to.have.property('id', '1'); - expect(files['2.json']).to.have.property('id', '2'); - expect(files['3.json']).to.have.property('id', '3'); - - // each of the above files should exist in the all.json - expect(files['all.json'].find(obj => obj.id === '1')).to.be.ok; - expect(files['all.json'].find(obj => obj.id === '2')).to.be.ok; - expect(files['all.json'].find(obj => obj.id === '3')).to.be.ok; - }); - it('should allow you to define a collection and for the specified content folder to be exported as an single JSONAPI array response', async () => { const subject = new StaticSiteJson(input.path(), { attributes: ['title'], diff --git a/test/core.js b/test/core.js index 64a6349..69eb121 100644 --- a/test/core.js +++ b/test/core.js @@ -115,47 +115,6 @@ describe('core functionality', function () { await input.dispose(); }); - it('should allow you to override the id a the JSON:API document with front-matter', async () => { - const input = await createTempDir(); - - const subject = new StaticSiteJson(input.path()); - const output = createBuilder(subject); - - input.write({ - 'index.md': '# Hello world', - 'other-id.md': `--- -id: face ---- -# Hello face world`, - }); - - await output.build(); - - const folderOutput = output.read(); - - expect(folderOutput.content).to.have.property('index.json'); - expect(folderOutput.content).to.have.property('face.json'); - - expect(JSON.parse(folderOutput.content['index.json']).data).to.deep.include({ - id: 'index', - attributes: { - content: '# Hello world', - html: '

Hello world

', - }, - }); - - expect(JSON.parse(folderOutput.content['face.json']).data).to.deep.include({ - id: 'face', - attributes: { - content: '# Hello face world', - html: '

Hello face world

', - }, - }); - - await output.dispose(); - await input.dispose(); - }); - it('should allow you to override the JSON:API type', async () => { const input = await createTempDir(); From c5d9198777cef2361ee0b5ae8996bb4e2cff1ea4 Mon Sep 17 00:00:00 2001 From: Chris Manson Date: Tue, 23 Jul 2019 09:47:12 +0100 Subject: [PATCH 5/7] add failing test for table-of-contents plugin --- test/plugins/table-of-contents.js | 64 +++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 test/plugins/table-of-contents.js diff --git a/test/plugins/table-of-contents.js b/test/plugins/table-of-contents.js new file mode 100644 index 0000000..e05d477 --- /dev/null +++ b/test/plugins/table-of-contents.js @@ -0,0 +1,64 @@ +/* eslint-disable no-useless-escape */ + +const { createBuilder, createTempDir } = require('broccoli-test-helper'); +const { expect } = require('chai'); +const TableOfContents = require('../../lib/table-of-contents'); + +describe('table-of-contents', function () { + it('should build', async function () { + const input = await createTempDir(); + try { + // TODO why does this need to be an array of folders + const subject = new TableOfContents([input.path()]); + const output = createBuilder(subject); + + try { + // INITIAL + input.write({ + 'pages.yml': `- title: "Getting Started" + url: 'getting-started' + pages: + - title: "How To Use The Guides" + url: "intro"`, + }); + + await output.build(); + + expect(output.read()).to.deep.equal({ + 'pages.json': '{"data":[{"type":"pages","id":"getting-started","attributes":{"title":"Getting Started","pages":[{"title":"How To Use The Guides","url":"getting-started/intro"}]}}]}', + }); + + expect(output.changes()).to.deep.equal({ + 'pages.json': 'create', + }); + + // UPDATE + input.write({ + 'pages.yml': `- title: "Tutorial" + url: 'tutorial' + pages: + - title: "Creating Your App" + url: "ember-cli"`, // change + }); + await output.build(); + + expect(output.read()).to.deep.equal({ + 'pages.json': '{"data":[{"type":"pages","id":"tutorial","attributes":{"title":"Tutorial","pages":[{"title":"Creating Your App","url":"tutorial/ember-cli"}]}}]}', + }); + + expect(output.changes()).to.deep.equal({ + 'pages.json': 'change', + }); + + // NOOP + await output.build(); + + expect(output.changes()).to.deep.equal({}); + } finally { + await output.dispose(); + } + } finally { + await input.dispose(); + } + }); +}); From 2981e5a939a705b29cba090d9c0c34cea367c849 Mon Sep 17 00:00:00 2001 From: Chris Manson Date: Tue, 7 Jan 2020 13:16:37 +0000 Subject: [PATCH 6/7] fix TOC implementation --- index.js | 5 +++- lib/table-of-contents.js | 49 ++++++++++++++++--------------- test/plugins/table-of-contents.js | 3 +- 3 files changed, 30 insertions(+), 27 deletions(-) diff --git a/index.js b/index.js index 85c6ddd..7d218a6 100644 --- a/index.js +++ b/index.js @@ -9,7 +9,10 @@ module.exports = function StaticSiteJson(folder, options = {}) { const cleanMarkdownFunnel = new BroccoliFunnel(folder, { include: ['**/*.md', '**/*.markdown'], }); - const pagesTree = new TableOfContents([folder], options); + const tocFunnel = new BroccoliFunnel(folder, { + include: ['**/pages.yml', '**/pages.json'], + }); + const pagesTree = new TableOfContents(tocFunnel, options); const jsonApiTree = new MarkdownToJsonApi(cleanMarkdownFunnel, options); const compiledTrees = new BroccoliMergeTrees([jsonApiTree, pagesTree]); return mv(compiledTrees, (options.contentFolder || 'content')); diff --git a/lib/table-of-contents.js b/lib/table-of-contents.js index d4da7bd..6ef1949 100644 --- a/lib/table-of-contents.js +++ b/lib/table-of-contents.js @@ -1,12 +1,6 @@ +const PersistentFilter = require('broccoli-persistent-filter'); const { Serializer } = require('jsonapi-serializer'); const yaml = require('js-yaml'); -const { join } = require('path'); -const Plugin = require('broccoli-plugin'); -const { - existsSync, - readFileSync, - writeFileSync, -} = require('fs'); const TableOfContentsSerializer = new Serializer('page', { id: 'url', @@ -31,23 +25,30 @@ function subpageUrls(parentUrl, currentPage, childPages) { } } -class TableOfContentsExtractor extends Plugin { - build() { - this.inputPaths.forEach((folder) => { - let pages; - if (existsSync(join(folder, 'pages.yml'))) { - pages = yaml.safeLoad(readFileSync(join(folder, 'pages.yml'), 'utf8')); - } else if (existsSync(join(folder, 'pages.json'))) { - // eslint-disable-next-line - pages = require(join(folder, 'pages.json')); - } - if (pages) { - // add the parent id to each subpage - subpageUrls(null, null, pages); - - writeFileSync(join(this.outputPath, 'pages.json'), JSON.stringify(TableOfContentsSerializer.serialize(pages))); - } - }); + +class TableOfContentsExtractor extends PersistentFilter { + constructor(folder, options) { + super(folder, options); + this.extensions = ['yml', 'json']; + this.targetExtension = 'json'; + } + + // eslint-disable-next-line class-methods-use-this,consistent-return + processString(content, relativePath) { + let pages; + + if (relativePath.endsWith('pages.yml')) { + pages = yaml.safeLoad(content); + } else if (relativePath.endsWith('pages.json')) { + pages = JSON.parse(content); + } + + if (pages) { + // add the parent id to each subpage + subpageUrls(null, null, pages); + + return JSON.stringify(TableOfContentsSerializer.serialize(pages)); + } } } diff --git a/test/plugins/table-of-contents.js b/test/plugins/table-of-contents.js index e05d477..6011d5e 100644 --- a/test/plugins/table-of-contents.js +++ b/test/plugins/table-of-contents.js @@ -8,8 +8,7 @@ describe('table-of-contents', function () { it('should build', async function () { const input = await createTempDir(); try { - // TODO why does this need to be an array of folders - const subject = new TableOfContents([input.path()]); + const subject = new TableOfContents(input.path()); const output = createBuilder(subject); try { From 239a50e0cb23129effed36dabfce18af2e2325de Mon Sep 17 00:00:00 2001 From: Chris Manson Date: Wed, 8 Jan 2020 12:49:25 +0000 Subject: [PATCH 7/7] fixing collation and pagination --- index.js | 10 +++++++- ...-json-blobs.js => collate-and-paginate.js} | 25 +++++++++++-------- test/collections.js | 2 -- 3 files changed, 24 insertions(+), 13 deletions(-) rename lib/{serialize-json-blobs.js => collate-and-paginate.js} (85%) diff --git a/index.js b/index.js index 7d218a6..5b15fab 100644 --- a/index.js +++ b/index.js @@ -2,6 +2,7 @@ const BroccoliMergeTrees = require('broccoli-merge-trees'); const BroccoliFunnel = require('broccoli-funnel'); const { mv } = require('broccoli-stew'); const TableOfContents = require('./lib/table-of-contents'); +const CollateJsonApiBlobs = require('./lib/collate-and-paginate'); const MarkdownToJsonApi = require('./lib/markdown-to-jsonapi'); @@ -14,6 +15,13 @@ module.exports = function StaticSiteJson(folder, options = {}) { }); const pagesTree = new TableOfContents(tocFunnel, options); const jsonApiTree = new MarkdownToJsonApi(cleanMarkdownFunnel, options); - const compiledTrees = new BroccoliMergeTrees([jsonApiTree, pagesTree]); + + // the default content folder is "content" and this tree needs to know + // about contentFolder for pagination links + const collationTree = new CollateJsonApiBlobs(jsonApiTree, { + contentFolder: 'content', + ...options, + }); + const compiledTrees = new BroccoliMergeTrees([jsonApiTree, pagesTree, collationTree]); return mv(compiledTrees, (options.contentFolder || 'content')); }; diff --git a/lib/serialize-json-blobs.js b/lib/collate-and-paginate.js similarity index 85% rename from lib/serialize-json-blobs.js rename to lib/collate-and-paginate.js index 0201adc..e384826 100644 --- a/lib/serialize-json-blobs.js +++ b/lib/collate-and-paginate.js @@ -2,12 +2,10 @@ const Plugin = require('broccoli-plugin'); const { readFileSync, writeFileSync, - mkdirSync, } = require('fs'); const walkSync = require('walk-sync'); const { join } = require('path'); const _ = require('lodash'); -const constructSerializer = require('./construct-serializer'); function preparePages(blobs, pageSize, pageSortFunction) { return _.chain(blobs) @@ -25,12 +23,9 @@ class SerializeJsonBlobs extends Plugin { constructor(inputNode, options = {}) { super([inputNode], options); this.options = _.assign({}, { - contentFolder: 'content', - contentTypes: ['html', 'content'], collationFileName: 'all.json', pageSize: 10, }, options); - this.contentSerializer = constructSerializer(options); } build() { @@ -38,20 +33,26 @@ class SerializeJsonBlobs extends Plugin { this.inputPaths.forEach((inputPath) => { const paths = walkSync(inputPath); const folderConents = []; + paths.forEach((path) => { if (path.endsWith('/')) { - mkdirSync(join(this.outputPath, path)); return; } const fileContent = readFileSync(join(inputPath, path)).toString(); const deserializedFile = JSON.parse(fileContent); folderConents.push(deserializedFile); - const jsonApiBlob = this.contentSerializer.serialize(deserializedFile); - writeFileSync(join(this.outputPath, path), JSON.stringify(jsonApiBlob)); }); + blobs = [...blobs, ...folderConents]; }); + function groupData(pageData) { + return pageData.reduce((prev, current) => { + prev.data.push(current.data); + return prev; + }, { data: [] }); + } + if (this.options.collate) { if (this.options.paginate) { const contentPages = preparePages( @@ -60,7 +61,7 @@ class SerializeJsonBlobs extends Plugin { this.options.paginateSortFunction, ); contentPages.forEach((pageData, index) => { - const serializedPageData = this.contentSerializer.serialize(pageData); + const serializedPageData = groupData(pageData); let fileName; const fileNameMatch = this.options.collationFileName.match(/(.*)\.json$/); @@ -98,7 +99,11 @@ class SerializeJsonBlobs extends Plugin { } }); } else { - const collection = this.contentSerializer.serialize(blobs); + const collection = blobs.reduce((prev, blob) => { + prev.data.push(blob.data); + return prev; + }, { data: [] }); + const outputFile = join(this.outputPath, this.options.collationFileName); writeFileSync(outputFile, JSON.stringify(collection)); } diff --git a/test/collections.js b/test/collections.js index e9da952..5079338 100644 --- a/test/collections.js +++ b/test/collections.js @@ -35,8 +35,6 @@ describe('collections', () => { }); afterEach(async () => { - // eslint-disable-next-line no-console - console.warn.restore(); try { await input.dispose(); } finally {