fall back to English on corrupt content files (#32629)
This commit is contained in:
@@ -1,6 +1,20 @@
|
|||||||
import path from 'path'
|
import path from 'path'
|
||||||
import fs from 'fs/promises'
|
import fs from 'fs/promises'
|
||||||
import Page from './page.js'
|
|
||||||
|
import languages from './languages.js'
|
||||||
|
import Page, { FrontmatterErrorsError } from './page.js'
|
||||||
|
|
||||||
|
// If you run `export DEBUG_TRANSLATION_FALLBACKS=true` in your terminal,
|
||||||
|
// every time a translation file fails to initialize we fall back to English
|
||||||
|
// and write a warning to stdout.
|
||||||
|
const DEBUG_TRANSLATION_FALLBACKS = Boolean(
|
||||||
|
JSON.parse(process.env.DEBUG_TRANSLATION_FALLBACKS || 'false')
|
||||||
|
)
|
||||||
|
// If you don't want to fall back to English automatically on corrupt
|
||||||
|
// translation files, set `export THROW_TRANSLATION_ERRORS=true`
|
||||||
|
const THROW_TRANSLATION_ERRORS = Boolean(
|
||||||
|
JSON.parse(process.env.THROW_TRANSLATION_ERRORS || 'false')
|
||||||
|
)
|
||||||
|
|
||||||
// Module level cache
|
// Module level cache
|
||||||
const _basePaths = new Map()
|
const _basePaths = new Map()
|
||||||
@@ -30,11 +44,39 @@ export default async function createTree(originalPath, langObj) {
|
|||||||
const relativePath = filepath.replace(`${basePath}/`, '')
|
const relativePath = filepath.replace(`${basePath}/`, '')
|
||||||
|
|
||||||
// Initialize the Page! This is where the file reads happen.
|
// Initialize the Page! This is where the file reads happen.
|
||||||
const page = await Page.init({
|
let page
|
||||||
|
try {
|
||||||
|
page = await Page.init({
|
||||||
basePath,
|
basePath,
|
||||||
relativePath,
|
relativePath,
|
||||||
languageCode: langObj.code,
|
languageCode: langObj.code,
|
||||||
})
|
})
|
||||||
|
} catch (err) {
|
||||||
|
if (
|
||||||
|
!THROW_TRANSLATION_ERRORS &&
|
||||||
|
err instanceof FrontmatterErrorsError &&
|
||||||
|
langObj.code !== 'en'
|
||||||
|
) {
|
||||||
|
// Something corrupt in the `.md` file caused it to throw an
|
||||||
|
// error from reading it in. Let's "gracefully" recover by
|
||||||
|
// swapping this one out for the English content and pretend it
|
||||||
|
// exists in this other language.
|
||||||
|
const englishBasePath = getBasePath(languages.en.dir)
|
||||||
|
page = await Page.init({
|
||||||
|
basePath: englishBasePath,
|
||||||
|
relativePath,
|
||||||
|
languageCode: langObj.code,
|
||||||
|
})
|
||||||
|
if (DEBUG_TRANSLATION_FALLBACKS) {
|
||||||
|
console.warn(
|
||||||
|
`Unable to initialized ${path.join(basePath, relativePath)} due to frontmatter errors. ` +
|
||||||
|
`Will proceed with using ${path.join(englishBasePath, relativePath)} instead.`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!page) {
|
if (!page) {
|
||||||
// Do not throw an error if Early Access is not available.
|
// Do not throw an error if Early Access is not available.
|
||||||
|
|||||||
12
lib/page.js
12
lib/page.js
@@ -25,6 +25,13 @@ import { allTools } from './all-tools.js'
|
|||||||
// every single time, we turn it into a Set once.
|
// every single time, we turn it into a Set once.
|
||||||
const productMapKeysAsSet = new Set(Object.keys(productMap))
|
const productMapKeysAsSet = new Set(Object.keys(productMap))
|
||||||
|
|
||||||
|
export class FrontmatterErrorsError extends Error {
|
||||||
|
constructor(message, frontmatterErrors) {
|
||||||
|
super(message)
|
||||||
|
this.frontmatterErrors = frontmatterErrors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class Page {
|
class Page {
|
||||||
static async init(opts) {
|
static async init(opts) {
|
||||||
opts = await Page.read(opts)
|
opts = await Page.read(opts)
|
||||||
@@ -61,7 +68,10 @@ class Page {
|
|||||||
|
|
||||||
constructor(opts) {
|
constructor(opts) {
|
||||||
if (opts.frontmatterErrors.length) {
|
if (opts.frontmatterErrors.length) {
|
||||||
throw new Error(JSON.stringify(opts.frontmatterErrors, null, 2))
|
throw new FrontmatterErrorsError(
|
||||||
|
`${opts.frontmatterErrors.length} frontmatter errors trying to load ${opts.fullPath}`,
|
||||||
|
opts.frontmatterErrors
|
||||||
|
)
|
||||||
}
|
}
|
||||||
delete opts.frontmatterErrors
|
delete opts.frontmatterErrors
|
||||||
Object.assign(this, { ...opts })
|
Object.assign(this, { ...opts })
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import path from 'path'
|
|||||||
import cheerio from 'cheerio'
|
import cheerio from 'cheerio'
|
||||||
import { describe, expect } from '@jest/globals'
|
import { describe, expect } from '@jest/globals'
|
||||||
|
|
||||||
import Page from '../../lib/page.js'
|
import Page, { FrontmatterErrorsError } from '../../lib/page.js'
|
||||||
import { allVersions } from '../../lib/all-versions.js'
|
import { allVersions } from '../../lib/all-versions.js'
|
||||||
import enterpriseServerReleases, { latest } from '../../lib/enterprise-server-releases.js'
|
import enterpriseServerReleases, { latest } from '../../lib/enterprise-server-releases.js'
|
||||||
import nonEnterpriseDefaultVersion from '../../lib/non-enterprise-default-version.js'
|
import nonEnterpriseDefaultVersion from '../../lib/non-enterprise-default-version.js'
|
||||||
@@ -727,7 +727,7 @@ describe('catches errors thrown in Page class', () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
await expect(getPage).rejects.toThrowError('invalid frontmatter entry')
|
await expect(getPage).rejects.toThrow(FrontmatterErrorsError)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('missing versions frontmatter', async () => {
|
test('missing versions frontmatter', async () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user