From b477863a30f76e8baf0fdef3648ed9b7a47f189f Mon Sep 17 00:00:00 2001 From: endiliey Date: Fri, 7 Sep 2018 13:59:17 +0800 Subject: [PATCH] chore: prepare for docs sidebar --- lib/load/docs/order.js | 34 ++++++++++ lib/load/docs/sidebars.js | 34 ++++++++++ lib/load/utils.js | 35 ++++++++++- package.json | 1 + .../docs/__snapshots__/order.test.js.snap | 59 +++++++++++++++++ .../docs/__snapshots__/sidebars.test.js.snap | 45 +++++++++++++ test/load/docs/order.test.js | 35 +++++++++++ test/load/docs/sidebars.test.js | 46 ++++++++++++++ test/load/utils.test.js | 63 ++++++++++++++++++- 9 files changed, 350 insertions(+), 2 deletions(-) create mode 100644 lib/load/docs/order.js create mode 100644 lib/load/docs/sidebars.js create mode 100644 test/load/docs/__snapshots__/order.test.js.snap create mode 100644 test/load/docs/__snapshots__/sidebars.test.js.snap create mode 100644 test/load/docs/order.test.js create mode 100644 test/load/docs/sidebars.test.js diff --git a/lib/load/docs/order.js b/lib/load/docs/order.js new file mode 100644 index 0000000000..9d69798e08 --- /dev/null +++ b/lib/load/docs/order.js @@ -0,0 +1,34 @@ +// build the docs meta such as next, previous, category and sidebar +module.exports = function createOrder(allSidebars = {}) { + const order = {}; + if (!allSidebars) { + return order; + } + Object.keys(allSidebars).forEach(sidebar => { + const categories = allSidebars[sidebar]; + + let ids = []; + const categoryOrder = []; + Object.keys(categories).forEach(category => { + ids = ids.concat(categories[category]); + for (let i = 0; i < categories[category].length; i++) { + categoryOrder.push(category); + } + }); + + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + let previous; + let next; + if (i > 0) previous = ids[i - 1]; + if (i < ids.length - 1) next = ids[i + 1]; + order[id] = { + previous, + next, + sidebar, + category: categoryOrder[i] + }; + } + }); + return order; +}; diff --git a/lib/load/docs/sidebars.js b/lib/load/docs/sidebars.js new file mode 100644 index 0000000000..92b0e16553 --- /dev/null +++ b/lib/load/docs/sidebars.js @@ -0,0 +1,34 @@ +const fs = require('fs-extra'); +const path = require('path'); +const {idx} = require('../utils'); + +module.exports = function loadSidebars({siteDir, env}) { + let allSidebars = {}; + + // current sidebars + const sidebarsJSONFile = path.join(siteDir, 'sidebars.json'); + if (fs.existsSync(sidebarsJSONFile)) { + allSidebars = require(sidebarsJSONFile); + } + + // versioned sidebars + if (idx(env, ['versioning', 'enabled'])) { + const versions = idx(env, ['versioning', 'versions']); + versions && + versions.forEach(version => { + const versionedSidebarsJSONFile = path.join( + siteDir, + 'versioned_sidebars', + `version-${version}-sidebars.json` + ); + if (fs.existsSync(versionedSidebarsJSONFile)) { + const sidebar = require(versionedSidebarsJSONFile); + Object.assign(allSidebars, sidebar); + } else { + const missingFile = path.relative(siteDir, versionedSidebarsJSONFile); + throw new Error(`Failed to load ${missingFile}. It does not exist.`); + } + }); + } + return allSidebars; +}; diff --git a/lib/load/utils.js b/lib/load/utils.js index 05be648bb2..060bcea9ac 100644 --- a/lib/load/utils.js +++ b/lib/load/utils.js @@ -1,4 +1,6 @@ const path = require('path'); +const fm = require('front-matter'); +const escapeStringRegexp = require('escape-string-regexp'); const fs = require('fs-extra'); const genPath = path.resolve(__dirname, '../core/generated'); @@ -40,9 +42,40 @@ function fileToComponentName(file) { return ext ? ext.toUpperCase() + str : str; } +function idx(target, keyPaths) { + return ( + target && + (Array.isArray(keyPaths) + ? keyPaths.reduce((obj, key) => obj && obj[key], target) + : target[keyPaths]) + ); +} + +function getSubFolder(file, refDir) { + const separator = escapeStringRegexp(path.sep); + const baseDir = escapeStringRegexp(path.basename(refDir)); + const regexSubFolder = new RegExp( + `${baseDir}${separator}(.*?)${separator}.*` + ); + const match = regexSubFolder.exec(file); + return match && match[1]; +} + +function parse(fileString) { + if (!fm.test(fileString)) { + return {metadata: null, content: fileString}; + } + const {attributes: metadata, body: content} = fm(fileString); + + return {metadata, content}; +} + module.exports = { encodePath, generate, fileToPath, - fileToComponentName + fileToComponentName, + getSubFolder, + idx, + parse }; diff --git a/package.json b/package.json index 5502c30878..b4f92d377e 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,7 @@ "connect-history-api-fallback": "^1.5.0", "css-loader": "^1.0.0", "escape-html": "^1.0.3", + "escape-string-regexp": "^1.0.5", "front-matter": "^2.3.0", "fs-extra": "^7.0.0", "globby": "^8.0.1", diff --git a/test/load/docs/__snapshots__/order.test.js.snap b/test/load/docs/__snapshots__/order.test.js.snap new file mode 100644 index 0000000000..11fb9ace9d --- /dev/null +++ b/test/load/docs/__snapshots__/order.test.js.snap @@ -0,0 +1,59 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`createOrder should populate docs index from multiple sidebars 1`] = ` +Object { + "doc1": Object { + "category": "Category1", + "next": "doc2", + "previous": undefined, + "sidebar": "docs", + }, + "doc2": Object { + "category": "Category1", + "next": "doc3", + "previous": "doc1", + "sidebar": "docs", + }, + "doc3": Object { + "category": "Category2", + "next": "doc4", + "previous": "doc2", + "sidebar": "docs", + }, + "doc4": Object { + "category": "Category2", + "next": undefined, + "previous": "doc3", + "sidebar": "docs", + }, + "doc5": Object { + "category": "Category1", + "next": undefined, + "previous": undefined, + "sidebar": "otherDocs", + }, +} +`; + +exports[`createOrder should resolve docs from older versions 1`] = ` +Object { + "doc1": Object { + "category": "Category1", + "next": undefined, + "previous": undefined, + "sidebar": "docs", + }, + "version-1.2.3-doc1": Object { + "category": "Category2", + "next": undefined, + "previous": "version-1.2.3-doc2", + "sidebar": "version-1.2.3-docs", + }, + "version-1.2.3-doc2": Object { + "category": "Category1", + "next": "version-1.2.3-doc1", + "previous": undefined, + "sidebar": "version-1.2.3-docs", + }, +} +`; diff --git a/test/load/docs/__snapshots__/sidebars.test.js.snap b/test/load/docs/__snapshots__/sidebars.test.js.snap new file mode 100644 index 0000000000..e96f753e75 --- /dev/null +++ b/test/load/docs/__snapshots__/sidebars.test.js.snap @@ -0,0 +1,45 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`loadSidebars normal site with sidebars 1`] = ` +Object { + "docs": Object { + "Getting Started": Array [ + "installation", + ], + "Guides": Array [ + "blog", + ], + }, +} +`; + +exports[`loadSidebars site with sidebars & versioned sidebars 1`] = ` +Object { + "docs": Object { + "Getting Started": Array [ + "installation", + ], + "Guides": Array [ + "blog", + ], + }, + "version-1.0.0-docs": Object { + "Getting Started": Array [ + "version-1.0.0-installation", + ], + "Guides": Array [ + "version-1.0.0-blog", + ], + }, + "version-1.0.1-docs": Object { + "Getting Started": Array [ + "version-1.0.1-installation", + ], + "Guides": Array [ + "version-1.0.1-blog", + ], + }, +} +`; + +exports[`loadSidebars site without sidebars 1`] = `Object {}`; diff --git a/test/load/docs/order.test.js b/test/load/docs/order.test.js new file mode 100644 index 0000000000..ef4d0f2b66 --- /dev/null +++ b/test/load/docs/order.test.js @@ -0,0 +1,35 @@ +import createOrder from '@lib/load/docs/order'; + +describe('createOrder', () => { + test('should populate docs index from multiple sidebars', () => { + const result = createOrder({ + docs: { + Category1: ['doc1', 'doc2'], + Category2: ['doc3', 'doc4'] + }, + otherDocs: { + Category1: ['doc5'] + } + }); + expect(result).toMatchSnapshot(); + }); + + test('should resolve docs from older versions', () => { + const result = createOrder({ + docs: { + Category1: ['doc1'] + }, + 'version-1.2.3-docs': { + Category1: ['version-1.2.3-doc2'], + Category2: ['version-1.2.3-doc1'] + } + }); + expect(result).toMatchSnapshot(); + }); + + test('edge cases', () => { + expect(createOrder({})).toEqual({}); + expect(createOrder(null)).toEqual({}); + expect(createOrder(undefined)).toEqual({}); + }); +}); diff --git a/test/load/docs/sidebars.test.js b/test/load/docs/sidebars.test.js new file mode 100644 index 0000000000..d5cb1411b8 --- /dev/null +++ b/test/load/docs/sidebars.test.js @@ -0,0 +1,46 @@ +import path from 'path'; +import loadSidebars from '@lib/load/docs/sidebars'; + +describe('loadSidebars', () => { + const fixtures = path.join(__dirname, '..', '__fixtures__'); + test('normal site with sidebars', () => { + const env = {}; + const siteDir = path.join(fixtures, 'simple-site'); + const result = loadSidebars({siteDir, env}); + expect(result).toMatchSnapshot(); + }); + + test('site without sidebars', () => { + const env = {}; + const siteDir = path.join(fixtures, 'bad-site'); + const result = loadSidebars({siteDir, env}); + expect(result).toMatchSnapshot(); + }); + + test('site with sidebars & versioned sidebars', () => { + const env = { + versioning: { + enabled: true, + versions: ['1.0.1', '1.0.0'] + } + }; + const siteDir = path.join(fixtures, 'versioned-site'); + const result = loadSidebars({siteDir, env}); + expect(result).toMatchSnapshot(); + }); + + test('site with missing versioned sidebars', () => { + const env = { + versioning: { + enabled: true, + versions: ['2.0.0'] + } + }; + const siteDir = path.join(fixtures, 'versioned-site'); + expect(() => { + loadSidebars({siteDir, env}); + }).toThrowErrorMatchingInlineSnapshot( + `"Failed to load versioned_sidebars/version-2.0.0-sidebars.json. It does not exist."` + ); + }); +}); diff --git a/test/load/utils.test.js b/test/load/utils.test.js index 7012f191bd..bf9e54e463 100644 --- a/test/load/utils.test.js +++ b/test/load/utils.test.js @@ -1,4 +1,10 @@ -import {fileToPath, fileToComponentName} from '@lib/load/utils'; +import path from 'path'; +import { + fileToPath, + fileToComponentName, + idx, + getSubFolder +} from '@lib/load/utils'; describe('load utils', () => { test('fileToComponentName', () => { @@ -34,4 +40,59 @@ describe('load utils', () => { expect(fileToPath(file)).toBe(asserts[file]); }); }); + + test('idx', () => { + const a = {}; + const b = {hello: 'world'}; + const env = { + translation: { + enabled: true, + enabledLanguages: [ + { + enabled: true, + name: 'English', + tag: 'en' + }, + { + enabled: true, + name: '日本語', + tag: 'ja' + } + ] + }, + versioning: { + enabled: false, + versions: [] + } + }; + const variable = 'enabledLanguages'; + expect(idx(a, [('b', 'c')])).toBeUndefined(); + expect(idx(b, ['hello'])).toEqual('world'); + expect(idx(b, 'hello')).toEqual('world'); + expect(idx(env, 'typo')).toBeUndefined(); + expect(idx(env, 'versioning')).toEqual({ + enabled: false, + versions: [] + }); + expect(idx(env, ['translation', 'enabled'])).toEqual(true); + expect(idx(env, ['translation', variable]).map(lang => lang.tag)).toEqual([ + 'en', + 'ja' + ]); + expect(idx(undefined)).toBeUndefined(); + expect(idx(null)).toBeNull(); + }); + + test('getSubFolder', () => { + const testA = path.join('folder', 'en', 'test.md'); + const testB = path.join('folder', 'ja', 'test.md'); + const testC = path.join('folder', 'ja', 'en', 'test.md'); + const testD = path.join('docs', 'ro', 'test.md'); + const testE = path.join('docs', 'test.md'); + expect(getSubFolder(testA, 'folder')).toBe('en'); + expect(getSubFolder(testB, 'folder')).toBe('ja'); + expect(getSubFolder(testC, 'folder')).toBe('ja'); + expect(getSubFolder(testD, 'docs')).toBe('ro'); + expect(getSubFolder(testE, 'docs')).toBeNull(); + }); });