Skip to content

Commit 140601f

Browse files
committed
initial list switch
1 parent 149fa1c commit 140601f

16 files changed

Lines changed: 209 additions & 221 deletions

File tree

apps/codingcatdev/src/lib/server/content.ts

Lines changed: 137 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
import { ContentType, ContentPublished, type Lesson, type Podcast } from '$lib/types';
1+
import { ContentType, ContentPublished, type Lesson, type Podcast, type Author } from '$lib/types';
22
import type { Content, Course } from '$lib/types';
33
import { env } from '$env/dynamic/private';
4+
import { fileURLToPath } from 'url';
5+
import { opendirSync, readFileSync } from "fs";
6+
import { compile } from 'mdsvex';
47

58
const LIMIT = 20;
69

@@ -11,113 +14,152 @@ export const preview = env.PREVIEW === "false" ? false : env.VERCEL_ENV === "pre
1114
// While developing locally this allows you to see pages without setting up firebase.
1215
export const allowLocal = env.PREVIEW === "false" ? false : import.meta.env.DEV;
1316

14-
export const parseModules = async (modules: Record<string, () => Promise<unknown>>) => {
15-
const contentList: Content[] = [];
16-
for (const path in modules) {
17-
await modules[path]().then((mod) => {
18-
const splitPath = path.split('/');
19-
const courseType = splitPath.at(-3);
20-
const normalType = splitPath.at(-2);
21-
const slug = splitPath.at(-1);
22-
const type = courseType === 'content' ? normalType : courseType;
23-
24-
if (courseType === 'content') {
25-
console.log(`Precompiling: ${type}/${slug}`);
26-
} else {
27-
console.log(`Precompiling: ${type}/${normalType}`);
28-
}
29-
30-
if (!type || !slug) {
31-
console.error('Missing name or type');
32-
return;
33-
}
34-
35-
const mdsvx = mod as {
36-
default: {
37-
render: () => { html: string };
38-
};
39-
metadata: Content;
40-
};
41-
const { html } = mdsvx.default.render();
42-
/**
43-
* This needs to match the function that adds the
44-
* same data to Firestore
45-
*/
46-
const content = {
47-
...mdsvx?.metadata,
48-
cover: mdsvx?.metadata?.cover ? decodeURI(mdsvx?.metadata?.cover) : '',
49-
type: type as ContentType,
50-
html,
51-
weight: mdsvx?.metadata?.weight ? mdsvx?.metadata?.weight : 0,
52-
published: mdsvx?.metadata?.published ? mdsvx?.metadata?.published : ContentPublished.draft,
53-
start: mdsvx?.metadata?.start ? new Date(mdsvx?.metadata?.start) : new Date('Jan 01, 1900'),
54-
};
55-
contentList.push(content);
56-
});
17+
export const getContentTypeDirectory = async <T>(contentType: ContentType, withCode = true) => {
18+
const contentList: T[] = [];
19+
20+
// Normal Files
21+
let root = fileURLToPath(new URL(`../../routes/(content-single)/(non-course)/${contentType}`, import.meta.url));
22+
if (contentType === ContentType.course) {
23+
root = fileURLToPath(new URL(`../../routes/(content-single)/${contentType}`, import.meta.url));
24+
}
25+
const dirs = opendirSync(root);
26+
for await (const dir of dirs) {
27+
const parsed = await parseContentType(`${root}/${dir.name}/+page.md`, withCode) as T;
28+
contentList.push(parsed);
5729
}
5830
return contentList;
5931
}
6032

61-
export const parseLessonModules = async ({ lessonModules, courses }: { lessonModules: Record<string, () => Promise<unknown>>, courses: Course[] }) => {
62-
for (const path in lessonModules) {
63-
await lessonModules[path]().then((mod) => {
64-
const splitPath = path.split('/');
65-
const type = splitPath.at(-4);
66-
const slug = splitPath.at(-3);
67-
const lessonSlug = splitPath?.at(-1)?.replace(/\.[^/.]+$/, '');
68-
69-
console.log(`Precompiling Lesson: ${type}/${slug}/lesson/${lessonSlug}`);
70-
71-
if (!type || !slug || !lessonSlug) {
72-
console.error('Lesson Param missing');
73-
return;
74-
}
75-
76-
const mdsvx = mod as {
77-
default: {
78-
render: () => { html: string };
79-
};
80-
metadata: Lesson;
81-
};
82-
const { html } = mdsvx.default.render();
83-
/**
84-
* This needs to match the function that adds the
85-
* same data to Firestore
86-
*/
87-
const content = {
88-
...mdsvx?.metadata,
89-
cover: mdsvx?.metadata?.cover ? decodeURI(mdsvx?.metadata?.cover) : '',
90-
type: ContentType.lesson,
91-
courseSlug: slug,
92-
html,
93-
weight: mdsvx?.metadata?.weight ? mdsvx?.metadata?.weight : 0,
94-
published: mdsvx?.metadata?.published ? mdsvx?.metadata?.published : ContentPublished.draft,
95-
start: mdsvx?.metadata?.start ? new Date(mdsvx?.metadata?.start) : new Date('Jan 01, 1900'),
96-
locked: mdsvx?.metadata?.locked || false,
97-
};
33+
export const parseContentType = (async (path: string, withCode = true) => {
34+
const md = readFileSync(path, 'utf8');
35+
const transformed = await compile(md);
36+
const frontmatter = transformed?.data?.fm as Content & Podcast | undefined;
9837

99-
courses
100-
.filter((c) => c.slug === slug)
101-
.map((c) => {
102-
c?.lesson ? c.lesson.push(content) : (c['lesson'] = [content]);
103-
});
104-
});
38+
// TODO: Add more checks?
39+
40+
if (!frontmatter?.type) {
41+
console.error('Missing Frontmatter details');
42+
return;
10543
}
106-
return courses;
107-
}
44+
45+
return {
46+
...frontmatter,
47+
cover: frontmatter?.cover ? decodeURI(frontmatter?.cover) : '',
48+
type: frontmatter?.type as ContentType,
49+
html: withCode ? transformed?.code : undefined,
50+
weight: frontmatter?.weight ? frontmatter?.weight : 0,
51+
published: frontmatter?.published ? frontmatter?.published : ContentPublished.draft,
52+
start: frontmatter?.start ? new Date(frontmatter?.start) : new Date('Jan 01, 2000'),
53+
};
54+
})
55+
56+
// export const parseModules = async (modules: Record<string, () => Promise<unknown>>) => {
57+
// const contentList: Content[] = [];
58+
// for (const path in modules) {
59+
// await modules[path]().then((mod) => {
60+
// const splitPath = path.split('/');
61+
// const courseType = splitPath.at(-3);
62+
// const normalType = splitPath.at(-2);
63+
// const slug = splitPath.at(-1);
64+
// const type = courseType === 'content' ? normalType : courseType;
65+
66+
// if (courseType === 'content') {
67+
// console.log(`Precompiling: ${type}/${slug}`);
68+
// } else {
69+
// console.log(`Precompiling: ${type}/${normalType}`);
70+
// }
71+
72+
// if (!type || !slug) {
73+
// console.error('Missing name or type');
74+
// return;
75+
// }
76+
77+
// const mdsvx = mod as {
78+
// default: {
79+
// render: () => { html: string };
80+
// };
81+
// metadata: Content;
82+
// };
83+
// const { html } = mdsvx.default.render();
84+
// /**
85+
// * This needs to match the function that adds the
86+
// * same data to Firestore
87+
// */
88+
// const content = {
89+
// ...mdsvx?.metadata,
90+
// cover: mdsvx?.metadata?.cover ? decodeURI(mdsvx?.metadata?.cover) : '',
91+
// type: type as ContentType,
92+
// html,
93+
// weight: mdsvx?.metadata?.weight ? mdsvx?.metadata?.weight : 0,
94+
// published: mdsvx?.metadata?.published ? mdsvx?.metadata?.published : ContentPublished.draft,
95+
// start: mdsvx?.metadata?.start ? new Date(mdsvx?.metadata?.start) : new Date('Jan 01, 1900'),
96+
// };
97+
// contentList.push(content);
98+
// });
99+
// }
100+
// return contentList;
101+
// }
102+
103+
// export const parseLessonModules = async ({ lessonModules, courses }: { lessonModules: Record<string, () => Promise<unknown>>, courses: Course[] }) => {
104+
// for (const path in lessonModules) {
105+
// await lessonModules[path]().then((mod) => {
106+
// const splitPath = path.split('/');
107+
// const type = splitPath.at(-4);
108+
// const slug = splitPath.at(-3);
109+
// const lessonSlug = splitPath?.at(-1)?.replace(/\.[^/.]+$/, '');
110+
111+
// console.log(`Precompiling Lesson: ${type}/${slug}/lesson/${lessonSlug}`);
112+
113+
// if (!type || !slug || !lessonSlug) {
114+
// console.error('Lesson Param missing');
115+
// return;
116+
// }
117+
118+
// const mdsvx = mod as {
119+
// default: {
120+
// render: () => { html: string };
121+
// };
122+
// metadata: Lesson;
123+
// };
124+
// const { html } = mdsvx.default.render();
125+
// /**
126+
// * This needs to match the function that adds the
127+
// * same data to Firestore
128+
// */
129+
// const content = {
130+
// ...mdsvx?.metadata,
131+
// cover: mdsvx?.metadata?.cover ? decodeURI(mdsvx?.metadata?.cover) : '',
132+
// type: ContentType.lesson,
133+
// courseSlug: slug,
134+
// html,
135+
// weight: mdsvx?.metadata?.weight ? mdsvx?.metadata?.weight : 0,
136+
// published: mdsvx?.metadata?.published ? mdsvx?.metadata?.published : ContentPublished.draft,
137+
// start: mdsvx?.metadata?.start ? new Date(mdsvx?.metadata?.start) : new Date('Jan 01, 1900'),
138+
// locked: mdsvx?.metadata?.locked || false,
139+
// };
140+
141+
// courses
142+
// .filter((c) => c.slug === slug)
143+
// .map((c) => {
144+
// c?.lesson ? c.lesson.push(content) : (c['lesson'] = [content]);
145+
// });
146+
// });
147+
// }
148+
// return courses;
149+
// }
108150

109151

110152
/**
111153
* List all content from specified content type
112154
* allows for optionally sending after object
113155
* */
114-
export const listContent = async ({
156+
export const listContent = async <T extends Content>({
115157
contentItems,
116158
after,
117159
limit,
118160
contentFilter = (c) => c.published === ContentPublished.published
119161
}: {
120-
contentItems: Content[];
162+
contentItems: T[]
121163
after?: number;
122164
limit?: number;
123165
contentFilter?: (c: Content) => boolean;
@@ -127,6 +169,10 @@ export const listContent = async ({
127169

128170
console.log(`List limit of ${theLimit}`);
129171

172+
for (const c of contentItems) {
173+
console.log(c?.title)
174+
}
175+
130176
const fullContent = contentItems
131177
.filter(preview ? () => true : contentFilter)
132178
.sort((a, b) => new Date(b.start).valueOf() - new Date(a.start).valueOf());

apps/codingcatdev/src/lib/types/index.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,8 @@ export interface Sponsor extends Content {
5151
url: string;
5252
}
5353

54-
export interface Author {
55-
cover: string;
54+
export interface Author extends Content {
5655
name: string;
57-
html?: string;
58-
slug: string;
59-
start: Date;
60-
published: ContentPublished;
6156
socials: Socials;
6257
websites?: string[];
6358
}

apps/codingcatdev/src/routes/(content-list)/ContentCards.svelte

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,19 @@
22
import Image from '$lib/components/content/Image.svelte';
33
import type { Content, ContentType } from '$lib/types';
44
5-
export let data: { content: Content[]; next?: any; contentType: ContentType };
5+
export let data: { content: Content[]; next?: any };
66
77
let next = data.next;
88
const more = async () => {
99
const response = await fetch('/api/more-content', {
1010
method: 'POST',
11-
body: JSON.stringify({ contentType: data.contentType, after: next }),
11+
body: JSON.stringify({ after: next }),
1212
headers: {
1313
'content-type': 'application/json'
1414
}
1515
});
1616
const d = await response.json();
1717
data = {
18-
contentType: data.contentType,
1918
content: [...data.content, ...d.content],
2019
next
2120
};
@@ -33,7 +32,7 @@
3332
<section class="relative grid gap-4 grid-cols-fit sm:gap-10">
3433
{#each data?.content as content}
3534
<div class="max-w-6xl ccd-grid-card">
36-
<a class="self-start" href={`/${data.contentType}/${content.slug}`}>
35+
<a class="self-start" href={`/${content.type}/${content.slug}`}>
3736
{#if content?.cover}
3837
<Image
3938
src={content.cover}
Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,10 @@
1-
import { listContent, parseModules } from '$lib/server/content';
1+
import { getContentTypeDirectory, listContent } from '$lib/server/content';
22
import { ContentType, type Author } from '$lib/types';
33

4-
const contentType = ContentType.author;
5-
64
export const load = async () => {
7-
const modules = import.meta.glob(['../../../content/author/*.md']);
8-
const contentItems = await parseModules(modules);
9-
const content = (await listContent({ contentItems, limit: 500 })) as unknown as {
10-
total: number;
11-
next: number | null;
12-
content: Author[];
13-
};
145
return {
15-
contentType,
16-
...content
6+
...await listContent<Author>({
7+
contentItems: await getContentTypeDirectory<Author>(ContentType.author)
8+
})
179
};
1810
};

apps/codingcatdev/src/routes/(content-list)/authors/AuthorCards.svelte

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,19 @@
22
import Image from '$lib/components/content/Image.svelte';
33
import type { Author, ContentType } from '$lib/types';
44
5-
export let data: { content: Author[]; next?: any; contentType: ContentType };
5+
export let data: { content: Author[]; next?: any };
66
77
let next = data.next;
88
const more = async () => {
99
const response = await fetch('/api/more-content', {
1010
method: 'POST',
11-
body: JSON.stringify({ contentType: data.contentType, after: next }),
11+
body: JSON.stringify({ after: next }),
1212
headers: {
1313
'content-type': 'application/json'
1414
}
1515
});
1616
const d = await response.json();
1717
data = {
18-
contentType: data.contentType,
1918
content: [...data.content, ...d.content],
2019
next
2120
};
Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
1-
import { listContent, parseModules } from '$lib/server/content';
2-
import { ContentType } from '$lib/types';
1+
import { getContentTypeDirectory, listContent } from '$lib/server/content';
2+
import { ContentType, type Content } from '$lib/types';
33

4-
5-
const contentType = ContentType.post;
6-
7-
export const load = (async () => {
8-
const modules = import.meta.glob(['../../../content/post/*.md']);
9-
const contentItems = await parseModules(modules);
4+
export const load = async () => {
105
return {
11-
contentType,
12-
...(await listContent({ contentItems }))
6+
...await listContent<Content>({
7+
contentItems: await getContentTypeDirectory<Content>(ContentType.post)
8+
})
139
};
14-
});
10+
};

0 commit comments

Comments
 (0)