Skip to content

Commit 5eb79b5

Browse files
authored
tidy some stuff up (sveltejs#230)
* tidy some stuff up * fix * more tidying
1 parent d6b38af commit 5eb79b5

File tree

4 files changed

+147
-178
lines changed

4 files changed

+147
-178
lines changed

src/lib/server/content.js

Lines changed: 145 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import fs from 'node:fs';
22
import path from 'node:path';
3+
import glob from 'tiny-glob/sync.js';
34
import { transform } from './markdown.js';
45

56
const text_files = new Set([
@@ -22,175 +23,169 @@ function json(file) {
2223
return JSON.parse(fs.readFileSync(file, 'utf-8'));
2324
}
2425

25-
export function get_index() {
26-
const parts = [];
27-
28-
/** @type {import('$lib/types').ExerciseRaw | null} */
29-
let last_exercise = null;
30-
31-
let last_part_meta = null;
32-
let last_chapter_meta = null;
33-
34-
for (const part of fs.readdirSync('content/tutorial')) {
35-
if (!/^\d{2}-/.test(part)) continue;
36-
37-
const part_meta = {
38-
...json(`content/tutorial/${part}/meta.json`),
39-
slug: part
40-
};
41-
42-
const chapters = [];
43-
44-
for (const chapter of fs.readdirSync(`content/tutorial/${part}`)) {
45-
if (!/^\d{2}-/.test(chapter)) continue;
46-
47-
const chapter_meta = {
48-
...json(`content/tutorial/${part}/${chapter}/meta.json`),
49-
slug: chapter
50-
};
51-
52-
const exercises = [];
53-
54-
for (const exercise of fs.readdirSync(`content/tutorial/${part}/${chapter}`)) {
55-
if (!/^\d{2}-/.test(exercise)) continue;
56-
57-
const dir = `content/tutorial/${part}/${chapter}/${exercise}`;
58-
if (!fs.statSync(dir).isDirectory()) continue;
59-
60-
const text = fs.readFileSync(`${dir}/README.md`, 'utf-8');
61-
const { frontmatter, markdown } = extract_frontmatter(text, dir);
62-
const { title, path = '/', focus } = frontmatter;
63-
const slug = exercise.slice(3);
64-
const meta = fs.existsSync(`${dir}/meta.json`) ? json(`${dir}/meta.json`) : {};
65-
66-
if (last_exercise) {
67-
last_exercise.next = {
68-
slug,
69-
title:
70-
last_part_meta !== part_meta
71-
? part_meta.title
72-
: last_chapter_meta !== chapter_meta
73-
? chapter_meta.title
74-
: title
75-
};
76-
}
77-
78-
exercises.push(
79-
(last_exercise = {
80-
slug: exercise.slice(3),
81-
title,
82-
path,
83-
focus,
84-
markdown,
85-
dir,
86-
prev: last_exercise ? { slug: last_exercise.slug } : null,
87-
meta,
88-
next: null
89-
})
90-
);
26+
/** @param {string} dir */
27+
function is_valid(dir) {
28+
return /^\d{2}-/.test(dir);
29+
}
9130

92-
last_chapter_meta = chapter_meta;
93-
last_part_meta = part_meta;
94-
}
31+
/**
32+
* @returns {import('$lib/types').PartStub[]}
33+
*/
34+
export function get_index() {
35+
const parts = fs.readdirSync('content/tutorial').filter(is_valid);
9536

96-
chapters.push({
97-
meta: {
98-
...part_meta,
99-
...chapter_meta
100-
},
101-
exercises
102-
});
103-
}
37+
return parts.map((part) => {
38+
const chapters = fs.readdirSync(`content/tutorial/${part}`).filter(is_valid);
10439

105-
parts.push({
40+
return {
10641
slug: part,
107-
meta: part_meta,
108-
chapters
109-
});
110-
}
111-
112-
return parts;
42+
title: json(`content/tutorial/${part}/meta.json`).title,
43+
chapters: chapters.map((chapter) => {
44+
const exercises = fs.readdirSync(`content/tutorial/${part}/${chapter}`).filter(is_valid);
45+
46+
return {
47+
slug: chapter,
48+
title: json(`content/tutorial/${part}/${chapter}/meta.json`).title,
49+
exercises: exercises.map((exercise) => {
50+
const dir = `content/tutorial/${part}/${chapter}/${exercise}`;
51+
52+
const text = fs.readFileSync(`${dir}/README.md`, 'utf-8');
53+
const { frontmatter } = extract_frontmatter(text, dir);
54+
const { title } = frontmatter;
55+
56+
return {
57+
slug: exercise.slice(3),
58+
title
59+
};
60+
})
61+
};
62+
})
63+
};
64+
});
11365
}
11466

11567
/**
11668
* @param {string} slug
11769
* @returns {import('$lib/types').Exercise | undefined}
11870
*/
11971
export function get_exercise(slug) {
120-
const index = get_index();
72+
const exercises = glob('[0-9][0-9]-*/[0-9][0-9]-*/[0-9][0-9]-*/README.md', {
73+
cwd: 'content/tutorial'
74+
});
12175

122-
for (let i = 0; i < index.length; i += 1) {
123-
const part = index[i];
76+
/** @type {string[]} */
77+
const chain = [];
12478

125-
/** @type {string[]} */
126-
const chain = [];
79+
for (let i = 0; i < exercises.length; i += 1) {
80+
const file = exercises[i];
81+
const [part_dir, chapter_dir, exercise_dir] = file.split('/');
82+
const exercise_slug = exercise_dir.slice(3);
12783

128-
for (const chapter of part.chapters) {
129-
for (const exercise of chapter.exercises) {
130-
if (fs.existsSync(`${exercise.dir}/app-a`)) {
131-
chain.length = 0;
132-
chain.push(`${exercise.dir}/app-a`);
133-
}
84+
const dir = `content/tutorial/${part_dir}/${chapter_dir}/${exercise_dir}`;
85+
86+
if (fs.existsSync(`${dir}/app-a`)) {
87+
chain.length = 0;
88+
chain.push(`${dir}/app-a`);
89+
}
90+
91+
if (exercise_slug === slug) {
92+
const a = {
93+
...walk('content/tutorial/common', {
94+
exclude: ['node_modules', 'static/tutorial', 'static/svelte-logo-mask.svg']
95+
}),
96+
...walk(`content/tutorial/${part_dir}/common`)
97+
};
98+
99+
for (const dir of chain) {
100+
Object.assign(a, walk(dir));
101+
}
134102

135-
if (exercise.slug === slug) {
136-
const a = {
137-
...walk('content/tutorial/common', {
138-
exclude: ['node_modules', 'static/tutorial', 'static/svelte-logo-mask.svg']
139-
}),
140-
...walk(`content/tutorial/${part.slug}/common`)
141-
};
142-
143-
for (const dir of chain) {
144-
Object.assign(a, walk(dir));
145-
}
146-
147-
const b = walk(`${exercise.dir}/app-b`);
148-
149-
const scope = chapter.meta.scope ?? part.meta.scope;
150-
const filenames = new Set(
151-
Object.keys(a)
152-
.filter(
153-
(filename) => filename.startsWith(scope.prefix) && a[filename].type === 'file'
154-
)
155-
.map((filename) => filename.slice(scope.prefix.length))
156-
);
157-
158-
return {
159-
part: {
160-
slug: part.meta.slug,
161-
title: part.meta.title,
162-
index: i
163-
},
164-
chapter: {
165-
slug: chapter.meta.slug,
166-
title: chapter.meta.title
167-
},
168-
scope,
169-
focus: exercise.focus ?? chapter.meta.focus ?? part.meta.focus,
170-
title: exercise.title,
171-
path: exercise.path,
172-
slug: exercise.slug,
173-
prev: exercise.prev,
174-
next: exercise.next,
175-
dir: exercise.dir,
176-
editing_constraints: {
177-
create: exercise.meta.editing_constraints?.create ?? [],
178-
remove: exercise.meta.editing_constraints?.remove ?? []
179-
},
180-
html: transform(exercise.markdown, {
181-
codespan: (text) =>
182-
filenames.size > 1 && filenames.has(text)
183-
? `<code data-file="${scope.prefix + text}">${text}</code>`
184-
: `<code>${text}</code>`
185-
}),
186-
a,
187-
b
188-
};
103+
const b = walk(`${dir}/app-b`);
104+
105+
const part_meta = json(`content/tutorial/${part_dir}/meta.json`);
106+
const chapter_meta = json(`content/tutorial/${part_dir}/${chapter_dir}/meta.json`);
107+
108+
const exercise_meta_file = `content/tutorial/${part_dir}/${chapter_dir}/${exercise_dir}/meta.json`;
109+
const exercise_meta = fs.existsSync(exercise_meta_file) ? json(exercise_meta_file) : {};
110+
111+
const scope = chapter_meta.scope ?? part_meta.scope;
112+
const filenames = new Set(
113+
Object.keys(a)
114+
.filter((filename) => filename.startsWith(scope.prefix) && a[filename].type === 'file')
115+
.map((filename) => filename.slice(scope.prefix.length))
116+
);
117+
118+
const text = fs.readFileSync(`${dir}/README.md`, 'utf-8');
119+
const { frontmatter, markdown } = extract_frontmatter(text, dir);
120+
const { title, path = '/', focus } = frontmatter;
121+
122+
const prev_slug = exercises[i - 1]?.split('/')[2].slice(3);
123+
const prev = prev_slug
124+
? {
125+
slug: prev_slug
126+
}
127+
: null;
128+
129+
let next = null;
130+
131+
const next_exercise = exercises[i + 1];
132+
133+
if (next_exercise) {
134+
/** @type {string} */
135+
let title;
136+
137+
const dirs = next_exercise.split('/');
138+
if (dirs[0] !== part_dir) {
139+
console.log({ dirs, part_dir, next_exercise });
140+
title = json(`content/tutorial/${dirs[0]}/meta.json`).title;
141+
} else if (dirs[1] !== chapter_dir) {
142+
title = json(`content/tutorial/${dirs[0]}/${dirs[1]}/meta.json`).title;
143+
} else {
144+
title = extract_frontmatter(
145+
fs.readFileSync(`content/tutorial/${next_exercise}`, 'utf-8'),
146+
next_exercise
147+
).frontmatter.title;
189148
}
190149

191-
chain.push(`${exercise.dir}/app-b`);
150+
next = {
151+
slug: next_exercise.split('/')[2].slice(3),
152+
title
153+
};
192154
}
155+
156+
return {
157+
part: {
158+
slug: part_dir,
159+
title: `Part ${part_dir.slice(1, 2)}`
160+
},
161+
chapter: {
162+
slug: chapter_dir,
163+
title: chapter_meta.title
164+
},
165+
scope,
166+
focus: focus ?? chapter_meta.focus ?? part_meta.focus,
167+
title,
168+
path,
169+
slug: exercise_slug,
170+
prev,
171+
next,
172+
dir,
173+
editing_constraints: {
174+
create: exercise_meta.editing_constraints?.create ?? [],
175+
remove: exercise_meta.editing_constraints?.remove ?? []
176+
},
177+
html: transform(markdown, {
178+
codespan: (text) =>
179+
filenames.size > 1 && filenames.has(text)
180+
? `<code data-file="${scope.prefix + text}">${text}</code>`
181+
: `<code>${text}</code>`
182+
}),
183+
a,
184+
b
185+
};
193186
}
187+
188+
chain.push(`${dir}/app-b`);
194189
}
195190
}
196191

src/lib/types/index.d.ts

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ export interface Exercise {
4040
part: {
4141
slug: string;
4242
title: string;
43-
index: number;
4443
};
4544
chapter: {
4645
slug: string;
@@ -64,23 +63,9 @@ export interface Exercise {
6463
b: Record<string, Stub>;
6564
}
6665

67-
export interface ExerciseRaw {
68-
title: string;
69-
path: string;
70-
focus: string;
71-
slug: string;
72-
prev: { slug: string } | null;
73-
next: { slug: string; title: string } | null;
74-
meta: any;
75-
markdown: string;
76-
dir: string;
77-
}
78-
7966
export interface ExerciseStub {
8067
title: string;
8168
slug: string;
82-
prev: { slug: string } | null;
83-
next: { slug: string; title: string } | null;
8469
}
8570

8671
export interface ChapterStub {

src/routes/tutorial/[slug]/+layout.server.js

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,6 @@ import { get_index } from '$lib/server/content';
22

33
export function load() {
44
return {
5-
index: get_index().map((part) => ({
6-
title: part.meta.title,
7-
slug: part.meta.slug,
8-
chapters: part.chapters.map((chapter) => ({
9-
title: chapter.meta.title,
10-
slug: chapter.meta.slug,
11-
exercises: chapter.exercises.map((exercise) => ({
12-
title: exercise.title,
13-
slug: exercise.slug
14-
}))
15-
}))
16-
}))
5+
index: get_index()
176
};
187
}

src/routes/tutorial/[slug]/Menu.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@
173173
<!-- we don't want this to be keyboard-navigable, because the menu button to the left does that job better -->
174174
<!-- svelte-ignore a11y-click-events-have-key-events -->
175175
<h1 on:click={() => (is_open = true)}>
176-
Part {current.part.index + 1} <span class="separator">/</span>
176+
{current.part.title} <span class="separator">/</span>
177177
{current.chapter.title} <span class="separator">/</span>
178178
<strong>{current.title}</strong>
179179
</h1>

0 commit comments

Comments
 (0)