Skip to content

Commit fb30a07

Browse files
authored
Unravel pages (the array + map object) (github#16708)
* Revise the 'pages' module to export two methods: 'loadPages' and 'loadPageMap' Update all existing references to use 'loadPages' for now * Remove explicit Promise resolutions on loadPage* methods * Condense reduction method into its now-singular usage spot * Opt for for-of instead of forEach * Make require of pages in warm-server more explicit * Be more explicit about find-page using a pageMap * Be more explicit about find-page-in-version using a pageMap * Be more explicit about site-tree using a pageMap * Extract the map creation from loadPageMap * Be more explicit about using a pageMap * Update redirects precompile to take two arguments: pageList, pageMap * Rename internal loadPages method to loadPageList * Clarify pageMap is what is stored in context.pages * Use loadPageMap in tests and stuff
1 parent 510d503 commit fb30a07

25 files changed

+262
-198
lines changed

lib/algolia/find-indexable-pages.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const loadPages = require('../pages')
1+
const { loadPages } = require('../pages')
22

33
module.exports = async function findIndexablePages () {
44
const allPages = await loadPages()

lib/find-page-in-version.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
const findPage = require('./find-page')
22
const getApplicableVersions = require('./get-applicable-versions')
33

4-
module.exports = function findPageInVersion (href, pages, redirects, languageCode, version, isDotcomOnly = false) {
4+
module.exports = function findPageInVersion (href, pageMap, redirects, languageCode, version, isDotcomOnly = false) {
55
// findPage() will throw an error if an English page can't be found
6-
const page = findPage(href, pages, redirects, languageCode)
6+
const page = findPage(href, pageMap, redirects, languageCode)
77
if (!page) return null
88

99
// if the link is on the homepage, return the page as soon as it's found

lib/find-page.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ const patterns = require('./patterns')
33
const allVersions = Object.keys(require('./all-versions'))
44
const { getVersionedPathWithLanguage } = require('./path-utils')
55

6-
module.exports = function findPage (href, pages, redirects = {}, languageCode = 'en', sourceLanguage = null) {
6+
module.exports = function findPage (href, pageMap, redirects = {}, languageCode = 'en', sourceLanguage = null) {
77
// Convert Windows backslashes to forward slashes
88
// remove trailing slash
99
href = slash(href).replace(patterns.trailingSlash, '$1')
@@ -16,14 +16,14 @@ module.exports = function findPage (href, pages, redirects = {}, languageCode =
1616
// get the first found path of the page (account for redirects)
1717
let pathToPage = versionedPathsToCheck.find(path => {
1818
path = redirects[path] || path
19-
return pages[removeFragment(path)]
19+
return pageMap[removeFragment(path)]
2020
})
2121

2222
// need to account for redirects again
2323
pathToPage = redirects[pathToPage] || pathToPage
2424

2525
// find the page
26-
const page = pages[removeFragment(pathToPage)]
26+
const page = pageMap[removeFragment(pathToPage)]
2727

2828
if (page) return page
2929

@@ -52,7 +52,7 @@ module.exports = function findPage (href, pages, redirects = {}, languageCode =
5252
// because localized content is not yet synced
5353
if (languageCode !== 'en') {
5454
// pass the source language so we can surface it in error messages
55-
return findPage(href, pages, redirects, 'en', languageCode)
55+
return findPage(href, pageMap, redirects, 'en', languageCode)
5656
}
5757
}
5858

lib/find-unused-assets.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const walk = require('walk-sync')
66
const { execSync } = require('child_process')
77
const assert = require('assert')
88
const loadSiteData = require('./site-data')
9-
const loadPages = require('./pages')
9+
const { loadPages } = require('./pages')
1010
const patterns = require('./patterns')
1111
const getDataReferences = require('./get-liquid-data-references')
1212
const imagesPath = '/assets/images'

lib/get-english-headings.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22
const headingRegex = /^###?#? (.*?)$/gm
33

44
// for any translated page, first get corresponding English markdown
5-
// then get the headings on both the translated and English pages
5+
// then get the headings on both the translated and English pageMap
66
// finally, create a map of translation:English for all headings on the page
7-
module.exports = function getEnglishHeadings (page, pages) {
7+
module.exports = function getEnglishHeadings (page, pageMap) {
88
const translatedHeadings = page.markdown.match(headingRegex)
99
if (!translatedHeadings) return
1010

11-
const englishPage = pages[`/en/${page.relativePath.replace(/.md$/, '')}`]
11+
const englishPage = pageMap[`/en/${page.relativePath.replace(/.md$/, '')}`]
1212
if (!englishPage) return
1313

1414
// FIX there may be bugs if English headings are updated before Crowdin syncs up :/

lib/get-map-topic-content.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
const findPage = require('./find-page')
22

33
// get the page.childArticles set on english map topics in lib/site-tree.js
4-
module.exports = function getMapTopicContent (page, pages, redirects) {
4+
module.exports = function getMapTopicContent (page, pageMap, redirects) {
55
const englishPage = page.languageCode !== 'en'
6-
? findPage(`/${page.relativePath.replace(/.md$/, '')}`, pages, redirects, 'en')
6+
? findPage(`/${page.relativePath.replace(/.md$/, '')}`, pageMap, redirects, 'en')
77
: page
88

99
if (!englishPage) {

lib/pages.js

+32-11
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ const Page = require('./page')
44
const languages = require('./languages')
55
const fs = require('fs')
66

7-
module.exports = async function () {
8-
const pages = []
7+
async function loadPageList () {
8+
const pageList = []
99

1010
// load english pages
1111
const englishPath = path.join(__dirname, '..', languages.en.dir, 'content')
@@ -15,7 +15,8 @@ module.exports = async function () {
1515
!relativePath.includes('README')
1616
})
1717
.map(fileData => new Page({ ...fileData, languageCode: languages.en.code }))
18-
pages.push(...englishPages)
18+
19+
pageList.push(...englishPages)
1920

2021
// load matching pages in other languages
2122
for (const page of englishPages) {
@@ -29,20 +30,40 @@ module.exports = async function () {
2930
} catch (_) {
3031
continue
3132
}
32-
pages.push(new Page({
33+
34+
pageList.push(new Page({
3335
relativePath: page.relativePath,
3436
basePath,
3537
languageCode: language.code
3638
}))
3739
}
3840
}
3941

40-
// add named keys to the array for fast object-like reference
41-
for (const page of pages) {
42-
page.permalinks.forEach(permalink => {
43-
pages[permalink.href] = page
44-
})
45-
}
42+
return pageList
43+
}
44+
45+
function createMapFromArray (pageList) {
46+
// add keys to the object for each of the page's permalinks for fast lookup
47+
const pageMap =
48+
pageList.reduce(
49+
(pageMap, page) => {
50+
for (const permalink of page.permalinks) {
51+
pageMap[permalink.href] = page
52+
}
53+
return pageMap
54+
},
55+
{}
56+
)
57+
58+
return pageMap
59+
}
60+
61+
async function loadPageMap (pageList) {
62+
const pages = pageList || await loadPageList()
63+
return createMapFromArray(pages)
64+
}
4665

47-
return Promise.resolve(pages)
66+
module.exports = {
67+
loadPages: loadPageList,
68+
loadPageMap
4869
}

lib/redirects/get-docs-path-from-developer-path.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const { getVersionedPathWithLanguage } = require('../path-utils')
44

55
// This function takes a known pre-migration path from developer.github.com and
66
// infers and returns a current, correct docs.github.com path.
7-
module.exports = function getDocsPathFromDeveloperPath (oldDeveloperPath, allRedirects, pages) {
7+
module.exports = function getDocsPathFromDeveloperPath (oldDeveloperPath, allRedirects, pageMap) {
88
let newPath = oldDeveloperPath
99

1010
// Look up the old path in the redirects object BEFORE modifying /v3 and /v4 paths
@@ -59,7 +59,7 @@ module.exports = function getDocsPathFromDeveloperPath (oldDeveloperPath, allRed
5959

6060
// show an error if the page to be redirected to doesn't exist
6161
// but only if the current collection of pages includes REST and GraphQL reference pages
62-
if (process.env.NODE_ENV !== 'test' && !pages[newPath] && pages['/en/rest/reference'] && pages['/en/graphql/reference']) {
62+
if (process.env.NODE_ENV !== 'test' && !pageMap[newPath] && pageMap['/en/rest/reference'] && pageMap['/en/graphql/reference']) {
6363
console.error(`can't find ${newPath}! based on ${oldDeveloperPath}`)
6464
}
6565

lib/redirects/precompile.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ const nonEnterpriseDefaultVersion = require('../non-enterprise-default-version')
1010

1111
// This function runs at server warmup and precompiles possible redirect routes.
1212
// It outputs them in key-value pairs within a neat Javascript object: { oldPath: newPath }
13-
module.exports = async function precompileRedirects (pages) {
13+
module.exports = async function precompileRedirects (pageList, pageMap) {
1414
const allRedirects = {}
1515

1616
// 1. CURRENT PAGES PERMALINKS AND FRONTMATTER
1717
// These redirects are created in lib/page.js via lib/redirects/permalinks.js
18-
pages.forEach(page => Object.assign(allRedirects, page.redirects))
18+
pageList.forEach(page => Object.assign(allRedirects, page.redirects))
1919

2020
// 2. REDIRECTS FOR ARCHIVED ROUTES FROM 2.13 TO 2.17
2121
// Starting with 2.18, we updated the archival script to create stubbed HTML redirect files,
@@ -41,7 +41,7 @@ module.exports = async function precompileRedirects (pages) {
4141
// Note that the list only includes supported enterprise paths up to 2.21, which is
4242
// the last version that was available on developer.github.com before the migration.
4343
DEVELOPER_ROUTES.forEach(developerRoute => {
44-
const newPath = getDocsPathFromDevPath(developerRoute, allRedirects, pages)
44+
const newPath = getDocsPathFromDevPath(developerRoute, allRedirects, pageMap)
4545

4646
// add the redirect to the object
4747
allRedirects[developerRoute] = newPath

lib/site-tree.js

+12-12
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const allVersions = Object.keys(require('./all-versions'))
1414
// or if a category has direct child articles:
1515
// siteTree[languageCode][version].products[productHref].categories[categoryHref].articles
1616

17-
module.exports = async function buildSiteTree (pages, site, redirects) {
17+
module.exports = async function buildSiteTree (pageMap, site, redirects) {
1818
const siteTree = {}
1919

2020
languageCodes.forEach(languageCode => {
@@ -38,12 +38,12 @@ module.exports = async function buildSiteTree (pages, site, redirects) {
3838
product.href = item.href
3939

4040
// find the product TOC page and get TOC items
41-
const page = findPageInVersion(item.href, pages, redirects, languageCode, version)
41+
const page = findPageInVersion(item.href, pageMap, redirects, languageCode, version)
4242

4343
// skip if page can't be found in this version
4444
if (!page) return
4545

46-
product.categories = buildCategoriesTree(page.tocItems, item.href, pages, redirects, version, languageCode)
46+
product.categories = buildCategoriesTree(page.tocItems, item.href, pageMap, redirects, version, languageCode)
4747

4848
productTree[item.id] = product
4949
return null
@@ -58,7 +58,7 @@ module.exports = async function buildSiteTree (pages, site, redirects) {
5858
return siteTree
5959
}
6060

61-
function buildCategoriesTree (tocItems, productHref, pages, redirects, version, languageCode) {
61+
function buildCategoriesTree (tocItems, productHref, pageMap, redirects, version, languageCode) {
6262
const categoryTree = {}
6363

6464
// for every category in a product TOC...
@@ -71,7 +71,7 @@ function buildCategoriesTree (tocItems, productHref, pages, redirects, version,
7171
category.href = versionedCategoryHref
7272

7373
// find the category TOC page and get its TOC items
74-
const page = findPageInVersion(categoryHref, pages, redirects, languageCode, version)
74+
const page = findPageInVersion(categoryHref, pageMap, redirects, languageCode, version)
7575

7676
// skip if page can't be found in this version
7777
if (!page) return
@@ -90,9 +90,9 @@ function buildCategoriesTree (tocItems, productHref, pages, redirects, version,
9090
// if TOC contains maptopics, build a maptopics tree
9191
// otherwise build an articles tree
9292
if (hasMaptopics) {
93-
category.maptopics = buildMaptopicsTree(page.tocItems, categoryHref, pages, redirects, version, languageCode)
93+
category.maptopics = buildMaptopicsTree(page.tocItems, categoryHref, pageMap, redirects, version, languageCode)
9494
} else {
95-
category.articles = buildArticlesTree(page.tocItems, categoryHref, pages, redirects, version, languageCode)
95+
category.articles = buildArticlesTree(page.tocItems, categoryHref, pageMap, redirects, version, languageCode)
9696
}
9797
}
9898

@@ -102,7 +102,7 @@ function buildCategoriesTree (tocItems, productHref, pages, redirects, version,
102102
return categoryTree
103103
}
104104

105-
function buildMaptopicsTree (tocItems, categoryHref, pages, redirects, version, languageCode) {
105+
function buildMaptopicsTree (tocItems, categoryHref, pageMap, redirects, version, languageCode) {
106106
const maptopicTree = {}
107107

108108
// for every maptopic in a category TOC...
@@ -118,7 +118,7 @@ function buildMaptopicsTree (tocItems, categoryHref, pages, redirects, version,
118118

119119
// we already have access to the child articles via the category TOC items
120120
// but we still need the page to get the available versions
121-
const page = findPageInVersion(maptopicHref, pages, redirects, languageCode, version)
121+
const page = findPageInVersion(maptopicHref, pageMap, redirects, languageCode, version)
122122

123123
// skip if page can't be found in this version
124124
if (!page) return
@@ -135,14 +135,14 @@ function buildMaptopicsTree (tocItems, categoryHref, pages, redirects, version,
135135
// make the child articles accessible to the page object for maptopic rendering
136136
if (!page.childArticles) page.childArticles = childArticles
137137

138-
maptopic.articles = buildArticlesTree(childArticles, categoryHref, pages, redirects, version, languageCode)
138+
maptopic.articles = buildArticlesTree(childArticles, categoryHref, pageMap, redirects, version, languageCode)
139139
maptopicTree[versionedMaptopicHref] = maptopic
140140
})
141141

142142
return maptopicTree
143143
}
144144

145-
function buildArticlesTree (tocItems, categoryHref, pages, redirects, version, languageCode) {
145+
function buildArticlesTree (tocItems, categoryHref, pageMap, redirects, version, languageCode) {
146146
const articleTree = {}
147147

148148
// REST categories may not have TOC items
@@ -157,7 +157,7 @@ function buildArticlesTree (tocItems, categoryHref, pages, redirects, version, l
157157
const versionedArticleHref = getVersionedPathWithoutLanguage(articleHref, version)
158158
article.href = versionedArticleHref
159159

160-
const page = findPageInVersion(articleHref, pages, redirects, languageCode, version)
160+
const page = findPageInVersion(articleHref, pageMap, redirects, languageCode, version)
161161

162162
// skip if page can't be found in this version
163163
if (!page) return

lib/warm-server.js

+15-9
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
const statsd = require('./statsd')
2-
const loadPages = require('./pages')
2+
const { loadPages, loadPageMap } = require('./pages')
33
const loadRedirects = require('./redirects/precompile')
44
const loadSiteData = require('./site-data')
55
const loadSiteTree = require('./site-tree')
66

77
// For local caching
8-
let pages, site, redirects, siteTree
8+
let pageList, pageMap, site, redirects, siteTree
99

1010
function isFullyWarmed () {
11-
return Boolean(pages && site && redirects && siteTree)
11+
// NOTE: Yes, `pageList` is specifically excluded here as it is transient data
12+
const fullyWarmed = !!(pageMap && site && redirects && siteTree)
13+
return fullyWarmed
1214
}
1315

1416
function getWarmedCache () {
1517
return {
16-
pages,
18+
pages: pageMap,
1719
site,
1820
redirects,
1921
siteTree
@@ -27,20 +29,24 @@ async function warmServer () {
2729
console.log('Priming context information...')
2830
}
2931

30-
if (!pages || !site) {
32+
if (!pageList || !site) {
3133
// Promise.all is used to load multiple things in parallel
32-
[pages, site] = await Promise.all([
33-
pages || loadPages(),
34+
[pageList, site] = await Promise.all([
35+
pageList || loadPages(),
3436
site || loadSiteData()
3537
])
3638
}
3739

40+
if (!pageMap) {
41+
pageMap = await loadPageMap(pageList)
42+
}
43+
3844
if (!redirects) {
39-
redirects = await loadRedirects(pages)
45+
redirects = await loadRedirects(pageList, pageMap)
4046
}
4147

4248
if (!siteTree) {
43-
siteTree = await loadSiteTree(pages, site, redirects)
49+
siteTree = await loadSiteTree(pageMap, site, redirects)
4450
}
4551

4652
if (process.env.NODE_ENV !== 'test') {

middleware/context.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ const featureFlags = Object.keys(require('../feature-flags'))
1212
// Note that additional middleware in middleware/index.js adds to this context object
1313
module.exports = async function contextualize (req, res, next) {
1414
// Ensure that we load some data only once on first request
15-
const { site, redirects, pages, siteTree } = await warmServer()
15+
const { site, redirects, siteTree, pages: pageMap } = await warmServer()
16+
1617
req.context = {}
1718

1819
// make feature flag environment variables accessible in layouts
@@ -39,7 +40,7 @@ module.exports = async function contextualize (req, res, next) {
3940
req.context.redirects = redirects
4041
req.context.site = site[req.language].site
4142
req.context.siteTree = siteTree
42-
req.context.pages = pages
43+
req.context.pages = pageMap
4344

4445
return next()
4546
}

script/check-s3-images.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
const path = require('path')
44
const { chain, difference } = require('lodash')
5-
const loadPages = require('../lib/pages')
5+
const { loadPages } = require('../lib/pages')
66
const loadSiteData = require('../lib/site-data')
77
const renderContent = require('../lib/render-content')
88
const allVersions = require('../lib/all-versions')

0 commit comments

Comments
 (0)