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

Convert 10 JavaScript files to TypeScript (#57642)

This commit is contained in:
Kevin Heis
2025-09-25 07:55:33 -07:00
committed by GitHub
parent 8aa5f5324c
commit e1713a7d28
13 changed files with 203 additions and 117 deletions

View File

@@ -1,16 +1,16 @@
// used below to remove extra newlines in TOC lists
const endLine = '</a>\r?\n'
const blankLine = '\\s*?[\r\n]*'
const startNextLine = '[^\\S\r\n]*?[-\\*] <a'
const blankLineInList = new RegExp(`(${endLine})${blankLine}(${startNextLine})`, 'mg')
const endLine: string = '</a>\r?\n'
const blankLine: string = '\\s*?[\r\n]*'
const startNextLine: string = '[^\\S\r\n]*?[-\\*] <a'
const blankLineInList: RegExp = new RegExp(`(${endLine})${blankLine}(${startNextLine})`, 'mg')
export function processLiquidPost(template) {
export function processLiquidPost(template: string): string {
template = cleanUpListEmptyLines(template)
template = cleanUpExtraEmptyLines(template)
return template
}
function cleanUpListEmptyLines(template) {
function cleanUpListEmptyLines(template: string): string {
// clean up empty lines in TOC lists left by unrendered list items (due to productVersions)
// for example, remove the blank line here:
// - <a>foo</a>
@@ -22,7 +22,7 @@ function cleanUpListEmptyLines(template) {
return template
}
function cleanUpExtraEmptyLines(template) {
function cleanUpExtraEmptyLines(template: string): string {
// this removes any extra newlines left by (now resolved) liquid
// statements so that extra space doesn't mess with list numbering
template = template.replace(/(\r?\n){3}/g, '\n\n')

View File

@@ -1,17 +1,26 @@
// src/content-render/liquid/prompt.js
// src/content-render/liquid/prompt.ts
// Defines {% prompt %}…{% endprompt %} to wrap its content in <code> and append the Copilot icon.
// @ts-ignore - @primer/octicons doesn't provide TypeScript declarations
import octicons from '@primer/octicons'
export const Prompt = {
interface LiquidTag {
type: 'block'
templates?: any[] // Note: Using 'any' because liquidjs doesn't provide proper types for template objects
// Note: Using 'any' for liquid-related parameters because liquidjs doesn't provide comprehensive TypeScript definitions
parse(tagToken: any, remainTokens: any): void
render(scope: any): Generator<any, string, unknown>
}
export const Prompt: LiquidTag = {
type: 'block',
// Collect everything until {% endprompt %}
parse(tagToken, remainTokens) {
parse(tagToken: any, remainTokens: any): void {
this.templates = []
const stream = this.liquid.parser.parseStream(remainTokens)
stream
.on('template', (tpl) => this.templates.push(tpl))
.on('template', (tpl: any) => this.templates.push(tpl))
.on('tag:endprompt', () => stream.stop())
.on('end', () => {
throw new Error(`{% prompt %} tag not closed`)
@@ -20,12 +29,12 @@ export const Prompt = {
},
// Render the inner Markdown, wrap in <code>, then append the SVG
render: function* (scope) {
render: function* (scope: any): Generator<any, string, unknown> {
const content = yield this.liquid.renderer.renderTemplates(this.templates, scope)
// build a URL with the prompt text encoded as query parameter
const promptParam = encodeURIComponent(content)
const href = `https://github.com/copilot?prompt=${promptParam}`
const promptParam: string = encodeURIComponent(content as string)
const href: string = `https://github.com/copilot?prompt=${promptParam}`
return `<code>${content}</code><a href="${href}" target="_blank" class="tooltipped tooltipped-nw ml-1" aria-label="Run this prompt in Copilot Chat" style="text-decoration:none;">${octicons.copilot.toSVG()}</a>`
},
}

View File

@@ -3,8 +3,8 @@ import { renderContent } from '@/content-render/index'
describe('prompt tag', () => {
test('wraps content in <code> and appends svg', async () => {
const input = 'Here is your prompt: {% prompt %}example prompt text{% endprompt %}.'
const output = await renderContent(input)
const input: string = 'Here is your prompt: {% prompt %}example prompt text{% endprompt %}.'
const output: string = await renderContent(input)
expect(output).toContain('<code>example prompt text</code><a')
expect(output).toContain('<svg')
})

View File

@@ -1,32 +0,0 @@
// Based on https://spec.commonmark.org/0.30/#info-string
// Parse out info strings on fenced code blocks, example:
// ```javascript lineNumbers:left copy:all annotate
// becomes...
// node.lang = javascript
// node.meta = { lineNumbers: 'left', copy: 'all', annotate: true }
// Also parse equals signs, where id=some-id becomes { id: 'some-id' }
import { visit } from 'unist-util-visit'
const matcher = (node) => node.type === 'code' && node.lang
export default function parseInfoString() {
return (tree) => {
visit(tree, matcher, (node) => {
node.meta = strToObj(node.meta)
// Temporary, remove {:copy} to avoid highlight parse error in translations.
node.lang = node.lang.replace('{:copy}', '')
})
}
}
function strToObj(str) {
if (!str) return {}
return Object.fromEntries(
str
.split(/\s+/g)
.map((k) => k.split(/[:=]/)) // split by colon or equals sign
.map(([k, ...v]) => [k, v.length ? v.join(':') : true]),
)
}

View File

@@ -0,0 +1,45 @@
// Based on https://spec.commonmark.org/0.30/#info-string
// Parse out info strings on fenced code blocks, example:
// ```javascript lineNumbers:left copy:all annotate
// becomes...
// node.lang = javascript
// node.meta = { lineNumbers: 'left', copy: 'all', annotate: true }
// Also parse equals signs, where id=some-id becomes { id: 'some-id' }
import { visit } from 'unist-util-visit'
interface CodeNode {
type: 'code'
lang?: string
meta?: string | Record<string, string | boolean>
value: string
}
// Note: Using 'any' for node because unist-util-visit's type constraints
// don't easily allow for proper code node typing without complex generics
const matcher = (node: any): node is CodeNode => node.type === 'code' && node.lang
export default function parseInfoString() {
// Note: Using 'any' for tree because unified's AST types are complex and
// this function works with different tree types depending on the processor
return (tree: any) => {
visit(tree, matcher, (node: CodeNode) => {
node.meta = strToObj(node.meta as string)
// Temporary, remove {:copy} to avoid highlight parse error in translations.
if (node.lang) {
node.lang = node.lang.replace('{:copy}', '')
}
})
}
}
function strToObj(str?: string): Record<string, string | boolean> {
if (!str) return {}
return Object.fromEntries(
str
.split(/\s+/g)
.map((k: string) => k.split(/[:=]/)) // split by colon or equals sign
.map(([k, ...v]: string[]) => [k, v.length ? v.join(':') : true]),
)
}

View File

@@ -1,29 +0,0 @@
import GithubSlugger from 'github-slugger'
import { encode } from 'html-entities'
import { toString } from 'hast-util-to-string'
import { visit } from 'unist-util-visit'
const slugger = new GithubSlugger()
const matcher = (node) => node.type === 'element' && ['h2', 'h3', 'h4'].includes(node.tagName)
// replace translated IDs and links in headings with English
export default function useEnglishHeadings({ englishHeadings }) {
if (!englishHeadings) return
return (tree) => {
visit(tree, matcher, (node) => {
slugger.reset()
// Get the plain text content of the heading node
const text = toString(node)
// find English heading in the collection
const englishHeading = englishHeadings[encode(text)]
// get English slug
const englishSlug = slugger.slug(englishHeading)
// use English slug for heading ID and link
if (englishSlug) {
// only use English slug if there is one, otherwise we'll end up with
// empty IDs
node.properties.id = englishSlug
}
})
}
}

View File

@@ -0,0 +1,41 @@
import GithubSlugger from 'github-slugger'
import { encode } from 'html-entities'
import { toString } from 'hast-util-to-string'
import { visit } from 'unist-util-visit'
const slugger = new GithubSlugger()
// Note: Using 'any' for node because the unist/hast type system is complex and
// the visit function's type constraints don't easily allow for proper element typing
// without extensive type gymnastics. The runtime check ensures type safety.
const matcher = (node: any) => node.type === 'element' && ['h2', 'h3', 'h4'].includes(node.tagName)
interface UseEnglishHeadingsOptions {
englishHeadings?: Record<string, string>
}
// replace translated IDs and links in headings with English
export default function useEnglishHeadings({ englishHeadings }: UseEnglishHeadingsOptions) {
if (!englishHeadings) return
// Note: Using 'any' for tree because unified's AST types are complex and
// this function works with different tree types depending on the processor
return (tree: any) => {
// Note: Using 'any' for node because visit() callback typing is restrictive
// and doesn't easily allow for proper element typing without complex generics
visit(tree, matcher, (node: any) => {
slugger.reset()
// Get the plain text content of the heading node
const text: string = toString(node)
// find English heading in the collection
const englishHeading: string = englishHeadings[encode(text)]
// get English slug
const englishSlug: string = slugger.slug(englishHeading)
// use English slug for heading ID and link
if (englishSlug) {
// only use English slug if there is one, otherwise we'll end up with
// empty IDs
node.properties.id = englishSlug
}
})
}
}