Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion dev/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { parse, join } from 'path'
import * as esbuild from 'esbuild'

import { rootDir, DEV, time } from './utils.js'
import { generateExoFile } from './exo-parser.js'

const getHash = async head => {
if (!head.startsWith('ref:')) return { hash: head.trim(), branch: 'detached' }
Expand All @@ -12,7 +13,7 @@ const getHash = async head => {
const hash = await readFile(join(rootDir, '.git', ...parts), 'utf8')
return { hash: hash.trim(), branch }
}

try {
const head = await readFile(join(rootDir, '.git/HEAD'), 'utf8')
const { hash, branch } = await getHash(head)
Expand Down Expand Up @@ -62,6 +63,8 @@ const generate = async (file = 'index') => {
templates[key]?.replace(/<!-- ([a-zA-Z0-9]+) -->/gm, replace) ||
`<!-- missing template ${key} -->`)

await generateExoFile()

return readTemplate(file)
}

Expand Down
98 changes: 98 additions & 0 deletions dev/exo-parser.js
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'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think text is a good name to check if something is a paragraph. I you want to keep it short call it isP and isList could be isLI so it's like HTML tags

just text give me the impression that it will return the text of the node


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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fait plutot:

content.notions = children(node).map(getTrimValue).filter(Boolean)

}
} else if (mode === 'instructions') {
if (text(node)) {
let p = node.children.map((e) => e.value)
content.instructions = p.join('')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On veut garder la structure, juste stock toute la node, pas besoin de faire un cas special.
Comme ca on pourra render tout les element en (p)react

}
} 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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

## Instructions pourra etre gerer ici

} else {
// before any mode is set, we are writing the description
content.description += textContent(node)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ca vaut ptet le coup de rajouter un \n quand on concatene ici:

content.description += `${textContent(node).trim()}\n`

}
}
return content
}

const dirList = await readdir('js-introduction')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the name of the directory should be defined by the build.js file, not hard coded here

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}`]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[`${filename}`] -> [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('./')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

la t'utilise un chemin relatif, ton script va pas faire la meme chose en fonction du cwd, ou tu le run, c'est mieu de ce baser sur la position du fichier la avec fileURLToPath(dirname(import.meta.url)) comme tu l'a fait ailleur

).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}`),
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ca serais bien d'avoir:

const rootDir = fileURLToPath(dirname(import.meta.url))

plutot que de le repeter a chaque fois

})
}
6 changes: 5 additions & 1 deletion dev/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down Expand Up @@ -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()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

il faudrais generer avant ca, et aussi s'assurer que les fichier sont accessible a fetch

console.log(`Dev server ready on ${process.env.DOMAIN}`)
})
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"dependencies": {
"esbuild": "^0.11.2",
"fast-toml": "^0.5.4",
"mdast-util-from-markdown": "^1.0.4",
"preact": "^10.5.13"
}
}