fix: align v2 publisher review with HMDesign stories and tokens

This commit is contained in:
DESKTOP-KPCP6AL\ㅅㅎ committed 2026-10-11 11:36:32 +09:00
1 parent 1c4e0bb9a1
commit 7b22fe88a1
27 files changed
+413 -49

No files matched your search

+65 -2
View File
@@ -3,7 +3,70 @@ import path from 'node:path'
import { execFileSync } from 'node:child_process'
const root = process.cwd()
const files = [...new Set(execFileSync('git', ['ls-files', '--cached', '--others', '--exclude-standard', '-z'], { encoding: 'utf8' }).split('\0'))].filter(file => file.endsWith('.md'))
// A portable checkout must not accidentally use a parent directory's Git repository.
let collectionMode
try {
fs.lstatSync(path.join(root, '.git'))
collectionMode = 'git'
} catch (error) {
if (error.code === 'ENOENT') collectionMode = 'filesystem'
else {
console.error(JSON.stringify({ error: 'Git metadata detection failed', detail: error.message }))
process.exit(2)
}
}
function ignoreRules(directory) {
const ignoreFile = path.join(directory, '.gitignore')
let source
try { source = fs.readFileSync(ignoreFile, 'utf8') }
catch (error) { if (error.code === 'ENOENT') return []; throw error }
return source.split(/\r?\n/).flatMap(line => {
if (!line || line.startsWith('#')) return []
const negate = line.startsWith('!')
let pattern = (negate ? line.slice(1) : line).trimEnd()
const directoryOnly = pattern.endsWith('/')
if (directoryOnly) pattern = pattern.slice(0, -1)
const anchored = pattern.startsWith('/') || pattern.includes('/')
pattern = pattern.replace(/^\//, '')
// Repository ignore rules use literal paths, *, **, ? and negation.
let expression = ''
for (let i = 0; i < pattern.length; i++) {
const char = pattern[i]
if (char === '*' && pattern[i + 1] === '*') {
i++
if (pattern[i + 1] === '/') { expression += '(?:.*/)?'; i++ }
else expression += '.*'
} else if (char === '*') expression += '[^/]*'
else if (char === '?') expression += '[^/]'
else expression += char.replace(/[\\^$+?.()|{}\[\]]/g, '\\$&')
}
return [{ directory, negate, directoryOnly, regex: new RegExp(`${anchored ? '^' : '(?:^|/)'}${expression}$`) }]
})
}
function filesystemMarkdown(directory = root, inherited = []) {
const rules = [...inherited, ...ignoreRules(directory)]
return fs.readdirSync(directory, { withFileTypes: true }).flatMap(entry => {
if (entry.name === '.git') return []
const absolute = path.join(directory, entry.name)
let ignored = false
for (const rule of rules) {
const relative = path.relative(rule.directory, absolute).replaceAll('\\', '/')
if ((!rule.directoryOnly || entry.isDirectory()) && rule.regex.test(relative)) ignored = !rule.negate
}
if (ignored) return []
if (entry.isDirectory()) return filesystemMarkdown(absolute, rules)
return entry.isFile() && entry.name.endsWith('.md') ? [path.relative(root, absolute).replaceAll('\\', '/')] : []
})
}
let files
try {
files = collectionMode === 'git'
? [...new Set(execFileSync('git', ['ls-files', '--cached', '--others', '--exclude-standard', '-z'], { cwd: root, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }).split('\0'))].filter(file => file.endsWith('.md'))
: filesystemMarkdown().sort()
} catch (error) {
console.error(JSON.stringify({ collectionMode, error: 'Markdown collection failed', detail: error.message }))
process.exit(2)
}
const stripCode = text => text.replace(/^```[^\n]*\n[\s\S]*?^```[^\n]*$/gm, '')
const slug = text => text.toLowerCase().replace(/`/g, '').replace(/[^\p{Letter}\p{Number}\p{Mark}_\- ]/gu, '').replace(/ /g, '-')
const anchors = file => {
@@ -30,5 +93,5 @@ for (const file of files) for (const [, raw] of stripCode(fs.readFileSync(file,
if (reason) problems.push({ file, href, reason })
} catch (error) { problems.push({ file, href, reason: error.message }) }
}
console.log(JSON.stringify({ files: files.length, internalLinks, problems }, null, 2))
console.log(JSON.stringify({ collectionMode, files: files.length, internalLinks, problems }, null, 2))
if (problems.length) process.exitCode = 1