このコミットが含まれているのは:
+138
-4
@@ -18,6 +18,18 @@ export type HelpSearchEntry = HelpEditionText & {
|
||||
identifier: string
|
||||
edition: string }
|
||||
|
||||
export type HelpEditionContent = HelpEditionText & { content: ComponentType }
|
||||
|
||||
export type HelpEditionSource = HelpEditionText & { source: string }
|
||||
export type HelpChange = HelpSearchEntry & { pred: string | null }
|
||||
export type HelpDiffLine = {
|
||||
type: 'added' | 'removed' | 'unchanged'
|
||||
content: string }
|
||||
export type HelpDiff = {
|
||||
from: HelpEditionSource
|
||||
to: HelpEditionSource
|
||||
lines: HelpDiffLine[] }
|
||||
|
||||
const articleFiles = import.meta.glob<string> ('../../help/*/article.yml',
|
||||
{ query: '?raw',
|
||||
import: 'default',
|
||||
@@ -72,6 +84,18 @@ export const getHelpEditions = (
|
||||
}
|
||||
|
||||
|
||||
export const getHelpPublishedEditions = (
|
||||
identifier: string,
|
||||
language: string,
|
||||
): string[] => {
|
||||
const currentEdition = getHelpArticle (identifier)?.languages[language]?.currentEdition
|
||||
return currentEdition == null
|
||||
? []
|
||||
: getHelpEditions (identifier, language)
|
||||
.filter (edition => compareEditions (edition, currentEdition) <= 0)
|
||||
}
|
||||
|
||||
|
||||
export const getHelpEdition = async (
|
||||
identifier: string,
|
||||
language: string,
|
||||
@@ -87,28 +111,40 @@ export const getHelpEdition = async (
|
||||
}
|
||||
|
||||
|
||||
export const getHelpEditionText = async (
|
||||
export const getHelpEditionSource = async (
|
||||
identifier: string,
|
||||
language: string,
|
||||
edition: string,
|
||||
): Promise<HelpEditionText | null> => {
|
||||
): Promise<HelpEditionSource | null> => {
|
||||
const path = `../../help/${ identifier }/editions/${ language }/${ edition }.mdx`
|
||||
const loader = rawEditionFiles[path]
|
||||
|
||||
if (!(loader))
|
||||
return null
|
||||
|
||||
const mdx = (await loader ()).replace (/^\uFEFF/, '')
|
||||
const source = await loader ()
|
||||
const mdx = source.replace (/^\uFEFF/, '')
|
||||
const heading = mdx.match (/^# +([^\r\n]+)(?:\r?\n|$)/)
|
||||
|
||||
if (!(heading))
|
||||
throw new Error (`Help MDX の先頭に H1 がありません: ${ path }`)
|
||||
|
||||
return { title: heading[1].trim (),
|
||||
return { source,
|
||||
title: heading[1].trim (),
|
||||
body: mdx.slice (heading[0].length) }
|
||||
}
|
||||
|
||||
|
||||
export const getHelpEditionText = async (
|
||||
identifier: string,
|
||||
language: string,
|
||||
edition: string,
|
||||
): Promise<HelpEditionText | null> => {
|
||||
const content = await getHelpEditionSource (identifier, language, edition)
|
||||
return content == null ? null : { title: content.title, body: content.body }
|
||||
}
|
||||
|
||||
|
||||
export const getHelpSearchEntries = async (
|
||||
language: string,
|
||||
): Promise<HelpSearchEntry[]> => {
|
||||
@@ -130,3 +166,101 @@ export const getHelpSearchEntries = async (
|
||||
|
||||
return entries.filter ((entry): entry is HelpSearchEntry => entry != null)
|
||||
}
|
||||
|
||||
|
||||
export const getHelpEditionContent = async (
|
||||
identifier: string,
|
||||
language: string,
|
||||
edition: string,
|
||||
): Promise<HelpEditionContent | null> => {
|
||||
const [content, text] = await Promise.all ([
|
||||
getHelpEdition (identifier, language, edition),
|
||||
getHelpEditionText (identifier, language, edition)])
|
||||
|
||||
return content != null && text != null ? { content, ...text } : null
|
||||
}
|
||||
|
||||
|
||||
export const getHelpChanges = async (language: string): Promise<HelpChange[]> => {
|
||||
const articles = Object.values (articleFiles).map (source =>
|
||||
cameliseArticle (parseYAML (source)))
|
||||
const changes = await Promise.all (articles.flatMap (article => {
|
||||
const editions = getHelpPublishedEditions (article.identifier, language)
|
||||
return editions.map (async (edition, index) => {
|
||||
const text = await getHelpEditionText (article.identifier, language, edition)
|
||||
if (text == null)
|
||||
throw new Error (`Help edition が見つかりません: ${ article.identifier }/${ edition }`)
|
||||
|
||||
return { identifier: article.identifier,
|
||||
edition, pred: editions[index - 1] ?? null, ...text }
|
||||
})
|
||||
}))
|
||||
|
||||
return changes.sort ((a, b) =>
|
||||
compareEditions (b.edition, a.edition)
|
||||
|| a.identifier.localeCompare (b.identifier))
|
||||
}
|
||||
|
||||
|
||||
const diffHelpLines = (from: string, to: string): HelpDiffLine[] => {
|
||||
const before = from.match (/[^\n]*\n|[^\n]+$/g) ?? []
|
||||
const after = to.match (/[^\n]*\n|[^\n]+$/g) ?? []
|
||||
const lengths = Array.from ({ length: before.length + 1 },
|
||||
() => new Uint32Array (after.length + 1))
|
||||
|
||||
for (let i = before.length - 1; i >= 0; --i)
|
||||
{
|
||||
for (let j = after.length - 1; j >= 0; --j)
|
||||
{
|
||||
lengths[i][j] =
|
||||
before[i] === after[j]
|
||||
? lengths[i + 1][j + 1] + 1
|
||||
: Math.max (lengths[i + 1][j], lengths[i][j + 1])
|
||||
}
|
||||
}
|
||||
|
||||
const lines: HelpDiffLine[] = []
|
||||
let i = 0
|
||||
let j = 0
|
||||
while (i < before.length || j < after.length)
|
||||
{
|
||||
if (i < before.length && j < after.length && before[i] === after[j])
|
||||
{
|
||||
lines.push ({ type: 'unchanged', content: before[i] })
|
||||
++i
|
||||
++j
|
||||
}
|
||||
else if (i < before.length
|
||||
&& (j === after.length || lengths[i + 1][j] >= lengths[i][j + 1]))
|
||||
{
|
||||
lines.push ({ type: 'removed', content: before[i] })
|
||||
++i
|
||||
}
|
||||
else
|
||||
{
|
||||
lines.push ({ type: 'added', content: after[j] })
|
||||
++j
|
||||
}
|
||||
}
|
||||
return lines
|
||||
}
|
||||
|
||||
|
||||
export const getHelpDiff = async (
|
||||
identifier: string,
|
||||
language: string,
|
||||
from: string,
|
||||
to: string,
|
||||
): Promise<HelpDiff | null> => {
|
||||
const editions = getHelpPublishedEditions (identifier, language)
|
||||
if (!(editions.includes (from)) || !(editions.includes (to)))
|
||||
return null
|
||||
|
||||
const [before, after] = await Promise.all ([
|
||||
getHelpEditionSource (identifier, language, from),
|
||||
getHelpEditionSource (identifier, language, to)])
|
||||
if (before == null || after == null)
|
||||
return null
|
||||
|
||||
return { from: before, to: after, lines: diffHelpLines (before.source, after.source) }
|
||||
}
|
||||
|
||||
新しいイシューから参照
ユーザーをブロックする