Nuxt/docs/scripts/gen-docs.ts

163 lines
4.2 KiB
TypeScript
Raw Normal View History

import { readFile, writeFile, mkdir } from 'node:fs/promises'
import { dirname } from 'node:path'
import type { Schema } from 'untyped'
import { resolve } from 'pathe'
import { upperFirst } from 'scule'
export async function main () {
const rootDir = resolve(__dirname, '..')
const configTemplate = resolve(__dirname, 'nuxt.config.md')
const configFile = resolve(rootDir, 'content/3.api/6.configuration/nuxt.config.md')
await generateDocs({ configFile, configTemplate })
}
function generateMarkdown (schema: Schema, title: string, level: string, parentVersions: string[] = []) {
const lines: string[] = []
// Skip private
if (schema.tags?.includes('@private')) {
return []
}
// Versions
const versions = (schema.tags || []).map(t => t.match(/@version (\d+)/)?.[1]).filter(Boolean)
if (!versions.length) {
// Inherit from parent if not specified
versions.push(...parentVersions)
}
if (!versions.includes('3')) {
return []
}
// Render heading
lines.push(`${level} ${title}`, '')
// Render meta info
if (schema.type !== 'object' || !schema.properties) {
// Type and default
if (schema.type !== 'any') {
lines.push(`- **Type**: \`${schema.type}\``)
}
const defaultValue = formatValue(schema.default)
if (defaultValue && defaultValue.length) {
if (defaultValue.length === 1) {
lines.push(`- **Default:** ${defaultValue[0]}`)
} else {
lines.push('- **Default**', ...defaultValue)
}
}
// lines.push(`- **Version**: ${versions.join(', ')}`)
lines.push('')
}
// Render title
if (schema.title) {
lines.push('> ' + schema.title, '')
}
// Render description
if (schema.description) {
lines.push(schema.description, '')
}
// Render @ tags
if (schema.tags) {
lines.push(...schema.tags.map(renderTag).flat())
}
// Render properties
if (schema.type === 'object') {
const keys = Object.keys(schema.properties || {}).sort()
for (const key of keys) {
const val = schema.properties[key] as Schema
const propLines = generateMarkdown(val, `\`${key}\``, level + '#', versions)
if (propLines.length) {
lines.push('', ...propLines)
}
}
}
return lines
}
const TAG_REGEX = /^@([\d\w]+)[\s\n]/i
const TagAlertType = {
note: 'info',
warning: 'warning',
deprecated: 'danger'
}
const InternalTypes = new Set([
'version',
'deprecated'
])
function formatValue (val) {
const stringified = JSON.stringify(val, null, 2)
if (!stringified || stringified === '{}' || stringified === '[]') { return null }
if (stringified.includes('\n')) {
return ['```json', stringified, '```']
} else {
return ['`' + stringified + '`']
}
}
function renderTag (tag: string) {
const type = tag.match(TAG_REGEX)?.[1]
if (!type) {
return [`<!-- ${tag} -->`]
}
if (InternalTypes.has(type)) {
return []
}
tag = tag.replace(`@${type}`, `**${upperFirst(type)}**:`)
if (TagAlertType[type]) {
return [`::alert{type="${TagAlertType[type]}"}`, tag, '::', '']
}
return tag
}
async function generateDocs ({ configFile, configTemplate }) {
const GENERATE_KEY = '<!-- GENERATED_CONFIG_DOCS -->'
// Prepare content directory
const start = Date.now()
console.log(`Updating docs on ${configFile}`)
const template = await readFile(configTemplate, 'utf8')
const rootSchema = require('../../packages/schema/schema/config.schema.json') as Schema
const keys = Object.keys(rootSchema.properties).sort()
let generatedDocs = ''
if (!template.includes(GENERATE_KEY)) {
throw new Error(`Could not find ${GENERATE_KEY} in ${configFile}`)
}
// Generate each section
for (const key of keys) {
const schema = rootSchema.properties[key]
const lines = generateMarkdown(schema, key, '##')
// Skip empty sections
if (lines.length < 3) {
continue
}
// Add lines to new file content
generatedDocs += lines.join('\n') + '\n'
}
const body = template.replace(GENERATE_KEY, generatedDocs)
await mkdir(dirname(configFile), { recursive: true })
await writeFile(configFile, body)
console.log(`Generate done in ${(Date.now() - start) / 1000} seconds!`)
}
main().catch((err) => {
console.error(err)
process.exit(1)
})