Skip to content

Commit f3c832e

Browse files
authored
Migrate TypeScript: 9 files converted (#57928)
1 parent 4ac96b1 commit f3c832e

File tree

9 files changed

+182
-80
lines changed

9 files changed

+182
-80
lines changed

src/content-linter/lib/helpers/liquid-utils.js renamed to src/content-linter/lib/helpers/liquid-utils.ts

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,19 @@ import { Tokenizer, TokenKind } from 'liquidjs'
22

33
import { deprecated } from '@/versions/lib/enterprise-server-releases'
44

5-
const liquidTokenCache = new Map()
6-
7-
export function getLiquidTokens(content, { noCache = false } = {}) {
5+
// Using `any` for the cache because TopLevelToken is a complex union type from liquidjs
6+
// that includes TagToken, OutputToken, and HTMLToken with different properties.
7+
// The cache is private to this module and we control all access to it.
8+
const liquidTokenCache = new Map<string, any>()
9+
10+
// Returns `any[]` instead of `TopLevelToken[]` because TopLevelToken is a union type
11+
// (TagToken | OutputToken | HTMLToken) and consumers of this function access properties
12+
// like `name` and `args` that only exist on TagToken. Using `any` here avoids complex
13+
// type narrowing throughout the codebase.
14+
export function getLiquidTokens(
15+
content: string,
16+
{ noCache = false }: { noCache?: boolean } = {},
17+
): any[] {
818
if (!content) return []
919

1020
if (noCache) {
@@ -30,7 +40,12 @@ export const TAG_CLOSE = '}}'
3040
export const conditionalTags = ['if', 'elseif', 'unless', 'case', 'ifversion']
3141
const CONDITIONAL_TAG_NAMES = ['if', 'ifversion', 'elsif', 'else', 'endif']
3242

33-
export function getPositionData(token, lines) {
43+
// Token is `any` because it's used with different token types from liquidjs
44+
// that all have `begin` and `end` properties but are part of complex union types.
45+
export function getPositionData(
46+
token: any,
47+
lines: string[],
48+
): { lineNumber: number; column: number; length: number } {
3449
// Liquid indexes are 0-based, but we want to
3550
// covert to the system used by Markdownlint
3651
const begin = token.begin + 1
@@ -62,9 +77,14 @@ export function getPositionData(token, lines) {
6277
* by Markdownlint:
6378
* [ { lineNumber: 1, column: 1, deleteCount: 3, }]
6479
*/
65-
export function getContentDeleteData(token, tokenEnd, lines) {
80+
// Token is `any` because it's used with different token types from liquidjs.
81+
export function getContentDeleteData(
82+
token: any,
83+
tokenEnd: number,
84+
lines: string[],
85+
): Array<{ lineNumber: number; column: number; deleteCount: number }> {
6686
const { lineNumber, column } = getPositionData(token, lines)
67-
const errorInfo = []
87+
const errorInfo: Array<{ lineNumber: number; column: number; deleteCount: number }> = []
6888
let begin = column - 1
6989
// Subtract one from end of next token tag. The end of the
7090
// current tag is one position before that.
@@ -103,13 +123,15 @@ export function getContentDeleteData(token, tokenEnd, lines) {
103123
// related elsif, else, and endif tags).
104124
// Docs doesn't use the standard `if` tag for versioning, instead the
105125
// `ifversion` tag is used.
106-
export function getLiquidIfVersionTokens(content) {
126+
// Returns `any[]` because the tokens need to be accessed as TagToken with `name` and `args` properties,
127+
// but TopLevelToken union type would require complex type narrowing.
128+
export function getLiquidIfVersionTokens(content: string): any[] {
107129
const tokens = getLiquidTokens(content)
108130
.filter((token) => token.kind === TokenKind.Tag)
109131
.filter((token) => CONDITIONAL_TAG_NAMES.includes(token.name))
110132

111133
let inIfStatement = false
112-
const ifVersionTokens = []
134+
const ifVersionTokens: any[] = []
113135
for (const token of tokens) {
114136
if (token.name === 'if') {
115137
inIfStatement = true
@@ -125,7 +147,7 @@ export function getLiquidIfVersionTokens(content) {
125147
return ifVersionTokens
126148
}
127149

128-
export function getSimplifiedSemverRange(release) {
150+
export function getSimplifiedSemverRange(release: string): string {
129151
// Liquid conditionals only use the format > or < but not
130152
// >= or <=. Not sure exactly why.
131153
// if startswith >, we'll check to see if the release number

src/content-linter/style/base.js renamed to src/content-linter/style/base.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,21 @@ import fs from 'fs'
22
import yaml from 'js-yaml'
33

44
const allowedCodeFenceLanguages = Object.keys(
5-
yaml.load(fs.readFileSync('data/code-languages.yml', 'utf8')),
5+
yaml.load(fs.readFileSync('data/code-languages.yml', 'utf8')) as Record<string, unknown>,
66
)
77

8-
export const baseConfig = {
8+
type RuleConfig = {
9+
severity: 'error' | 'warning'
10+
'partial-markdown-files': boolean
11+
'yml-files': boolean
12+
[key: string]: any
13+
}
14+
15+
type BaseConfig = {
16+
[key: string]: boolean | RuleConfig
17+
}
18+
19+
export const baseConfig: BaseConfig = {
920
// Don't run all rules by default. This must be done first to
1021
// enable a specific set of rules.
1122
default: false,

src/content-render/tests/render-changed-and-deleted-files.js renamed to src/content-render/tests/render-changed-and-deleted-files.ts

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ function getDeletedContentFiles() {
4747
return getContentFiles(process.env.DELETED_FILES)
4848
}
4949

50-
function getContentFiles(spaceSeparatedList) {
50+
function getContentFiles(spaceSeparatedList: string | undefined): string[] {
5151
return (spaceSeparatedList || '').split(/\s+/g).filter((filePath) => {
5252
// This filters out things like '', or `data/foo.md` or `content/something/README.md`
5353
return (
@@ -69,23 +69,25 @@ describe('changed-content', () => {
6969

7070
// `test.each` will throw if the array is empty, so we need to add a dummy
7171
// when there are no changed files in the environment.
72-
if (!changedContentFiles.length) changedContentFiles.push(EMPTY)
72+
const testFiles: Array<string | symbol> = changedContentFiles.length
73+
? changedContentFiles
74+
: [EMPTY]
7375

74-
test.each(changedContentFiles)('changed-content: %s', async (file) => {
76+
test.each(testFiles)('changed-content: %s', async (file: string | symbol) => {
7577
// Necessary because `test.each` will throw if the array is empty
7678
if (file === EMPTY) return
7779

7880
const page = pageList.find((p) => {
7981
return path.join(p.basePath, p.relativePath) === file
8082
})
8183
if (!page) {
82-
throw new Error(`Could not find page for ${file} in all loaded English content`)
84+
throw new Error(`Could not find page for ${file as string} in all loaded English content`)
8385
}
8486
// Each version of the page should successfully render
8587
for (const { href } of page.permalinks) {
8688
const res = await get(href)
8789
if (!res.ok) {
88-
let msg = `This error happened when rendering from: ${file}\n`
90+
let msg = `This error happened when rendering from: ${file as string}\n`
8991
msg +=
9092
'To see the full error from vitest re-run the test with DEBUG_MIDDLEWARE_TESTS=true set\n'
9193
msg += `Or, to view it locally start the server (npm run dev) and visit http://localhost:4000${href}`
@@ -101,30 +103,34 @@ describe('deleted-content', () => {
101103

102104
// `test.each` will throw if the array is empty, so we need to add a dummy
103105
// when there are no deleted files in the environment.
104-
if (!deletedContentFiles.length) deletedContentFiles.push(EMPTY)
106+
const testFiles: Array<string | symbol> = deletedContentFiles.length
107+
? deletedContentFiles
108+
: [EMPTY]
105109

106-
test.each(deletedContentFiles)('deleted-content: %s', async (file) => {
110+
test.each(testFiles)('deleted-content: %s', async (file: string | symbol) => {
107111
// Necessary because `test.each` will throw if the array is empty
108112
if (file === EMPTY) return
109113

110114
const page = pageList.find((p) => {
111115
return path.join(p.basePath, p.relativePath) === file
112116
})
113117
if (page) {
114-
throw new Error(`The supposedly deleted file ${file} is still in list of loaded pages`)
118+
throw new Error(
119+
`The supposedly deleted file ${file as string} is still in list of loaded pages`,
120+
)
115121
}
116122
// You can't know what the possible permalinks were for a deleted page,
117123
// because it's deleted so we can't look at its `versions` front matter.
118124
// However, we always make sure all pages work in versionless.
119125
const indexmdSuffixRegex = new RegExp(`${path.sep}index\\.md$`)
120126
const mdSuffixRegex = /\.md$/
121-
const relativePath = file.split(path.sep).slice(1).join(path.sep)
127+
const relativePath = (file as string).split(path.sep).slice(1).join(path.sep)
122128
const href = `/en/${relativePath.replace(indexmdSuffixRegex, '').replace(mdSuffixRegex, '')}`
123129

124130
const res = await head(href)
125131
const error =
126132
res.statusCode === 404
127-
? `The deleted file ${file} did not set up a redirect when deleted.`
133+
? `The deleted file ${file as string} did not set up a redirect when deleted.`
128134
: ''
129135
// Certain articles that are deleted and moved under a directory with the same article name
130136
// should just route to the subcategory page instead of redirecting (docs content team confirmed).

src/frame/lib/path-utils.js renamed to src/frame/lib/path-utils.ts

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,17 @@ const supportedVersions = new Set(Object.keys(allVersions))
99

1010
// Extracts the language code from the path
1111
// if href is '/en/something', returns 'en'
12-
export function getLangFromPath(href) {
12+
export function getLangFromPath(href: string | undefined): string | null {
13+
if (!href) return null
1314
// first remove the version from the path so we don't match, say, `/free-pro-team` as `/fr/`
1415
const match = getPathWithoutVersion(href).match(patterns.getLanguageCode)
1516
return match ? match[1] : null
1617
}
1718

1819
// Add the language to the given HREF
1920
// /articles/foo -> /en/articles/foo
20-
export function getPathWithLanguage(href, languageCode) {
21+
export function getPathWithLanguage(href: string | undefined, languageCode: string): string {
22+
if (!href) return `/${languageCode}`
2123
return slash(path.posix.join('/', languageCode, getPathWithoutLanguage(href))).replace(
2224
patterns.trailingSlash,
2325
'$1',
@@ -26,12 +28,14 @@ export function getPathWithLanguage(href, languageCode) {
2628

2729
// Remove the language from the given HREF
2830
// /en/articles/foo -> /articles/foo
29-
export function getPathWithoutLanguage(href) {
31+
export function getPathWithoutLanguage(href: string | undefined): string {
32+
if (!href) return '/'
3033
return slash(href.replace(patterns.hasLanguageCode, '/'))
3134
}
3235

3336
// Remove the version segment from the path
34-
export function getPathWithoutVersion(href) {
37+
export function getPathWithoutVersion(href: string | undefined): string {
38+
if (!href) return '/'
3539
const versionFromPath = getVersionStringFromPath(href)
3640

3741
// If the derived version is not found in the list of all versions, just return the HREF
@@ -41,7 +45,16 @@ export function getPathWithoutVersion(href) {
4145
}
4246

4347
// Return the version segment in a path
44-
export function getVersionStringFromPath(href, supportedOnly = false) {
48+
export function getVersionStringFromPath(
49+
href: string | undefined,
50+
supportedOnly: true,
51+
): string | undefined
52+
export function getVersionStringFromPath(href: string | undefined, supportedOnly?: false): string
53+
export function getVersionStringFromPath(
54+
href: string | undefined,
55+
supportedOnly = false,
56+
): string | undefined {
57+
if (!href) return nonEnterpriseDefaultVersion
4558
href = getPathWithoutLanguage(href)
4659

4760
// Some URLs don't ever have a version in the URL and it won't be found
@@ -90,15 +103,16 @@ export function getVersionStringFromPath(href, supportedOnly = false) {
90103
}
91104

92105
// Return the corresponding object for the version segment in a path
93-
export function getVersionObjectFromPath(href) {
94-
const versionFromPath = getVersionStringFromPath(href)
106+
export function getVersionObjectFromPath(href: string | undefined) {
107+
const versionFromPath = getVersionStringFromPath(href, false)
95108

96109
return allVersions[versionFromPath]
97110
}
98111

99112
// TODO needs refactoring + tests
100113
// Return the product segment from the path
101-
export function getProductStringFromPath(href) {
114+
export function getProductStringFromPath(href: string | undefined): string {
115+
if (!href) return 'homepage'
102116
href = getPathWithoutLanguage(href)
103117

104118
if (href === '/') return 'homepage'
@@ -124,14 +138,15 @@ export function getProductStringFromPath(href) {
124138
return productString
125139
}
126140

127-
export function getCategoryStringFromPath(href) {
141+
export function getCategoryStringFromPath(href: string | undefined): string | undefined {
142+
if (!href) return undefined
128143
href = getPathWithoutLanguage(href)
129144

130-
if (href === '/') return null
145+
if (href === '/') return undefined
131146

132147
const pathParts = href.split('/')
133148

134-
if (pathParts.includes('early-access')) return null
149+
if (pathParts.includes('early-access')) return undefined
135150

136151
const productIndex = productIds.includes(pathParts[2]) ? 2 : 1
137152

0 commit comments

Comments
 (0)