Skip to content

Commit 47e8481

Browse files
feat(3.2.0): refactor parser for plugin system (#482)
2 parents 4feaf70 + b473a95 commit 47e8481

45 files changed

Lines changed: 1155 additions & 798 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.freeCodeCamp/client/components/block.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,11 @@ export const Block = ({
6060
) : null}
6161
</h2>
6262
<div className='block-info'>
63-
<p>{description}</p>
63+
<p
64+
dangerouslySetInnerHTML={{
65+
__html: description
66+
}}
67+
></p>
6468
<span aria-hidden='true'>
6569
{lessonsCompleted}/{numberOfLessons}
6670
</span>
Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { ConsoleError } from '../types';
2-
import { parseMarkdown } from '../utils';
32

43
export const Console = ({ cons }: { cons: ConsoleError[] }) => {
54
return (
@@ -12,16 +11,14 @@ export const Console = ({ cons }: { cons: ConsoleError[] }) => {
1211
};
1312

1413
const ConsoleElement = ({ testText, testId, error }: ConsoleError) => {
15-
const consoleMarkdown = `<details>\n<summary>${testId + 1}) ${parseMarkdown(
16-
testText
17-
)}</summary>\n\n\`\`\`json\n${JSON.stringify(
18-
error,
19-
null,
20-
2
21-
)}\n\`\`\`\n\n</details>`;
14+
const details = `<summary>${testId + 1} ${testText}</summary>
15+
16+
${error}`;
2217
return (
23-
<div
24-
dangerouslySetInnerHTML={{ __html: parseMarkdown(consoleMarkdown) }}
25-
></div>
18+
<details
19+
dangerouslySetInnerHTML={{
20+
__html: details
21+
}}
22+
></details>
2623
);
2724
};

.freeCodeCamp/client/components/heading.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export const Heading = ({
2828
const canGoForward =
2929
lessonNumberExists && numberOfLessons && lessonNumber < numberOfLessons - 1;
3030

31+
const h1 = title + (lessonNumberExists ? ' - Lesson ' + lessonNumber : '');
3132
return (
3233
<nav className='heading'>
3334
{goToPreviousLesson && (
@@ -40,10 +41,13 @@ export const Heading = ({
4041
{'<'}
4142
</button>
4243
)}
43-
<h1 id='project-heading' className={anim}>
44-
{title}
45-
{lessonNumberExists && ' - Lesson ' + lessonNumber}
46-
</h1>
44+
<h1
45+
id='project-heading'
46+
className={anim}
47+
dangerouslySetInnerHTML={{
48+
__html: h1
49+
}}
50+
></h1>
4751
{goToNextLesson && (
4852
<button
4953
className='next-lesson-btn'
Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import { parseMarkdown } from '../utils';
2-
31
export const Hints = ({ hints }: { hints: string[] }) => {
42
return (
53
<ul style={{ listStyle: 'none' }}>
@@ -11,12 +9,16 @@ export const Hints = ({ hints }: { hints: string[] }) => {
119
};
1210

1311
const HintElement = ({ hint, i }: { hint: string; i: number }) => {
14-
const consoleMarkdown = `<details>\n<summary>Hint ${
15-
i + 1
16-
}</summary>\n${hint}\n\n</details>`;
12+
const details = `<summary>Hint ${i + 1}</summary>
13+
14+
${hint}`;
1715
return (
18-
<div
19-
dangerouslySetInnerHTML={{ __html: parseMarkdown(consoleMarkdown) }}
20-
></div>
16+
<div>
17+
<details
18+
dangerouslySetInnerHTML={{
19+
__html: details
20+
}}
21+
></details>
22+
</div>
2123
);
2224
};

.freeCodeCamp/client/components/test.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { Loader } from './loader';
22
import { TestType } from '../types';
3-
import { parseMarkdown } from '../utils';
43

54
export const Test = ({ testText, passed, isLoading, testId }: TestType) => {
65
return (
@@ -10,7 +9,7 @@ export const Test = ({ testText, passed, isLoading, testId }: TestType) => {
109
</span>
1110
<div
1211
style={{ display: 'inline' }}
13-
dangerouslySetInnerHTML={{ __html: parseMarkdown(testText) }}
12+
dangerouslySetInnerHTML={{ __html: testText }}
1413
></div>
1514
</li>
1615
);

.freeCodeCamp/client/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
import { Loader } from './components/loader';
1111
import { Landing } from './templates/landing';
1212
import { Project } from './templates/project';
13-
import { parseMarkdown, parse } from './utils/index';
13+
import { parse } from './utils/index';
1414
import { Header } from './components/header';
1515
import './styles.css';
1616
import { E44o5 } from './components/error';
@@ -129,7 +129,7 @@ const App = () => {
129129
}
130130

131131
function updateDescription({ description }: { description: string }) {
132-
setDescription(parseMarkdown(description));
132+
setDescription(description);
133133
}
134134

135135
function updateTests({ tests }: { tests: TestType[] }) {

.freeCodeCamp/client/utils/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ marked.use(
1414
})
1515
);
1616

17-
export function parseMarkdown(markdown: string) {
17+
function parseMarkdown(markdown: string) {
1818
return marked.parse(markdown, { gfm: true });
1919
}
2020

.freeCodeCamp/plugin/index.js

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
import { readFile } from 'fs/promises';
2+
import { freeCodeCampConfig, getState, ROOT } from '../tooling/env.js';
3+
import { CoffeeDown, parseMarkdown } from '../tooling/parser.js';
4+
import { join } from 'path';
5+
import { logover } from '../tooling/logger.js';
6+
17
/**
28
* Project config from `config/projects.json`
39
* @typedef {Object} Project
@@ -24,6 +30,19 @@
2430
* @property {boolean} isLoading
2531
*/
2632

33+
/**
34+
* @typedef {Object} Lesson
35+
* @property {string} description
36+
* @property {[[string, string]]} tests
37+
* @property {string[]} hints
38+
* @property {[{filePath: string; fileSeed: string} | string]} seed
39+
* @property {boolean?} isForce
40+
* @property {string?} beforeAll
41+
* @property {string?} afterAll
42+
* @property {string?} beforeEach
43+
* @property {string?} afterEach
44+
*/
45+
2746
export const pluginEvents = {
2847
/**
2948
* @param {Project} project
@@ -55,5 +74,78 @@ export const pluginEvents = {
5574
/**
5675
* @param {Project} project
5776
*/
58-
onLessonFailed: async project => {}
77+
onLessonFailed: async project => {},
78+
79+
/**
80+
* @param {string} projectDashedName
81+
* @returns {Promise<{title: string; description: string; numberOfLessons: number}>}
82+
*/
83+
getProjectMeta: async projectDashedName => {
84+
const { locale } = await getState();
85+
const projectFilePath = join(
86+
ROOT,
87+
freeCodeCampConfig.curriculum.locales[locale],
88+
projectDashedName + '.md'
89+
);
90+
const projectFile = await readFile(projectFilePath, 'utf8');
91+
const coffeeDown = new CoffeeDown(projectFile);
92+
const projectMeta = coffeeDown.getProjectMeta();
93+
// Remove `<p>` tags if present
94+
const title = parseMarkdown(projectMeta.title).replace(/<p>|<\/p>/g, '');
95+
const description = parseMarkdown(projectMeta.description);
96+
const numberOfLessons = projectMeta.numberOfLessons;
97+
return { title, description, numberOfLessons };
98+
},
99+
100+
/**
101+
* @param {string} projectDashedName
102+
* @param {number} lessonNumber
103+
* @returns {Promise<Lesson>} lesson
104+
*/
105+
getLesson: async (projectDashedName, lessonNumber) => {
106+
const { locale } = await getState();
107+
const projectFilePath = join(
108+
ROOT,
109+
freeCodeCampConfig.curriculum.locales[locale],
110+
projectDashedName + '.md'
111+
);
112+
const projectFile = await readFile(projectFilePath, 'utf8');
113+
const coffeeDown = new CoffeeDown(projectFile);
114+
const lesson = coffeeDown.getLesson(lessonNumber);
115+
let seed = lesson.seed;
116+
if (!seed.length) {
117+
// Check for external seed file
118+
const seedFilePath = projectFilePath.replace(/.md$/, '-seed.md');
119+
try {
120+
const seedContent = await readFile(seedFilePath, 'utf-8');
121+
const coffeeDown = new CoffeeDown(seedContent);
122+
seed = coffeeDown.getLesson(lessonNumber).seed;
123+
} catch (e) {
124+
if (e?.code !== 'ENOENT') {
125+
logover.debug(e);
126+
throw new Error(
127+
`Error reading external seed for lesson ${lessonNumber}`
128+
);
129+
}
130+
}
131+
}
132+
const { afterAll, afterEach, beforeAll, beforeEach, isForce } = lesson;
133+
const description = parseMarkdown(lesson.description);
134+
const tests = lesson.tests.map(([testText, test]) => [
135+
parseMarkdown(testText),
136+
test
137+
]);
138+
const hints = lesson.hints.map(parseMarkdown);
139+
return {
140+
description,
141+
tests,
142+
hints,
143+
seed,
144+
beforeAll,
145+
afterAll,
146+
beforeEach,
147+
afterEach,
148+
isForce
149+
};
150+
}
59151
};

.freeCodeCamp/tests/fixtures/expected-format.md

Lines changed: 0 additions & 74 deletions
This file was deleted.

.freeCodeCamp/tests/fixtures/valid-poor-format.md

Lines changed: 0 additions & 31 deletions
This file was deleted.

0 commit comments

Comments
 (0)