1
0
mirror of synced 2025-12-19 18:10:59 -05:00

Migrate 15 JavaScript files to TypeScript (#57875)

This commit is contained in:
Kevin Heis
2025-10-08 11:55:44 -07:00
committed by GitHub
parent f5df15a5c8
commit fac4a0ee03
17 changed files with 170 additions and 97 deletions

View File

@@ -9,7 +9,7 @@
import fs from 'fs'
import { spawn } from 'child_process'
const rule = process.argv[2]
const rule: string | undefined = process.argv[2]
if (!rule) {
console.error('Please specify a rule to disable.')
process.exit(1)
@@ -38,36 +38,40 @@ const childProcess = spawn('npm', [
rule,
])
childProcess.stdout.on('data', (data) => {
childProcess.stdout.on('data', (data: Buffer) => {
if (verbose) console.log(data.toString())
})
childProcess.stderr.on('data', function (data) {
childProcess.stderr.on('data', function (data: Buffer) {
if (verbose) console.log(data.toString())
})
let matchingRulesFound = 0
childProcess.on('close', (code) => {
childProcess.on('close', (code: number | null) => {
if (code === 0) {
console.log(`No violations for rule, "${rule}" found.`)
process.exit(0)
}
const markdownViolations = JSON.parse(fs.readFileSync('markdown-violations.json', 'utf8'))
const markdownViolations: Record<string, Array<{ lineNumber: number }>> = JSON.parse(
fs.readFileSync('markdown-violations.json', 'utf8'),
)
console.log(`${Object.values(markdownViolations).flat().length} violations found.`)
Object.entries(markdownViolations).forEach(([fileName, results]) => {
console.log(fileName)
console.log(results)
const fileLines = fs.readFileSync(fileName, 'utf8').split('\n')
results.forEach((result) => {
matchingRulesFound++
const lineIndex = result.lineNumber - 1
const offendingLine = fileLines[lineIndex]
fileLines[lineIndex] = offendingLine.concat(` <!-- markdownlint-disable-line ${rule} -->`)
})
fs.writeFileSync(fileName, fileLines.join('\n'), 'utf8')
})
Object.entries(markdownViolations).forEach(
([fileName, results]: [string, Array<{ lineNumber: number }>]) => {
console.log(fileName)
console.log(results)
const fileLines = fs.readFileSync(fileName, 'utf8').split('\n')
results.forEach((result) => {
matchingRulesFound++
const lineIndex = result.lineNumber - 1
const offendingLine = fileLines[lineIndex]
fileLines[lineIndex] = offendingLine.concat(` <!-- markdownlint-disable-line ${rule} -->`)
})
fs.writeFileSync(fileName, fileLines.join('\n'), 'utf8')
},
)
console.log(`${matchingRulesFound} violations ignored.`)
})

View File

@@ -3,6 +3,7 @@ import path from 'path'
import { afterAll, beforeAll, describe, expect, test } from 'vitest'
import { runRule } from '../../lib/init-test'
import type { Rule } from '@/content-linter/types'
import {
liquidDataReferencesDefined,
liquidDataTagFormat,
@@ -31,7 +32,7 @@ describe(liquidDataReferencesDefined.names.join(' - '), () => {
'{% data ui.nested.nested.not-there %}',
'{% data some.random.path %}',
]
const result = await runRule(liquidDataReferencesDefined, {
const result = await runRule(liquidDataReferencesDefined as Rule, {
strings: { markdown: markdown.join('\n') },
})
const errors = result.markdown
@@ -45,7 +46,7 @@ describe(liquidDataReferencesDefined.names.join(' - '), () => {
'{% data variables.location.product_location %}',
'{% data ui.header.notices.release_candidate %}',
].join('\n')
const result = await runRule(liquidDataReferencesDefined, { strings: { markdown } })
const result = await runRule(liquidDataReferencesDefined as Rule, { strings: { markdown } })
const errors = result.markdown
expect(errors.length).toBe(0)
})
@@ -57,7 +58,7 @@ describe(liquidDataReferencesDefined.names.join(' - '), () => {
'{% data %}',
'{% indented_data_reference %}',
]
const result = await runRule(liquidDataTagFormat, {
const result = await runRule(liquidDataTagFormat as Rule, {
strings: { markdown: markdown.join('\n') },
})
const errors = result.markdown
@@ -69,7 +70,7 @@ describe(liquidDataReferencesDefined.names.join(' - '), () => {
'{% data ui.header.notices.release_candidate %}',
'{% indented_data_reference ui.header.notices.release_candidate spaces=3 %}',
]
const result = await runRule(liquidDataTagFormat, {
const result = await runRule(liquidDataTagFormat as Rule, {
strings: { markdown: markdown.join('\n') },
})
const errors = result.markdown

View File

@@ -4,6 +4,7 @@ import { execSync } from 'child_process'
import { renderLiquid } from '@/content-render/liquid/index'
import shortVersionsMiddleware from '@/versions/middleware/short-versions'
import type { ExtendedRequest } from '@/types'
const { loadPages } = await import('@/frame/lib/page-data.js')
const { allVersions } = await import('@/versions/lib/all-versions.js')
@@ -32,20 +33,20 @@ for (const page of pages) {
console.log(`---\nStart: Creating directories for: ${page.relativePath}`)
const dirnames = page.relativePath.substring(0, page.relativePath.lastIndexOf('/'))
fs.mkdirSync(`${contentCopilotDir}/${dirnames}`, { recursive: true }, (err) => {
if (err) throw err
})
fs.mkdirSync(`${contentCopilotDir}/${dirnames}`, { recursive: true })
// Context needed to render the content liquid
const req = { language: 'en' }
const contextualize = (req) => {
req.context.currentVersionObj = req.context.allVersions[req.context.currentVersion]
const req = { language: 'en' } as ExtendedRequest
const contextualize = (req: ExtendedRequest): void => {
if (!req.context) return
if (!req.context.currentVersion) return
req.context.currentVersionObj = req.context.allVersions?.[req.context.currentVersion]
shortVersionsMiddleware(req, null, () => {})
}
req.context = {
currentLanguage: 'en',
currentVersion: 'free-pro-team@latest',
page: {},
page: {} as any, // Empty page object used only for context initialization
allVersions,
}
contextualize(req)
@@ -77,7 +78,8 @@ for (const page of pages) {
'utf8',
)
console.log(`Done: written file\n---`)
} catch (err) {
} catch (err: any) {
// Standard catch-all for error handling in scripts
console.log(err)
}
}

View File

@@ -4,9 +4,16 @@ Custom "Alerts", based on similar filter/styling in the monolith code.
import { visit } from 'unist-util-visit'
import { h } from 'hastscript'
// @ts-ignore - no types available for @primer/octicons
import octicons from '@primer/octicons'
import type { Element } from 'hast'
const alertTypes = {
interface AlertType {
icon: string
color: string
}
const alertTypes: Record<string, AlertType> = {
NOTE: { icon: 'info', color: 'accent' },
IMPORTANT: { icon: 'report', color: 'done' },
WARNING: { icon: 'alert', color: 'attention' },
@@ -17,14 +24,17 @@ const alertTypes = {
// Must contain one of [!NOTE], [!IMPORTANT], ...
const ALERT_REGEXP = new RegExp(`\\[!(${Object.keys(alertTypes).join('|')})\\]`, 'gi')
const matcher = (node) =>
// Using any due to conflicting unist/hast type definitions between dependencies
const matcher = (node: any): boolean =>
node.type === 'element' &&
node.tagName === 'blockquote' &&
ALERT_REGEXP.test(JSON.stringify(node.children))
export default function alerts({ alertTitles = {} }) {
return (tree) => {
visit(tree, matcher, (node) => {
export default function alerts({ alertTitles = {} }: { alertTitles?: Record<string, string> }) {
// Using any due to conflicting unist/hast type definitions between dependencies
return (tree: any) => {
// Using any due to conflicting unist/hast type definitions between dependencies
visit(tree, matcher, (node: any) => {
const key = getAlertKey(node)
if (!(key in alertTypes)) {
console.warn(
@@ -48,13 +58,14 @@ export default function alerts({ alertTitles = {} }) {
}
}
function getAlertKey(node) {
function getAlertKey(node: Element): string {
const body = JSON.stringify(node.children)
const matches = body.match(ALERT_REGEXP)
return matches[0].slice(2, -1)
return matches![0].slice(2, -1)
}
function removeAlertSyntax(node) {
// Using any to handle both array and object node types recursively
function removeAlertSyntax(node: any): any {
if (Array.isArray(node)) {
return node.map(removeAlertSyntax)
}
@@ -67,7 +78,7 @@ function removeAlertSyntax(node) {
return node
}
function getOcticonSVG(name) {
function getOcticonSVG(name: string): Element {
return h(
'svg',
{
@@ -78,6 +89,6 @@ function getOcticonSVG(name) {
className: 'octicon mr-2',
ariaHidden: true,
},
h('path', { d: octicons[name].heights[16].path.match(/d="(.*)"/)[1] }),
h('path', { d: octicons[name].heights[16].path.match(/d="(.*)"/)![1] }),
)
}

View File

@@ -32,7 +32,7 @@ const section = {
},
},
],
}
} as const
export default {
type: 'object',
@@ -69,7 +69,10 @@ export default {
'errata',
'closing_down',
'retired',
].reduce((prev, curr) => ({ ...prev, [curr]: section }), {}),
].reduce(
(prev: Record<string, typeof section>, curr: string) => ({ ...prev, [curr]: section }),
{},
),
},
},
}
} as const

View File

@@ -28,13 +28,20 @@ page.permalinks is an array of objects that looks like this:
]
*/
class Permalink {
constructor(languageCode, pageVersion, relativePath, title) {
languageCode: string
pageVersion: string
relativePath: string
title: string
hrefWithoutLanguage: string
href: string
constructor(languageCode: string, pageVersion: string, relativePath: string, title: string) {
this.languageCode = languageCode
this.pageVersion = pageVersion
this.relativePath = relativePath
this.title = title
const permalinkSuffix = this.constructor.relativePathToSuffix(relativePath)
const permalinkSuffix = Permalink.relativePathToSuffix(relativePath)
this.hrefWithoutLanguage = removeFPTFromPath(
path.posix.join('/', pageVersion, permalinkSuffix),
@@ -46,18 +53,23 @@ class Permalink {
return this
}
static derive(languageCode, relativePath, title, applicableVersions) {
static derive(
languageCode: string,
relativePath: string,
title: string,
applicableVersions: string[],
): Permalink[] {
assert(relativePath, 'relativePath is required')
assert(languageCode, 'languageCode is required')
const permalinks = applicableVersions.map((pageVersion) => {
const permalinks = applicableVersions.map((pageVersion: string) => {
return new Permalink(languageCode, pageVersion, relativePath, title)
})
return permalinks
}
static relativePathToSuffix(relativePath) {
static relativePathToSuffix(relativePath: string): string {
if (relativePath === 'index.md') return '/'
// When you turn `foo/bar.md`, which is a file path, into a URL pathname,
// you just need to chop off the `.md` suffix.

View File

@@ -2,7 +2,12 @@ import matter from '@gr2m/gray-matter'
import { validateJson } from '@/tests/lib/validate-json-schema'
function readFrontmatter(markdown, opts = {}) {
interface ReadFrontmatterOptions {
schema?: Record<string, any> // Schema can have arbitrary properties for validation
filepath?: string | null
}
function readFrontmatter(markdown: string, opts: ReadFrontmatterOptions = {}) {
const schema = opts.schema || { type: 'object', properties: {} }
const filepath = opts.filepath || null
@@ -10,7 +15,7 @@ function readFrontmatter(markdown, opts = {}) {
try {
;({ content, data } = matter(markdown))
} catch (e) {
} catch (e: any) {
const defaultReason = 'invalid frontmatter entry'
const reason = e.reason
@@ -21,7 +26,7 @@ function readFrontmatter(markdown, opts = {}) {
: e.reason
: defaultReason
const error = {
const error: any = {
reason,
message: 'YML parsing error!',
}
@@ -33,7 +38,7 @@ function readFrontmatter(markdown, opts = {}) {
return { errors }
}
const validate = validateJson(schema, data)
const validate: any = validateJson(schema, data)
// Combine the AJV-supplied `instancePath` and `params` into a more user-friendly frontmatter path.
// For example, given:
@@ -44,7 +49,7 @@ function readFrontmatter(markdown, opts = {}) {
//
// The purpose is to help users understand that the error is on the `fpt` key within the `versions` object.
// Note if the error is on a top-level FM property like `title`, the `instancePath` will be empty.
const cleanPropertyPath = (params, instancePath) => {
const cleanPropertyPath = (params: Record<string, any>, instancePath: string) => {
const mainProps = Object.values(params)[0]
if (!instancePath) return mainProps
@@ -55,8 +60,8 @@ function readFrontmatter(markdown, opts = {}) {
const errors = []
if (!validate.isValid && filepath) {
const formattedErrors = validate.errors.map((error) => {
const userFriendly = {}
const formattedErrors = validate.errors.map((error: any) => {
const userFriendly: any = {}
userFriendly.property = cleanPropertyPath(error.params, error.instancePath)
userFriendly.message = error.message
userFriendly.reason = error.keyword

View File

@@ -1,3 +1,12 @@
import type { Response } from 'express'
interface CacheControlOptions {
key?: string
public_?: boolean
immutable?: boolean
maxAgeZero?: boolean
}
// Return a function you can pass a Response object to and it will
// set the `Cache-Control` header.
//
@@ -11,9 +20,14 @@
// Max age is in seconds
// Max age should not be greater than 31536000 https://www.ietf.org/rfc/rfc2616.txt
function cacheControlFactory(
maxAge = 60 * 60,
{ key = 'cache-control', public_ = true, immutable = false, maxAgeZero = false } = {},
) {
maxAge: number = 60 * 60,
{
key = 'cache-control',
public_ = true,
immutable = false,
maxAgeZero = false,
}: CacheControlOptions = {},
): (res: Response) => void {
const directives = [
maxAge && public_ && 'public',
maxAge && `max-age=${maxAge}`,
@@ -26,7 +40,7 @@ function cacheControlFactory(
]
.filter(Boolean)
.join(', ')
return (res) => {
return (res: Response) => {
if (process.env.NODE_ENV !== 'production' && res.hasHeader('set-cookie') && maxAge) {
console.warn(
"You can't set a >0 cache-control header AND set-cookie or else the CDN will never respect the cache-control.",
@@ -50,7 +64,7 @@ const searchBrowserCacheControl = cacheControlFactory(60 * 60)
const searchCdnCacheControl = cacheControlFactory(60 * 60 * 24, {
key: 'surrogate-control',
})
export function searchCacheControl(res) {
export function searchCacheControl(res: Response): void {
searchBrowserCacheControl(res)
searchCdnCacheControl(res)
}
@@ -65,7 +79,7 @@ const defaultCDNCacheControl = cacheControlFactory(60 * 60 * 24, {
const defaultBrowserCacheControl = cacheControlFactory(60)
// A general default configuration that is useful to almost all responses
// that can be cached.
export function defaultCacheControl(res) {
export function defaultCacheControl(res: Response): void {
defaultCDNCacheControl(res)
defaultBrowserCacheControl(res)
}
@@ -74,7 +88,7 @@ export function defaultCacheControl(res) {
// x-user-language is a custom request header derived from req.cookie:user_language
// accept-language is truncated to one of our available languages
// https://bit.ly/3u5UeRN
export function languageCacheControl(res) {
export function languageCacheControl(res: Response): void {
defaultCacheControl(res)
res.set('vary', 'accept-language, x-user-language')
}

View File

@@ -24,7 +24,7 @@ describe('Permalink class', () => {
const homepagePermalink = permalinks.find(
(permalink) => permalink.pageVersion === nonEnterpriseDefaultVersion,
)
expect(homepagePermalink.href).toBe('/en')
expect(homepagePermalink?.href).toBe('/en')
})
test('derives info for non-enterprise versioned homepage', () => {

View File

@@ -6,6 +6,7 @@ import EnterpriseServerReleases from '@/versions/lib/enterprise-server-releases'
import { loadSiteTree } from '@/frame/lib/page-data'
import nonEnterpriseDefaultVersion from '@/versions/lib/non-enterprise-default-version'
import { formatAjvErrors } from '@/tests/helpers/schemas'
import type { SiteTree, Tree } from '@/types'
const latestEnterpriseRelease = EnterpriseServerReleases.latest
@@ -14,9 +15,9 @@ const siteTreeValidate = getJsonValidator(schema.childPage)
describe('siteTree', () => {
vi.setConfig({ testTimeout: 3 * 60 * 1000 })
let siteTree
let siteTree: SiteTree
beforeAll(async () => {
siteTree = await loadSiteTree()
siteTree = (await loadSiteTree()) as SiteTree
})
test('has language codes as top-level keys', () => {
@@ -39,12 +40,12 @@ describe('siteTree', () => {
// TODO: use new findPageInSiteTree helper when it's available
const pageWithDynamicTitle = ghesSiteTree.childPages
.find((child) => child.href === `/en/${ghesLatest}/admin`)
.childPages.find(
?.childPages.find(
(child) => child.href === `/en/${ghesLatest}/admin/installing-your-enterprise-server`,
)
// Confirm the raw title contains Liquid
expect(pageWithDynamicTitle.page.title).toEqual(
expect(pageWithDynamicTitle?.page.title).toEqual(
'Installing {% data variables.product.prodname_enterprise %}',
)
})
@@ -58,18 +59,22 @@ describe('siteTree', () => {
})
})
function validate(currentPage) {
;(currentPage.childPages || []).forEach((childPage) => {
function validate(currentPage: Tree): void {
const childPages: Tree[] = currentPage.childPages || []
childPages.forEach((childPage) => {
// Store page reference before validation to avoid type narrowing
const pageRef: Tree = childPage
const isValid = siteTreeValidate(childPage)
let errors
let errors: string | undefined
if (!isValid) {
errors = `file ${childPage.page.fullPath}: ${formatAjvErrors(siteTreeValidate.errors)}`
const fullPath = pageRef.page.fullPath
errors = `file ${fullPath}: ${formatAjvErrors(siteTreeValidate.errors || [])}`
}
expect(isValid, errors).toBe(true)
// Run recursively until we run out of child pages
validate(childPage)
validate(pageRef)
})
}

View File

@@ -58,7 +58,7 @@ export function updateContentFiles() {
// To preserve newlines when stringifying,
// you can set the lineWidth option to -1
// This prevents updates to the file that aren't actual changes.
fs.writeFileSync(file, frontmatter.stringify(content, data, { lineWidth: -1 } as any))
fs.writeFileSync(file, frontmatter.stringify(content!, data, { lineWidth: -1 } as any))
continue
}
if (featureAppliesToAllVersions) {
@@ -71,7 +71,7 @@ export function updateContentFiles() {
// To preserve newlines when stringifying,
// you can set the lineWidth option to -1
// This prevents updates to the file that aren't actual changes.
fs.writeFileSync(file, frontmatter.stringify(content, data, { lineWidth: -1 } as any))
fs.writeFileSync(file, frontmatter.stringify(content!, data, { lineWidth: -1 } as any))
continue
}
@@ -94,7 +94,7 @@ export function updateContentFiles() {
// Remove the ghes property from versions Fm and return
delete data.versions.ghes
console.log('Removing GHES version from: ', file)
fs.writeFileSync(file, frontmatter.stringify(content, data, { lineWidth: -1 } as any))
fs.writeFileSync(file, frontmatter.stringify(content!, data, { lineWidth: -1 } as any))
}
}
}

View File

@@ -9,7 +9,9 @@ import { GRAPHQL_DATA_DIR } from '../lib/index'
const allVersionValues = Object.values(allVersions)
const graphqlVersions = allVersionValues.map((v) => v.openApiVersionName)
const graphqlTypes = readJsonFile('./src/graphql/lib/types.json').map((t) => t.kind)
const graphqlTypes = (readJsonFile('./src/graphql/lib/types.json') as Array<{ kind: string }>).map(
(t) => t.kind,
)
const previewsValidate = getJsonValidator(previewsValidator)
const upcomingChangesValidate = getJsonValidator(upcomingChangesValidator)
@@ -20,9 +22,11 @@ describe('graphql json files', () => {
// The typeObj is repeated thousands of times in each .json file
// so use a cache of which we've already validated to speed this
// test up significantly.
const typeObjsTested = new Set()
const typeObjsTested = new Set<string>()
graphqlVersions.forEach((version) => {
const schemaJsonPerVersion = readJsonFile(`${GRAPHQL_DATA_DIR}/${version}/schema.json`)
const schemaJsonPerVersion = readJsonFile(
`${GRAPHQL_DATA_DIR}/${version}/schema.json`,
) as Record<string, Array<{ kind: string; name: string }>>
// all graphql types are arrays except for queries
graphqlTypes.forEach((type) => {
test(`${version} schemas object validation for ${type}`, () => {
@@ -31,12 +35,15 @@ describe('graphql json files', () => {
if (typeObjsTested.has(key)) return
typeObjsTested.add(key)
const { isValid, errors } = validateJson(schemaValidator[type], typeObj)
const { isValid, errors } = validateJson(
schemaValidator[type as keyof typeof schemaValidator],
typeObj,
)
let formattedErrors = errors
let formattedErrors: any = errors // Can be either raw errors array or formatted string
if (!isValid) {
formattedErrors = `kind: ${typeObj.kind}, name: ${typeObj.name}: ${formatAjvErrors(
errors,
errors || [],
)}`
}
@@ -48,13 +55,13 @@ describe('graphql json files', () => {
test('previews object validation', () => {
graphqlVersions.forEach((version) => {
const previews = readJsonFile(`${GRAPHQL_DATA_DIR}/${version}/previews.json`)
const previews = readJsonFile(`${GRAPHQL_DATA_DIR}/${version}/previews.json`) as Array<any> // GraphQL preview schema structure is dynamic
previews.forEach((preview) => {
const isValid = previewsValidate(preview)
let errors
let errors: string | undefined
if (!isValid) {
errors = formatAjvErrors(previewsValidate.errors)
errors = formatAjvErrors(previewsValidate.errors || [])
}
expect(isValid, errors).toBe(true)
@@ -64,15 +71,17 @@ describe('graphql json files', () => {
test('upcoming changes object validation', () => {
graphqlVersions.forEach((version) => {
const upcomingChanges = readJsonFile(`${GRAPHQL_DATA_DIR}/${version}/upcoming-changes.json`)
const upcomingChanges = readJsonFile(
`${GRAPHQL_DATA_DIR}/${version}/upcoming-changes.json`,
) as Record<string, Array<any>> // GraphQL change object structure is dynamic
for (const changes of Object.values(upcomingChanges)) {
// each object value is an array of changes
changes.forEach((changeObj) => {
const isValid = upcomingChangesValidate(changeObj)
let errors
let errors: string | undefined
if (!isValid) {
errors = formatAjvErrors(upcomingChangesValidate.errors)
errors = formatAjvErrors(upcomingChangesValidate.errors || [])
}
expect(isValid, errors).toBe(true)

View File

@@ -66,7 +66,7 @@ async function main(): Promise<void> {
for (const file of files) {
const contents = await fs.promises.readFile(file)
const contentPath = path.relative(ROOTDIR, file)
const { data } = readFrontmatter(contents)
const { data } = readFrontmatter(contents.toString())
const versionString = JSON.stringify(data?.versions || {}).replaceAll('"', "'")
const pathToQuery = getPathToQuery(file)
// Pass null to get all versions (the default if no version is provided)

View File

@@ -5,17 +5,21 @@ import path from 'path'
import { allVersions } from '@/versions/lib/all-versions'
const OPEN_API_RELEASES_DIR = '../github/app/api/description/config/releases'
const configData = JSON.parse(await readFile('src/rest/lib/config.json', 'utf8'))
const configData: { versionMapping: Record<string, string> } = JSON.parse(
await readFile('src/rest/lib/config.json', 'utf8'),
)
// Gets the full list of unpublished + active, deprecated + active,
// or active schemas from the github/github repo
// `openApiReleaseDir` is the path to the `app/api/description/config/releases`
// directory in `github/github`
// You can also specify getting specific versions of schemas.
export async function getSchemas(directory = OPEN_API_RELEASES_DIR) {
export async function getSchemas(
directory: string = OPEN_API_RELEASES_DIR,
): Promise<{ currentReleases: string[]; unpublished: string[]; deprecated: string[] }> {
const openAPIConfigs = await readdir(directory)
const unpublished = []
const deprecated = []
const currentReleases = []
const unpublished: string[] = []
const deprecated: string[] = []
const currentReleases: string[] = []
// The file content in the `github/github` repo is YAML before it is
// bundled into JSON.
@@ -23,7 +27,7 @@ export async function getSchemas(directory = OPEN_API_RELEASES_DIR) {
const fileBaseName = path.basename(file, '.yaml')
const newFileName = `${fileBaseName}.deref.json`
const content = await readFile(path.join(directory, file), 'utf8')
const yamlContent = yaml.load(content)
const yamlContent = yaml.load(content) as { published?: boolean; deprecated?: boolean }
const releaseMatch = Object.keys(configData.versionMapping).find((name) =>
fileBaseName.startsWith(name),
@@ -62,7 +66,7 @@ export async function getSchemas(directory = OPEN_API_RELEASES_DIR) {
return { currentReleases, unpublished, deprecated }
}
export async function validateVersionsOptions(versions) {
export async function validateVersionsOptions(versions: string[]): Promise<void> {
const schemas = await getSchemas()
// Validate individual versions provided
versions.forEach((version) => {

View File

@@ -4,14 +4,17 @@ import { allVersions } from '@/versions/lib/all-versions'
import { liquid } from '@/content-render/index'
import { supported } from '@/versions/lib/enterprise-server-releases'
import shortVersionsMiddleware from '@/versions/middleware/short-versions'
import type { ExtendedRequest } from '@/types'
const contextualize = (req) => {
req.context.currentVersionObj = req.context.allVersions[req.context.currentVersion]
const contextualize = (req: ExtendedRequest): void => {
if (!req.context) throw new Error('No context on request')
if (!req.context.currentVersion) throw new Error('No currentVersion in context')
req.context.currentVersionObj = req.context.allVersions?.[req.context.currentVersion]
shortVersionsMiddleware(req, null, () => {})
}
describe('ifversion conditionals', () => {
const req = {}
const req: ExtendedRequest = {} as ExtendedRequest
beforeAll(async () => {
req.context = {
allVersions,