Description
Is your feature request related to a problem?
Collection entries are the way you can access to a single lesson using tutorialkit:store
or Astro built-in API.
However collection are tad barebone in the fields they provide. A lot of additional logic exists in the getTutorial
method used in the main [...slug]
route.
The biggest missing point is accessing the lesson URL based on slugs.
Describe the solution you'd like.
Have a way to access the lesson complete URL more easily, using collections.
Describe alternatives you've considered.
Here is some logic I've crafted while building an RSS feed:
/**
* Dictionary of lesson id to actual slug
* Not super efficient but will be statically rendered anyway so don't bother
* { "1_patterns/2_chapter/3_lesson/content.mdx": "part-slug/chapter-slug/lesson-slug" }
* The key is "lesson.id" in the collection
* The value is the slug used as URL in the [...slug] page of tutorial kit
* @returns
*/
async function getSlugs() {
const chapters = await getCollection(
"tutorial",
({ data }) => data.type === "chapter"
);
const parts = await getCollection(
"tutorial",
({ data }) => data.type === "part"
);
const lessons = await getCollection(
"tutorial",
({ data }) => data.type === "lesson"
);
// p/1_foobar/2_baz/3_qux
// => we want to turn that into tutorial-slug/part-slug/chapter-slug/lesson-slug
let ids: Array<string> = lessons.map((l) => l.id);
const idsToSlugs = new Map<string, string>(ids.map((id) => [id, id]));
idsToSlugs.forEach((slug, id) => {
idsToSlugs.set(id, slug.replace("1-patterns", "p"));
// remove the dangling "/content.mdx"
idsToSlugs.set(id, slug.replace("/content.mdx", ""));
});
parts.forEach((pt) => {
// id is the actual filename, we want the slug
// 1_patterns/meta.md => 1_patterns
const partId = pt.id.split("/")[0];
idsToSlugs.forEach((slug, id) => {
// 3_part => part-3-slug
idsToSlugs.set(id, slug.replace(partId, pt.slug));
});
});
chapters.forEach((ch) => {
// 1_patterns/2_chapter/meta.md => 2_chapter
const chapterId = ch.id.split("/")[1];
idsToSlugs.forEach((slug, id, m) => {
// 2_chapter => chapter-2-slug
idsToSlugs.set(id, slug.replace(chapterId, ch.slug));
});
});
lessons.forEach((l) => {
// 1_patterns/2_chapter/meta.md => 2_chapter
const lessonId = l.id.split("/")[2];
idsToSlugs.forEach((slug, id, m) => {
// 2_chapter => chapter-2-slug
idsToSlugs.set(id, slug.replace(lessonId, l.slug));
});
});
return Object.fromEntries(idsToSlugs.entries());
}
Not super efficient but does the job: now idsToSlug[lesson.id]
gives you the final URL slug of that lesson.
Additional context
I don't know how we can compute derived fields in an Astro entry, if it's possible, nor how we can handle a hierarchy of content, if it's possible.
It might not be possible to rely only on Astro collection system, in which case it could be interesting to expose the getTutorial
method or part of it.