Skip to content

Commit 7f22544

Browse files
authored
Merge pull request #40775 from github/repo-sync
Repo sync
2 parents 1e8d3a0 + c2170ac commit 7f22544

File tree

18 files changed

+240
-129
lines changed

18 files changed

+240
-129
lines changed

Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ WORKDIR $APP_HOME
8484
# Source code
8585
COPY --chown=node:node src src/
8686
COPY --chown=node:node package.json ./
87-
COPY --chown=node:node next.config.js ./
87+
COPY --chown=node:node next.config.ts ./
8888
COPY --chown=node:node tsconfig.json ./
8989

9090
# From the clones stage
@@ -125,7 +125,7 @@ WORKDIR $APP_HOME
125125
# Source code
126126
COPY --chown=node:node src src/
127127
COPY --chown=node:node package.json ./
128-
COPY --chown=node:node next.config.js ./
128+
COPY --chown=node:node next.config.ts ./
129129
COPY --chown=node:node tsconfig.json ./
130130

131131
# From clones stage

next.config.js renamed to next.config.ts

Lines changed: 9 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,22 @@
11
import fs from 'fs'
22
import path from 'path'
3+
import type { NextConfig } from 'next'
34

45
import frontmatter from '@gr2m/gray-matter'
5-
// Hardcoded log level function since next.config.js cannot import from TypeScript files
6-
// Matches ./src/observability/logger/lib/log-levels
7-
function getLogLevelNumber() {
8-
const LOG_LEVELS = {
9-
error: 0,
10-
warn: 1,
11-
info: 2,
12-
debug: 3,
13-
}
6+
import { getLogLevelNumber } from '@/observability/logger/lib/log-levels'
147

15-
let defaultLogLevel = 'info'
16-
if (
17-
!process.env.LOG_LEVEL &&
18-
(process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'test')
19-
) {
20-
defaultLogLevel = 'debug'
21-
}
22-
23-
const envLogLevel = process.env.LOG_LEVEL?.toLowerCase() || defaultLogLevel
24-
const logLevel = LOG_LEVELS[envLogLevel] !== undefined ? envLogLevel : defaultLogLevel
25-
26-
return LOG_LEVELS[logLevel]
27-
}
28-
29-
// Replace imports with hardcoded values
308
const ROOT = process.env.ROOT || '.'
319

32-
// Hard-coded language keys to avoid TypeScript import in config file
10+
// Language keys are defined here because Next.js config compilation doesn't resolve the @/ path alias
11+
// Importing from src/languages/lib/languages.ts would fail when it tries to import @/frame/lib/constants
12+
// This must match the languages defined in src/languages/lib/languages.ts
3313
const languageKeys = ['en', 'es', 'ja', 'pt', 'zh', 'ru', 'fr', 'ko', 'de']
3414

3515
const homepage = path.posix.join(ROOT, 'content/index.md')
3616
const { data } = frontmatter(fs.readFileSync(homepage, 'utf8'))
37-
const productIds = data.children
17+
const productIds = data.children as string[]
3818

