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

Eslint review, round 1 (#58168)

This commit is contained in:
Kevin Heis
2025-10-27 15:01:39 -07:00
committed by GitHub
parent f57c6ec88f
commit 232a0f8516
73 changed files with 246 additions and 182 deletions

View File

@@ -67,63 +67,33 @@ export default [
// JSX A11y recommended rules
...jsxA11y.configs.recommended.rules,
// TypeScript-specific overrides
// Overrides
'import/no-extraneous-dependencies': [
'error',
{
packageDir: '.',
},
],
'import/extensions': 'off',
'no-console': 'off',
camelcase: 'off',
'no-shadow': 'off',
'prefer-template': 'off',
'no-constant-condition': 'off',
'no-unused-vars': 'off',
'no-undef': 'off',
'no-use-before-define': 'off',
'no-redeclare': 'off', // Allow function overloads in TypeScript
'import/no-named-as-default-member': 'off',
'one-var': 'off',
'import/no-namespace': 'off',
'import/no-anonymous-default-export': 'off',
'object-shorthand': 'off',
'no-empty': 'off',
'prefer-const': 'off',
'import/no-named-as-default': 'off',
'no-useless-concat': 'off',
'func-style': 'off',
// TypeScript ESLint specific rules
'import/extensions': ['error', { json: 'always' }],
'no-empty': ['error', { allowEmptyCatch: true }],
'@typescript-eslint/no-unused-vars': 'error',
'prefer-const': ['error', { destructuring: 'all' }],
// Disable GitHub plugin rules that were disabled in original config
'github/array-foreach': 'off',
'github/no-then': 'off',
// Rules that must be disabled
'no-redeclare': 'off', // Allow function overloads in TypeScript
'i18n-text/no-en': 'off', // This rule causes eslint to not run at all
'filenames/match-regex': 'off', // This rule causes eslint to not run at all
// Disable rules that might not exist or cause issues initially
'i18n-text/no-en': 'off',
'filenames/match-regex': 'off',
'eslint-comments/no-use': 'off',
'eslint-comments/no-unused-disable': 'off',
'eslint-comments/no-unlimited-disable': 'off',
// Disable new ESLint 9 rules that are causing issues
'no-constant-binary-expression': 'off',
// Disable stricter TypeScript rules initially
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-unused-expressions': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/no-wrapper-object-types': 'off',
'@typescript-eslint/no-non-null-asserted-optional-chain': 'off',
'@typescript-eslint/no-unsafe-function-type': 'off',
'@typescript-eslint/no-empty-object-type': 'off',
'@typescript-eslint/prefer-as-const': 'off',
// React/JSX specific rules
'jsx-a11y/no-onchange': 'off',
// Disabled rules to review
'github/no-then': 'off', // 30+
'@typescript-eslint/ban-ts-comment': 'off', // 50+
'no-undef': 'off', // 50+
'no-shadow': 'off', // 150+
'prefer-template': 'off', // 150+
'github/array-foreach': 'off', // 250+
camelcase: 'off', // 600+
'no-console': 'off', // 800+
'@typescript-eslint/no-explicit-any': 'off', // 1000+
},
},

View File

