Skip to content

Commit 4ffcff3

Browse files
committed
add lib modules for refactored siteTree
1 parent c1e3348 commit 4ffcff3

5 files changed

Lines changed: 166 additions & 2 deletions

File tree

lib/create-tree.js

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
const fs = require('fs')
2+
const path = require('path')
3+
const Page = require('./page')
4+
const { sortBy } = require('lodash')
5+
6+
let basePath
7+
8+
module.exports = async function createTree (originalPath, langObj) {
9+
// Do not reset this value on recursive runs
10+
if (!basePath) basePath = originalPath
11+
12+
// On recursive runs, this is processing page.children items in `/<link>` format.
13+
// If the path exists as is, assume this is a directory with a child index.md.
14+
// Otherwise, assume it's a child .md file and add `.md` to the path.
15+
let filepath
16+
try {
17+
await fs.promises.access(originalPath)
18+
filepath = `${originalPath}/index.md`
19+
} catch {
20+
filepath = `${originalPath}.md`
21+
}
22+
23+
const relativePath = filepath.replace(`${basePath}/`, '')
24+
const localizedBasePath = path.join(__dirname, '..', langObj.dir, 'content')
25+
26+
// Initialize the Page! This is where the magic happens (sorry).
27+
const page = await Page.init({
28+
basePath: localizedBasePath,
29+
relativePath,
30+
languageCode: langObj.code
31+
})
32+
33+
if (!page) {
34+
// Do not throw an error if early access is not available
35+
if (relativePath.startsWith('early-access')) return
36+
37+
throw Error(`Cannot initialize page for ${filepath}`)
38+
}
39+
40+
// Create the root tree object on the first run, and create children recursively
41+
const item = {
42+
relativePath,
43+
title: page.shortTitle || page.title,
44+
// parentPath: parentPath || null,
45+
page
46+
}
47+
48+
// Process frontmatter children recursively
49+
if (item.page.children) {
50+
item.childPages = sortBy(
51+
(await Promise.all(item.page.children
52+
.map(async (child) => await createTree(path.join(originalPath, child), langObj))))
53+
.filter(Boolean),
54+
// Sort by the ordered array of children in the frontmatter
55+
item.page.children
56+
)
57+
}
58+
59+
return item
60+
}

lib/frontmatter.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ const schema = {
3636
minimum: 2,
3737
maximum: 4
3838
},
39+
children: {
40+
type: 'array'
41+
},
3942
mapTopic: {
4043
type: 'boolean'
4144
},

lib/get-document-type.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
module.exports = function getDocumentType (relativePath) {
2+
if (!relativePath.endsWith('index.md')) {
3+
return 'article'
4+
}
5+
6+
// Derive the document type from the path segment length
7+
switch (relativePath.split('/').length) {
8+
case 1:
9+
return 'homepage'
10+
case 2:
11+
return 'product'
12+
case 3:
13+
return 'category'
14+
case 4:
15+
return 'mapTopic'
16+
}
17+
}

lib/page.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const statsd = require('./statsd')
1818
const readFileContents = require('./read-file-contents')
1919
const getLinkData = require('./get-link-data')
2020
const union = require('lodash/union')
21+
// const getDocumentType = require('./get-document-type')
2122

