Compute category versions from combining versions of children (#45685)
This commit is contained in:
@@ -2,10 +2,11 @@
|
|||||||
title: Versioning
|
title: Versioning
|
||||||
intro: 'Testing of pages that only available in *some* versions'
|
intro: 'Testing of pages that only available in *some* versions'
|
||||||
versions:
|
versions:
|
||||||
fpt: '*'
|
# Note that these here are wrong!
|
||||||
ghes: '*'
|
# They deliberately don't match what's set in the versions
|
||||||
ghae: '*'
|
# within the children.
|
||||||
ghec: '*'
|
ghes: '>=3.99'
|
||||||
|
ghec: '<1.0'
|
||||||
children:
|
children:
|
||||||
- /only-fpt
|
- /only-fpt
|
||||||
- /only-ghec
|
- /only-ghec
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { expect } from '@jest/globals'
|
|
||||||
import { getDOM, head } from '#src/tests/helpers/e2etest.js'
|
import { getDOM, head } from '#src/tests/helpers/e2etest.js'
|
||||||
import { supported } from '#src/versions/lib/enterprise-server-releases.js'
|
import { supported } from '#src/versions/lib/enterprise-server-releases.js'
|
||||||
|
|
||||||
@@ -47,6 +46,33 @@ describe('article versioning', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('category versioning', () => {
|
||||||
|
test('category page work in all children versions', async () => {
|
||||||
|
{
|
||||||
|
// Note that in the `versions:` of get-started/versioning/index.md
|
||||||
|
// it *lacks* fpt. It's a deliberate pretend omission/mistake.
|
||||||
|
// But clearly the page works.
|
||||||
|
const res = await head('/en/get-started/versioning')
|
||||||
|
expect(res.statusCode).toBe(200)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// The actual version number of get-started/versioning/index.md
|
||||||
|
// does not specify this version of ghes, it still works.
|
||||||
|
const res = await head('/en/enterprise-server@latest/get-started/versioning')
|
||||||
|
expect(res.statusCode).toBe(302)
|
||||||
|
expect(res.headers.location).toMatch(
|
||||||
|
/\/en\/enterprise-server@[\d.]+\/get-started\/versioning/,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// The actual version number of get-started/versioning/index.md
|
||||||
|
// does not specify this version of ghec, it still works.
|
||||||
|
const res = await head('/en/enterprise-cloud@latest/get-started/versioning')
|
||||||
|
expect(res.statusCode).toBe(200)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('home page versioning', () => {
|
describe('home page versioning', () => {
|
||||||
test('invalid language and valid version', async () => {
|
test('invalid language and valid version', async () => {
|
||||||
// Don't use 'latest' here because that will trigger a redirect
|
// Don't use 'latest' here because that will trigger a redirect
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import createTree from './create-tree.js'
|
|||||||
import nonEnterpriseDefaultVersion from '#src/versions/lib/non-enterprise-default-version.js'
|
import nonEnterpriseDefaultVersion from '#src/versions/lib/non-enterprise-default-version.js'
|
||||||
import readFileContents from './read-file-contents.js'
|
import readFileContents from './read-file-contents.js'
|
||||||
import Page from './page.js'
|
import Page from './page.js'
|
||||||
|
import Permalink from './permalink.js'
|
||||||
import frontmatterSchema from './frontmatter.js'
|
import frontmatterSchema from './frontmatter.js'
|
||||||
import { correctTranslatedContentStrings } from '#src/languages/lib/correct-translation-content.js'
|
import { correctTranslatedContentStrings } from '#src/languages/lib/correct-translation-content.js'
|
||||||
|
|
||||||
@@ -42,6 +43,7 @@ export async function loadUnversionedTree(languagesOnly = null) {
|
|||||||
}
|
}
|
||||||
const unversionedTree = {}
|
const unversionedTree = {}
|
||||||
unversionedTree.en = await createTree(path.join(languages.en.dir, 'content'))
|
unversionedTree.en = await createTree(path.join(languages.en.dir, 'content'))
|
||||||
|
setCategoryApplicableVersions(unversionedTree.en)
|
||||||
|
|
||||||
const languagesValues = Object.entries(languages)
|
const languagesValues = Object.entries(languages)
|
||||||
.filter(([language]) => {
|
.filter(([language]) => {
|
||||||
@@ -67,6 +69,52 @@ export async function loadUnversionedTree(languagesOnly = null) {
|
|||||||
return unversionedTree
|
return unversionedTree
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setCategoryApplicableVersions(tree) {
|
||||||
|
// Now that the tree has been fully computed, we can for any node that
|
||||||
|
// is a category page, re-set its `.applicableVersions` and `.permalinks`
|
||||||
|
// based on the union set of all its immediate children's
|
||||||
|
// `.applicableVersions`.
|
||||||
|
for (const childPage of tree.childPages) {
|
||||||
|
if (childPage.page.relativePath.endsWith('index.md')) {
|
||||||
|
const combinedApplicableVersions = []
|
||||||
|
let moreThanOneChild = false
|
||||||
|
for (const childChildPage of childPage.childPages || []) {
|
||||||
|
for (const version of childChildPage.page.applicableVersions) {
|
||||||
|
if (!combinedApplicableVersions.includes(version)) {
|
||||||
|
combinedApplicableVersions.push(version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setCategoryApplicableVersions(childPage)
|
||||||
|
moreThanOneChild = true
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
// Some landing pages have no children at all.
|
||||||
|
// For example the search/index.md page. With no children,
|
||||||
|
// the combined applicableVersions would be [].
|
||||||
|
moreThanOneChild &&
|
||||||
|
!equalSets(
|
||||||
|
new Set(childPage.page.applicableVersions),
|
||||||
|
new Set(combinedApplicableVersions),
|
||||||
|
) &&
|
||||||
|
!childPage.page.relativePath.startsWith('early-access')
|
||||||
|
) {
|
||||||
|
const newPermalinks = Permalink.derive(
|
||||||
|
childPage.page.languageCode,
|
||||||
|
childPage.page.relativePath,
|
||||||
|
childPage.page.title,
|
||||||
|
combinedApplicableVersions,
|
||||||
|
)
|
||||||
|
childPage.page.permalinks = newPermalinks
|
||||||
|
childPage.page.applicableVersions = combinedApplicableVersions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function equalSets(setA, setB) {
|
||||||
|
return setA.size === setB.size && [...setA].every((x) => setB.has(x))
|
||||||
|
}
|
||||||
|
|
||||||
async function translateTree(dir, langObj, enTree) {
|
async function translateTree(dir, langObj, enTree) {
|
||||||
const item = {}
|
const item = {}
|
||||||
const enPage = enTree.page
|
const enPage = enTree.page
|
||||||
|
|||||||
@@ -23,7 +23,20 @@ export default async function findPage(
|
|||||||
|
|
||||||
let page = req.context.pages[req.pagePath]
|
let page = req.context.pages[req.pagePath]
|
||||||
if (page && isDev && englishPrefixRegex.test(req.pagePath)) {
|
if (page && isDev && englishPrefixRegex.test(req.pagePath)) {
|
||||||
|
// The .applicableVersions and .permalinks properties are computed
|
||||||
|
// when the page is read in from disk. But when the initial tree
|
||||||
|
// was created at startup, the pages in the tree were mutated
|
||||||
|
// based on their context. For example, a category page's versions
|
||||||
|
// is based on looping through all its children's versions.
|
||||||
|
const reuseOldVersions = page.relativePath.endsWith('index.md')
|
||||||
|
const oldApplicableVersions = page.applicableVersions
|
||||||
|
const oldPermalinks = page.permalinks
|
||||||
|
|
||||||
page = await rereadByPath(req.pagePath, contentRoot, req.context.currentVersion)
|
page = await rereadByPath(req.pagePath, contentRoot, req.context.currentVersion)
|
||||||
|
if (reuseOldVersions) {
|
||||||
|
page.applicableVersions = oldApplicableVersions
|
||||||
|
page.permalinks = oldPermalinks
|
||||||
|
}
|
||||||
|
|
||||||
// This can happen if the page we just re-read has changed which
|
// This can happen if the page we just re-read has changed which
|
||||||
// versions it's available in (the `versions` frontmatter) meaning
|
// versions it's available in (the `versions` frontmatter) meaning
|
||||||
|
|||||||
@@ -578,7 +578,9 @@ async function processPermalink(core, permalink, page, pageMap, redirects, opts,
|
|||||||
try {
|
try {
|
||||||
html = await renderInnerHTML(page, permalink)
|
html = await renderInnerHTML(page, permalink)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn(`The error happened trying to render ${page.relativePath}`)
|
console.warn(
|
||||||
|
`The error happened trying to render ${page.relativePath} (permalink: ${permalink.href})`,
|
||||||
|
)
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
const $ = cheerio.load(html, { xmlMode: true })
|
const $ = cheerio.load(html, { xmlMode: true })
|
||||||
|
|||||||
Reference in New Issue
Block a user