Files
mono/components/sdk/scripts/generate-index-comments.ts
T
Alex Ant 14fbce5a6b docs #2
2025-05-01 21:52:49 +05:00

208 lines
7.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import * as fs from 'node:fs'
import * as path from 'node:path'
import { fileURLToPath } from 'node:url'
function extractMutationDescriptions(schemaPath: string): Record<string, string> {
const schema = fs.readFileSync(schemaPath, 'utf-8')
// 1. Выдёргиваем блоки type Query {...} и type Mutation {...}
// Флаг [\s\S]*? — жадный для любых символов, включая переносы
// "?" — чтобы матчить в "ленивом" режиме, не захватывая дальше следующей фигурной скобки
//
// Пример:
// type Query {
// """Док для getAccount"""
// getAccount(...): ...
// }
// eslint-disable-next-line regexp/no-super-linear-backtracking
const typeBlockRegex = /type\s+(Query|Mutation)\s*\{\s*([\s\S]*?)\}/g
let match
const blocks: { [key: string]: string } = {
Query: '',
Mutation: '',
}
// eslint-disable-next-line no-cond-assign
while ((match = typeBlockRegex.exec(schema)) !== null) {
const typeName = match[1] // Query или Mutation
const blockContent = match[2] // содержимое скобок { ... }
blocks[typeName] = blockContent
}
// 2. Функция, чтобы распарсить **внутренности** типа
// Ищем пары:
// """
// многострочный
// комментарий
// """
// getAccount(...): ...
//
// или
//
// """
// многострочный
// """
// getAccount(...): ...
function parseBlock(content: string): Record<string, string> {
const results: Record<string, string> = {}
// Находим все подряд:
// """ (что угодно, включая переносы) """ (пробелы/переносы) (methodName) ( или :
//
// Обрати внимание:
// [\s\S]*? -> ленивый, чтобы не "съесть" весь текст до следующего """
// (\w+) -> имя запроса/мутации
// (?:\(|:) -> либо '(' (вызов), либо ':' (может быть тип)
const itemRegex = /"""([\s\S]*?)"""\s*(\w+)\s*(?:\(|:)/g
let m
// eslint-disable-next-line no-cond-assign
while ((m = itemRegex.exec(content)) !== null) {
let doc = m[1]
const name = m[2]
// Убираем лишние пробелы/переносы внутри doc
doc = doc
.replace(/\r/g, '')
.replace(/\n+/g, ' ')
.replace(/\s+/g, ' ')
.trim()
results[name] = doc
}
return results
}
// 3. Собираем итоговый словарь
let descriptions: Record<string, string> = {}
// Парсим блок Query
const queryMap = parseBlock(blocks.Query)
// Парсим блок Mutation
const mutationMap = parseBlock(blocks.Mutation)
// Склеиваем (если названия методов совпадут, Mutation перекроет)
descriptions = {
...queryMap,
...mutationMap,
}
console.log('📜 Загружено описаний мутаций:', Object.keys(descriptions).length)
return descriptions
}
// 📌 2. Функция для обработки одного index.ts
function processIndexFile(filePath: string, descriptions: Record<string, string>) {
const content = fs.readFileSync(filePath, 'utf-8')
const lines = content.split('\n')
// Регулярка для ОДНОГО экспорта в строке (без якоря ^ и с ;? на конце):
// "export * as SomeName from './someName';" (с пробелами/отступами, возможная ;)
const exportRegex = /export\s+\*\s+as\s+(\w+)\s+from\s+["']\.\/([\w-]+)["'];?/
const newLines: string[] = []
let modified = false
// Функция-проверка: является ли строка частью JSDoc/комментария
function isDocComment(line: string) {
const t = line.trim()
return (
t.startsWith('/**')
|| t.startsWith('/*')
|| t.startsWith('*')
|| t.startsWith('*/')
|| t.startsWith('//')
)
}
for (let i = 0; i < lines.length; i++) {
const originalLine = lines[i]
const trimmedLine = originalLine.trim()
// Проверяем, соответствует ли строка экспорт-паттерну
const match = trimmedLine.match(exportRegex)
// Если НЕ экспорт, копируем строку как есть
if (!match) {
newLines.push(originalLine)
continue
}
// Если это действительно экспорт, обрабатываем
const [, exportName, fileName] = match
// Преобразуем первую букву в нижний регистр
const exportNameKey = exportName.charAt(0).toLowerCase() + exportName.slice(1)
const description = descriptions[exportNameKey]
console.log(` ✅ Найден экспорт: ${exportName} -> ${fileName}`)
// 1) Удаляем ВСЕ комментарии, которые шли прямо над этим экспортом
while (newLines.length > 0 && isDocComment(newLines[newLines.length - 1])) {
newLines.pop()
modified = true
}
// 2) Если есть описание, добавляем новый комментарий
if (description) {
console.log(` ➕ Добавляем (или обновляем) комментарий: "${description}"`)
newLines.push(`/** ${description} */`)
modified = true
}
else {
console.warn(` ⚠️ Описание не найдено в schema.gql для "${exportName}" (ключ "${exportNameKey}")`)
}
// 3) Добавляем сам экспорт
newLines.push(originalLine)
}
// Перезаписываем файл, только если что-то изменилось
if (modified) {
fs.writeFileSync(filePath, newLines.join('\n'), 'utf-8')
}
else {
// логов не добавляем
}
}
// 📌 3. Функция для обхода подпапок `mutations` и `queries`, обработка всех index.ts
function processSubdirectories(sdkDirectory: string, schemaPath: string) {
console.log('\n🚀 Запуск обработки `index.ts` файлов в подпапках...')
const descriptions = extractMutationDescriptions(schemaPath)
const subDirs = ['mutations', 'queries']
subDirs.forEach((subDir) => {
const baseDir = path.join(sdkDirectory, subDir)
if (fs.existsSync(baseDir) && fs.statSync(baseDir).isDirectory()) {
console.log(`📂 Поиск в: ${baseDir}`)
// Получаем список всех подпапок
const subfolders = fs
.readdirSync(baseDir)
.filter(folder => fs.statSync(path.join(baseDir, folder)).isDirectory())
subfolders.forEach((folder) => {
const indexPath = path.join(baseDir, folder, 'index.ts')
if (fs.existsSync(indexPath)) {
processIndexFile(indexPath, descriptions)
}
else {
console.warn(`⚠️ Файл index.ts не найден в: ${path.join(baseDir, folder)}`)
}
})
}
else {
console.warn(`⚠️ Директория отсутствует: ${baseDir}`)
}
})
}
// 📌 4. Запуск скрипта (эмуляция __dirname в ES-модулях)
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
const schemaPath = path.resolve(__dirname, '../../controller/schema.gql')
const sdkDirectory = path.resolve(__dirname, '../src')
processSubdirectories(sdkDirectory, schemaPath)