import { type TutorialConfig } from '@stores/tutorialStore' const tutorialConfigs = import.meta.glob('../../content/**/config.json', { eager: true }) const stepModules = import.meta.glob('../../content/**/steps/*.mdx', { eager: true }) function extractTutorialId(configPath: string) { const match = configPath.match(/\.\.\/\.\.\/content\/(.+)\/config\.json$/) return match?.[1] || '' } function getTutorialConfig(configModule: unknown): TutorialConfig | null { if (!configModule || typeof configModule !== 'object') { return null } const maybeDefault = (configModule as { default?: TutorialConfig }).default if (maybeDefault && typeof maybeDefault === 'object') { return maybeDefault } return configModule as TutorialConfig } export const tutorialConfigMap = Object.entries(tutorialConfigs).reduce>( (acc, [path, moduleValue]) => { const tutorialId = extractTutorialId(path) const config = getTutorialConfig(moduleValue) if (tutorialId && config) { acc[tutorialId] = config } return acc }, {} ) export const availableTutorialIds = Object.keys(tutorialConfigMap).sort() export interface TutorialSummary { id: string title: string description?: string coverEmoji?: string category?: string difficulty?: string duration?: string tags?: string[] featured?: boolean stepCount: number } export const tutorialSummaries: TutorialSummary[] = availableTutorialIds .map((id) => { const config = tutorialConfigMap[id] return { id, title: config.title, description: config.description, coverEmoji: config.coverEmoji, category: config.category, difficulty: config.difficulty, duration: config.duration, tags: config.tags, featured: config.featured, stepCount: config.steps.length, } }) .sort((a, b) => { if (a.featured && !b.featured) return -1 if (!a.featured && b.featured) return 1 return a.title.localeCompare(b.title, 'zh-Hans-CN') }) export function resolveActiveTutorialId(requestedTutorialId: string) { if (requestedTutorialId && availableTutorialIds.includes(requestedTutorialId)) { return requestedTutorialId } if (availableTutorialIds.includes('example-tutorial')) { return 'example-tutorial' } return availableTutorialIds[0] || '' } export function isValidTutorialId(tutorialId: string) { return availableTutorialIds.includes(tutorialId) } export function loadStepComponents(activeTutorialId: string) { const loadedSteps: Record = {} Object.entries(stepModules).forEach(([path, module]: [string, any]) => { if (!path.includes(`../../content/${activeTutorialId}/steps/`)) { return } const match = path.match(/\/(\d+)-[^/]+\.mdx$/) if (match) { const index = parseInt(match[1], 10) - 1 loadedSteps[index] = module.default } }) return loadedSteps }