-
Notifications
You must be signed in to change notification settings - Fork 66.9k
Expand file tree
/
Copy pathsecret-scanning-transformer.ts
More file actions
105 lines (88 loc) · 3.92 KB
/
secret-scanning-transformer.ts
File metadata and controls
105 lines (88 loc) · 3.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import type { Context, Page, SecretScanningData } from '@/types'
import type { PageTransformer } from './types'
import fs from 'fs'
import yaml from 'js-yaml'
import path from 'path'
import { getVersionInfo } from '@/app/lib/constants'
import { liquid, renderContent } from '@/content-render/index'
import { allVersions } from '@/versions/lib/all-versions'
import { loadTemplate } from '@/article-api/lib/load-template'
/**
* Transformer for Secret Scanning pages.
* Loads pattern data and converts secret scanning documentation into markdown format using a Liquid template.
* Used by the Article API to render Secret Scanning documentation dynamically.
*/
export class SecretScanningTransformer implements PageTransformer {
templateName = 'secret-scanning-page.template.md'
canTransform(page: Page): boolean {
return page.autogenerated === 'secret-scanning'
}
async transform(page: Page, _pathname: string, context: Context): Promise<string> {
if (!context.secretScanningData) {
const currentVersion = context.currentVersion
if (!currentVersion) throw new Error('currentVersion is required')
const { isEnterpriseCloud, isEnterpriseServer } = getVersionInfo(currentVersion)
const versionPath = isEnterpriseCloud
? 'ghec'
: isEnterpriseServer
? `ghes-${allVersions[currentVersion].currentRelease}`
: 'fpt'
const secretScanningDir = path.join(process.cwd(), 'src/secret-scanning/data/pattern-docs')
const filepath = path.join(secretScanningDir, versionPath, 'public-docs.yml')
if (fs.existsSync(filepath)) {
const data = yaml.load(fs.readFileSync(filepath, 'utf-8')) as SecretScanningData[]
// Process Liquid in values
for (const entry of data) {
// Only process Liquid for the hasValidityCheck field, as in the middleware
if (typeof entry.hasValidityCheck === 'string' && entry.hasValidityCheck.includes('{%')) {
// Render Liquid and parse as YAML to get correct boolean type
entry.hasValidityCheck = yaml.load(
await liquid.parseAndRender(entry.hasValidityCheck, context),
) as boolean
}
if (entry.isduplicate) {
entry.secretType += ' <br/><a href="#token-versions">Token versions</a>'
}
}
context.secretScanningData = data
} else {
// If the file does not exist, set to empty array to ensure predictable behavior
context.secretScanningData = []
}
}
context.markdownRequested = true
let content = await page.render(context)
// Strip HTML comments from the rendered content
content = content.replace(/<!--.*?-->/gs, '')
// Replace HTML icon spans with plain text equivalents
content = content.replace(/<span[^>]*aria-label="Supported"[^>]*>[^<]*<\/span>/g, '✓')
content = content.replace(/<span[^>]*aria-label="Unsupported"[^>]*>[^<]*<\/span>/g, '✗')
// Convert <br/> tags to newlines and <a href="...">text</a> to markdown links
content = content.replace(/<br\s*\/?>/gi, '\n')
content = content.replace(/<a\s+href="([^"]*)"[^>]*>([^<]*)<\/a>/gi, '[$2]($1)')
// Strip any remaining HTML tags (loop to handle nested/malformed tags)
let previous = ''
while (content !== previous) {
previous = content
content = content.replace(/<[^>]+>/g, '')
}
// Normalize whitespace after stripping comments
content = content.replace(/\n{3,}/g, '\n\n').trim()
const intro = page.intro ? await page.renderProp('intro', context, { textOnly: true }) : ''
// Prepare template data
const templateData: Record<string, unknown> = {
page: {
title: page.title,
intro,
},
content,
}
// Load and render template
const templateContent = loadTemplate(this.templateName)
return await renderContent(templateContent, {
...context,
...templateData,
markdownRequested: true,
})
}
}