You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte/sites/svelte.dev/src/lib/server/tutorial/index.js

99 lines
2.8 KiB

import { modules } from '$lib/generated/type-info';
import fs from 'node:fs';
import { CONTENT_BASE_PATHS } from '../../../constants.js';
import { extract_frontmatter } from '../markdown/index.js';
import { render_markdown } from '../markdown/renderer.js';
/**
* @param {import('./types').TutorialData} tutorial_data
* @param {string} slug
*/
export async function get_parsed_tutorial(tutorial_data, slug) {
const tutorial = tutorial_data
.find(({ tutorials }) => tutorials.find((t) => t.slug === slug))
?.tutorials?.find((t) => t.slug === slug);
if (!tutorial) return null;
return {
...tutorial,
content: await render_markdown(`tutorial/${tutorial.dir}`, tutorial.content, { modules })
};
}
/**
* @returns {import('./types').TutorialData}
*/
export function get_tutorial_data(base = CONTENT_BASE_PATHS.TUTORIAL) {
const tutorials = [];
for (const subdir of fs.readdirSync(base)) {
const section = {
title: '', // Initialise with empty
slug: subdir.split('-').slice(1).join('-'),
tutorials: []
};
if (!(fs.statSync(`${base}/${subdir}`).isDirectory() || subdir.endsWith('meta.json'))) continue;
if (!subdir.endsWith('meta.json'))
section.title = JSON.parse(fs.readFileSync(`${base}/${subdir}/meta.json`, 'utf-8')).title;
for (const section_dir of fs.readdirSync(`${base}/${subdir}`)) {
const match = /\d{2}-(.+)/.exec(section_dir);
if (!match) continue;
const slug = match[1];
const tutorial_base_dir = `${base}/${subdir}/${section_dir}`;
// Read the file, get frontmatter
const contents = fs.readFileSync(`${tutorial_base_dir}/text.md`, 'utf-8');
const { metadata, body } = extract_frontmatter(contents);
// Get the contents of the apps.
const completion_states_data = { initial: [], complete: [] };
for (const app_dir of fs.readdirSync(tutorial_base_dir)) {
if (!app_dir.startsWith('app-')) continue;
const app_dir_path = `${tutorial_base_dir}/${app_dir}`;
const app_contents = fs.readdirSync(app_dir_path, 'utf-8');
for (const file of app_contents) {
completion_states_data[app_dir === 'app-a' ? 'initial' : 'complete'].push({
name: file,
type: file.split('.').at(-1),
content: fs.readFileSync(`${app_dir_path}/${file}`, 'utf-8')
});
}
}
section.tutorials.push({
title: metadata.title,
slug,
content: body,
dir: `${subdir}/${section_dir}`,
...completion_states_data
});
}
tutorials.push(section);
}
return tutorials;
}
/**
* @param {import('./types').TutorialData} tutorial_data
* @returns {import('./types').TutorialsList}
*/
export function get_tutorial_list(tutorial_data) {
return tutorial_data.map((section) => ({
title: section.title,
tutorials: section.tutorials.map((tutorial) => ({
title: tutorial.title,
slug: tutorial.slug
}))
}));
}