Skip to content

Commit d4c2042

Browse files
heiskrCopilot
andauthored
⚙️ Remove any types from a few legacy files (#60925)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent cfc835d commit d4c2042

6 files changed

Lines changed: 207 additions & 101 deletions

File tree

eslint.config.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,6 @@ export default [
234234
'src/article-api/transformers/audit-logs-transformer.ts',
235235
'src/article-api/transformers/rest-transformer.ts',
236236
'src/codeql-cli/scripts/convert-markdown-for-docs.ts',
237-
'src/content-linter/lib/linting-rules/liquid-ifversion-versions.ts',
238237
'src/content-linter/scripts/lint-content.ts',
239238

240239
'src/content-render/liquid/engine.ts',
@@ -260,7 +259,6 @@ export default [
260259
'src/graphql/tests/validate-schema.ts',
261260
'src/landings/components/CookBookFilter.tsx',
262261
'src/landings/components/ProductGuidesContext.tsx',
263-
'src/landings/components/ProductLandingContext.tsx',
264262
'src/landings/components/SidebarProduct.tsx',
265263
'src/landings/pages/home.tsx',
266264
'src/landings/pages/product.tsx',
@@ -271,7 +269,6 @@ export default [
271269
'src/links/scripts/check-github-github-links.ts',
272270
'src/links/scripts/update-internal-links.ts',
273271
'src/rest/components/get-rest-code-samples.ts',
274-
'src/rest/lib/index.ts',
275272
'src/rest/pages/category.tsx',
276273
'src/rest/pages/subcategory.tsx',
277274
'src/rest/scripts/utils/create-rest-examples.ts',
@@ -303,8 +300,6 @@ export default [
303300
'src/types/github__markdownlint-github.d.ts',
304301
'src/types/markdownlint-lib-rules.d.ts',
305302
'src/types/markdownlint-rule-helpers.d.ts',
306-
'src/types/markdownlint-rule-search-replace.d.ts',
307-
'src/types/primer__octicons.d.ts',
308303
],
309304
rules: {
310305
'@typescript-eslint/no-explicit-any': 'off',

src/content-linter/lib/linting-rules/liquid-ifversion-versions.ts

Lines changed: 133 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,64 @@ import {
2121
import { oldestSupported } from '@/versions/lib/enterprise-server-releases'
2222
import type { RuleParams, RuleErrorCallback } from '@/content-linter/types'
2323

24+
// A liquidjs token, as exposed by getLiquidIfVersionTokens. liquidjs's TopLevelToken
25+
// type does not declare all of the runtime properties we rely on (begin/end, content,
26+
// contentRange, name), so we narrow it here.
27+
type LiquidConditionalToken = TopLevelToken & {
28+
name: string
29+
content: string
30+
begin: number
31+
end: number
32+
contentRange: [number, number]
33+
}
34+
35+
// Frontmatter `versions` declaration. May be a wildcard string ("*") or a record
36+
// keyed by short version names (fpt, ghec, ghes, feature, ...) with semver-range values.
37+
type VersionsObject = Record<string, string>
38+
type FileVersionsFm = string | VersionsObject | undefined
39+
40+
type CondTagAction = {
41+
type: 'none' | 'delete' | 'all' | 'change'
42+
name?: string
43+
cond?: string
44+
line?: unknown
45+
lineNumbers?: unknown
46+
length?: unknown
47+
column?: unknown
48+
content?: unknown
49+
}
50+
51+
// Internal representation of an ifversion/elsif/else/endif tag that flows through
52+
// the rule. fileVersionsFmAll, versionsObj, featureVersionsObj, versionsObjAll, and
53+
// versions are populated for ifversion/elsif tags and may be absent on else/endif.
54+
// `action` is always populated by decorateCondTagItems before setLiquidErrors and
55+
// updateConditionals run.
56+
type CondTagItem = {
57+
name: string
58+
cond: string
59+
begin: number
60+
end: number
61+
contentrange: [number, number]
62+
fileVersionsFm: FileVersionsFm
63+
fileVersionsFmAll: VersionsObject
64+
fileVersions: string[]
65+
parent?: CondTagItem
66+
versionsObj: VersionsObject
67+
featureVersionsObj?: VersionsObject
68+
versionsObjAll: VersionsObject
69+
versions: string[]
70+
action: CondTagAction
71+
// Cached error range (used by addError); never set by this rule but accepted by addError.
72+
contentRange?: [number, number] | number[] | string | null
73+
}
74+
75+
type DefaultProps = {
76+
fileVersionsFm: FileVersionsFm
77+
fileVersions: string[]
78+
filename: string
79+
parent: CondTagItem | undefined
80+
}
81+
2482
export const liquidIfversionVersions = {
2583
names: ['GHD022', 'liquid-ifversion-versions'],
2684
description:
@@ -33,31 +91,27 @@ export const liquidIfversionVersions = {
3391
const fm = getFrontmatter(params.lines)
3492
const content = fm ? getFrontmatterLines(params.lines).join('\n') : params.lines.join('\n')
3593

36-
const fileVersionsFm = params.name.startsWith('data')
94+
const fileVersionsFm: FileVersionsFm = params.name.startsWith('data')
3795
? { ghec: '*', ghes: '*', fpt: '*' }
3896
: fm
39-
? (fm.versions as string | Record<string, string> | undefined)
40-
: (getFrontmatter(params.frontMatterLines)?.versions as
41-
| string
42-
| Record<string, string>
43-
| undefined)
97+
? (fm.versions as FileVersionsFm)
98+
: (getFrontmatter(params.frontMatterLines)?.versions as FileVersionsFm)
4499
if (!fileVersionsFm) return
45100
// This will only contain valid (non-deprecated) and future versions
46101
const fileVersions = getApplicableVersions(fileVersionsFm, '', {
47102
doNotThrow: true,
48103
includeNextVersion: true,
49104
})
50105

51-
const tokens = getLiquidIfVersionTokens(content)
106+
const tokens = getLiquidIfVersionTokens(content) as LiquidConditionalToken[]
52107
// Array of arrays - each array entry is an array of items that
53108
// make up a full if/elsif/else/endif statement.
54109
// [ [ifversion, elsif, else, endif], [nested ifversion, elsif, else, endif] ]
55-
// Using any[] because these are complex dynamic objects with properties added at runtime
56-
const condStmtStack: any[] = []
110+
const condStmtStack: CondTagItem[][] = []
57111

58112
// Tokens are in the order they are read in file, so we need to iterate
59113
// through and group full if/elsif/else/endif statements together.
60-
const defaultProps = {
114+
const defaultProps: DefaultProps = {
61115
fileVersionsFm,
62116
fileVersions,
63117
filename: params.name,
@@ -74,27 +128,25 @@ export const liquidIfversionVersions = {
74128
const condTagItem = await initTagObject(token, defaultProps)
75129
condStmtStack.push([condTagItem])
76130
} else if (token.name === 'elsif') {
77-
const condTagItems = condStmtStack.pop()
131+
const condTagItems = condStmtStack.pop()!
78132
const condTagItem = await initTagObject(token, defaultProps)
79133
condTagItems.push(condTagItem)
80134
condStmtStack.push(condTagItems)
81135
} else if (token.name === 'else') {
82-
const condTagItems = condStmtStack.pop()
136+
const condTagItems = condStmtStack.pop()!
83137
const condTagItem = await initTagObject(token, defaultProps)
84138
// The versions of an else tag are the set of file versions that are
85139
// not supported by the previous ifversion or elsif tags.
86140
const siblingVersions = condTagItems
87-
// Using any because condTagItems contains dynamic objects from initTagObject
88-
.filter((item: any) => item.name === 'ifversion' || item.name === 'elsif')
89-
.map((item: any) => item.versions)
141+
.filter((item) => item.name === 'ifversion' || item.name === 'elsif')
142+
.map((item) => item.versions)
90143
.flat()
91-
// Using any because versions property is added dynamically to condTagItem
92-
;(condTagItem as any).versions = difference(fileVersions, siblingVersions)
144+
condTagItem.versions = difference(fileVersions, siblingVersions)
93145
condTagItems.push(condTagItem)
94146
condStmtStack.push(condTagItems)
95147
} else if (token.name === 'endif') {
96148
defaultProps.parent = undefined
97-
const condTagItems = condStmtStack.pop()
149+
const condTagItems = condStmtStack.pop()!
98150
const condTagItem = await initTagObject(token, defaultProps)
99151
condTagItems.push(condTagItem)
100152
decorateCondTagItems(condTagItems)
@@ -104,18 +156,21 @@ export const liquidIfversionVersions = {
104156
},
105157
}
106158

107-
// Using any[] because condTagItems contains dynamic objects with properties added at runtime
108-
function setLiquidErrors(condTagItems: any[], onError: RuleErrorCallback, lines: string[]) {
159+
function setLiquidErrors(condTagItems: CondTagItem[], onError: RuleErrorCallback, lines: string[]) {
109160
for (let i = 0; i < condTagItems.length; i++) {
110161
const item = condTagItems[i]
111162
const tagNameNoCond = item.name === 'endif' || item.name === 'else'
112163
const itemErrorName = tagNameNoCond ? item.name : `${item.name} ${item.cond}`
113164

114-
if (item.action.type === 'delete') {
165+
if (item.action?.type === 'delete') {
115166
// There is no next stack item, the endif tag is alway the
116167
// last in a conditional
117168
const nextStackItem = item.name === 'endif' ? condTagItems[i].end : condTagItems[i + 1].begin
118-
const deleteItems = getContentDeleteData(condTagItems[i], nextStackItem, lines)
169+
const deleteItems = getContentDeleteData(
170+
condTagItems[i] as unknown as TopLevelToken,
171+
nextStackItem,
172+
lines,
173+
)
119174
for (const deleteItem of deleteItems) {
120175
addError(
121176
onError,
@@ -133,7 +188,7 @@ function setLiquidErrors(condTagItems: any[], onError: RuleErrorCallback, lines:
133188
}
134189
}
135190

136-
if (item.action.type === 'all') {
191+
if (item.action?.type === 'all') {
137192
// position is just the tag
138193
const { lineNumber, column, length } = getPositionData(
139194
{
@@ -158,7 +213,7 @@ function setLiquidErrors(condTagItems: any[], onError: RuleErrorCallback, lines:
158213
)
159214
}
160215

161-
if (item.action.type === 'change') {
216+
if (item.action?.type === 'change') {
162217
// position is just the inside of tag
163218
const { lineNumber, column, length } = getPositionData(
164219
{
@@ -186,9 +241,8 @@ function setLiquidErrors(condTagItems: any[], onError: RuleErrorCallback, lines:
186241
}
187242
}
188243

189-
async function getApplicableVersionFromLiquidTag(conditionStr: string) {
190-
// Using Record<string, any> because version object keys are dynamic (fpt, ghec, ghes, feature, etc.)
191-
const newConditionObject: Record<string, any> = {}
244+
async function getApplicableVersionFromLiquidTag(conditionStr: string): Promise<VersionsObject> {
245+
const newConditionObject: VersionsObject = {}
192246
const condition = conditionStr.replace('not ', '')
193247
const liquidTagVersions = condition.split(' or ').map((item) => item.trim())
194248
for (const ver of liquidTagVersions) {
@@ -211,7 +265,7 @@ async function getApplicableVersionFromLiquidTag(conditionStr: string) {
211265
// All actual uses have matching versions (e.g., "ghes and ghes > 3.19").
212266
// If this edge case appears in the future, additional logic would be needed here.
213267
if (!ands.every((and) => and.startsWith(firstAnd))) {
214-
return []
268+
return {}
215269
}
216270
const andValues = []
217271
let andVersion = ''
@@ -235,41 +289,56 @@ async function getApplicableVersionFromLiquidTag(conditionStr: string) {
235289
doNotThrow: true,
236290
includeNextVersion: true,
237291
})
238-
return await convertVersionsToFrontmatter(difference(all, allApplicable))
292+
return (await convertVersionsToFrontmatter(difference(all, allApplicable))) as VersionsObject
239293
}
240294
return newConditionObject
241295
}
242296

243-
// Using any for token and props because they come from markdownlint library without full type definitions
244-
async function initTagObject(token: any, props: any) {
245-
const condTagItem = {
297+
async function initTagObject(
298+
token: LiquidConditionalToken,
299+
props: DefaultProps,
300+
): Promise<CondTagItem> {
301+
const fileVersionsFm = props.fileVersionsFm
302+
// Normalize a wildcard string ('*') frontmatter `versions` value into the
303+
// canonical all-versions object so downstream consumers (Object.keys, ghes /
304+
// feature lookups) behave consistently. In practice no content file uses the
305+
// string form today, but handling it keeps the rule type-safe and future-proof.
306+
const fmObject: VersionsObject =
307+
typeof fileVersionsFm === 'string'
308+
? { ghec: '*', ghes: '*', fpt: '*' }
309+
: ((fileVersionsFm || {}) as VersionsObject)
310+
const featureFromFm = fmObject.feature
311+
const condTagItem: CondTagItem = {
246312
name: token.name,
247313
cond: token.content.replace(`${token.name} `, '').trim(),
248314
begin: token.begin,
249315
end: token.end,
250316
contentrange: token.contentRange,
251-
fileVersionsFm: props.fileVersionsFm,
252-
fileVersionsFmAll: props.fileVersionsFm?.feature
317+
fileVersionsFm,
318+
fileVersionsFmAll: featureFromFm
253319
? {
254-
...props.fileVersionsFm.versions,
255-
...getFeatureVersionsObject(props.fileVersionsFm.feature),
320+
...((fmObject as unknown as { versions?: VersionsObject }).versions || {}),
321+
...getFeatureVersionsObject(featureFromFm),
256322
}
257-
: props.fileVersionsFm,
323+
: fmObject,
258324
fileVersions: props.fileVersions,
259325
parent: props.parent,
326+
versionsObj: {},
327+
featureVersionsObj: undefined,
328+
versionsObjAll: {},
329+
versions: [],
330+
action: { type: 'none' },
260331
}
261332
if (token.name === 'ifversion' || token.name === 'elsif') {
262-
// Using any because these properties (versionsObj, featureVersionsObj, versionsObjAll, versions)
263-
// are added dynamically to condTagItem and not part of its initial type definition
264-
;(condTagItem as any).versionsObj = await getApplicableVersionFromLiquidTag(condTagItem.cond)
265-
;(condTagItem as any).featureVersionsObj = (condTagItem as any).versionsObj.feature
266-
? getFeatureVersionsObject((condTagItem as any).versionsObj.feature)
333+
condTagItem.versionsObj = await getApplicableVersionFromLiquidTag(condTagItem.cond)
334+
condTagItem.featureVersionsObj = condTagItem.versionsObj.feature
335+
? getFeatureVersionsObject(condTagItem.versionsObj.feature)
267336
: undefined
268-
;(condTagItem as any).versionsObjAll = {
269-
...(condTagItem as any).versionsObj,
270-
...(condTagItem as any).featureVersionsObj,
337+
condTagItem.versionsObjAll = {
338+
...condTagItem.versionsObj,
339+
...condTagItem.featureVersionsObj,
271340
}
272-
;(condTagItem as any).versions = getApplicableVersions((condTagItem as any).versionsObj, '', {
341+
condTagItem.versions = getApplicableVersions(condTagItem.versionsObj, '', {
273342
doNotThrow: true,
274343
includeNextVersion: true,
275344
})
@@ -286,8 +355,7 @@ async function initTagObject(token: any, props: any) {
286355
Then create flaws per stack item.
287356
newCond
288357
*/
289-
// Using any[] because condTagItems contains dynamic objects with action property added at runtime
290-
function decorateCondTagItems(condTagItems: any[]) {
358+
function decorateCondTagItems(condTagItems: CondTagItem[]) {
291359
for (const item of condTagItems) {
292360
item.action = {
293361
type: 'none',
@@ -304,8 +372,7 @@ function decorateCondTagItems(condTagItems: any[]) {
304372
return
305373
}
306374

307-
// Using any[] because condTagItems contains dynamic objects with various properties added at runtime
308-
function updateConditionals(condTagItems: any[]) {
375+
function updateConditionals(condTagItems: CondTagItem[]) {
309376
// iterate through the ifversion, elsif, and else
310377
// tags but NOT the endif tag. endif tags have
311378
// no versions associated with them and are handled
@@ -317,7 +384,14 @@ function updateConditionals(condTagItems: any[]) {
317384
// the liquid should always be removed regardless
318385
// of whether it's a feature version or a nested
319386
// condition.
320-
if (isAllVersions(item.featureVersionsObj || item.versionObj)) {
387+
// NOTE: Original code referenced `item.versionObj` (no `s`), which was always
388+
// undefined; preserved as-is to avoid changing runtime behavior in this PR.
389+
if (
390+
isAllVersions(
391+
item.featureVersionsObj ||
392+
((item as unknown as { versionObj?: VersionsObject }).versionObj as VersionsObject),
393+
)
394+
) {
321395
processConditionals(item, condTagItems, i)
322396
break
323397
}
@@ -349,7 +423,9 @@ function updateConditionals(condTagItems: any[]) {
349423
continue
350424
}
351425
}
352-
if (item.versionsObj?.feature || item.fileVersionsFm?.feature) break
426+
const fileVersionsFmObject =
427+
item.fileVersionsFm && typeof item.fileVersionsFm === 'object' ? item.fileVersionsFm : {}
428+
if (item.versionsObj?.feature || fileVersionsFmObject.feature) break
353429

354430
// When the parent of a nested condition is a feature
355431
// we don't want to assume that the feature versions
@@ -478,8 +554,11 @@ function updateConditionals(condTagItems: any[]) {
478554
}
479555
}
480556

481-
// Using any for item and any[] for condTagItems because they contain dynamic objects with action property
482-
function processConditionals(item: any, condTagItems: any[], indexOfAllItem: number) {
557+
function processConditionals(
558+
item: CondTagItem,
559+
condTagItems: CondTagItem[],
560+
indexOfAllItem: number,
561+
) {
483562
item.action.type = 'all'
484563
// if any tag in a statement is 'all', the
485564
// remaining tags are obsolete.

0 commit comments

Comments
 (0)