diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 371ff408..a079c4b4 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -17,8 +17,8 @@ const vitepressConfig = defineConfig({ markdown: { lineNumbers: true, theme: { - light: await themeService.getTheme('Eva Light'), - dark: await themeService.getTheme('Eva Dark'), + light: await themeService.getTheme('Eva-Light'), + dark: await themeService.getTheme('Eva-Dark'), }, codeTransformers: [transformerTwoslash()], }, diff --git a/docs/services/EmojiService.ts b/docs/services/EmojiService.ts index a699e3a4..c737df91 100644 --- a/docs/services/EmojiService.ts +++ b/docs/services/EmojiService.ts @@ -28,7 +28,7 @@ class FluentEmojiHandler extends EmojiHandler { } async getEmojiUrl(variant: EmojiVariant, emoji: DocumentIcon): Promise { const hex = this.getHexOfEmoji(emoji); - const match = (await githubService.fromRepository('bignutty/fluent-emoji').getTree()).filter( + const match = (await githubService.fromRepository('bignutty/fluent-emoji').getTree({})).filter( x => x.path?.includes(hex) && x.path.includes('animated-static') ); if (!match.length) throw new Error(`APNG path of emoji ${emoji} not found. Hex: ${hex}`); diff --git a/docs/services/GithubService.ts b/docs/services/GithubService.ts index a45d684a..519cefb0 100644 --- a/docs/services/GithubService.ts +++ b/docs/services/GithubService.ts @@ -19,23 +19,48 @@ class GithubRepositoryEndPointMethods { }) ).data as RepoFileResponse; } - async getTree(branchSHA?: string): Promise { - return ( - await octokit.rest.git.getTree({ - owner: this.owner, - repo: this.repo, - tree_sha: - branchSHA ?? - ( - await octokit.rest.git.getRef({ - owner: this.owner, - repo: this.repo, - ref: `heads/main`, - }) - ).data.object.sha, - recursive: 'true', - }) - ).data.tree; + async getTree(options: { branchSHA?: string; branch?: string }): Promise { + let branch: string = options.branch ?? 'main'; + let sha: string; + try { + sha = + options.branchSHA ?? + ( + await octokit.rest.git.getRef({ + owner: this.owner, + repo: this.repo, + ref: `heads/${branch}}`, + }) + ).data.object.sha; + } catch (error) { + console.log( + `Error fetching ref of ${JSON.stringify({ + repo: `${this.owner}/${this.repo}`, + branch: branch, + })}`, + error + ); + throw error; + } + try { + return ( + await octokit.rest.git.getTree({ + owner: this.owner, + repo: this.repo, + tree_sha: sha, + recursive: 'true', + }) + ).data.tree; + } catch (error) { + console.log( + `Error fetching tree of ${JSON.stringify({ + repo: `${this.owner}/${this.repo}`, + branch: branch, + })}`, + error + ); + throw error; + } } async getFiles(dir: string, searchOption: 'top' | 'deep'): Promise { const current = await this.fetchStructureByPath(dir); diff --git a/docs/services/IThemeService.ts b/docs/services/IThemeService.ts index 4488e81c..1d95ba0f 100644 --- a/docs/services/IThemeService.ts +++ b/docs/services/IThemeService.ts @@ -1,14 +1,11 @@ import * as shiki from 'shiki'; -import { themes } from '../../.github/workflows/beforeBuild/sync-themes.mjs'; -import { TextmateTheme } from '../../.github/workflows/beforeBuild/types.mjs'; -export type ThemeName = keyof typeof themes; +import { RemoteThemeInfo, TextmateTheme, ThemeName } from './ThemeService'; +// export type ThemeName = keyof typeof themes; export interface IThemeService { readonly innerThemeService: Awaited>; register(theme: TextmateTheme): Promise; getTheme(name: ThemeName): Promise; - themes(): any[]; isThemeRegistered(name: ThemeName): boolean; - parseTheme(path: string): TextmateTheme; - physicalPathOfTheme(name: ThemeName): string; + fetchThemeObject(themeInfo: RemoteThemeInfo): Promise; initializeRegistration(): Promise; } diff --git a/docs/services/ThemeService.ts b/docs/services/ThemeService.ts index b55ae021..b657ae76 100644 --- a/docs/services/ThemeService.ts +++ b/docs/services/ThemeService.ts @@ -1,11 +1,29 @@ -import * as fs from 'fs'; -import path from 'path'; +import axios from 'axios'; import * as shiki from 'shiki'; -import { themes as themeInfo } from '../../.github/workflows/beforeBuild/sync-themes.mjs'; -import type { TextmateTheme } from '../../.github/workflows/beforeBuild/types.mjs'; -import { projectRoot } from '../shared/FileSystem'; -import { IThemeService, ThemeName } from './IThemeService'; +import { getRepoFileInfo, githubService } from './GithubService'; +import { IThemeService } from './IThemeService'; const highlighter = await shiki.getSingletonHighlighter(); + +type TextmateRule = { + name?: string; + scope: string; + settings: { fontStyle?: string; foreground?: string }; +}; +export type TextmateTheme = { + name: string; + tokenColors: TextmateRule[]; +}; +export type RemoteThemeInfo = { + repo: string; + path: string; + branch: string; +}; + +const themeInfos = { + 'Eva-Light': { repo: 'fisheva/Eva-Theme', path: 'themes/Eva-Light.json', branch: 'master' }, + 'Eva-Dark': { repo: 'fisheva/Eva-Theme', path: 'themes/Eva-Dark.json', branch: 'master' }, +} satisfies Record; +export type ThemeName = keyof typeof themeInfos; class ThemeService implements IThemeService { readonly innerThemeService: Awaited> = highlighter; @@ -17,25 +35,28 @@ class ThemeService implements IThemeService { if (!this.isThemeRegistered(name)) throw new Error(`Theme \`${name}\` not registered.`); return this.innerThemeService.getTheme(name); } - themes(): any[] { - throw new Error('Method not implemented.'); - } isThemeRegistered(name: ThemeName): boolean { return this.innerThemeService.getLoadedThemes().includes(name); } - physicalPathOfTheme(name: ThemeName): string { - const ret = path.join(projectRoot().fullName, `public/${name}.json`); - if (!fs.existsSync(ret)) throw new Error(`${name}.json does not exist at /public`); - return ret; - } - parseTheme(filePath: string): TextmateTheme { - return JSON.parse(fs.readFileSync(filePath, 'utf-8')); + async fetchThemeObject(info: RemoteThemeInfo): Promise { + console.error('hello???'); + const matches = ( + await githubService.fromRepository(info.repo).getTree({ branch: info.branch }) + ).filter(x => x.path === info.path); + if (!matches.length) throw new Error(); + const url = (await getRepoFileInfo(info.repo, matches[0].path!)).download_url!; + try { + const response = await axios.get(url, { responseType: 'text' }); + return (await import('jsonc-parser')).parse(response.data) as TextmateTheme; + } catch (error) { + console.error('Error fetching JSON data:', error); + throw error; + } } async initializeRegistration(): Promise { await Promise.all( - (Object.entries(themeInfo) as [ThemeName, string][]).map(async x => { - const p = this.physicalPathOfTheme(x[0]); - const json = this.parseTheme(p); + (Object.entries(themeInfos) as [ThemeName, RemoteThemeInfo][]).map(async x => { + const json = await this.fetchThemeObject(x[1]); await this.register(json); }) );