(path, name, lang string, md goldmark.Markdown)
| 544 | } |
| 545 | |
| 546 | func renderExerciseFile(path, name, lang string, md goldmark.Markdown) (exercise, error) { |
| 547 | raw, err := os.ReadFile(path) |
| 548 | if err != nil { |
| 549 | return exercise{}, err |
| 550 | } |
| 551 | _, h1, body := splitChapter(raw) |
| 552 | title := cleanExerciseTitle(h1) |
| 553 | blurb := exerciseBlurb(body) |
| 554 | difficulty := extractDifficulty(body) |
| 555 | // Exercise pages sit under exercises/, parallel to how-to/, so a |
| 556 | // reference to a follow_along chapter resolves the same way. |
| 557 | rewritten := rewriteLinksFrom(body, "../chapters/") |
| 558 | |
| 559 | var buf bytes.Buffer |
| 560 | if err := md.Convert([]byte(rewritten), &buf); err != nil { |
| 561 | return exercise{}, err |
| 562 | } |
| 563 | |
| 564 | slug := strings.TrimSuffix(name, ".md") |
| 565 | num := "" |
| 566 | if m := exerciseNumRe.FindStringSubmatch(slug); len(m) == 2 { |
| 567 | num = m[1] |
| 568 | } |
| 569 | return exercise{ |
| 570 | Slug: slug, |
| 571 | Num: num, |
| 572 | Title: title, |
| 573 | Blurb: template.HTML(renderInline(blurb)), |
| 574 | Difficulty: difficulty, |
| 575 | Body: template.HTML(buf.String()), |
| 576 | Lang: lang, |
| 577 | URL: "exercises/" + slug + ".html", |
| 578 | OtherURL: slug + ".html", |
| 579 | }, nil |
| 580 | } |
| 581 | |
| 582 | var ( |
| 583 | // Pulls the leading "01-" out of "01-tool-retry-on-error". |
no test coverage detected