-
Notifications
You must be signed in to change notification settings - Fork 1
Parse markdown and generate JSON file for exercises #94
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 1 commit
e72c827
20affb8
090ccf6
64a0e37
bd3d844
2a28b85
0842a27
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| import { readFile, readdir, writeFile, mkdir, rename } from 'fs/promises' | ||
| import { fromMarkdown } from 'mdast-util-from-markdown' | ||
| import { fileURLToPath } from 'url' | ||
| import { join, dirname } from 'path' | ||
|
|
||
| // returns a flatten array of all the children | ||
| const children = (n) => | ||
| n.children ? [n, ...n.children.flatMap(children)] : [n] | ||
|
|
||
| const getTrimValue = (n) => n.value?.trim() | ||
| const textContent = (n) => | ||
| children(n).map(getTrimValue).filter(Boolean).join(' ') || '' | ||
|
|
||
| const isH1 = (node) => node.type === 'heading' && node.depth === 1 | ||
| const isH2 = (node) => node.type === 'heading' && node.depth === 2 | ||
| const isH3 = (node) => node.type === 'heading' && node.depth === 3 | ||
| const text = (node) => node.type === 'paragraph' | ||
| const isList = (node) => node.type === 'list' | ||
|
|
||
| const parseContent = (nodeList) => { | ||
| const content = { description: '' } | ||
| let mode, | ||
| test = text | ||
| for (const node of nodeList) { | ||
| if (!content.title && isH1(node)) { | ||
| content.title = textContent(node) | ||
| } else if (isH2(node)) { | ||
| mode = textContent(node).toLowerCase() | ||
| content[mode] = [] | ||
| } else if (mode === 'notions') { | ||
| if (isList(node)) { | ||
| let ul = node.children | ||
| .flatMap((e) => e.children) | ||
| .flatMap((v) => v.children) | ||
| .map((k) => k.value) | ||
| content.notions = ul | ||
|
||
| } | ||
| } else if (mode === 'instructions') { | ||
| if (text(node)) { | ||
| let p = node.children.map((e) => e.value) | ||
| content.instructions = p.join('') | ||
|
||
| } | ||
| } else if (mode === 'tests') { | ||
| if (isH3(node)) { | ||
| test = { name: textContent(node) } | ||
| content.tests.push(test) | ||
| } else if (test && node.type === 'code') { | ||
| test.code = textContent(node) | ||
| test.lang = node.lang | ||
| } else { | ||
| console.warn('ignored node', node) | ||
| } | ||
| } else if (mode) { | ||
| // any other mode is stored in raw tree | ||
| content[mode].push(node) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| } else { | ||
| // before any mode is set, we are writing the description | ||
| content.description += textContent(node) | ||
|
||
| } | ||
| } | ||
| return content | ||
| } | ||
|
|
||
| const dirList = await readdir('js-introduction') | ||
|
||
| const entries = await Promise.all( | ||
| dirList.map(async (name) => { | ||
| const file = await readFile(join('js-introduction/', name)) | ||
| const root = fromMarkdown(file) | ||
| return [name, parseContent(root.children)] | ||
| }), | ||
| ) | ||
|
|
||
| export const generateExoFile = async () => { | ||
| dirList.map(async (filename) => { | ||
| const data = Object.fromEntries(entries)[`${filename}`] | ||
|
||
|
|
||
| await Promise.all([ | ||
| // Create each exercise json file from a markdown file | ||
| writeFile(`${filename.split('.md')[0]}.json`, JSON.stringify(data)), | ||
|
|
||
| mkdir(join(fileURLToPath(dirname(import.meta.url)), '../exoBundle'), { | ||
| recursive: true, | ||
| }), | ||
| ]) | ||
| }) | ||
|
|
||
| const rootFile = await ( | ||
| await readdir('./') | ||
|
||
| ).filter((file) => file.includes('exercise.json')) | ||
|
|
||
| rootFile.map(async (file) => { | ||
| // Move each exercise json file in exo directory bundle | ||
| await rename( | ||
| join(fileURLToPath(dirname(import.meta.url)), `../${file}`), | ||
| join(fileURLToPath(dirname(import.meta.url)), `../exoBundle/${file}`), | ||
| ) | ||
|
||
| }) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,7 @@ import { readFile, writeFile } from 'fs/promises' | |
| import { join } from 'path' | ||
|
|
||
| import { rootDir } from './utils.js' | ||
| import { generateExoFile } from './exo-parser.js' | ||
|
|
||
| // Start esbuild's server on a random local port | ||
| const { generate, serve } = await import('./build.js') | ||
|
|
@@ -53,4 +54,7 @@ createServer(async (req, res) => { | |
|
|
||
| // Apply headers from the worker | ||
| sendResponse({ body, options, res }) | ||
| }).listen(PORT, () => console.log(`Dev server ready on ${process.env.DOMAIN}`)) | ||
| }).listen(PORT, async () => { | ||
| await generateExoFile() | ||
|
||
| console.log(`Dev server ready on ${process.env.DOMAIN}`) | ||
| }) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think
textis a good name to check if something is a paragraph. I you want to keep it short call itisPandisListcould beisLIso it's likeHTMLtagsjust
textgive me the impression that it will return thetextof the node