39-
export default {
19+
const config: NextConfig = {
4020
// Transpile @primer/react so Next's webpack can process its CSS and other assets
4121
// This ensures CSS in node_modules/@primer/react is handled by the app's loaders.
4222
transpilePackages: ['@primer/react'],
@@ -106,3 +86,5 @@ export default {
10686
styledComponents: true,
10787
},
10888
}
89+
90+
export default config

src/content-linter/lib/helpers/get-lintable-yml.js renamed to src/content-linter/lib/helpers/get-lintable-yml.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ import ajv from '@/tests/lib/validate-json-schema'
2424
// mdDict will be populated with:
2525
//
2626
// { '/foo/bar/0': 'item 1', '/foo/bar/1': 'item 2' }
27-
const mdDict = new Map()
28-
const lintableData = Object.keys(dataSchemas)
27+
const mdDict = new Map<string, string>()
28+
const lintableData: string[] = Object.keys(dataSchemas)
2929

3030
// To redefine a custom keyword, you must remove it
3131
// then re-add it with the new definition. The default
@@ -37,7 +37,8 @@ ajv.addKeyword({
3737
type: 'string',
3838
// For docs on defining validate see
3939
// https://ajv.js.org/keywords.html#define-keyword-with-validate-function
40-
validate: (compiled, data, schema, parentInfo) => {
40+
// Using any for validate function params because AJV's type definitions for custom keywords are complex
41+
validate: (compiled: any, data: any, schema: any, parentInfo: any): boolean => {
4142
mdDict.set(parentInfo.instancePath, data)
4243
return true
4344
},
@@ -55,13 +56,14 @@ ajv.addKeyword({
5556
// back to the location in the original schema file,
5657
// so we also need the parent path of the `lintable`
5758
// property in the schema.
58-
export async function getLintableYml(dataFilePath) {
59+
export async function getLintableYml(dataFilePath: string): Promise<Record<string, string> | null> {
5960
const matchingDataPath = lintableData.find(
6061
(ref) => dataFilePath === ref || dataFilePath.startsWith(ref),
6162
)
6263
if (!matchingDataPath) return null
6364

6465
const schemaFilePath = dataSchemas[matchingDataPath]
66+
if (!schemaFilePath) return null
6567
const schema = (await import(schemaFilePath)).default
6668
if (!schema) return null
6769

@@ -78,13 +80,15 @@ export async function getLintableYml(dataFilePath) {
7880
// back to a file in the data directory.
7981
// The resulting key looks like:
8082
// 'data/variables/product.yml /pat_v1_caps'
81-
function addPathToKey(mdDict, dataFilePath) {
83+
function addPathToKey(mdDict: Map<string, string>, dataFilePath: string): Map<string, string> {
8284
const keys = Array.from(mdDict.keys())
8385
keys.forEach((key) => {
8486
const newKey = `${dataFilePath} ${key}`
8587
const value = mdDict.get(key)
86-
mdDict.delete(key)
87-
mdDict.set(newKey, value)
88+
if (value !== undefined) {
89+
mdDict.delete(key)
90+
mdDict.set(newKey, value)
91+
}
8892
})
8993
return mdDict
9094
}

src/content-linter/lib/linting-rules/early-access-references.js renamed to src/content-linter/lib/linting-rules/early-access-references.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
1+
// @ts-ignore - markdownlint-rule-helpers doesn't provide TypeScript declarations
12
import { addError } from 'markdownlint-rule-helpers'
23
import yaml from 'js-yaml'
34

45
import { getRange, getFrontmatter } from '../helpers/utils'
6+
import type { RuleParams, RuleErrorCallback, Rule } from '@/content-linter/types'
7+
8+
interface Frontmatter {
9+
redirect_from?: string | string[]
10+
children?: string[]
11+
[key: string]: any
12+
}
513

614
const ERROR_MESSAGE =
715
'An early access reference appears to be used in a non-early access doc. Remove early access references or disable this rule.'
@@ -10,20 +18,20 @@ const ERROR_MESSAGE =
1018
// There are several existing allowed references to `early access`
1119
// as a GitHub feature. This rule focuses on references to early
1220
// access pages.
13-
const isEarlyAccessFilepath = (filepath) => filepath.includes('early-access')
21+
const isEarlyAccessFilepath = (filepath: string): boolean => filepath.includes('early-access')
1422

1523
const EARLY_ACCESS_REGEX = /early-access/gi
1624
// This is a pattern seen in link paths for articles about
1725
// early access. This pattern is ok.
1826
const EARLY_ACCESS_ARTICLE_REGEX = /-early-access-/
1927

20-
export const earlyAccessReferences = {
28+
export const earlyAccessReferences: Rule = {
2129
names: ['GHD008', 'early-access-references'],
2230
description:
2331
'Files that are not early access should not reference early-access or early-access files',
2432
tags: ['feature', 'early-access'],
2533
severity: 'error',
26-
function: (params, onError) => {
34+
function: (params: RuleParams, onError: RuleErrorCallback) => {
2735
if (isEarlyAccessFilepath(params.name)) return
2836

2937
// Find errors in content
@@ -44,17 +52,17 @@ export const earlyAccessReferences = {
4452
},
4553
}
4654

47-
export const frontmatterEarlyAccessReferences = {
55+
export const frontmatterEarlyAccessReferences: Rule = {
4856
names: ['GHD009', 'frontmatter-early-access-references'],
4957
description:
5058
'Files that are not early access should not have frontmatter that references early-access',
5159
tags: ['frontmatter', 'feature', 'early-access'],
52-
function: (params, onError) => {
60+
function: (params: RuleParams, onError: RuleErrorCallback) => {
5361
const filepath = params.name
5462
if (isEarlyAccessFilepath(filepath)) return
5563

5664
// Find errors in frontmatter
57-
const fm = getFrontmatter(params.lines)
65+
const fm = getFrontmatter(params.lines) as Frontmatter | null
5866
if (!fm) return
5967

6068
// The redirect_from property is allowed to contain early-access paths

src/content-linter/lib/linting-rules/expired-content.js renamed to src/content-linter/lib/linting-rules/expired-content.ts

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
// @ts-ignore - markdownlint-rule-helpers doesn't provide TypeScript declarations
12
import { addError, newLineRe } from 'markdownlint-rule-helpers'
23

4+
import type { RuleParams, RuleErrorCallback, MarkdownToken, Rule } from '@/content-linter/types'
5+
36
// This rule looks for opening and closing HTML comment tags that
47
// contain an expiration date in the format:
58
//
@@ -8,20 +11,20 @@ import { addError, newLineRe } from 'markdownlint-rule-helpers'
811
//
912
// The `end expires` closing tag closes the content that is expired
1013
// and must be removed.
11-
export const expiredContent = {
14+
export const expiredContent: Rule = {
1215
names: ['GHD038', 'expired-content'],
1316
description: 'Expired content must be remediated.',
1417
tags: ['expired'],
15-
function: (params, onError) => {
16-
const tokensToCheck = params.tokens.filter(
17-
(token) => token.type === 'inline' || token.type === 'html_block',
18+
function: (params: RuleParams, onError: RuleErrorCallback) => {
19+
const tokensToCheck = (params.tokens || []).filter(
20+
(token: MarkdownToken) => token.type === 'inline' || token.type === 'html_block',
1821
)
1922

20-
tokensToCheck.forEach((token) => {
23+
tokensToCheck.forEach((token: MarkdownToken) => {
2124
// Looking for just opening tag with format:
2225
// <!-- expires yyyy-mm-dd -->
23-
const match = token.content.match(/<!--\s*expires\s(\d\d\d\d)-(\d\d)-(\d\d)\s*-->/)
24-
if (!match) return
26+
const match = token.content?.match(/<!--\s*expires\s(\d\d\d\d)-(\d\d)-(\d\d)\s*-->/)
27+
if (!match || !token.content) return
2528

2629
const expireDate = new Date(match.splice(1, 3).join(' '))
2730
const today = new Date()
@@ -57,20 +60,20 @@ export const DAYS_TO_WARN_BEFORE_EXPIRED = 14
5760
//
5861
// The `end expires` closing tag closes the content that is expired
5962
// and must be removed.
60-
export const expiringSoon = {
63+
export const expiringSoon: Rule = {
6164
names: ['GHD039', 'expiring-soon'],
6265
description: 'Content that expires soon should be proactively addressed.',
6366
tags: ['expired'],
64-
function: (params, onError) => {
65-
const tokensToCheck = params.tokens.filter(
66-
(token) => token.type === 'inline' || token.type === 'html_block',
67+
function: (params: RuleParams, onError: RuleErrorCallback) => {
68+
const tokensToCheck = (params.tokens || []).filter(
69+
(token: MarkdownToken) => token.type === 'inline' || token.type === 'html_block',
6770
)
6871

69-
tokensToCheck.forEach((token) => {
72+
tokensToCheck.forEach((token: MarkdownToken) => {
7073
// Looking for just opening tag with format:
7174
// <!-- expires yyyy-mm-dd -->
72-
const match = token.content.match(/<!--\s*expires\s(\d\d\d\d)-(\d\d)-(\d\d)\s*-->/)
73-
if (!match) return
75+
const match = token.content?.match(/<!--\s*expires\s(\d\d\d\d)-(\d\d)-(\d\d)\s*-->/)
76+
if (!match || !token.content) return
7477

7578
const expireDate = new Date(match.splice(1, 3).join(' '))
7679
const today = new Date()

src/content-linter/lib/linting-rules/frontmatter-versions-whitespace.js renamed to src/content-linter/lib/linting-rules/frontmatter-versions-whitespace.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
1+
// @ts-ignore - markdownlint-rule-helpers doesn't provide TypeScript declarations
12
import { addError } from 'markdownlint-rule-helpers'
23
import { getFrontmatter } from '@/content-linter/lib/helpers/utils'
4+
import type { RuleParams, RuleErrorCallback, Rule } from '@/content-linter/types'
35

4-
export const frontmatterVersionsWhitespace = {
6+
interface Frontmatter {
7+
versions?: Record<string, string | string[]>
8+
[key: string]: any
9+
}
10+
11+
export const frontmatterVersionsWhitespace: Rule = {
512
names: ['GHD051', 'frontmatter-versions-whitespace'],
613
description: 'Versions frontmatter should not contain unnecessary whitespace',
714
tags: ['frontmatter', 'versions'],
8-
function: (params, onError) => {
9-
const fm = getFrontmatter(params.lines)
15+
function: (params: RuleParams, onError: RuleErrorCallback) => {
16+
const fm = getFrontmatter(params.lines) as Frontmatter | null
1017
if (!fm || !fm.versions) return
1118

1219
const versionsObj = fm.versions
@@ -58,7 +65,7 @@ export const frontmatterVersionsWhitespace = {
5865
* Allows whitespace in complex expressions like '<3.6 >3.8'
5966
* but disallows leading/trailing whitespace
6067
*/
61-
function checkForUnwantedWhitespace(value) {
68+
function checkForUnwantedWhitespace(value: string): boolean {
6269
// Don't flag if the value is just whitespace or empty
6370
if (!value || value.trim() === '') return false
6471

@@ -82,7 +89,7 @@ function checkForUnwantedWhitespace(value) {
8289
/**
8390
* Get the cleaned version of a value by removing appropriate whitespace
8491
*/
85-
function getCleanedValue(value) {
92+
function getCleanedValue(value: string): string {
8693
// For values with operators, just trim leading/trailing whitespace
8794
const hasOperators = /[<>=]/.test(value)
8895
if (hasOperators) {

src/content-linter/lib/linting-rules/frontmatter-video-transcripts.js renamed to src/content-linter/lib/linting-rules/frontmatter-video-transcripts.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,26 @@
1+
// @ts-ignore - markdownlint-rule-helpers doesn't provide TypeScript declarations
12
import { addError } from 'markdownlint-rule-helpers'
23
import path from 'path'
34

45
import { getFrontmatter } from '../helpers/utils'
6+
import type { RuleParams, RuleErrorCallback, Rule } from '@/content-linter/types'
57

6-
export const frontmatterVideoTranscripts = {
8+
interface Frontmatter {
9+
product_video?: string
10+
product_video_transcript?: string
11+
title?: string
12+
layout?: string
13+
[key: string]: any
14+
}
15+
16+
export const frontmatterVideoTranscripts: Rule = {
717
names: ['GHD011', 'frontmatter-video-transcripts'],
818
description: 'Video transcript must be configured correctly',
919
tags: ['frontmatter', 'feature', 'video-transcripts'],
10-
function: (params, onError) => {
20+
function: (params: RuleParams, onError: RuleErrorCallback) => {
1121
const filepath = params.name
1222

13-
const fm = getFrontmatter(params.lines)
23+
const fm = getFrontmatter(params.lines) as Frontmatter | null
1424
if (!fm) return
1525

1626
const isTranscriptContent =
@@ -29,7 +39,7 @@ export const frontmatterVideoTranscripts = {
2939
null, // No fix possible
3040
)
3141
}
32-
if (!fm.title.startsWith('Transcript - ')) {
42+
if (fm.title && !fm.title.startsWith('Transcript - ')) {
3343
const lineNumber = params.lines.findIndex((line) => line.startsWith('title:')) + 1
3444
const lineContent = params.lines[lineNumber - 1]
3545
addError(

src/content-linter/lib/linting-rules/multiple-emphasis-patterns.js renamed to src/content-linter/lib/linting-rules/multiple-emphasis-patterns.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,23 @@
1+
// @ts-ignore - markdownlint-rule-helpers doesn't provide TypeScript declarations
12
import { addError } from 'markdownlint-rule-helpers'
23
import { getRange } from '../helpers/utils'
34
import frontmatter from '@/frame/lib/read-frontmatter'
5+
import type { RuleParams, RuleErrorCallback, Rule } from '@/content-linter/types'
46

5-
export const multipleEmphasisPatterns = {
7+
interface Frontmatter {
8+
autogenerated?: boolean
9+
[key: string]: any
10+
}
11+
12+
export const multipleEmphasisPatterns: Rule = {
613
names: ['GHD050', 'multiple-emphasis-patterns'],
714
description: 'Do not use more than one emphasis/strong, italics, or uppercase for a string',
815
tags: ['formatting', 'emphasis', 'style'],
916
severity: 'warning',
10-
function: (params, onError) => {
17+
function: (params: RuleParams, onError: RuleErrorCallback) => {
1118
// Skip autogenerated files
1219
const frontmatterString = params.frontMatterLines.join('\n')
13-
const fm = frontmatter(frontmatterString).data
20+
const fm = frontmatter(frontmatterString).data as Frontmatter
1421
if (fm && fm.autogenerated) return
1522

1623
const lines = params.lines
@@ -38,9 +45,9 @@ export const multipleEmphasisPatterns = {
3845
/**
3946
* Check for multiple emphasis types in a single text segment
4047
*/
41-
function checkMultipleEmphasis(line, lineNumber, onError) {
48+
function checkMultipleEmphasis(line: string, lineNumber: number, onError: RuleErrorCallback): void {
4249
// Focus on the clearest violations of the style guide
43-
const multipleEmphasisPatterns = [
50+
const multipleEmphasisPatterns: Array<{ regex: RegExp; types: string[] }> = [
4451
// Bold + italic combinations (***text***)
4552
{ regex: /\*\*\*([^*]+)\*\*\*/g, types: ['bold', 'italic'] },
4653
{ regex: /___([^_]+)___/g, types: ['bold', 'italic'] },
@@ -76,7 +83,7 @@ function checkMultipleEmphasis(line, lineNumber, onError) {
7683
/**
7784
* Determine if a match should be skipped (likely intentional formatting)
7885
*/
79-
function shouldSkipMatch(fullMatch, content) {
86+
function shouldSkipMatch(fullMatch: string, content: string): boolean {
8087
// Skip common false positives
8188
if (!content) return true
8289

0 commit comments

Comments
 (0)