2223
class Page {
2324
static async init (opts) {
@@ -69,8 +70,14 @@ class Page {
6970
this.rawLearningTracks = this.learningTracks
7071
this.rawIncludeGuides = this.includeGuides
7172

73+
// Is this a Product, Category, Topic, or Article?
74+
// this.documentType = getDocumentType(this.relativePath)
75+
76+
// Get array of versions that the page is available in for fast lookup
77+
this.applicableVersions = getApplicableVersions(this.versions, this.fullPath)
78+
7279
// a page should only be available in versions that its parent product is available in
73-
const versionsParentProductIsNotAvailableIn = getApplicableVersions(this.versions, this.fullPath)
80+
const versionsParentProductIsNotAvailableIn = this.applicableVersions
7481
// only the homepage will not have this.parentProduct
7582
.filter(availableVersion => this.parentProduct && !this.parentProduct.versions.includes(availableVersion))
7683

lib/pages.js

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,82 @@
11
const path = require('path')
2+
const languages = require('./languages')
3+
const versions = Object.keys(require('./all-versions'))
4+
const createTree = require('./create-tree')
5+
const nonEnterpriseDefaultVersion = require('./non-enterprise-default-version')
6+
const englishPath = path.join(__dirname, '..', 'content')
27
const walk = require('walk-sync').entries
38
const Page = require('./page')
4-
const languages = require('./languages')
9+
10+
// This function creates a nested object that can be accessed like this:
11+
// siteTree[languageCode][version].childPages[<array of pages>].childPages[<array of pages>] (etc...)
12+
async function loadTree () {
13+
// We only need to initialize pages once per language (since pages don't change per version), so we do that
14+
// first since it's the most expensive work. This gets us a nested object with pages attached that we can use
15+
// as the basis for the siteTree after we do some versioning.
16+
const rawTree = {}
17+
await Promise.all(Object.values(languages)
18+
.map(async (langObj) => {
19+
rawTree[langObj.code] = await createTree(englishPath, langObj)
20+
}))
21+
22+
// Now that we have the paged tree, we can walk it for every version and do two operations:
23+
// 1. Add a versioned href to every node (we can get this easily from the permalinks array).
24+
// 2. Recurisvely drop any child pages that are not available in the current version.
25+
// Note that order of languages and versions doesn't matter, but order of child page arrays DOES matter (for nav).
26+
const siteTree = {}
27+
await Promise.all(Object.keys(languages).map(async (langCode) => {
28+
const treePerVersion = {}
29+
30+
await Promise.all(versions.map(async (version) => {
31+
// Yes, we are mutating the rawTree object here.
32+
versionPages(rawTree[langCode])
33+
34+
// This step can't be asynchronous because the order of child pages matters.
35+
function versionPages (item) {
36+
// Add a versioned href as a convenience for use in layouts.
37+
item.href = item.page.permalinks
38+
.find(pl => pl.pageVersion === version || (pl.pageVersion === 'homepage' && version === nonEnterpriseDefaultVersion))
39+
40+
if (!item.childPages) return item
41+
42+
// Drop child pages that do not apply to the current version
43+
item.childPages = item.childPages.filter(childPage => childPage.page.applicableVersions.includes(version))
44+
item.childPages.forEach(childPage => versionPages(childPage))
45+
}
46+
47+
treePerVersion[version] = rawTree[langCode]
48+
}))
49+
50+
siteTree[langCode] = treePerVersion
51+
}))
52+
53+
console.log(siteTree.en[nonEnterpriseDefaultVersion])
54+
55+
return siteTree
56+
}
57+
58+
async function loadPageListFromTree (tree) {
59+
const siteTree = tree || await loadTree()
60+
61+
// Traverse the tree of pages and create a simple array of pages.
62+
// (We don't care about the language code or version here, so we can start two levels in.)
63+
const collection = Object.values(siteTree).map(v => Object.values(v)).flat()
64+
const result = []
65+
collectPages(collection, result)
66+
67+
function collectPages (arr, result) {
68+
arr.forEach(item => {
69+
if (item.page) {
70+
result.push(item.page)
71+
}
72+
if (item.childPages) {
73+
collectPages(item.childPages, result)
74+
}
75+
})
76+
}
77+
78+
return result
79+
}
580

681
async function loadPageList () {
782
// load english pages
@@ -66,6 +141,8 @@ async function loadPageMap (pageList) {
66141
}
67142

68143
module.exports = {
144+
loadTree,
145+
// loadPages: loadPageListFromTree,
69146
loadPages: loadPageList,
70147
loadPageMap
71148
}

0 commit comments

Comments
 (0)