From 47e08a233a279861e54743d982dd2d7b162412d9 Mon Sep 17 00:00:00 2001 From: Kevin Heis Date: Wed, 8 Oct 2025 08:33:33 -0700 Subject: [PATCH 1/7] Migrate 18 JavaScript files to TypeScript (#57857) --- .../image-alt-text-end-punctuation.ts | 8 ++--- ...rsion.js => internal-links-old-version.ts} | 11 +++--- .../{link-quotation.js => link-quotation.ts} | 28 ++++++++------- ...whitespace.js => liquid-tag-whitespace.ts} | 17 +++++++-- ...n.js => list-first-word-capitalization.ts} | 15 ++++---- ...eusable-usage.js => rai-reusable-usage.ts} | 35 ++++++++++++++----- ...heduled-jobs.js => yaml-scheduled-jobs.ts} | 34 ++++++++++++------ ...-references.js => site-data-references.ts} | 18 +++++----- ...n.js => image-alt-text-end-punctuation.ts} | 0 .../liquid/{tool.js => tool.ts} | 16 ++++++--- .../{copilot-prompt.js => copilot-prompt.ts} | 33 ++++++++++++----- ...le-rows.js => rewrite-empty-table-rows.ts} | 23 ++++++++---- ...owheaders.js => rewrite-for-rowheaders.ts} | 30 ++++++++++++---- src/frame/lib/{patterns.js => patterns.ts} | 0 .../{read-json-file.js => read-json-file.ts} | 29 +++++++++------ ...peration-schema.js => operation-schema.ts} | 0 src/rest/tests/{api.js => api.ts} | 0 ...st-examples.js => create-rest-examples.ts} | 3 +- ...{update-markdown.js => update-markdown.ts} | 4 ++- ...files.js => update-versioning-in-files.ts} | 5 +-- 20 files changed, 212 insertions(+), 97 deletions(-) rename src/content-linter/lib/linting-rules/{internal-links-old-version.js => internal-links-old-version.ts} (83%) rename src/content-linter/lib/linting-rules/{link-quotation.js => link-quotation.ts} (70%) rename src/content-linter/lib/linting-rules/{liquid-tag-whitespace.js => liquid-tag-whitespace.ts} (82%) rename src/content-linter/lib/linting-rules/{list-first-word-capitalization.js => list-first-word-capitalization.ts} (82%) rename src/content-linter/lib/linting-rules/{rai-reusable-usage.js => rai-reusable-usage.ts} (65%) rename src/content-linter/lib/linting-rules/{yaml-scheduled-jobs.js => yaml-scheduled-jobs.ts} (61%) rename src/content-linter/tests/{site-data-references.js => site-data-references.ts} (75%) rename src/content-linter/tests/unit/{image-alt-text-end-punctuation.js => image-alt-text-end-punctuation.ts} (100%) rename src/content-render/liquid/{tool.js => tool.ts} (72%) rename src/content-render/unified/{copilot-prompt.js => copilot-prompt.ts} (59%) rename src/content-render/unified/{rewrite-empty-table-rows.js => rewrite-empty-table-rows.ts} (67%) rename src/content-render/unified/{rewrite-for-rowheaders.js => rewrite-for-rowheaders.ts} (57%) rename src/frame/lib/{patterns.js => patterns.ts} (100%) rename src/frame/lib/{read-json-file.js => read-json-file.ts} (64%) rename src/rest/scripts/utils/{operation-schema.js => operation-schema.ts} (100%) rename src/rest/tests/{api.js => api.ts} (100%) rename src/rest/tests/{create-rest-examples.js => create-rest-examples.ts} (94%) rename src/rest/tests/{update-markdown.js => update-markdown.ts} (96%) rename src/versions/scripts/{update-versioning-in-files.js => update-versioning-in-files.ts} (88%) diff --git a/src/content-linter/lib/linting-rules/image-alt-text-end-punctuation.ts b/src/content-linter/lib/linting-rules/image-alt-text-end-punctuation.ts index 520a09e51e86..c78c29e3501c 100644 --- a/src/content-linter/lib/linting-rules/image-alt-text-end-punctuation.ts +++ b/src/content-linter/lib/linting-rules/image-alt-text-end-punctuation.ts @@ -5,16 +5,16 @@ import { isStringQuoted, isStringPunctuated, } from '../helpers/utils' -import type { RuleParams, RuleErrorCallback } from '../../types' +import type { RuleParams, RuleErrorCallback, Rule, MarkdownToken } from '../../types' -export const imageAltTextEndPunctuation = { +export const imageAltTextEndPunctuation: Rule = { names: ['GHD032', 'image-alt-text-end-punctuation'], description: 'Alternate text for images should end with punctuation', tags: ['accessibility', 'images'], parser: 'markdownit', function: (params: RuleParams, onError: RuleErrorCallback) => { - forEachInlineChild(params, 'image', function forToken(token: any) { - const imageAltText = token.content.trim() + forEachInlineChild(params, 'image', function forToken(token: MarkdownToken) { + const imageAltText = token.content?.trim() // If the alt text is empty, there is nothing to check and you can't // produce a valid range. diff --git a/src/content-linter/lib/linting-rules/internal-links-old-version.js b/src/content-linter/lib/linting-rules/internal-links-old-version.ts similarity index 83% rename from src/content-linter/lib/linting-rules/internal-links-old-version.js rename to src/content-linter/lib/linting-rules/internal-links-old-version.ts index f9e90f8c0c9c..b66781ca2f93 100644 --- a/src/content-linter/lib/linting-rules/internal-links-old-version.js +++ b/src/content-linter/lib/linting-rules/internal-links-old-version.ts @@ -1,21 +1,24 @@ +// @ts-ignore - markdownlint-rule-helpers doesn't have TypeScript declarations import { addError, filterTokens } from 'markdownlint-rule-helpers' import { getRange } from '../helpers/utils' +import type { RuleParams, RuleErrorCallback, MarkdownToken, Rule } from '../../types' -export const internalLinksOldVersion = { +export const internalLinksOldVersion: Rule = { names: ['GHD006', 'internal-links-old-version'], description: 'Internal links must not have a hardcoded version using old versioning syntax', tags: ['links', 'url', 'versioning'], parser: 'markdownit', - function: (params, onError) => { - filterTokens(params, 'inline', (token) => { + function: (params: RuleParams, onError: RuleErrorCallback) => { + filterTokens(params, 'inline', (token: MarkdownToken) => { if ( params.name.endsWith('migrating-from-github-enterprise-1110x-to-2123.md') || params.name.endsWith('all-releases.md') ) return - for (const child of token.children) { + for (const child of token.children || []) { if (child.type !== 'link_open') continue + if (!child.attrs) continue // Things matched by this RegExp: // - /enterprise/2.19/admin/blah // - https://docs.github.com/enterprise/11.10.340/admin/blah diff --git a/src/content-linter/lib/linting-rules/link-quotation.js b/src/content-linter/lib/linting-rules/link-quotation.ts similarity index 70% rename from src/content-linter/lib/linting-rules/link-quotation.js rename to src/content-linter/lib/linting-rules/link-quotation.ts index 4fd2ce623d37..9d4eb089a170 100644 --- a/src/content-linter/lib/linting-rules/link-quotation.js +++ b/src/content-linter/lib/linting-rules/link-quotation.ts @@ -1,36 +1,41 @@ +// @ts-ignore - markdownlint-rule-helpers doesn't have TypeScript declarations import { addError, filterTokens } from 'markdownlint-rule-helpers' import { getRange, quotePrecedesLinkOpen } from '../helpers/utils' import { escapeRegExp } from 'lodash-es' +import type { RuleParams, RuleErrorCallback, MarkdownToken, Rule } from '../../types' -export const linkQuotation = { +export const linkQuotation: Rule = { names: ['GHD043', 'link-quotation'], description: 'Internal link titles must not be surrounded by quotations', tags: ['links', 'url'], parser: 'markdownit', - function: (params, onError) => { - filterTokens(params, 'inline', (token) => { + function: (params: RuleParams, onError: RuleErrorCallback) => { + filterTokens(params, 'inline', (token: MarkdownToken) => { const { children } = token - let previous_child = children[0] + if (!children) return + let previous_child: MarkdownToken = children[0] let inLinkWithPrecedingQuotes = false let linkUrl = '' - let content = [] - let line = '' + let content: string[] = [] for (let i = 1; i < children.length; i++) { const child = children[i] - if (child.type === 'link_open' && quotePrecedesLinkOpen(previous_child.content)) { + if (child.type === 'link_open' && quotePrecedesLinkOpen(previous_child.content || '')) { + if (!child.attrs) continue inLinkWithPrecedingQuotes = true linkUrl = escapeRegExp(child.attrs[0][1]) - line = child.line } else if (inLinkWithPrecedingQuotes && child.type === 'text') { - content.push(escapeRegExp(child.content.trim())) + content.push(escapeRegExp((child.content || '').trim())) } else if (inLinkWithPrecedingQuotes && child.type === 'code_inline') { - content.push('`' + escapeRegExp(child.content.trim()) + '`') + content.push('`' + escapeRegExp((child.content || '').trim()) + '`') } else if (child.type === 'link_close') { const title = content.join(' ') const regex = new RegExp(`"\\[${title}\\]\\(${linkUrl}\\)({%.*%})?(!|\\.|\\?|,)?"`) if (regex.test(child.line)) { - const match = child.line.match(regex)[0] + const matchResult = child.line.match(regex) + if (!matchResult) continue + const match = matchResult[0] const range = getRange(child.line, match) + if (!range) continue let newLine = match if (newLine.startsWith('"')) { newLine = newLine.slice(1) @@ -58,7 +63,6 @@ export const linkQuotation = { } inLinkWithPrecedingQuotes = false content = [] - line = '' linkUrl = '' } previous_child = child diff --git a/src/content-linter/lib/linting-rules/liquid-tag-whitespace.js b/src/content-linter/lib/linting-rules/liquid-tag-whitespace.ts similarity index 82% rename from src/content-linter/lib/linting-rules/liquid-tag-whitespace.js rename to src/content-linter/lib/linting-rules/liquid-tag-whitespace.ts index 62b5d90df622..3b06630ac4ad 100644 --- a/src/content-linter/lib/linting-rules/liquid-tag-whitespace.js +++ b/src/content-linter/lib/linting-rules/liquid-tag-whitespace.ts @@ -2,6 +2,15 @@ import { TokenKind } from 'liquidjs' import { getLiquidTokens, getPositionData } from '../helpers/liquid-utils' import { addFixErrorDetail } from '../helpers/utils' +import type { RuleParams, RuleErrorCallback, Rule } from '../../types' + +interface LiquidToken { + kind: number + content: string + contentRange: [number, number] + begin: number + end: number +} /* Liquid tags should start and end with one whitespace. For example: @@ -16,14 +25,16 @@ Liquid tags should start and end with one whitespace. For example: {%data arg1 arg2 %} */ -export const liquidTagWhitespace = { +export const liquidTagWhitespace: Rule = { names: ['GHD042', 'liquid-tag-whitespace'], description: 'Liquid tags should start and end with one whitespace. Liquid tag arguments should be separated by only one whitespace.', tags: ['liquid', 'format'], - function: (params, onError) => { + function: (params: RuleParams, onError: RuleErrorCallback) => { const content = params.lines.join('\n') - const tokens = getLiquidTokens(content).filter((token) => token.kind === TokenKind.Tag) + const tokens = (getLiquidTokens(content) as LiquidToken[]).filter( + (token: LiquidToken) => token.kind === TokenKind.Tag, + ) for (const token of tokens) { const { lineNumber, column, length } = getPositionData(token, params.lines) diff --git a/src/content-linter/lib/linting-rules/list-first-word-capitalization.js b/src/content-linter/lib/linting-rules/list-first-word-capitalization.ts similarity index 82% rename from src/content-linter/lib/linting-rules/list-first-word-capitalization.js rename to src/content-linter/lib/linting-rules/list-first-word-capitalization.ts index 8d79bd12c9bd..128f1e4f77ca 100644 --- a/src/content-linter/lib/linting-rules/list-first-word-capitalization.js +++ b/src/content-linter/lib/linting-rules/list-first-word-capitalization.ts @@ -1,25 +1,26 @@ import { addFixErrorDetail, getRange, filterTokensByOrder } from '../helpers/utils' +import type { RuleParams, RuleErrorCallback, MarkdownToken, Rule } from '../../types' -export const listFirstWordCapitalization = { +export const listFirstWordCapitalization: Rule = { names: ['GHD034', 'list-first-word-capitalization'], description: 'First word of list item should be capitalized', tags: ['ul', 'ol'], - function: (params, onError) => { + function: (params: RuleParams, onError: RuleErrorCallback) => { // Skip site-policy directory as these are legal documents with specific formatting requirements if (params.name && params.name.includes('content/site-policy/')) return // We're going to look for a sequence of 3 tokens. If the markdown // is a really small string, it might not even have that many tokens // in it. Can bail early. - if (params.tokens.length < 3) return + if (!params.tokens || params.tokens.length < 3) return const inlineListItems = filterTokensByOrder(params.tokens, [ 'list_item_open', 'paragraph_open', 'inline', - ]).filter((token) => token.type === 'inline') + ]).filter((token: MarkdownToken) => token.type === 'inline') - inlineListItems.forEach((token) => { + inlineListItems.forEach((token: MarkdownToken) => { // Only proceed if all of the token's children start with a text // node that is not empty. // This filters out cases where the list item is inline code, or @@ -27,12 +28,13 @@ export const listFirstWordCapitalization = { // This also avoids cases like `- **bold** text` where the first // child is a text node string but the text node content is empty. const firstWordTextNode = + token.children && token.children.length > 0 && token.children[0].type === 'text' && token.children[0].content !== '' if (!firstWordTextNode) return - const content = token.content.trim() + const content = (token.content || '').trim() const firstWord = content.trim().split(' ')[0] // If the first character in the first word is not an alphanumeric, @@ -49,6 +51,7 @@ export const listFirstWordCapitalization = { const lineNumber = token.lineNumber const range = getRange(token.line, firstWord) + if (!range) return addFixErrorDetail( onError, lineNumber, diff --git a/src/content-linter/lib/linting-rules/rai-reusable-usage.js b/src/content-linter/lib/linting-rules/rai-reusable-usage.ts similarity index 65% rename from src/content-linter/lib/linting-rules/rai-reusable-usage.js rename to src/content-linter/lib/linting-rules/rai-reusable-usage.ts index 0eb6156bd9c6..135c1e77a0b5 100644 --- a/src/content-linter/lib/linting-rules/rai-reusable-usage.js +++ b/src/content-linter/lib/linting-rules/rai-reusable-usage.ts @@ -1,24 +1,43 @@ +// @ts-ignore - markdownlint-rule-helpers doesn't have TypeScript declarations import { addError } from 'markdownlint-rule-helpers' import { TokenKind } from 'liquidjs' import path from 'path' import { getFrontmatter } from '../helpers/utils' import { getLiquidTokens, getPositionData } from '../helpers/liquid-utils' +import type { RuleParams, RuleErrorCallback, Rule } from '../../types' -export const raiReusableUsage = { +interface Frontmatter { + type?: string + // Allow any additional frontmatter properties since we only care about 'type' + [key: string]: any +} + +interface LiquidToken { + kind: number + name?: string + args: string + content: string + begin: number + end: number +} + +export const raiReusableUsage: Rule = { names: ['GHD035', 'rai-reusable-usage'], description: 'RAI articles and reusables can only reference reusable content in the data/reusables/rai directory', tags: ['feature', 'rai'], - function: (params, onError) => { + function: (params: RuleParams, onError: RuleErrorCallback) => { if (!isFileRai(params)) return const content = params.lines.join('\n') - const tokens = getLiquidTokens(content) - .filter((token) => token.kind === TokenKind.Tag) - .filter((token) => token.name === 'data' || token.name === 'indented_data_reference') + const tokens = (getLiquidTokens(content) as LiquidToken[]) + .filter((token: LiquidToken) => token.kind === TokenKind.Tag) + .filter( + (token: LiquidToken) => token.name === 'data' || token.name === 'indented_data_reference', + ) // It's ok to reference variables from rai content - .filter((token) => !token.args.startsWith('variables')) + .filter((token: LiquidToken) => !token.args.startsWith('variables')) for (const token of tokens) { // if token is 'data foo.bar` or `indented_data_reference foo.bar depth=3` @@ -42,7 +61,7 @@ export const raiReusableUsage = { // Rai file content can be in either the data/reusables/rai directory // or anywhere in the content directory -function isFileRai(params) { +function isFileRai(params: RuleParams): boolean { // ROOT is set in the test environment to src/fixtures/fixtures otherwise // it is set to the root of the project. const ROOT = process.env.ROOT || '.' @@ -53,6 +72,6 @@ function isFileRai(params) { return params.name.startsWith(dataRai) } - const fm = getFrontmatter(params.frontMatterLines) || {} + const fm: Frontmatter = (getFrontmatter(params.frontMatterLines) as Frontmatter) || {} return fm.type === 'rai' } diff --git a/src/content-linter/lib/linting-rules/yaml-scheduled-jobs.js b/src/content-linter/lib/linting-rules/yaml-scheduled-jobs.ts similarity index 61% rename from src/content-linter/lib/linting-rules/yaml-scheduled-jobs.js rename to src/content-linter/lib/linting-rules/yaml-scheduled-jobs.ts index 3013d25e4b9c..d3c684d68377 100644 --- a/src/content-linter/lib/linting-rules/yaml-scheduled-jobs.js +++ b/src/content-linter/lib/linting-rules/yaml-scheduled-jobs.ts @@ -1,21 +1,35 @@ import yaml from 'js-yaml' +// @ts-ignore - markdownlint-rule-helpers doesn't have TypeScript declarations import { addError, filterTokens } from 'markdownlint-rule-helpers' import { liquid } from '@/content-render/index' import { allVersions } from '@/versions/lib/all-versions' +import type { RuleParams, RuleErrorCallback, MarkdownToken, Rule } from '../../types' -const scheduledYamlJobs = [] +interface YamlSchedule { + cron: string +} + +interface YamlWorkflow { + on?: { + schedule?: YamlSchedule[] + } +} + +const scheduledYamlJobs: string[] = [] -export const yamlScheduledJobs = { +export const yamlScheduledJobs: Rule = { names: ['GHD021', 'yaml-scheduled-jobs'], description: 'YAML snippets that include scheduled workflows must not run on the hour and must be unique', tags: ['feature', 'actions'], parser: 'markdownit', asynchronous: true, - function: (params, onError) => { - filterTokens(params, 'fence', async (token) => { - const lang = token.info.trim().split(/\s+/u).shift().toLowerCase() + function: (params: RuleParams, onError: RuleErrorCallback) => { + filterTokens(params, 'fence', async (token: MarkdownToken) => { + if (!token.info) return + if (!token.content) return + const lang = token.info.trim().split(/\s+/u).shift()?.toLowerCase() if (lang !== 'yaml' && lang !== 'yml') return if (!token.content.includes('schedule:')) return if (!token.content.includes('- cron:')) return @@ -26,15 +40,15 @@ export const yamlScheduledJobs = { } // If we don't parse the Liquid first, yaml loading chokes on {% raw %} tags const renderedYaml = await liquid.parseAndRender(token.content, context) - const yamlObj = yaml.load(renderedYaml) + const yamlObj = yaml.load(renderedYaml) as YamlWorkflow if (!yamlObj.on) return if (!yamlObj.on.schedule) return - yamlObj.on.schedule.forEach((schedule) => { + yamlObj.on.schedule.forEach((schedule: YamlSchedule) => { if (schedule.cron.split(' ')[0] === '0') { addError( onError, - getLineNumber(token.content, schedule.cron) + token.lineNumber, + getLineNumber(token.content!, schedule.cron) + token.lineNumber, `YAML scheduled workflow must not run on the hour`, schedule.cron, ) @@ -43,7 +57,7 @@ export const yamlScheduledJobs = { if (scheduledYamlJobs.includes(schedule.cron)) { addError( onError, - getLineNumber(token.content, schedule.cron) + token.lineNumber, + getLineNumber(token.content!, schedule.cron) + token.lineNumber, `YAML scheduled workflow must be unique`, schedule.cron, ) @@ -55,7 +69,7 @@ export const yamlScheduledJobs = { }, } -function getLineNumber(tokenContent, schedule) { +function getLineNumber(tokenContent: string, schedule: string): number { const contentLines = tokenContent.split('\n') return contentLines.findIndex((line) => line.includes(schedule)) + 1 } diff --git a/src/content-linter/tests/site-data-references.js b/src/content-linter/tests/site-data-references.ts similarity index 75% rename from src/content-linter/tests/site-data-references.js rename to src/content-linter/tests/site-data-references.ts index 4cca081f5179..ced39b018c5f 100644 --- a/src/content-linter/tests/site-data-references.js +++ b/src/content-linter/tests/site-data-references.ts @@ -17,7 +17,7 @@ const getDataPathRegex = const rawLiquidPattern = /{%\s*raw\s*%}.*?{%\s*endraw\s*%}/gs -const getDataReferences = (content) => { +const getDataReferences = (content: string): string[] => { // When looking for things like `{% data reusables.foo %}` in the // content, we first have to exclude any Liquid that isn't real. // E.g. @@ -26,14 +26,15 @@ const getDataReferences = (content) => { // {% endraw %} const withoutRawLiquidBlocks = content.replace(rawLiquidPattern, '') const refs = withoutRawLiquidBlocks.match(patterns.dataReference) || [] - return refs.map((ref) => ref.replace(getDataPathRegex, '$1')) + return refs.map((ref: string) => ref.replace(getDataPathRegex, '$1')) } describe('data references', () => { vi.setConfig({ testTimeout: 60 * 1000 }) test('every data reference found in English variable files is defined and has a value', async () => { - let errors = [] + // value can be any type returned by getDataByLanguage - we check if it's a string + let errors: Array<{ key: string; value: unknown; variableFile: string }> = [] const allVariables = getDeepDataByLanguage('variables', 'en') const variables = Object.values(allVariables) expect(variables.length).toBeGreaterThan(0) @@ -42,13 +43,11 @@ describe('data references', () => { variables.map(async (variablesPerFile) => { const variableRefs = getDataReferences(JSON.stringify(variablesPerFile)) - variableRefs.forEach((key) => { + variableRefs.forEach((key: string) => { const value = getDataByLanguage(key, 'en') if (typeof value !== 'string') { - const variableFile = path.join( - 'data/variables', - getFilenameByValue(allVariables, variablesPerFile), - ) + const filename = getFilenameByValue(allVariables, variablesPerFile) + const variableFile = path.join('data/variables', filename || '') errors.push({ key, value, variableFile }) } }) @@ -60,6 +59,7 @@ describe('data references', () => { }) }) -function getFilenameByValue(object, value) { +// object is the allVariables object with dynamic keys, value is the nested object we're searching for +function getFilenameByValue(object: Record, value: unknown): string | undefined { return Object.keys(object).find((key) => object[key] === value) } diff --git a/src/content-linter/tests/unit/image-alt-text-end-punctuation.js b/src/content-linter/tests/unit/image-alt-text-end-punctuation.ts similarity index 100% rename from src/content-linter/tests/unit/image-alt-text-end-punctuation.js rename to src/content-linter/tests/unit/image-alt-text-end-punctuation.ts diff --git a/src/content-render/liquid/tool.js b/src/content-render/liquid/tool.ts similarity index 72% rename from src/content-render/liquid/tool.js rename to src/content-render/liquid/tool.ts index c7731299b1bd..42152db538a5 100644 --- a/src/content-render/liquid/tool.js +++ b/src/content-render/liquid/tool.ts @@ -1,7 +1,7 @@ import { allTools } from '@/tools/lib/all-tools' import { allPlatforms } from '@/tools/lib/all-platforms' -export const tags = Object.keys(allTools).concat(allPlatforms).concat(['rowheaders']) +export const tags: string[] = Object.keys(allTools).concat(allPlatforms).concat(['rowheaders']) // The trailing newline is important. Without it, the line immediately after // the `` will be considered part of the previous block, which means the Markdown following the `` will not be rendered to HTML correctly. For example: @@ -44,23 +44,29 @@ export const tags = Object.keys(allTools).concat(allPlatforms).concat(['rowheade const template = '
{{ output }}
\n' export const Tool = { - type: 'block', + type: 'block' as const, + tagName: '', + // Liquid template objects don't have TypeScript definitions + templates: [] as any[], - parse(tagToken, remainTokens) { + // tagToken and remainTokens are Liquid internal types without TypeScript definitions + parse(tagToken: any, remainTokens: any) { this.tagName = tagToken.name this.templates = [] const stream = this.liquid.parser.parseStream(remainTokens) stream .on(`tag:end${this.tagName}`, () => stream.stop()) - .on('template', (tpl) => this.templates.push(tpl)) + // tpl is a Liquid template object without TypeScript definitions + .on('template', (tpl: any) => this.templates.push(tpl)) .on('end', () => { throw new Error(`tag ${tagToken.getText()} not closed`) }) stream.start() }, - render: function* (scope) { + // scope is a Liquid scope object, Generator yields/returns Liquid template values - no TypeScript definitions available + render: function* (scope: any): Generator { const output = yield this.liquid.renderer.renderTemplates(this.templates, scope) return yield this.liquid.parseAndRender(template, { tagName: this.tagName, diff --git a/src/content-render/unified/copilot-prompt.js b/src/content-render/unified/copilot-prompt.ts similarity index 59% rename from src/content-render/unified/copilot-prompt.js rename to src/content-render/unified/copilot-prompt.ts index 1d2ca48d67c1..1874b4aec91e 100644 --- a/src/content-render/unified/copilot-prompt.js +++ b/src/content-render/unified/copilot-prompt.ts @@ -4,12 +4,15 @@ import { find } from 'unist-util-find' import { h } from 'hastscript' +// @ts-ignore - @primer/octicons doesn't have TypeScript declarations import octicons from '@primer/octicons' import { parse } from 'parse5' import { fromParse5 } from 'hast-util-from-parse5' import { getPreMeta } from './code-header' -export function getPrompt(node, tree, code) { +// node and tree are hast/unist AST nodes without proper TypeScript definitions +// Returns a hast element node for the prompt button +export function getPrompt(node: any, tree: any, code: string): any { const hasPrompt = Boolean(getPreMeta(node).prompt) if (!hasPrompt) return null @@ -28,7 +31,12 @@ export function getPrompt(node, tree, code) { ) } -function buildPromptData(node, tree, code) { +// node and tree are hast/unist AST nodes without proper TypeScript definitions +function buildPromptData( + node: any, + tree: any, + code: string, +): { promptContent: string; ariaLabel: string } { // Find a ref meta in the format 'ref=' const ref = getPreMeta(node).ref @@ -43,31 +51,38 @@ function buildPromptData(node, tree, code) { console.warn(`Can't find referenced code block with id=${ref}`) return promptOnly(code) } - const matchingCode = matchingCodeEl?.children[0].children[0].value || null + // Cast needed to access children property on untyped AST node + const matchingCode = (matchingCodeEl as any)?.children[0].children[0].value || null return promptAndContext(code, matchingCode) } -function promptOnly(code) { +function promptOnly(code: string): { promptContent: string; ariaLabel: string } { return { promptContent: code, ariaLabel: 'Run this prompt in Copilot Chat', } } -function promptAndContext(code, matchingCode) { +function promptAndContext( + code: string, + matchingCode: string, +): { promptContent: string; ariaLabel: string } { return { promptContent: `${matchingCode}\n${code}`, ariaLabel: 'Run this prompt with context in Copilot Chat', } } -function findMatchingCode(ref, tree) { - return find(tree, (node) => { - return node.type === 'element' && node.tagName === 'pre' && getPreMeta(node).id === ref +// tree and node are hast/unist AST nodes without proper TypeScript definitions +function findMatchingCode(ref: string, tree: any): any { + return find(tree, (node: any) => { + // Cast needed to access tagName property on untyped element node + return node.type === 'element' && (node as any).tagName === 'pre' && getPreMeta(node).id === ref }) } -function copilotIcon() { +// Returns a hast element node for the Copilot icon +function copilotIcon(): any { const copilotIconHtml = octicons.copilot.toSVG() const copilotIconAst = parse(String(copilotIconHtml), { sourceCodeLocationInfo: true }) const copilotIcon = fromParse5(copilotIconAst, { file: copilotIconHtml }) diff --git a/src/content-render/unified/rewrite-empty-table-rows.js b/src/content-render/unified/rewrite-empty-table-rows.ts similarity index 67% rename from src/content-render/unified/rewrite-empty-table-rows.js rename to src/content-render/unified/rewrite-empty-table-rows.ts index ff3a740815f4..d10e95e875c5 100644 --- a/src/content-render/unified/rewrite-empty-table-rows.js +++ b/src/content-render/unified/rewrite-empty-table-rows.ts @@ -1,4 +1,4 @@ -import { visit } from 'unist-util-visit' +import { visit, SKIP } from 'unist-util-visit' /** * Where it can mutate the AST to swap from: @@ -49,22 +49,31 @@ import { visit } from 'unist-util-visit' * isn't the same all the way down. But Unified will still parse it. * */ -function matcher(node) { +// node is a hast element node without proper TypeScript definitions +function matcher(node: any): boolean { return node.type === 'element' && node.tagName === 'tr' } -function visitor(node, index, parent) { +// node, parent, and grandChild are hast element nodes without proper TypeScript definitions +function visitor( + node: any, + index: number | undefined, + parent: any, +): [typeof SKIP, number] | undefined { if ( node.children.every( - (grandChild) => + (grandChild: any) => grandChild.type === 'element' && grandChild.tagName === 'td' && !grandChild.children.length, ) ) { - parent.children.splice(index, 1) - return [visit.SKIP, index] + if (index !== undefined) { + parent.children.splice(index, 1) + return [SKIP, index] + } } } +// tree is a hast root node without proper TypeScript definitions export default function rewriteEmptyTableRows() { - return (tree) => visit(tree, matcher, visitor) + return (tree: any) => visit(tree, matcher, visitor) } diff --git a/src/content-render/unified/rewrite-for-rowheaders.js b/src/content-render/unified/rewrite-for-rowheaders.ts similarity index 57% rename from src/content-render/unified/rewrite-for-rowheaders.js rename to src/content-render/unified/rewrite-for-rowheaders.ts index dedcccbfae43..f86e1d7162dc 100644 --- a/src/content-render/unified/rewrite-for-rowheaders.js +++ b/src/content-render/unified/rewrite-for-rowheaders.ts @@ -1,5 +1,21 @@ import { visitParents } from 'unist-util-visit-parents' +interface ElementNode { + type: 'element' + tagName: string + properties: { + // Properties can have any value type (strings, booleans, arrays, etc.) + [key: string]: any + } + _scoped?: boolean +} + +interface AncestorNode { + properties?: { + className?: string[] + } +} + /** * Where it can mutate the AST to swap from: * @@ -33,22 +49,23 @@ import { visitParents } from 'unist-util-visit-parents' * * */ -function matcher(node) { +function matcher(node: any): node is ElementNode { return node.type === 'element' && node.tagName === 'td' && !('scope' in node.properties) } -function insideRowheaders(ancestors) { +function insideRowheaders(ancestors: AncestorNode[]): boolean { return ancestors.some( - (node) => + (node: AncestorNode) => node.properties && node.properties.className && node.properties.className.includes('rowheaders'), ) } -function visitor(node, ancestors) { +// ancestors is an array of hast nodes without proper TypeScript definitions +function visitor(node: ElementNode, ancestors: any[]): void { if (insideRowheaders(ancestors)) { - const tr = ancestors.at(-1) + const tr = ancestors.at(-1) as ElementNode if (!tr._scoped) { tr._scoped = true node.properties.scope = 'row' @@ -57,6 +74,7 @@ function visitor(node, ancestors) { } } +// tree is a hast root node without proper TypeScript definitions export default function rewriteForRowheaders() { - return (tree) => visitParents(tree, matcher, visitor) + return (tree: any) => visitParents(tree, matcher, visitor) } diff --git a/src/frame/lib/patterns.js b/src/frame/lib/patterns.ts similarity index 100% rename from src/frame/lib/patterns.js rename to src/frame/lib/patterns.ts diff --git a/src/frame/lib/read-json-file.js b/src/frame/lib/read-json-file.ts similarity index 64% rename from src/frame/lib/read-json-file.js rename to src/frame/lib/read-json-file.ts index e7a7c5da973f..99824f21bedf 100644 --- a/src/frame/lib/read-json-file.js +++ b/src/frame/lib/read-json-file.ts @@ -1,15 +1,18 @@ import fs from 'fs' import { brotliDecompressSync } from 'zlib' -export default function readJsonFile(xpath) { +// Returns parsed JSON which can be any valid JSON type +export default function readJsonFile(xpath: string): any { return JSON.parse(fs.readFileSync(xpath, 'utf8')) } -export function readCompressedJsonFile(xpath) { +// Returns parsed JSON which can be any valid JSON type +export function readCompressedJsonFile(xpath: string): any { if (!xpath.endsWith('.br')) { xpath += '.br' } - return JSON.parse(brotliDecompressSync(fs.readFileSync(xpath))) + // Cast to any needed due to TypeScript's strict typing of Buffer vs expected input types for brotliDecompressSync + return JSON.parse(brotliDecompressSync(fs.readFileSync(xpath) as any).toString()) } // Ask it to read a `foo.json` file and it will automatically @@ -19,10 +22,12 @@ export function readCompressedJsonFile(xpath) { // possible (in terms of disk) for them to deploy faster. So the // staging deployment process will compress a bunch of large // `.json` files before packaging it up. -export function readCompressedJsonFileFallback(xpath) { +// Returns parsed JSON which can be any valid JSON type +export function readCompressedJsonFileFallback(xpath: string): any { try { return readCompressedJsonFile(xpath) - } catch (err) { + } catch (err: any) { + // err is any because fs errors can have various shapes with code property if (err.code === 'ENOENT') { return readJsonFile(xpath) } else { @@ -33,12 +38,14 @@ export function readCompressedJsonFileFallback(xpath) { // This is used to make sure the `readCompressedJsonFileFallbackLazily()` // function isn't used with the same exact first argument more than once. -const globalCacheCounter = {} +const globalCacheCounter: Record = {} // Wrapper on readCompressedJsonFileFallback that initially only checks // if the file exists but doesn't read the content till you call it. -export function readCompressedJsonFileFallbackLazily(xpath) { - const cache = new Map() +// Returns a function that returns parsed JSON which can be any valid JSON type +export function readCompressedJsonFileFallbackLazily(xpath: string): () => any { + // Cache stores parsed JSON values which can be any valid JSON type + const cache = new Map() // This will throw if the file isn't accessible at all, e.g. ENOENT // But, the file might have been replaced by one called `SAMENAME.json.br` // because in staging, we ship these files compressed to make the @@ -46,11 +53,13 @@ export function readCompressedJsonFileFallbackLazily(xpath) { // account for that. try { fs.accessSync(xpath) - } catch (err) { + } catch (err: any) { + // err is any because fs errors can have various shapes with code property if (err.code === 'ENOENT') { try { fs.accessSync(xpath + '.br') - } catch (err) { + } catch (err: any) { + // err is any because fs errors can have various shapes with code property if (err.code === 'ENOENT') { throw new Error(`Neither ${xpath} nor ${xpath}.br is accessible`) } diff --git a/src/rest/scripts/utils/operation-schema.js b/src/rest/scripts/utils/operation-schema.ts similarity index 100% rename from src/rest/scripts/utils/operation-schema.js rename to src/rest/scripts/utils/operation-schema.ts diff --git a/src/rest/tests/api.js b/src/rest/tests/api.ts similarity index 100% rename from src/rest/tests/api.js rename to src/rest/tests/api.ts diff --git a/src/rest/tests/create-rest-examples.js b/src/rest/tests/create-rest-examples.ts similarity index 94% rename from src/rest/tests/create-rest-examples.js rename to src/rest/tests/create-rest-examples.ts index 20006210de9a..09b021fe0307 100644 --- a/src/rest/tests/create-rest-examples.js +++ b/src/rest/tests/create-rest-examples.ts @@ -52,7 +52,8 @@ describe('rest example requests and responses', () => { test('check example number and status code appear', async () => { const mergedExamples = await getCodeSamples(operation) - mergedExamples.forEach((example, index) => { + // example is any because getCodeSamples returns objects from untyped JavaScript module + mergedExamples.forEach((example: any, index: number) => { expect(example.request.description).toBe( 'Example ' + (index + 1) + ': Status Code ' + example.response.statusCode, ) diff --git a/src/rest/tests/update-markdown.js b/src/rest/tests/update-markdown.ts similarity index 96% rename from src/rest/tests/update-markdown.js rename to src/rest/tests/update-markdown.ts index a172cd875d98..99b134b8fd67 100644 --- a/src/rest/tests/update-markdown.js +++ b/src/rest/tests/update-markdown.ts @@ -51,7 +51,9 @@ describe('GHES version extraction for update-markdown', () => { expect(extractedVersion).toBe('3.10') // This should be false - 3.10 is NOT in the deprecated list - expect(deprecated.includes(extractedVersion)).toBe(false) + if (extractedVersion) { + expect(deprecated.includes(extractedVersion)).toBe(false) + } // The old buggy logic would have incorrectly flagged this as deprecated // because it would find '3.1' as a substring in the path diff --git a/src/versions/scripts/update-versioning-in-files.js b/src/versions/scripts/update-versioning-in-files.ts similarity index 88% rename from src/versions/scripts/update-versioning-in-files.js rename to src/versions/scripts/update-versioning-in-files.ts index 89c797773565..87608b2091ef 100755 --- a/src/versions/scripts/update-versioning-in-files.js +++ b/src/versions/scripts/update-versioning-in-files.ts @@ -56,10 +56,11 @@ contentFiles.forEach((file) => { }) } - fs.writeFileSync(file, frontmatter.stringify(newContent, data, { lineWidth: 10000 })) + // Cast to any needed because frontmatter.stringify options parameter doesn't include lineWidth in its type definition + fs.writeFileSync(file, frontmatter.stringify(newContent, data || {}, { lineWidth: 10000 } as any)) }) -function updateLiquid(content) { +function updateLiquid(content: string): string { return content .replace(/page.version/g, 'currentVersion') .replace(/["'](?:')?dotcom["'](?:')?/g, '"free-pro-team@latest"') From b0bbdcf7998989227a961f645328b0deed0eae5c Mon Sep 17 00:00:00 2001 From: Steve Ward Date: Wed, 8 Oct 2025 12:28:19 -0400 Subject: [PATCH 2/7] Copilot CLI 10/03 updates (#57855) Co-authored-by: hubwriter --- content/copilot/concepts/agents/about-copilot-cli.md | 12 +----------- .../how-tos/use-copilot-agents/use-copilot-cli.md | 12 ++++++++++++ 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/content/copilot/concepts/agents/about-copilot-cli.md b/content/copilot/concepts/agents/about-copilot-cli.md index ab05afb4f899..0993b3f450a4 100644 --- a/content/copilot/concepts/agents/about-copilot-cli.md +++ b/content/copilot/concepts/agents/about-copilot-cli.md @@ -266,17 +266,7 @@ You can mitigate the risks associated with using the automatic approval options The default model used by {% data variables.copilot.copilot_cli %} is {% data variables.copilot.cca_current_model %}. {% data variables.product.github %} reserves the right to change this model. -You can change the model by setting the `COPILOT_MODEL` environment variable to one of the supported values. For example, change the model to {% data variables.copilot.copilot_gpt_5 %} by setting `COPILOT_MODEL` to `gpt-5`. - -{% rowheaders %} - -| Model name | `COPILOT_MODEL` value | -|------------------------------------------------------------------|-----------------------| -| {% data variables.copilot.cca_current_model %} | `claude-sonnet-4` | -| {% data variables.copilot.copilot_claude_sonnet_45 %} | `claude-sonnet-4.5` | -| {% data variables.copilot.copilot_gpt_5 %} | `gpt-5` | - -{% endrowheaders %} +You can change the model used by {% data variables.copilot.copilot_cli %} by using the `/model` slash command. Enter this command and select a model from the list. Each time you submit a prompt to {% data variables.product.prodname_copilot_short %} in {% data variables.copilot.copilot_cli_short %}'s interactive mode, and each time you use {% data variables.copilot.copilot_cli_short %} in programmatic mode, your monthly quota of {% data variables.product.prodname_copilot_short %} premium requests is reduced by one. For information about premium requests, see [AUTOTITLE](/copilot/managing-copilot/monitoring-usage-and-entitlements/about-premium-requests). diff --git a/content/copilot/how-tos/use-copilot-agents/use-copilot-cli.md b/content/copilot/how-tos/use-copilot-agents/use-copilot-cli.md index 5c2091c69420..eaeebf70223d 100644 --- a/content/copilot/how-tos/use-copilot-agents/use-copilot-cli.md +++ b/content/copilot/how-tos/use-copilot-agents/use-copilot-cli.md @@ -102,6 +102,14 @@ If all of the files you want to work with are in a different location, you can s /cwd /path/to/directory ``` +### Run shell commands + +You can prepend your input with `!` to directly run shell commands, without making a call to the model. + +```shell +!git clone https://github.com/github/copilot-cli +``` + ### Resume an interactive session You can return to a previous interactive session, and continue your conversation with {% data variables.product.prodname_copilot_short %}, by using the `--resume` command line option, then choosing the session you want to resume from the list that's displayed. @@ -135,6 +143,10 @@ To extend the functionality available to you in {% data variables.copilot.copilo Details of your configured MCP servers are stored in the `mcp-config.json` file, which is located, by default, in the `~/.config` directory. This location can be changed by setting the `XDG_CONFIG_HOME` environment variable. For information about the JSON structure of a server definition, see [AUTOTITLE](/copilot/how-tos/use-copilot-agents/coding-agent/extend-coding-agent-with-mcp#writing-a-json-configuration-for-mcp-servers). +### View context and usage statistics for the current session + +You can use the `/usage` slash command to view how many premium requests you've used in the current session, the duration of the session, how many lines of code have been edited, and the breakdown of token usage per model. When you have less than 20% of a model's token limit remaining, {% data variables.copilot.copilot_cli_short %} will display a warning that the context will be truncated when the token limit is reached. + ## Find out more For a complete list of the command line options and slash commands that you can use with {% data variables.copilot.copilot_cli_short %}, do one of the following: From dfd2e4265c0bda626a4d9f8318f63450acdadc79 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Oct 2025 10:32:56 -0700 Subject: [PATCH 3/7] Bump github/codeql-action from 3.26.0 to 4.30.7 (#57862) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 3e6240e2d050..b761b7d6d659 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -36,10 +36,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - uses: github/codeql-action/init@eb055d739abdc2e8de2e5f4ba1a8b246daa779aa # v3.26.0 + - uses: github/codeql-action/init@e296a935590eb16afc0c0108289f68c87e2a89a5 # v4.30.7 with: languages: javascript # comma separated list of values from {go, python, javascript, java, cpp, csharp, ruby} - - uses: github/codeql-action/analyze@eb055d739abdc2e8de2e5f4ba1a8b246daa779aa # v3.26.0 + - uses: github/codeql-action/analyze@e296a935590eb16afc0c0108289f68c87e2a89a5 # v4.30.7 continue-on-error: true - uses: ./.github/actions/slack-alert From 530e41f83f52dd314077317e6d69af7eea61befd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Oct 2025 17:33:14 +0000 Subject: [PATCH 4/7] Bump peter-evans/find-comment from 3.1.0 to 4.0.0 (#57861) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/dont-delete-assets.yml | 2 +- .github/workflows/dont-delete-features.yml | 2 +- .github/workflows/notify-about-deployment.yml | 2 +- .github/workflows/readability.yml | 2 +- .github/workflows/review-comment.yml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/dont-delete-assets.yml b/.github/workflows/dont-delete-assets.yml index c509ad60c6db..79bfa40cb8b5 100644 --- a/.github/workflows/dont-delete-assets.yml +++ b/.github/workflows/dont-delete-assets.yml @@ -42,7 +42,7 @@ jobs: - name: Find possible previous comment if: ${{ steps.comment.outputs.markdown != '' }} - uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e + uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad id: findComment with: issue-number: ${{ github.event.number }} diff --git a/.github/workflows/dont-delete-features.yml b/.github/workflows/dont-delete-features.yml index 665714ac28e5..f36ed31390ce 100644 --- a/.github/workflows/dont-delete-features.yml +++ b/.github/workflows/dont-delete-features.yml @@ -42,7 +42,7 @@ jobs: - name: Find possible previous comment if: ${{ steps.comment.outputs.markdown != '' }} - uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e + uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad id: findComment with: issue-number: ${{ github.event.number }} diff --git a/.github/workflows/notify-about-deployment.yml b/.github/workflows/notify-about-deployment.yml index 331dc5e740b1..377755f99e2d 100644 --- a/.github/workflows/notify-about-deployment.yml +++ b/.github/workflows/notify-about-deployment.yml @@ -47,7 +47,7 @@ jobs: - name: Find content directory changes comment if: ${{ steps.get-number.outputs.number != '' }} - uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e + uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad id: findComment with: issue-number: ${{ steps.get-number.outputs.number }} diff --git a/.github/workflows/readability.yml b/.github/workflows/readability.yml index 32ffec056a33..b1bb7393c215 100644 --- a/.github/workflows/readability.yml +++ b/.github/workflows/readability.yml @@ -69,7 +69,7 @@ jobs: - name: Find existing readability comment if: ${{ steps.changed_files.outputs.filtered_changed_files }} - uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e + uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad id: findComment with: issue-number: ${{ github.event_name == 'workflow_dispatch' && inputs.pull_request_number || github.event.number }} diff --git a/.github/workflows/review-comment.yml b/.github/workflows/review-comment.yml index d5c11214515b..b520ff54d8a0 100644 --- a/.github/workflows/review-comment.yml +++ b/.github/workflows/review-comment.yml @@ -49,7 +49,7 @@ jobs: echo "APP_URL=https://adjective-noun-hash-4000.app.github.dev" >> $GITHUB_ENV fi - name: Find code changes comment - uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e + uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad id: findComment with: issue-number: ${{ github.event.pull_request.number }} From bca16fd5d1ac83d3fda79b9bb7587e9a1bf0d48d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Oct 2025 17:33:19 +0000 Subject: [PATCH 5/7] Bump peter-evans/create-or-update-comment from 4.0.0 to 5.0.0 (#57860) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/comment-release-note-info.yml | 2 +- .github/workflows/dont-delete-assets.yml | 2 +- .github/workflows/dont-delete-features.yml | 2 +- .github/workflows/needs-sme-workflow.yml | 4 ++-- .github/workflows/notify-about-deployment.yml | 2 +- .github/workflows/readability.yml | 2 +- .github/workflows/review-comment.yml | 2 +- .github/workflows/site-policy-reminder.yml | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/comment-release-note-info.yml b/.github/workflows/comment-release-note-info.yml index af000c7bb5dd..8aabdb277200 100644 --- a/.github/workflows/comment-release-note-info.yml +++ b/.github/workflows/comment-release-note-info.yml @@ -21,7 +21,7 @@ jobs: if: github.event.pull_request.user.login != 'release-controller[bot]' && github.repository == 'github/docs-internal' runs-on: ubuntu-latest steps: - - uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 + - uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 with: issue-number: ${{ github.event.pull_request.number }} body: | diff --git a/.github/workflows/dont-delete-assets.yml b/.github/workflows/dont-delete-assets.yml index 79bfa40cb8b5..e2ed22fe2d01 100644 --- a/.github/workflows/dont-delete-assets.yml +++ b/.github/workflows/dont-delete-assets.yml @@ -51,7 +51,7 @@ jobs: - name: Update comment if: ${{ steps.comment.outputs.markdown != '' }} - uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 + uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 with: comment-id: ${{ steps.findComment.outputs.comment-id }} issue-number: ${{ github.event.number }} diff --git a/.github/workflows/dont-delete-features.yml b/.github/workflows/dont-delete-features.yml index f36ed31390ce..950c3377a3ce 100644 --- a/.github/workflows/dont-delete-features.yml +++ b/.github/workflows/dont-delete-features.yml @@ -51,7 +51,7 @@ jobs: - name: Update comment if: ${{ steps.comment.outputs.markdown != '' }} - uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 + uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 with: comment-id: ${{ steps.findComment.outputs.comment-id }} issue-number: ${{ github.event.number }} diff --git a/.github/workflows/needs-sme-workflow.yml b/.github/workflows/needs-sme-workflow.yml index 171a4d8093f3..9e930545c1f6 100644 --- a/.github/workflows/needs-sme-workflow.yml +++ b/.github/workflows/needs-sme-workflow.yml @@ -24,7 +24,7 @@ jobs: - name: Check out repo uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 + - uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 with: issue-number: ${{ github.event.issue.number }} body: | @@ -43,7 +43,7 @@ jobs: - name: Check out repo uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 + - uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 with: issue-number: ${{ github.event.pull_request.number }} body: | diff --git a/.github/workflows/notify-about-deployment.yml b/.github/workflows/notify-about-deployment.yml index 377755f99e2d..c16fecc57bb5 100644 --- a/.github/workflows/notify-about-deployment.yml +++ b/.github/workflows/notify-about-deployment.yml @@ -56,7 +56,7 @@ jobs: - name: Update comment if: ${{ steps.get-number.outputs.number != '' }} - uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 + uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 with: comment-id: ${{ steps.findComment.outputs.comment-id }} issue-number: ${{ steps.get-number.outputs.number }} diff --git a/.github/workflows/readability.yml b/.github/workflows/readability.yml index b1bb7393c215..ef68d9c7982c 100644 --- a/.github/workflows/readability.yml +++ b/.github/workflows/readability.yml @@ -90,7 +90,7 @@ jobs: - name: Create or update readability comment if: ${{ steps.changed_files.outputs.filtered_changed_files && steps.read_report.outputs.report }} - uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 + uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 with: comment-id: ${{ steps.findComment.outputs.comment-id }} issue-number: ${{ github.event_name == 'workflow_dispatch' && inputs.pull_request_number || github.event.number }} diff --git a/.github/workflows/review-comment.yml b/.github/workflows/review-comment.yml index b520ff54d8a0..78aff73f539d 100644 --- a/.github/workflows/review-comment.yml +++ b/.github/workflows/review-comment.yml @@ -65,7 +65,7 @@ jobs: HEAD_SHA: ${{ github.event.pull_request.head.sha }} run: npm run content-changes-table-comment - name: Update comment - uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 + uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 with: comment-id: ${{ steps.findComment.outputs.comment-id }} issue-number: ${{ github.event.pull_request.number }} diff --git a/.github/workflows/site-policy-reminder.yml b/.github/workflows/site-policy-reminder.yml index 2a3a50cb67fe..4bedf71d8f17 100644 --- a/.github/workflows/site-policy-reminder.yml +++ b/.github/workflows/site-policy-reminder.yml @@ -19,7 +19,7 @@ jobs: github.repository == 'github/docs-internal' runs-on: ubuntu-latest steps: - - uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 + - uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 env: GITHUB_TOKEN: ${{ secrets.API_TOKEN_SITEPOLICY }} with: From b15df1140babc9492c5b4c2e2ee6f7dd7791f3ee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Oct 2025 17:33:25 +0000 Subject: [PATCH 6/7] Bump peter-evans/create-issue-from-file from 5.0.0 to 6.0.0 (#57859) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/check-broken-links-github-github.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check-broken-links-github-github.yml b/.github/workflows/check-broken-links-github-github.yml index 06f3bcc895d8..fc7ea6ff06bd 100644 --- a/.github/workflows/check-broken-links-github-github.yml +++ b/.github/workflows/check-broken-links-github-github.yml @@ -64,7 +64,7 @@ jobs: - name: Create issue from file if: ${{ hashFiles('broken_github_github_links.md') != '' }} id: github-github-broken-link-report - uses: peter-evans/create-issue-from-file@24452a72d85239eacf1468b0f1982a9f3fec4c94 + uses: peter-evans/create-issue-from-file@fca9117c27cdc29c6c4db3b86c48e4115a786710 with: token: ${{ env.GITHUB_TOKEN }} title: ${{ steps.check.outputs.title }} From f5df15a5c890d3ec0c598d1350cc4b4485d58b26 Mon Sep 17 00:00:00 2001 From: Kevin Heis Date: Wed, 8 Oct 2025 11:07:23 -0700 Subject: [PATCH 7/7] Replace `sx` and inline `style` declarations with CSS modules (#57848) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../components/GroupedEvents.module.scss | 16 +++++++++ src/audit-logs/components/GroupedEvents.tsx | 14 ++++---- .../components/DefaultLayout.module.scss | 3 ++ src/frame/components/DefaultLayout.tsx | 6 ++-- src/frame/components/GenericError.module.scss | 3 ++ src/frame/components/GenericError.tsx | 4 ++- .../components/page-header/Header.module.scss | 4 +++ src/frame/components/page-header/Header.tsx | 9 +++-- .../HeaderSearchAndWidgets.module.scss | 26 +++++++++++++++ .../page-header/HeaderSearchAndWidgets.tsx | 32 ++---------------- .../components/sidebar/SidebarNav.module.scss | 33 +++++++++++++++++++ src/frame/components/sidebar/SidebarNav.tsx | 29 ++++++++-------- .../MarkdownContent.module.scss | 11 +++++++ .../UnrenderedMarkdownContent.tsx | 12 +++---- src/frame/components/ui/MiniTocs/MiniTocs.tsx | 8 +---- .../ui/MiniTocs/Minitocs.module.scss | 16 +++++++++ .../PermissionsStatement.module.scss | 6 ++++ .../PermissionsStatement.tsx | 7 ++-- .../ui/ScrollButton/ScrollButton.module.scss | 5 +++ .../ui/ScrollButton/ScrollButton.tsx | 3 +- src/graphql/pages/explorer.module.scss | 3 ++ src/graphql/pages/explorer.tsx | 5 +-- .../components/LanguagePicker.module.scss | 12 +++++++ src/languages/components/LanguagePicker.tsx | 11 +++---- .../components/GHESReleaseNotePatch.tsx | 8 ++--- .../components/PatchNotes.module.scss | 9 +++++ .../components/RestCodeSamples.module.scss | 1 + src/rest/components/RestCodeSamples.tsx | 25 -------------- src/rest/components/RestOperation.module.scss | 4 +++ src/rest/components/RestOperation.tsx | 6 ++-- src/tools/components/Fields.module.scss | 7 ++++ src/tools/components/Fields.tsx | 12 +++---- src/tools/components/Picker.module.scss | 12 +++++++ src/tools/components/Picker.tsx | 10 ++---- src/webhooks/components/Webhook.tsx | 4 +-- .../WebhookPayloadExample.module.scss | 4 +++ 36 files changed, 246 insertions(+), 134 deletions(-) create mode 100644 src/audit-logs/components/GroupedEvents.module.scss create mode 100644 src/frame/components/DefaultLayout.module.scss create mode 100644 src/frame/components/GenericError.module.scss create mode 100644 src/frame/components/sidebar/SidebarNav.module.scss create mode 100644 src/frame/components/ui/PermissionsStatement/PermissionsStatement.module.scss create mode 100644 src/graphql/pages/explorer.module.scss create mode 100644 src/languages/components/LanguagePicker.module.scss create mode 100644 src/tools/components/Picker.module.scss diff --git a/src/audit-logs/components/GroupedEvents.module.scss b/src/audit-logs/components/GroupedEvents.module.scss new file mode 100644 index 000000000000..b0c6f70f3e4c --- /dev/null +++ b/src/audit-logs/components/GroupedEvents.module.scss @@ -0,0 +1,16 @@ +.eventItem { + margin-bottom: 3rem; +} + +.eventAction { + font-style: normal; +} + +.eventDetail { + margin-left: 1rem; + font-style: normal; +} + +.eventDescription { + margin-left: 1rem; +} diff --git a/src/audit-logs/components/GroupedEvents.tsx b/src/audit-logs/components/GroupedEvents.tsx index d5c161b03f76..1240f328bcdc 100644 --- a/src/audit-logs/components/GroupedEvents.tsx +++ b/src/audit-logs/components/GroupedEvents.tsx @@ -4,6 +4,8 @@ import { HeadingLink } from '@/frame/components/article/HeadingLink' import { useTranslation } from '@/languages/components/useTranslation' import type { AuditLogEventT } from '../types' +import styles from './GroupedEvents.module.scss' + type Props = { auditLogEvents: AuditLogEventT[] category: string @@ -47,15 +49,15 @@ export default function GroupedEvents({ auditLogEvents, category, categoryNote } )}
{auditLogEvents.map((event) => ( -
+
-
+
{event.action}
{event.description}
-
{t('fields')}
-
+
{t('fields')}
+
{event.fields ? event.fields.map((field, index) => ( @@ -68,8 +70,8 @@ export default function GroupedEvents({ auditLogEvents, category, categoryNote } {event.docs_reference_links && event.docs_reference_links !== 'N/A' && ( <> -
{t('reference')}
-
{renderReferenceLinks(event)}
+
{t('reference')}
+
{renderReferenceLinks(event)}
)}
diff --git a/src/frame/components/DefaultLayout.module.scss b/src/frame/components/DefaultLayout.module.scss new file mode 100644 index 000000000000..3612da8c340b --- /dev/null +++ b/src/frame/components/DefaultLayout.module.scss @@ -0,0 +1,3 @@ +.mainContent { + scroll-margin-top: 5rem; +} diff --git a/src/frame/components/DefaultLayout.tsx b/src/frame/components/DefaultLayout.tsx index 69f8db09638e..5c3d5bf2990c 100644 --- a/src/frame/components/DefaultLayout.tsx +++ b/src/frame/components/DefaultLayout.tsx @@ -15,6 +15,8 @@ import { useLanguages } from '@/languages/components/LanguagesContext' import { ClientSideLanguageRedirect } from './ClientSideLanguageRedirect' import { SearchOverlayContextProvider } from '@/search/components/context/SearchOverlayContext' +import styles from './DefaultLayout.module.scss' + const MINIMAL_RENDER = Boolean(JSON.parse(process.env.MINIMAL_RENDER || 'false')) type Props = { children?: React.ReactNode } @@ -51,7 +53,7 @@ export const DefaultLayout = (props: Props) => {
-
+
{props.children}
@@ -150,7 +152,7 @@ export const DefaultLayout = (props: Props) => { {/* Need to set an explicit height for sticky elements since we also set overflow to auto */}
-
+
diff --git a/src/frame/components/GenericError.module.scss b/src/frame/components/GenericError.module.scss new file mode 100644 index 000000000000..247697d1cbf3 --- /dev/null +++ b/src/frame/components/GenericError.module.scss @@ -0,0 +1,3 @@ +.logoContainer { + z-index: 3; +} diff --git a/src/frame/components/GenericError.tsx b/src/frame/components/GenericError.tsx index 3e5f0b36c7eb..d2b59d47deef 100644 --- a/src/frame/components/GenericError.tsx +++ b/src/frame/components/GenericError.tsx @@ -5,6 +5,8 @@ import { useRouter } from 'next/router' import { MarkGithubIcon, CommentDiscussionIcon } from '@primer/octicons-react' import { Lead } from '@/frame/components/ui/Lead' +import styles from './GenericError.module.scss' + export function GenericError() { return (
@@ -44,7 +46,7 @@ export const SimpleHeader = () => { role="banner" aria-label="Main" > -
+
diff --git a/src/frame/components/page-header/Header.module.scss b/src/frame/components/page-header/Header.module.scss index 27b117d45644..3a5e1285e4d0 100644 --- a/src/frame/components/page-header/Header.module.scss +++ b/src/frame/components/page-header/Header.module.scss @@ -57,3 +57,7 @@ padding: 0 !important; } } + +.headerContainer { + row-gap: 1rem; +} diff --git a/src/frame/components/page-header/Header.tsx b/src/frame/components/page-header/Header.tsx index ed4154567027..07e8409fcbb3 100644 --- a/src/frame/components/page-header/Header.tsx +++ b/src/frame/components/page-header/Header.tsx @@ -156,11 +156,10 @@ export const Header = () => { aria-label="Main" >