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

Convert 12 JavaScript files to TypeScript (#57575)

This commit is contained in:
Kevin Heis
2025-09-19 11:57:12 -07:00
committed by GitHub
parent f313d7bcc0
commit a91e77679a
16 changed files with 200 additions and 95 deletions

View File

@@ -4,8 +4,8 @@ import { runRule } from '../../lib/init-test'
import { githubOwnedActionReferences } from '../../lib/linting-rules/github-owned-action-references'
describe(githubOwnedActionReferences.names.join(' - '), () => {
test('Using hardcoded GitHub-owned actions causes error', async () => {
const markdown = [
test('Using hardcoded GitHub-owned actions causes error', async (): Promise<void> => {
const markdown: string = [
'Hello actions/checkout@v2 apps.',
'A actions/delete-package-versions@v2 for apps.',
'Hello actions/download-artifact@v2.',
@@ -13,7 +13,11 @@ describe(githubOwnedActionReferences.names.join(' - '), () => {
'actions/cache@',
'[link title](/actions/cache)',
].join('\n')
const result = await runRule(githubOwnedActionReferences, { strings: { markdown } })
const result = await runRule(githubOwnedActionReferences, {
strings: { markdown },
files: undefined,
ruleConfig: true,
})
const errors = result.markdown
expect(errors.length).toBe(3)
})

View File

@@ -6,12 +6,16 @@ import { tableLiquidVersioning } from '../../lib/linting-rules/table-liquid-vers
const FIXTURE_FILEPATH = 'src/content-linter/tests/fixtures/tables.md'
describe(tableLiquidVersioning.names.join(' - '), () => {
test('non-early access file with early access references fails', async () => {
const result = await runRule(tableLiquidVersioning, { files: [FIXTURE_FILEPATH] })
test('non-early access file with early access references fails', async (): Promise<void> => {
const result = await runRule(tableLiquidVersioning, {
strings: undefined,
files: [FIXTURE_FILEPATH],
ruleConfig: true,
})
const errors = result[FIXTURE_FILEPATH]
expect(errors.length).toBe(11)
const lineNumbers = errors.map((error) => error.lineNumber)
const expectedErrorLines = [38, 40, 43, 44, 51, 53, 54, 55, 57, 58, 59]
const lineNumbers: number[] = errors.map((error) => error.lineNumber)
const expectedErrorLines: number[] = [38, 40, 43, 44, 51, 53, 54, 55, 57, 58, 59]
expect(JSON.stringify(lineNumbers)).toEqual(JSON.stringify(expectedErrorLines))
})
})

View File

@@ -7,11 +7,12 @@ import { decode } from 'html-entities'
// Take advantage of the subtle fact that a lot of the times, the html value
// we get here is a single line that starts with `<p>` and ends with `</p>`
// and contains no longer HTML tags.
export function fastTextOnly(html) {
export function fastTextOnly(html: string): string {
if (!html) return ''
if (html.startsWith('<p>') && html.endsWith('</p>')) {
const middle = html.slice(3, -4)
if (!middle.includes('<')) return decode(middle.trim())
}
return cheerio.load(html, { xmlMode: true }).text().trim()
const $ = cheerio.load(html, { xmlMode: true })
return $.root().text().trim()
}

View File

@@ -1,20 +1,24 @@
import { schema } from '@/frame/lib/frontmatter'
// Copy the properties from the frontmatter schema.
const featureVersions = {
interface FeatureVersionsSchema {
type: 'object'
properties: {
versions: Object.assign({}, schema.properties.versions),
versions: any
}
additionalProperties: false
}
// Copy the properties from the frontmatter schema.
const featureVersions: FeatureVersionsSchema = {
type: 'object',
properties: {
versions: Object.assign({}, (schema.properties as any).versions),
},
additionalProperties: false,
}
// Remove the feature versions properties.
// We don't want to allow features within features! We just want pure versioning.
delete featureVersions.properties.versions.properties.feature
// Call it invalid if any properties other than version properties are found.
featureVersions.additionalProperties = false
// avoid ajv strict warning
featureVersions.type = 'object'
export default featureVersions

View File

@@ -1,18 +0,0 @@
export const term = {
type: 'string',
minLength: 1,
pattern: '^((?!\\*).)*$', // no asterisks allowed
}
export default {
type: 'array',
items: {
type: 'object',
required: ['term'],
additionalProperties: false,
properties: {
term,
},
},
minItems: 21,
}

View File

@@ -0,0 +1,43 @@
export interface TermSchema {
type: 'string'
minLength: number
pattern: string
}
export const term: TermSchema = {
type: 'string',
minLength: 1,
pattern: '^((?!\\*).)*$', // no asterisks allowed
}
export interface GlossaryCandidateItem {
term: string
}
export interface GlossaryCandidatesSchema {
type: 'array'
items: {
type: 'object'
required: ['term']
additionalProperties: false
properties: {
term: TermSchema
}
}
minItems: number
}
const schema: GlossaryCandidatesSchema = {
type: 'array',
items: {
type: 'object',
required: ['term'],
additionalProperties: false,
properties: {
term,
},
},
minItems: 21,
}
export default schema

View File

@@ -1,18 +0,0 @@
import { term } from './glossaries-candidates'
export default {
type: 'array',
items: {
type: 'object',
required: ['term', 'description'],
additionalProperties: false,
properties: {
term,
description: {
type: 'string',
lintable: true,
},
},
},
minItems: 21,
}

View File

@@ -0,0 +1,44 @@
import { term, type TermSchema } from './glossaries-candidates'
export interface GlossaryExternalItem {
term: string
description: string
}
export interface DescriptionSchema {
type: 'string'
lintable: boolean
}
export interface GlossariesExternalSchema {
type: 'array'
items: {
type: 'object'
required: ['term', 'description']
additionalProperties: false
properties: {
term: TermSchema
description: DescriptionSchema
}
}
minItems: number
}
const schema: GlossariesExternalSchema = {
type: 'array',
items: {
type: 'object',
required: ['term', 'description'],
additionalProperties: false,
properties: {
term,
description: {
type: 'string',
lintable: true,
},
},
},
minItems: 21,
}
export default schema

View File

@@ -3,15 +3,15 @@ import { describe, expect, test } from 'vitest'
import filenameToKey from '@/data-directory/lib/filename-to-key'
describe('filename-to-key', () => {
test('converts filenames to object keys', () => {
test('converts filenames to object keys', (): void => {
expect(filenameToKey('foo/bar/baz.txt')).toBe('foo.bar.baz')
})
test('ignores leading slash on filenames', () => {
test('ignores leading slash on filenames', (): void => {
expect(filenameToKey('/foo/bar/baz.txt')).toBe('foo.bar.baz')
})
test('supports MS Windows paths', () => {
test('supports MS Windows paths', (): void => {
expect(filenameToKey('path\\to\\file.txt')).toBe('path.to.file')
})
})

View File

@@ -5,13 +5,13 @@ import { get } from '@/tests/helpers/e2etest'
describe('general /api pages', () => {
vi.setConfig({ testTimeout: 60 * 1000 })
test("any /api URL that isn't found should JSON", async () => {
test("any /api URL that isn't found should JSON", async (): Promise<void> => {
const res = await get('/api')
expect(res.statusCode).toBe(404)
expect(res.headers['content-type']).toMatch(/application\/json/)
})
test("any /api/* URL that isn't found should be JSON", async () => {
test("any /api/* URL that isn't found should be JSON", async (): Promise<void> => {
const res = await get('/api/yadayada')
expect(res.statusCode).toBe(404)
expect(JSON.parse(res.body).error).toBe('/yadayada not found')

View File

@@ -1,15 +0,0 @@
import yaml from 'js-yaml'
import { groupBy } from 'lodash-es'
import { renderContent } from '@/content-render/index'
export default async function processUpcomingChanges(upcomingChangesYml) {
const upcomingChanges = yaml.load(upcomingChangesYml).upcoming_changes
for (const change of upcomingChanges) {
change.date = change.date.slice(0, 10)
change.reason = await renderContent(change.reason)
change.description = await renderContent(change.description)
}
return groupBy(upcomingChanges.reverse(), 'date')
}

View File

@@ -0,0 +1,28 @@
import yaml from 'js-yaml'
import { groupBy } from 'lodash-es'
import { renderContent } from '@/content-render/index'
interface UpcomingChange {
date: string
reason: string
description: string
[key: string]: unknown
}
interface UpcomingChangesData {
upcoming_changes: UpcomingChange[]
}
export default async function processUpcomingChanges(
upcomingChangesYml: string,
): Promise<Record<string, UpcomingChange[]>> {
const upcomingChanges = (yaml.load(upcomingChangesYml) as UpcomingChangesData).upcoming_changes
for (const change of upcomingChanges) {
change.date = change.date.slice(0, 10)
change.reason = await renderContent(change.reason)
change.description = await renderContent(change.description)
}
return groupBy(upcomingChanges.reverse(), 'date')
}

View File

@@ -5,15 +5,15 @@
// in the JSON file.
// These paths must match the paths in src/pages/[versionId]/rest
export const nonAutomatedRestPaths = [
export const nonAutomatedRestPaths: readonly string[] = [
'/rest/quickstart',
'/rest/about-the-rest-api',
'/rest/using-the-rest-api',
'/rest/authentication',
'/rest/guides',
]
] as const
// This path is used to set the page in the
// src/rest/components/ApiVersionPicker.tsx component. That component
// has a link to the page that describes what api versioning is.
export const apiVersionPath = '/rest/about-the-rest-api/api-versions'
export const apiVersionPath: string = '/rest/about-the-rest-api/api-versions'

View File

@@ -1,16 +0,0 @@
import { expect } from 'vitest'
import { SURROGATE_ENUMS } from '@/frame/middleware/set-fastly-surrogate-key'
export function checkCachingHeaders(res, defaultSurrogateKey = false, minMaxAge = 60 * 60) {
expect(res.headers['set-cookie']).toBeUndefined()
expect(res.headers['cache-control']).toContain('public')
const maxAgeSeconds = parseInt(res.headers['cache-control'].match(/max-age=(\d+)/)[1], 10)
// Let's not be too specific in the tests, just as long as it's testing
// that it's a reasonably large number of seconds.
expect(maxAgeSeconds).toBeGreaterThanOrEqual(minMaxAge)
// Because it doesn't have a unique URL
expect(res.headers['surrogate-key'].split(/\s/g)[0]).toBe(
defaultSurrogateKey ? SURROGATE_ENUMS.DEFAULT : SURROGATE_ENUMS.MANUAL,
)
}

View File

@@ -0,0 +1,34 @@
import { expect } from 'vitest'
import { SURROGATE_ENUMS } from '@/frame/middleware/set-fastly-surrogate-key'
interface ResponseWithHeaders {
headers: Record<string, string | string[] | undefined>
}
export function checkCachingHeaders(
res: ResponseWithHeaders,
defaultSurrogateKey: boolean = false,
minMaxAge: number = 60 * 60,
): void {
expect(res.headers['set-cookie']).toBeUndefined()
expect(res.headers['cache-control']).toContain('public')
const cacheControlHeader = res.headers['cache-control'] as string
const maxAgeMatch = cacheControlHeader.match(/max-age=(\d+)/)
if (!maxAgeMatch) {
throw new Error('Cache-Control header does not contain max-age directive')
}
const maxAgeSeconds = parseInt(maxAgeMatch[1], 10)
// Let's not be too specific in the tests, just as long as it's testing
// that it's a reasonably large number of seconds.
expect(maxAgeSeconds).toBeGreaterThanOrEqual(minMaxAge)
// Because it doesn't have a unique URL
const surrogateKeyHeader = res.headers['surrogate-key'] as string
expect(surrogateKeyHeader.split(/\s/g)[0]).toBe(
defaultSurrogateKey ? SURROGATE_ENUMS.DEFAULT : SURROGATE_ENUMS.MANUAL,
)
}

View File

@@ -2,7 +2,12 @@ import semver from 'semver'
// Where "release" is a release number, like `3.1` for Enterprise Server,
// and "range" is a semver range operator with another number, like `<=3.2`.
export default function versionSatisfiesRange(release, range) {
export default function versionSatisfiesRange(release: string | undefined, range: string): boolean {
// Handle undefined release
if (!release) {
return false
}
// workaround for Enterprise Server 11.10.340 because we can't use semver to
// compare it to 2.x like we can with 2.0+
if (release === '11.10.340') return range.startsWith('<')
@@ -15,5 +20,10 @@ export default function versionSatisfiesRange(release, range) {
release = '1.0'
}
return semver.satisfies(semver.coerce(release), range)
const coercedRelease = semver.coerce(release)
if (!coercedRelease) {
throw new Error(`Unable to coerce release version: ${release}`)
}
return semver.satisfies(coercedRelease, range)
}