このコミットが含まれているのは:
+36
-109
@@ -1,15 +1,12 @@
|
||||
import { parse as parseYAML } from 'yaml'
|
||||
|
||||
import type { ComponentType } from 'react'
|
||||
|
||||
type ArticleLanguage = { currentEdition: string }
|
||||
type ArticleLanguage = { currentEdition: string; publishedEditions: string[] }
|
||||
|
||||
type ArticleMeta = {
|
||||
export type ArticleMeta = {
|
||||
identifier: string
|
||||
canonicalLanguage: string
|
||||
languages: Record<string, ArticleLanguage> }
|
||||
|
||||
type MDXModule = { default: ComponentType }
|
||||
|
||||
export type HelpEditionText = {
|
||||
title: string
|
||||
body: string }
|
||||
@@ -21,7 +18,11 @@ export type HelpSearchEntry = HelpEditionText & {
|
||||
export type HelpEditionContent = HelpEditionText & { content: ComponentType }
|
||||
|
||||
export type HelpEditionSource = HelpEditionText & { source: string }
|
||||
export type HelpChange = HelpSearchEntry & { pred: string | null }
|
||||
export type HelpChange = Pick<HelpSearchEntry, 'identifier' | 'edition' | 'title'> & {
|
||||
pred: string | null
|
||||
change: 'new' | 'update'
|
||||
date: string }
|
||||
export type HelpTitleIndex = Record<string, string[]>
|
||||
export type HelpDiffLine = {
|
||||
type: 'added' | 'removed' | 'unchanged'
|
||||
content: string }
|
||||
@@ -30,70 +31,26 @@ export type HelpDiff = {
|
||||
to: HelpEditionSource
|
||||
lines: HelpDiffLine[] }
|
||||
|
||||
const articleFiles = import.meta.glob<string> ('../../help/*/article.yml',
|
||||
{ query: '?raw',
|
||||
import: 'default',
|
||||
eager: true })
|
||||
|
||||
const editionFiles = import.meta.glob<MDXModule> ('../../help/*/editions/*/*.mdx')
|
||||
|
||||
const rawEditionFiles = import.meta.glob<string> ('../../help/*/editions/*/*.mdx',
|
||||
{ query: '?raw',
|
||||
import: 'default' })
|
||||
|
||||
|
||||
const cameliseArticle = (
|
||||
value: { identifier: string
|
||||
languages: Record<string, { ['current_edition']: string }> },
|
||||
): ArticleMeta => (
|
||||
{ identifier: value.identifier,
|
||||
languages: Object.fromEntries (
|
||||
Object.entries (value.languages).map (([language, data]) => [
|
||||
language, { currentEdition: data['current_edition'] }])) })
|
||||
|
||||
|
||||
const compareEditions = (a: string, b: string): number => {
|
||||
const [dateA, revisionA = '1'] = a.split ('.')
|
||||
const [dateB, revisionB = '1'] = b.split ('.')
|
||||
|
||||
return dateA.localeCompare (dateB) || Number (revisionA) - Number (revisionB)
|
||||
export const getHelpArticle = async (identifier: string): Promise<ArticleMeta | null> => {
|
||||
const { default: articles } = await import ('virtual:help/articles')
|
||||
return Object.hasOwn (articles, identifier) ? articles[identifier] : null
|
||||
}
|
||||
|
||||
|
||||
export const getHelpArticle = (identifier: string): ArticleMeta | null => {
|
||||
const path = `../../help/${ identifier }/article.yml`
|
||||
const source = articleFiles[path]
|
||||
|
||||
if (!(source))
|
||||
return null
|
||||
|
||||
return cameliseArticle (parseYAML (source))
|
||||
}
|
||||
|
||||
|
||||
export const getHelpEditions = (
|
||||
export const getHelpEditions = async (
|
||||
identifier: string,
|
||||
language: string,
|
||||
): string[] => {
|
||||
const prefix = `../../help/${ identifier }/editions/${ language }/`
|
||||
|
||||
return (Object.keys (editionFiles)
|
||||
.filter (path => path.startsWith (prefix))
|
||||
.map (path => path.slice (prefix.length, -'.mdx'.length))
|
||||
.sort (compareEditions))
|
||||
): Promise<string[]> => {
|
||||
const article = await getHelpArticle (identifier)
|
||||
return article?.languages[language]?.publishedEditions ?? []
|
||||
}
|
||||
|
||||
|
||||
export const getHelpPublishedEditions = (
|
||||
export const getHelpPublishedEditions = async (
|
||||
identifier: string,
|
||||
language: string,
|
||||
): string[] => {
|
||||
const currentEdition = getHelpArticle (identifier)?.languages[language]?.currentEdition
|
||||
return currentEdition == null
|
||||
? []
|
||||
: getHelpEditions (identifier, language)
|
||||
.filter (edition => compareEditions (edition, currentEdition) <= 0)
|
||||
}
|
||||
): Promise<string[]> => await getHelpEditions (identifier, language)
|
||||
|
||||
|
||||
export const getHelpEdition = async (
|
||||
@@ -101,8 +58,8 @@ export const getHelpEdition = async (
|
||||
language: string,
|
||||
edition: string,
|
||||
): Promise<ComponentType | null> => {
|
||||
const path = `../../help/${ identifier }/editions/${ language }/${ edition }.mdx`
|
||||
const loader = editionFiles[path]
|
||||
const { components } = await import ('virtual:help/loaders')
|
||||
const loader = components[`${ identifier }/${ language }/${ edition }`]
|
||||
|
||||
if (!(loader))
|
||||
return null
|
||||
@@ -116,22 +73,15 @@ export const getHelpEditionSource = async (
|
||||
language: string,
|
||||
edition: string,
|
||||
): Promise<HelpEditionSource | null> => {
|
||||
const path = `../../help/${ identifier }/editions/${ language }/${ edition }.mdx`
|
||||
const loader = rawEditionFiles[path]
|
||||
const { sources } = await import ('virtual:help/loaders')
|
||||
const loader = sources[`${ identifier }/${ language }/${ edition }`]
|
||||
|
||||
if (!(loader))
|
||||
return null
|
||||
|
||||
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 { source,
|
||||
title: heading[1].trim (),
|
||||
body: mdx.slice (heading[0].length) }
|
||||
const [source, text] = await Promise.all ([
|
||||
loader (), getHelpEditionText (identifier, language, edition)])
|
||||
return text == null ? null : { source: source.default, ...text }
|
||||
}
|
||||
|
||||
|
||||
@@ -140,31 +90,17 @@ export const getHelpEditionText = async (
|
||||
language: string,
|
||||
edition: string,
|
||||
): Promise<HelpEditionText | null> => {
|
||||
const content = await getHelpEditionSource (identifier, language, edition)
|
||||
return content == null ? null : { title: content.title, body: content.body }
|
||||
const { texts } = await import ('virtual:help/loaders')
|
||||
const loader = texts[`${ identifier }/${ language }/${ edition }`]
|
||||
return loader == null ? null : (await loader ()).default
|
||||
}
|
||||
|
||||
|
||||
export const getHelpSearchEntries = async (
|
||||
language: string,
|
||||
): Promise<HelpSearchEntry[]> => {
|
||||
const entries = await Promise.all (
|
||||
Object.values (articleFiles).map (async source => {
|
||||
const article = cameliseArticle (parseYAML (source))
|
||||
const edition = article.languages[language]?.currentEdition
|
||||
|
||||
if (!(edition))
|
||||
return null
|
||||
|
||||
const content = await getHelpEditionText (article.identifier, language, edition)
|
||||
|
||||
if (!(content))
|
||||
throw new Error (`Help edition が見つかりません: ${ article.identifier }/${ edition }`)
|
||||
|
||||
return { identifier: article.identifier, edition, ...content }
|
||||
}))
|
||||
|
||||
return entries.filter ((entry): entry is HelpSearchEntry => entry != null)
|
||||
const { default: search } = await import ('virtual:help/search')
|
||||
return search[language] ?? []
|
||||
}
|
||||
|
||||
|
||||
@@ -182,23 +118,14 @@ export const getHelpEditionContent = async (
|
||||
|
||||
|
||||
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 }`)
|
||||
const { default: changes } = await import ('virtual:help/changes')
|
||||
return changes[language] ?? []
|
||||
}
|
||||
|
||||
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))
|
||||
export const getHelpTitleIndex = async (language: string): Promise<HelpTitleIndex> => {
|
||||
const { default: titles } = await import ('virtual:help/titles')
|
||||
return titles[language] ?? { }
|
||||
}
|
||||
|
||||
|
||||
@@ -252,7 +179,7 @@ export const getHelpDiff = async (
|
||||
from: string,
|
||||
to: string,
|
||||
): Promise<HelpDiff | null> => {
|
||||
const editions = getHelpPublishedEditions (identifier, language)
|
||||
const editions = await getHelpPublishedEditions (identifier, language)
|
||||
if (!(editions.includes (from)) || !(editions.includes (to)))
|
||||
return null
|
||||
|
||||
|
||||
新しいイシューから参照
ユーザーをブロックする