@@ -53,7 +53,7 @@
"index-general-search": "tsx src/search/scripts/index/index-cli general-search",
"index-test-fixtures": "./src/search/scripts/index-test-fixtures.sh",
"labeler": "tsx .github/actions/labeler/labeler.ts",
"lint": "eslint '**/*.{js,mjs,ts,tsx}'",
"lint": "eslint '**/*.{ts,tsx}'",
"lint-content": "tsx src/content-linter/scripts/lint-content.ts",
"lint-translation": "vitest src/content-linter/tests/lint-files.ts",
"liquid-markdown-tables": "tsx src/tools/scripts/liquid-markdown-tables/index.ts",
@@ -67,8 +67,8 @@
"postinstall": "cp package-lock.json .installed.package-lock.json && echo \"Updated .installed.package-lock.json\" # see husky/post-checkout and husky/post-merge",
"precompute-pageinfo": "tsx src/article-api/scripts/precompute-pageinfo.ts",
"prepare": "husky src/workflows/husky",
"prettier": "prettier -w \"**/*.{ts,tsx,js,mjs,scss,yml,yaml}\"",
"prettier-check": "prettier -c \"**/*.{ts,tsx,js,mjs,scss,yml,yaml}\"",
"prettier": "prettier -w \"**/*.{ts,tsx,scss,yml,yaml}\"",
"prettier-check": "prettier -c \"**/*.{ts,tsx,scss,yml,yaml}\"",
"prevent-pushes-to-main": "tsx src/workflows/prevent-pushes-to-main.ts",
"purge-fastly-edge-cache": "tsx src/workflows/purge-fastly-edge-cache.ts",
"purge-fastly-edge-cache-per-language": "tsx src/languages/scripts/purge-fastly-edge-cache-per-language.ts",
@@ -105,12 +105,12 @@
"what-docs-early-access-branch": "tsx src/early-access/scripts/what-docs-early-access-branch.ts"
},
"lint-staged": {
"*.{js,mjs,ts,tsx}": "eslint --cache --fix",
"*.{js,mjs,scss,ts,tsx,yml,yaml}": "prettier --write",
"*.{ts,tsx}": "eslint --cache --fix",
"*.{scss,ts,tsx,yml,yaml}": "prettier --write",
"{content,data}/**/*.md": "npm run lint-content -- --precommit --paths"
},
"nodemonConfig": {
"ext": "ts,js,json,yml,md,html,scss",
"ext": "ts,json,yml,md,html,scss",
"exec": "tsx",
"ignore": [
"assets",
@@ -139,7 +139,7 @@
},
{
"files": [
"**/*.{ts,tsx,js,mjs}"
"**/*.{ts,tsx}"
],
"options": {
"printWidth": 100,

View File

@@ -235,7 +235,7 @@ export default async function archivedEnterpriseVersions(
// `x-host` is a custom header set by Fastly.
// GLB automatically deletes the `x-forwarded-host` header.
const host = req.get('x-host') || req.get('x-forwarded-host') || req.get('host')
let modifiedBody = body
const modifiedBody = body
.replaceAll(
`${OLD_AZURE_BLOB_ENTERPRISE_DIR}/${requestedVersion}/assets/cb-`,
`${ENTERPRISE_GH_PAGES_URL_PREFIX}${requestedVersion}/assets/cb-`,

View File

@@ -1,4 +1,4 @@
import * as github from '@actions/github'
import github from '@actions/github'
import core from '@actions/core'
const { GITHUB_TOKEN } = process.env

View File

@@ -145,7 +145,9 @@ async function main(opts: MainOptions) {
// Add exceptions
sourceFiles.push('.github/CONTRIBUTING.md')
sourceFiles.push('README.md')
verbose && console.log(`${sourceFiles.length.toLocaleString()} source files found in total.`)
if (verbose) {
console.log(`${sourceFiles.length.toLocaleString()} source files found in total.`)
}
const allImages = new Set(
walk(
@@ -159,7 +161,9 @@ async function main(opts: MainOptions) {
).filter((filePath) => !filePath.endsWith('.md')),
)
verbose && console.log(`${allImages.size.toLocaleString()} images found in total.`)
if (verbose) {
console.log(`${allImages.size.toLocaleString()} images found in total.`)
}
for (const sourceFile of sourceFiles) {
const content = fs.readFileSync(sourceFile, 'utf-8')

View File

@@ -43,7 +43,7 @@ type MockResponse = {
_json?: string
_send?: string
headers: Record<string, string>
set?: (key: string | Object, value: string) => void
set?: (key: string | object, value: string) => void
removeHeader?: (key: string) => void
hasHeader?: (key: string) => boolean
}

View File

@@ -96,7 +96,7 @@ async function resolveReferenceLinksToTitles(
// docs_reference_links: 'event reference links'
// },
// ]
export function getAuditLogEvents(page: string, version: string) {
export function getAuditLogEvents(page: string, version: string): AuditLogEventT[] {
const openApiVersion = getOpenApiVersion(version)
const auditLogFileName = path.join(AUDIT_LOG_DATA_DIR, openApiVersion, `${page}.json`)
@@ -115,14 +115,14 @@ export function getAuditLogEvents(page: string, version: string) {
?.set(page, readCompressedJsonFileFallback(auditLogFileName))
}
const auditLogEvents = auditLogEventsCache.get(openApiVersion)?.get(page)!
const auditLogEvents = auditLogEventsCache.get(openApiVersion)?.get(page)
// If an event doesn't yet have a description (value will be empty string or
// "N/A"), then we don't show the event.
const filteredAuditLogEvents = auditLogEvents.filter(
const filteredAuditLogEvents = auditLogEvents?.filter(
(event) => event.description !== 'N/A' && event.description !== '',
)
return filteredAuditLogEvents
return filteredAuditLogEvents || []
}
// get categorized audit log event data for the requested page and version
@@ -137,7 +137,7 @@ export function getAuditLogEvents(page: string, version: string) {
// repo: [ [Object] ],
// user: [ [Object], [Object] ]
// }
export function getCategorizedAuditLogEvents(page: string, version: string) {
export function getCategorizedAuditLogEvents(page: string, version: string): CategorizedEvents {
const events = getAuditLogEvents(page, version)
const openApiVersion = getOpenApiVersion(version)
@@ -148,7 +148,7 @@ export function getCategorizedAuditLogEvents(page: string, version: string) {
categorizedAuditLogEventsCache.get(openApiVersion)?.set(page, categorizeEvents(events))
}
return categorizedAuditLogEventsCache.get(openApiVersion)?.get(page)!
return categorizedAuditLogEventsCache.get(openApiVersion)?.get(page) || {}
}
// Filters audit log events based on allowlist values.

View File

@@ -86,7 +86,9 @@ export const getServerSideProps: GetServerSideProps<Props> = async (context) =>
Object.keys(auditLogEvents).map((category) => category),
context,
)
auditLogEventsMiniTocs && miniTocItems.push(...auditLogEventsMiniTocs)
if (auditLogEventsMiniTocs) {
miniTocItems.push(...auditLogEventsMiniTocs)
}
return {
props: {

View File

@@ -1,4 +1,4 @@
import * as nodePath from 'path'
import nodePath from 'path'
import { reportingConfig } from '@/content-linter/style/github-docs'
interface LintFlaw {

View File

@@ -30,7 +30,7 @@ export const liquidIfversionVersions = {
// The versions frontmatter object or all versions if the file
// being processed is a data file.
const fm = getFrontmatter(params.lines)
let content = fm ? getFrontmatterLines(params.lines).join('\n') : params.lines.join('\n')
const content = fm ? getFrontmatterLines(params.lines).join('\n') : params.lines.join('\n')
const fileVersionsFm = params.name.startsWith('data')
? { ghec: '*', ghes: '*', fpt: '*' }

View File

@@ -72,7 +72,7 @@ export const noteWarningFormatting = {
// Collect content inside legacy notes
if (inLegacyNote) {
noteContent.push({ text: line, lineNumber: lineNumber })
noteContent.push({ text: line, lineNumber })
continue
}

View File

@@ -78,8 +78,8 @@ function findOutdatedTerminologyMatches(line: string): MatchInfo[] {
start: match.index,
end: match.index + match[0].length,
text: match[0],
replacement: replacement,
outdatedTerm: outdatedTerm,
replacement,
outdatedTerm,
})
}
}

View File

@@ -240,7 +240,9 @@ if (ymlToLint.length === 0) {
describe('lint yaml content', () => {
if (ymlToLint.length < 1) return
describe.each(ymlToLint)('%s', (yamlRelPath, yamlAbsPath) => {
let dictionary, isEarlyAccess, fileContents
let dictionary
let isEarlyAccess
let fileContents
// This variable is used to determine if the file was parsed successfully.
// When `yaml.load()` fails to parse the file, it is overwritten with the error message.
// `false` is intentionally chosen since `null` and `undefined` are valid return values.

View File

@@ -37,7 +37,7 @@ const notRegex = /(?:^|\s)not\s/
// native Liquid `if` block tag. It has special handling for statements like {% ifversion ghes < 3.0 %},
// using semver to evaluate release numbers instead of doing standard number comparisons, which
// don't work the way we want because they evaluate 3.2 > 3.10 = true.
export default class extends Tag {
export default class Ifversion extends Tag {
tagToken: TagToken
branches: Branch[]
elseTemplates: Template[]

View File

@@ -29,7 +29,7 @@ export const Prompt: LiquidTag = {
},
// Render the inner Markdown, wrap in <code>, then append the SVG
render: function* (scope: any): Generator<any, string, unknown> {
*render(scope: any): Generator<any, string, unknown> {
const content = yield this.liquid.renderer.renderTemplates(this.templates, scope)
// build a URL with the prompt text encoded as query parameter

View File

@@ -53,7 +53,7 @@ export const Spotlight = {
stream.start()
},
render: function* (scope: Record<string, unknown>): Generator<unknown, unknown, unknown> {
*render(scope: Record<string, unknown>): Generator<unknown, unknown, unknown> {
const output = yield this.liquid!.renderer.renderTemplates(this.templates, scope)
return yield this.liquid!.parseAndRender(template, {

View File

@@ -66,7 +66,7 @@ export const Tool = {
},
// scope is a Liquid scope object, Generator yields/returns Liquid template values - no TypeScript definitions available
render: function* (scope: any): Generator<any, any, any> {
*render(scope: any): Generator<any, any, any> {
const output = yield this.liquid.renderer.renderTemplates(this.templates, scope)
return yield this.liquid.parseAndRender(template, {
tagName: this.tagName,

View File

@@ -8,7 +8,7 @@
*
*/
import * as github from '@actions/github'
import github from '@actions/github'
import core from '@actions/core'
import { program } from 'commander'

View File

@@ -20,7 +20,7 @@ describe('data-directory', () => {
})
test('option: preprocess function', async () => {
const preprocess = function (content: string) {
function preprocess(content: string) {
return content.replace('markdown', 'MARKDOWN')
}
const data = dataDirectory(fixturesDir, { preprocess })

View File

@@ -42,7 +42,7 @@ const sentValidationErrors = new QuickLRU({
// to prevent sending multiple validation errors that can spam requests to Hydro
const getValidationErrorHash = (validateErrors: ErrorObject[]) => {
// limit to 10 second windows
const window: Number = Math.floor(new Date().getTime() / 10000)
const window: number = Math.floor(new Date().getTime() / 10000)
return `${window}:${(validateErrors || [])
.map((error: ErrorObject) => error.message + error.instancePath + JSON.stringify(error.params))
.join(':')}`

View File

@@ -105,12 +105,12 @@ export type EventPropsByType = {
link_samepage?: boolean
link_container?: string
}
[EventType.page]: {}
[EventType.page]: { type: string } // no unique properties
[EventType.preference]: {
preference_name: string
preference_value: string
}
[EventType.print]: {}
[EventType.print]: { type: string } // no unique properties
[EventType.search]: {
search_query: string
search_context?: string

View File

@@ -47,7 +47,7 @@ describe('annotations', () => {
const noteTexts = notes.map((i: number, el: any) => $(el).text()).get()
expect(noteTexts).toEqual([
'Configures this workflow to run every time a change is pushed to the branch called release.',
'This job checks out the repository contents ...\n' + "And here's the second comment line.",
"This job checks out the repository contents ...\nAnd here's the second comment line.",
])
}
})

View File

@@ -748,7 +748,7 @@ test.describe('survey', () => {
await page.evaluate(() => {
Object.defineProperty(document, 'visibilityState', {
configurable: true,
get: function () {
get() {
return 'hidden'
},
})
@@ -803,7 +803,7 @@ test.describe('survey', () => {
await page.evaluate(() => {
Object.defineProperty(document, 'visibilityState', {
configurable: true,
get: function () {
get() {
return 'hidden'
},
})

View File

@@ -24,8 +24,8 @@ export const SharedUIContextProvider = ({ children }: { children: React.ReactNod
return (
<SharedUIContext.Provider
value={{
hasOpenHeaderNotifications: hasOpenHeaderNotifications,
setHasOpenHeaderNotifications: setHasOpenHeaderNotifications,
hasOpenHeaderNotifications,
setHasOpenHeaderNotifications,
}}
>
{children}

View File

@@ -1,6 +1,6 @@
import { useMainContext } from '@/frame/components/context/MainContext'
export type FeatureFlags = {}
export type FeatureFlags = Record<string, string>
export const useFeatureFlags = (): FeatureFlags => {
const { featureFlags } = useMainContext()

View File

@@ -86,8 +86,8 @@ export const UnrenderedMarkdownContent = ({
sendEvent({
type: EventType.clipboard,
clipboard_operation: 'copy',
eventGroupKey: eventGroupKey,
eventGroupId: eventGroupId,
eventGroupKey,
eventGroupId,
})
}}
></IconButton>

View File

@@ -11,7 +11,8 @@ function readFrontmatter(markdown: string, opts: ReadFrontmatterOptions = {}) {
const schema = opts.schema || { type: 'object', properties: {} }
const filepath = opts.filepath || null
let content, data
let content
let data
try {
;({ content, data } = matter(markdown))

View File

@@ -5,9 +5,18 @@ import { createLogger } from '@/observability/logger'
const logger = createLogger(import.meta.url)
type WarmServerResult = {
pages: Awaited<ReturnType<typeof loadPageMap>>
redirects: Awaited<ReturnType<typeof loadRedirects>>
unversionedTree: Awaited<ReturnType<typeof loadUnversionedTree>>
siteTree: Awaited<ReturnType<typeof loadSiteTree>>
pageList: Awaited<ReturnType<typeof loadPages>>
pageMap: Awaited<ReturnType<typeof loadPageMap>>
}
// Instrument these functions so that
// it's wrapped in a timer that reports to Datadog
const dog: Record<string, Function> = {
const dog = {
loadUnversionedTree: statsd.asyncTimer(loadUnversionedTree, 'load_unversioned_tree'),
loadSiteTree: statsd.asyncTimer(loadSiteTree, 'load_site_tree'),
loadPages: statsd.asyncTimer(loadPages, 'load_pages'),
@@ -17,9 +26,9 @@ const dog: Record<string, Function> = {
}
// For multiple-triggered Promise sharing
let promisedWarmServer: any
let promisedWarmServer: Promise<WarmServerResult> | undefined
async function warmServer(languagesOnly = []) {
async function warmServer(languagesOnly: string[] = []): Promise<WarmServerResult> {
const startTime = Date.now()
logger.debug(
@@ -42,6 +51,7 @@ async function warmServer(languagesOnly = []) {
unversionedTree,
siteTree,
pageList,
pageMap,
}
}

View File

@@ -42,7 +42,7 @@ if (process.env.ELASTICSEARCH_URL) {
createProxyMiddleware({
target: 'https://docs.github.com',
changeOrigin: true,
pathRewrite: function (path, req: ExtendedRequest) {
pathRewrite(path, req: ExtendedRequest) {
return req.originalUrl
},
}),

View File

@@ -23,7 +23,7 @@ export function blockIndex(path: string) {
return pathRegExps.some((pathRe) => pathRe.test(path))
}
const middleware = function blockRobots(req: Request, res: Response, next: NextFunction) {
function middleware(req: Request, res: Response, next: NextFunction) {
if (blockIndex(req.path)) res.set('x-robots-tag', 'noindex')
return next()
}

View File

@@ -170,7 +170,7 @@ async function getTocItems(node: Tree, context: Context, opts: Options): Promise
}
}
let childTocItems = []
const childTocItems = []
if (child.childPages) {
childTocItems.push(...(await getTocItems(child, context, opts)))
}

View File

@@ -18,9 +18,7 @@ const DEFAULT_OPTIONS = {
crossOriginResourcePolicy: true,
crossOriginEmbedderPolicy: false, // doesn't work with youtube
referrerPolicy: {
// See docs-engineering #2426
// The `... as 'no-referrer-when-downgrade'` is a workaround for TypeScript
policy: 'no-referrer-when-downgrade' as 'no-referrer-when-downgrade',
policy: 'no-referrer-when-downgrade' as const,
},
// This module defines a Content Security Policy (CSP) to disallow
// inline scripts and content from untrusted sources.

View File

@@ -78,11 +78,15 @@ const ENABLE_FASTLY_TESTING = JSON.parse(process.env.ENABLE_FASTLY_TESTING || 'f
// Catch unhandled promise rejections and passing them to Express's error handler
// https://medium.com/@Abazhenov/using-async-await-in-express-with-node-8-b8af872c0016
const asyncMiddleware = (fn: Function) => (req: Request, res: Response, next: NextFunction) => {
Promise.resolve(fn(req, res, next)).catch(next)
}
const asyncMiddleware =
<TReq extends Request = Request, T = void>(
fn: (req: TReq, res: Response, next: NextFunction) => T | Promise<T>,
) =>
(req: Request, res: Response, next: NextFunction) => {
Promise.resolve(fn(req as TReq, res, next)).catch(next)
}
export default function (app: Express) {
export default function index(app: Express) {
// *** Request connection management ***
if (!isTest) app.use(timeout(MAX_REQUEST_TIMEOUT))
app.use(abort)

View File

@@ -356,7 +356,7 @@ async function isExistingIssue(
): Promise<boolean> {
const { labels, searchQuery, titleMatch } = opts
const labelQuery = labels && labels.map((label) => `label:"${encodeURI(label)}"`).join('+')
let query = encodeURIComponent('is:issue ' + `repo:${repo} `)
let query = encodeURIComponent(`is:issue repo:${repo} `)
if (searchQuery) {
query += '+' + searchQuery

View File

@@ -63,10 +63,11 @@ main()
async function main() {
console.log(`Archiving Enterprise version: ${version}`)
let pageList: PageList, urls: Array<string>
let pageList: PageList
let urls: Array<string>
if (singlePage) {
const pageName = singlePage.trim().startsWith('/') ? singlePage.slice(1) : singlePage
const urls = languageKeys
urls = languageKeys
.map((key) => `/${key}/enterprise-server@${version}/${pageName}`)
.map((href) => `${host}${href}`)
console.log(`\nScraping HTML for a single page only:\n${urls.join('\n')}\n`)

View File

@@ -1,6 +1,18 @@
import fs from 'fs'
import path from 'path'
interface ScraperResource {
isHtml(): boolean
isCss(): boolean
getText(): string
getFilename(): string
encoding: BufferEncoding
}
interface ResourceSavedArgs {
resource: ScraperResource
}
export class RewriteAssetPathsPlugin {
tempDirectory: string
localDev: boolean
@@ -12,8 +24,10 @@ export class RewriteAssetPathsPlugin {
this.replaceUrl = replaceUrl
}
apply(registerAction: Function) {
registerAction('onResourceSaved', async ({ resource }: any) => {
apply(
registerAction: (event: string, callback: (args: ResourceSavedArgs) => Promise<void>) => void,
) {
registerAction('onResourceSaved', async ({ resource }: ResourceSavedArgs) => {
// Show some activity
process.stdout.write('.')

View File

@@ -70,7 +70,9 @@ export async function getAppsServerSideProps(
? Object.values(appsItems).map((item: any) => item.displayTitle!)
: Object.keys(appsItems)
const appMiniToc = await getAutomatedPageMiniTocItems(titles, context)
appMiniToc && miniTocItems.push(...appMiniToc)
if (appMiniToc) {
miniTocItems.push(...appMiniToc)
}
return { currentVersion, appsItems, categoriesWithoutSubcategories }
}

View File

@@ -452,7 +452,7 @@ function getDisplayTitle(
: `"${sentenceCase(title)}" ` + resourceGroup + ' permissions'
: !resourceGroup
? sentenceCase(title) + ' permissions'
: sentenceCase(resourceGroup) + ' permissions for ' + `"${title}"`
: sentenceCase(resourceGroup) + ` permissions for "${title}"`
return { title, displayTitle }
}

View File

@@ -18,7 +18,7 @@ import {
type Props = {
mainContext: MainContextT
automatedPageContext: AutomatedPageContextT
schema: Object
schema: object
language: string
graphqlPageName: string
objects?: ObjectT[]

View File

@@ -28,7 +28,7 @@ const versionsToBuild = Object.keys(allVersions)
main()
let allIgnoredChanges = []
const allIgnoredChanges = []
async function main() {
for (const version of versionsToBuild) {

View File

@@ -2,12 +2,38 @@ import type { ProductGroupT } from '@/landings/components/ProductSelections'
import React from 'react'
import { Link } from '@/frame/components/Link'
import * as Octicons from '@primer/octicons-react'
import { LinkExternalIcon } from '@primer/octicons-react'
import {
LinkExternalIcon,
RocketIcon,
CommentDiscussionIcon,
CopilotIcon,
GearIcon,
ShieldLockIcon,
DeviceMobileIcon,
ProjectIcon,
OrganizationIcon,
CodeSquareIcon,
GlobeIcon,
PencilIcon,
} from '@primer/octicons-react'
type ProductSelectionCardProps = {
group: ProductGroupT
}
const octiconMap: { [name: string]: React.FunctionComponent } = {
LinkExternalIcon,
RocketIcon,
CommentDiscussionIcon,
CopilotIcon,
GearIcon,
ShieldLockIcon,
DeviceMobileIcon,
ProjectIcon,
OrganizationIcon,
CodeSquareIcon,
GlobeIcon,
PencilIcon,
}
export const ProductSelectionCard = ({ group }: ProductSelectionCardProps) => {
// Don't display the group if it has no children due to versioning
@@ -27,9 +53,11 @@ export const ProductSelectionCard = ({ group }: ProductSelectionCardProps) => {
</div>
)
} else if (group.octicon) {
const octicon: React.FunctionComponent = (
Octicons as { [name: string]: React.FunctionComponent }
)[group.octicon] as React.FunctionComponent
const octicon: React.FunctionComponent = octiconMap[group.octicon]
if (!octicon) {
throw new Error(`Octicon ${group.octicon} not found`)
}
return (
<div className="mr-2">

View File

@@ -78,7 +78,7 @@ export function createTranslationFallbackComment(error: Error, property: string)
} else if (isAutotitleError(error)) {
// For AUTOTITLE errors, include the error message
if (error.message) {
let cleanMessage = error.message
const cleanMessage = error.message
.replace(/\n/g, ' ')
.replace(/\s+/g, ' ')
.trim()

View File

@@ -175,7 +175,7 @@ async function indexOfLearningTrackGuide(
const renderOpts = { textOnly: true }
for (let i = 0; i < trackGuidePaths.length; i++) {
// Learning track URLs may have Liquid conditionals.
let renderedGuidePath = await executeWithFallback(
const renderedGuidePath = await executeWithFallback(
context,
() => renderContent(trackGuidePaths[i], context, renderOpts),
() => '', // todo use english trackGuidePaths[i]

View File

@@ -180,7 +180,10 @@ async function main(opts: MainOptions, args: string[]) {
'utf-8',
)
}
const brokenLinks: {}[] = []
const brokenLinks: {
linkPath: string
file: string
}[] = []
// Break up the long list of URLs to test into batches
for (const batch of [...Array(Math.floor(docsLinksFiles.length / BATCH_SIZE)).keys()]) {

View File

@@ -145,7 +145,7 @@ let cleanPath = getCleanPath(providedPath)
// Get the version
let version: string | null = getVersion(cleanPath)
let usingFptOnly = !!options.fptOnly
const usingFptOnly = !!options.fptOnly
// If the URL does not specify a version, default to all versions unless --fptOnly is passed
if (version === FREE_PRO_TEAM) {

View File

@@ -1,4 +1,4 @@
import type { NextFunction, Response, ErrorRequestHandler } from 'express'
import type { NextFunction, Response } from 'express'
import FailBot from '../lib/failbot'
import { nextApp } from '@/frame/middleware/next'
@@ -50,7 +50,7 @@ function timedOut(req: ExtendedRequest) {
statsd.increment('middleware.timeout', 1, incrementTags)
}
const handleError: ErrorRequestHandler = async function handleError(
async function handleError(
error: ErrorWithCode | number,
req: ExtendedRequest,
res: Response,

View File

@@ -43,7 +43,7 @@ describe('getAutomaticRequestLogger', () => {
}
// Override res.end to simulate response completion
const endOverride = function (this: any, chunk?: any, encoding?: any) {
function endOverride(this: any, chunk?: any, encoding?: any) {
if (!responseEnded) {
responseEnded = true
// Simulate a small delay for response time
@@ -143,7 +143,7 @@ describe('getAutomaticRequestLogger', () => {
}
// Override res.end to simulate response completion
const endOverride = function (this: any, chunk?: any, encoding?: any) {
function endOverride(this: any, chunk?: any, encoding?: any) {
if (!responseEnded) {
responseEnded = true
// Simulate a small delay for response time

View File

@@ -75,7 +75,7 @@ function FineGrainedAccess({ progAccess }: FineGrainedProps) {
// Each object represents a set of permissions containing one
// or more key-value pairs. All permissions in a set are required.
// If there is more than one set of permissions, any set can be used.
const formattedPermissions = progAccess.permissions.map((permissionSet: Object, index) => {
const formattedPermissions = progAccess.permissions.map((permissionSet: object, index) => {
// Given the example above, the first object is now an array of tuples
// [['"Actions" repository permissions', 'read'], ['"Administration" organization permissions', 'read']]
// that can be formatted as a string like `"Administration" organization permissions (write)'

View File

@@ -18,7 +18,7 @@ export interface ProgAccessT {
userToServerRest?: boolean
serverToServer?: boolean
fineGrainedPat?: boolean
permissions: Array<Object>
permissions: Array<object>
allowPermissionlessAccess?: boolean
allowsPublicRead?: boolean
basicAuth?: boolean
@@ -90,8 +90,8 @@ export type ExampleT = {
statusCode: string
contentType?: string
description: string
example?: Object
schema?: Object
example?: object
schema?: object
}
}

View File

@@ -200,7 +200,9 @@ export const getServerSideProps: GetServerSideProps<Props> = async (context) =>
req.context,
)) as MinitocItemsT
restOperationsMiniTocItems && miniTocItems.push(...restOperationsMiniTocItems)
if (restOperationsMiniTocItems) {
miniTocItems.push(...restOperationsMiniTocItems)
}
}
// Replace the toc items in the context with the REST toc items we just

View File

@@ -80,7 +80,9 @@ export const getServerSideProps: GetServerSideProps<Props> = async (context) =>
req.context,
)) as MinitocItemsT
restOperationsMiniTocItems && miniTocItems.push(...restOperationsMiniTocItems)
if (restOperationsMiniTocItems) {
miniTocItems.push(...restOperationsMiniTocItems)
}
}
const mainContext = await getMainContext(req, res)

View File

@@ -98,9 +98,11 @@ async function createCheckContentDirectory(contentFiles: string[]): Promise<Chec
})
allCompleteVersions.forEach((version) => {
!checkContent[version][category]
? (checkContent[version][category] = [subCategory])
: checkContent[version][category].push(subCategory)
if (!checkContent[version][category]) {
checkContent[version][category] = [subCategory]
} else {
checkContent[version][category].push(subCategory)
}
checkContent[version][category].sort()
})
}
@@ -121,9 +123,11 @@ function getOnlyApiVersions(version: string): string[] {
function createCheckObj(): CheckObject {
const versions: CheckObject = {}
Object.keys(allVersions).forEach((version) => {
isApiVersioned(version)
? getOnlyApiVersions(version).forEach((apiVersion) => (versions[apiVersion] = {}))
: (versions[`${allVersions[version].version}`] = {})
if (isApiVersioned(version)) {
getOnlyApiVersions(version).forEach((apiVersion) => (versions[apiVersion] = {}))
} else {
versions[`${allVersions[version].version}`] = {}
}
})
return versions

View File

@@ -242,7 +242,7 @@ async function validateInputParameters(): Promise<void> {
}
// Check that the source repo exists.
for (let sourceRepoDirectory of sourceRepoDirectories) {
for (const sourceRepoDirectory of sourceRepoDirectories) {
if (!existsSync(sourceRepoDirectory)) {
const errorMsg =
sourceRepoDirectory === 'github' || sourceRepoDirectory === GITHUB_REP_DIR

View File

@@ -77,13 +77,13 @@ export async function injectModelsSchema(schema: any, schemaName: string): Promi
tags: operationObject.tags || ['models'], // Only use 'models' if no tags present
verb: operation,
requestPath: path,
category: category,
category,
subcategory: operationObject['x-github']?.subcategory || '',
summary: name,
description: description,
description,
'x-github': {
...operationObject['x-github'], // Preserve all x-github metadata
category: category,
category,
enabledForGitHubApps: operationObject['x-github']?.enabledForGitHubApps,
githubCloudOnly: operationObject['x-github']?.githubCloudOnly,
permissions: operationObject['x-github']?.permissions || {},

View File

@@ -74,9 +74,9 @@ export async function executeCombinedSearch(
debug = false,
abortSignal?: AbortSignal,
) {
let language = router.locale || 'en'
const language = router.locale || 'en'
const params = new URLSearchParams({ query: query, version, language })
const params = new URLSearchParams({ query, version, language })
if (debug) {
params.set('debug', '1')
}

View File

@@ -29,7 +29,7 @@ const DEBOUNCE_TIME = 100 // In milliseconds
// Results are only cached for the current session
// We cache results so if a user presses backspace, we can show the results immediately without burdening the API
let sessionCache = {} as Record<string, SearchOptions>
const sessionCache = {} as Record<string, SearchOptions>
// Helper to incorporate version & locale into the cache key
function getCacheKey(query: string, version: string, locale: string) {

View File

@@ -45,9 +45,17 @@ export function useMultiQueryParams() {
const searchParams = new URLSearchParams(asPathQuery)
initialKeys.forEach((key) => {
if (key === 'search-overlay-ask-ai') {
newParams[key] === 'true' ? searchParams.set(key, 'true') : searchParams.delete(key)
if (newParams[key] === 'true') {
searchParams.set(key, 'true')
} else {
searchParams.delete(key)
}
} else {
newParams[key] ? searchParams.set(key, newParams[key]) : searchParams.delete(key)
if (newParams[key]) {
searchParams.set(key, newParams[key])
} else {
searchParams.delete(key)
}
}
})
const paramsString = searchParams.toString() ? `?${searchParams.toString()}` : ''

View File

@@ -566,6 +566,6 @@ function sendAISearchResultEvent({
ai_search_result_response_status: status,
ai_search_result_connected_event_id: connectedEventId,
eventGroupKey: ASK_AI_EVENT_GROUP,
eventGroupId: eventGroupId,
eventGroupId,
})
}

View File

@@ -358,7 +358,7 @@ export function SearchOverlay({
if (searchParams.has('query')) {
searchParams.delete('query')
}
router.push(`${selectedOption.url}?${searchParams.toString()}` || '')
router.push(`${selectedOption.url}?${searchParams.toString()}`)
onClose()
}
@@ -412,12 +412,12 @@ export function SearchOverlay({
if (searchParams.has('query')) {
searchParams.delete('query')
}
window.open(`${url}?${searchParams.toString()}` || '', '_blank')
window.open(`${url}?${searchParams.toString()}`, '_blank')
}
// Handle keyboard navigation of suggestions
const handleKeyDown = (event: React.KeyboardEvent<HTMLElement>) => {
let optionsLength = listElementsRef.current?.length ?? 0
const optionsLength = listElementsRef.current?.length ?? 0
if (event.key === 'ArrowDown') {
event.preventDefault()
if (optionsLength > 0) {
@@ -855,8 +855,8 @@ function renderSearchGroups(
) {
const groups = []
let isInAskAIState = askAIState?.isAskAIState && !askAIState.aiSearchError
let isInAskAIStateButNoAnswer = isInAskAIState && askAIState.aiCouldNotAnswer
const isInAskAIState = askAIState?.isAskAIState && !askAIState.aiSearchError
const isInAskAIStateButNoAnswer = isInAskAIState && askAIState.aiCouldNotAnswer
// This spinner is for both the AI search and the general search results.
// We already show a spinner when streaming AI response, so don't want to show 2 here

View File

@@ -19,7 +19,7 @@ export async function getAISearchAutocompleteResults({
const t0 = new Date()
const client = getElasticsearchClient() as Client
let searchQuery: any = {
const searchQuery: any = {
index: indexName,
size,
// Send absolutely minimal from Elasticsearch to here. Less data => faster.

View File

@@ -70,7 +70,7 @@ router.get(
// If no query is provided, we want to return the top 5 most popular terms
// This is a special case for AI search autocomplete
// So we use `force` to allow the query to be empty without the usual validation error
let force = {} as any
const force = {} as any
if (!req.query.query) {
force.query = ''
}

View File

@@ -76,8 +76,8 @@ export async function populateIndex(
},
)
if (bulkResponse.errors) {
console.error(`Bulk response errors: ${bulkResponse.errors}`)
if (bulkResponse.failed > 0) {
console.error(`Bulk response failed: ${bulkResponse.failed} documents failed`)
throw new Error('Bulk errors happened.')
}
const t1 = new Date()
@@ -140,7 +140,7 @@ export async function updateAlias(
)
for (const index of indices) {
if (index.index !== indexAlias && index.index.startsWith(indexName)) {
if (index.index && index.index !== indexAlias && index.index.startsWith(indexName)) {
aliasUpdates.push({ remove_index: { index: index.index } })
console.log('Deleting old index', index.index)
}

View File

@@ -21,9 +21,9 @@
import { sleep } from '@/search/lib/helpers/time'
export async function retryOnErrorTest(
errorTest: (error: any) => boolean,
callback: Function,
export async function retryOnErrorTest<T>(
errorTest: (error: unknown) => boolean,
callback: () => Promise<T>,
{
attempts = 4,
sleepTime = 1000,
@@ -37,7 +37,7 @@ export async function retryOnErrorTest(
jitterPercent?: number
onError?: (error: Error, attempts: number, sleepTime: number) => void
} = {},
) {
): Promise<T> {
while (true) {
try {
return await callback()

View File

@@ -10,7 +10,7 @@ describe('AI Search Routes', () => {
afterAll(() => stopMockServer())
test('/api/ai-search/v1 should handle a successful response', async () => {
let apiBody = { query: 'How do I create a Repository?', language: 'en', version: 'dotcom' }
const apiBody = { query: 'How do I create a Repository?', language: 'en', version: 'dotcom' }
const response = await fetch('http://localhost:4000/api/ai-search/v1', {
method: 'POST',
@@ -80,7 +80,7 @@ describe('AI Search Routes', () => {
})
test('should handle validation errors: query missing', async () => {
let body = { language: 'en', version: 'dotcom' }
const body = { language: 'en', version: 'dotcom' }
const response = await post('/api/ai-search/v1', {
body: JSON.stringify(body),
headers: { 'Content-Type': 'application/json' },
@@ -95,7 +95,7 @@ describe('AI Search Routes', () => {
})
test('should handle validation errors: version missing', async () => {
let body = { query: 'example query' }
const body = { query: 'example query' }
const response = await post('/api/ai-search/v1', {
body: JSON.stringify(body),
headers: { 'Content-Type': 'application/json' },
@@ -110,7 +110,7 @@ describe('AI Search Routes', () => {
})
test('should handle multiple validation errors: query missing and version', async () => {
let body = { language: 'fr', version: 'fpt' }
const body = { language: 'fr', version: 'fpt' }
const response = await post('/api/ai-search/v1', {
body: JSON.stringify(body),
headers: { 'Content-Type': 'application/json' },
@@ -140,7 +140,7 @@ describe('AI Search Routes', () => {
if (response.body) {
const reader = response.body.getReader()
const decoder = new TextDecoder()
let chunks = []
const chunks = []
try {
while (true) {

View File

@@ -139,7 +139,7 @@ export type Context = {
enPage?: Page
productNames?: ProductNames
currentVersion?: string
process?: { env: {} }
process?: { env: Record<string, string> }
site?: {
data: {
ui: any

View File

@@ -131,7 +131,8 @@ main().then(
// Using any for token objects as liquidjs doesn't provide TypeScript types
function removeInputProps(arrayOfObjects: any[]): any[] {
return arrayOfObjects.map((obj: any) => {
delete obj.input || delete obj.token.input
delete obj.input
delete obj.token.input
return obj
})
}

View File

@@ -21,7 +21,7 @@ interface WebhookActionData {
codeExamples: Array<CodeSample>
availability: Array<string>
action: string
payloadExample?: Object
payloadExample?: object
}
export interface WebhookAction {
name: string

View File

@@ -96,7 +96,9 @@ export const getServerSideProps: GetServerSideProps<Props> = async (context) =>
webhooks.map((webhook) => webhook.data.category),
context,
)
webhooksMiniTocs && miniTocItems.push(...webhooksMiniTocs)
if (webhooksMiniTocs) {
miniTocItems.push(...webhooksMiniTocs)
}
return {
props: {

View File

@@ -10,7 +10,7 @@
import fs from 'node:fs'
import path from 'node:path'
import * as github from '@actions/github'
import github from '@actions/github'
import core from '@actions/core'
import walk from 'walk-sync'

View File

@@ -22,7 +22,7 @@ async function main() {
console.log('ID:', issue.id)
console.log('Number:', issue.number)
console.log('URL:', issue.html_url)
number = issue.number
number = String(issue.number)
if (number) {
// We've found the issue (pull request), but before we accept
// this `number`, check that the issue isn't locked.

View File

@@ -234,12 +234,12 @@ async function searchCode(
}
}
async function secondaryRateLimitRetry(
callable: Function,
args: Record<string, any>,
async function secondaryRateLimitRetry<T, TArgs = Record<string, unknown>>(
callable: (args: TArgs) => Promise<T>,
args: TArgs,
maxAttempts = 10,
sleepTime = 1000,
) {
): Promise<T> {
try {
const response = await callable(args)
return response

View File

@@ -7,10 +7,10 @@ const DEFAULT_ATTEMPTS = parseInt(process.env.SECONDARY_RATELIMIT_RETRY_ATTEMPTS
// "You have exceeded a secondary rate limit".
// More info about what they are here:
// https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api?apiVersion=2022-11-28#about-secondary-rate-limits
export async function octoSecondaryRatelimitRetry(
fn: Function,
export async function octoSecondaryRatelimitRetry<T>(
fn: () => Promise<T>,
{ attempts = DEFAULT_ATTEMPTS, sleepTime = DEFAULT_SLEEPTIME } = {},
) {
): Promise<T> {
let tries = 0
while (true) {
try {

View File

@@ -25,11 +25,17 @@ export function readFiles(dir = 'content', ext = 'md', opts = {}) {
return paths.map((path) => [path, fs.readFileSync(path, 'utf8')])
}
export function filterFiles(files: [path: string, file: string][], fn: Function) {
export function filterFiles(
files: [path: string, file: string][],
fn: (path: string, file: string) => boolean,
) {
return files.filter(([path, file]) => fn(path, file))
}
export function withFiles(files: [path: string, file: string][], fn: Function) {
export function withFiles(
files: [path: string, file: string][],
fn: (path: string, file: string) => string,
) {
return files.map(([path, file]) => [path, fn(path, file)])
}