refactor(tools,client): remove head and tail logic (#66524)

This commit is contained in:
Sem Bauke
2026-03-17 17:59:16 +01:00
committed by GitHub
parent 1990ba6794
commit e66bf09dce
34 changed files with 24 additions and 998 deletions
@@ -97,8 +97,6 @@ function formatChallengeData({
name: 'script', name: 'script',
ext: 'js', ext: 'js',
contents: javascript.challengeFiles[0].contents, contents: javascript.challengeFiles[0].contents,
head: '',
tail: '',
path: '', path: '',
history: ['script.js'], history: ['script.js'],
fileKey: 'scriptjs' fileKey: 'scriptjs'
@@ -123,9 +121,7 @@ function formatChallengeData({
ext: 'py', ext: 'py',
name: 'main', name: 'main',
contents: python.challengeFiles[0].contents, contents: python.challengeFiles[0].contents,
head: '',
path: '', path: '',
tail: '',
editableRegionBoundaries: [], editableRegionBoundaries: [],
history: ['main.py'] history: ['main.py']
} }
-4
View File
@@ -211,7 +211,6 @@ export type ChallengeNode = {
fields: Fields; fields: Fields;
fillInTheBlank: FillInTheBlank; fillInTheBlank: FillInTheBlank;
forumTopicId: number; forumTopicId: number;
head: string[];
hasEditableBoundaries: boolean; hasEditableBoundaries: boolean;
helpCategory: string; helpCategory: string;
hooks?: Hooks; hooks?: Hooks;
@@ -245,7 +244,6 @@ export type ChallengeNode = {
sourceInstanceName: string; sourceInstanceName: string;
superOrder: number; superOrder: number;
superBlock: SuperBlocks; superBlock: SuperBlocks;
tail: string[];
template: string; template: string;
tests: Test[]; tests: Test[];
title: string; title: string;
@@ -548,11 +546,9 @@ export type ExperienceData = {
export type FileKeyChallenge = { export type FileKeyChallenge = {
contents: string; contents: string;
ext: Ext; ext: Ext;
head: string;
id: string; id: string;
key: string; key: string;
name: string; name: string;
tail: string;
}; };
export type ChallengeFiles = ChallengeFile[] | null; export type ChallengeFiles = ChallengeFile[] | null;
@@ -10,8 +10,6 @@ const jsChallenge = {
fileKey: 'jsFileKey', fileKey: 'jsFileKey',
name: 'name', name: 'name',
ext: 'js' as const, ext: 'js' as const,
head: 'head',
tail: 'tail',
history: [], history: [],
seed: 'original js contents', seed: 'original js contents',
path: 'index.js' path: 'index.js'
@@ -22,8 +20,6 @@ const cssChallenge = {
fileKey: 'cssFileKey', fileKey: 'cssFileKey',
name: 'name', name: 'name',
ext: 'css' as const, ext: 'css' as const,
head: 'head',
tail: 'tail',
history: [], history: [],
seed: 'original css contents', seed: 'original css contents',
path: 'styles.css' path: 'styles.css'
@@ -34,8 +30,6 @@ const htmlChallenge = {
fileKey: 'htmlFileKey', fileKey: 'htmlFileKey',
name: 'name', name: 'name',
ext: 'html' as const, ext: 'html' as const,
head: 'head',
tail: 'tail',
history: [], history: [],
seed: 'original html contents', seed: 'original html contents',
path: 'index.html' path: 'index.html'
@@ -39,7 +39,6 @@ const defaultProps = {
fields: {} as Fields, fields: {} as Fields,
forumTopicId: 12345, forumTopicId: 12345,
guideUrl: 'https://mockurl.com', guideUrl: 'https://mockurl.com',
head: ['mockHead'],
hasEditableBoundaries: false, hasEditableBoundaries: false,
helpCategory: 'mockHelpCategory', helpCategory: 'mockHelpCategory',
id: 'mockId', id: 'mockId',
@@ -70,7 +69,6 @@ const defaultProps = {
sourceInstanceName: 'mockSourceInstanceName', sourceInstanceName: 'mockSourceInstanceName',
superOrder: 1, superOrder: 1,
superBlock: SuperBlocks.FullStackDeveloperV9, superBlock: SuperBlocks.FullStackDeveloperV9,
tail: ['mockTail'],
template: 'mockTemplate', template: 'mockTemplate',
tests: [] as Test[], tests: [] as Test[],
title: 'mockTitle', title: 'mockTitle',
-14
View File
@@ -5,12 +5,10 @@ export const challengeFiles: ChallengeFile[] = [
contents: 'some ts', contents: 'some ts',
error: null, error: null,
ext: 'ts', ext: 'ts',
head: '',
history: ['index.ts'], history: ['index.ts'],
fileKey: 'indexts', fileKey: 'indexts',
name: 'index', name: 'index',
seed: 'some ts', seed: 'some ts',
tail: '',
editableRegionBoundaries: [], editableRegionBoundaries: [],
usesMultifileEditor: true, usesMultifileEditor: true,
path: 'index.ts' path: 'index.ts'
@@ -19,12 +17,10 @@ export const challengeFiles: ChallengeFile[] = [
contents: 'some css', contents: 'some css',
error: null, error: null,
ext: 'css', ext: 'css',
head: '',
history: ['styles.css'], history: ['styles.css'],
fileKey: 'stylescss', fileKey: 'stylescss',
name: 'styles', name: 'styles',
seed: 'some css', seed: 'some css',
tail: '',
editableRegionBoundaries: [], editableRegionBoundaries: [],
usesMultifileEditor: true, usesMultifileEditor: true,
path: 'styles.css' path: 'styles.css'
@@ -33,12 +29,10 @@ export const challengeFiles: ChallengeFile[] = [
contents: 'some html', contents: 'some html',
error: null, error: null,
ext: 'html', ext: 'html',
head: '',
history: ['index.html'], history: ['index.html'],
fileKey: 'indexhtml', fileKey: 'indexhtml',
name: 'index', name: 'index',
seed: 'some html', seed: 'some html',
tail: '',
editableRegionBoundaries: [], editableRegionBoundaries: [],
usesMultifileEditor: true, usesMultifileEditor: true,
path: 'index.html' path: 'index.html'
@@ -47,12 +41,10 @@ export const challengeFiles: ChallengeFile[] = [
contents: 'some js', contents: 'some js',
error: null, error: null,
ext: 'js', ext: 'js',
head: '',
history: ['script.js'], history: ['script.js'],
fileKey: 'scriptjs', fileKey: 'scriptjs',
name: 'script', name: 'script',
seed: 'some js', seed: 'some js',
tail: '',
editableRegionBoundaries: [], editableRegionBoundaries: [],
usesMultifileEditor: true, usesMultifileEditor: true,
path: 'script.js' path: 'script.js'
@@ -61,12 +53,10 @@ export const challengeFiles: ChallengeFile[] = [
contents: 'some jsx', contents: 'some jsx',
error: null, error: null,
ext: 'jsx', ext: 'jsx',
head: '',
history: ['index.jsx'], history: ['index.jsx'],
fileKey: 'indexjsx', fileKey: 'indexjsx',
name: 'index', name: 'index',
seed: 'some jsx', seed: 'some jsx',
tail: '',
editableRegionBoundaries: [], editableRegionBoundaries: [],
usesMultifileEditor: true, usesMultifileEditor: true,
path: 'index.jsx' path: 'index.jsx'
@@ -75,12 +65,10 @@ export const challengeFiles: ChallengeFile[] = [
contents: 'some tsx', contents: 'some tsx',
error: null, error: null,
ext: 'tsx', ext: 'tsx',
head: '',
history: ['index.tsx'], history: ['index.tsx'],
fileKey: 'indextsx', fileKey: 'indextsx',
name: 'index', name: 'index',
seed: 'some tsx', seed: 'some tsx',
tail: '',
editableRegionBoundaries: [], editableRegionBoundaries: [],
usesMultifileEditor: true, usesMultifileEditor: true,
path: 'index.tsx' path: 'index.tsx'
@@ -89,12 +77,10 @@ export const challengeFiles: ChallengeFile[] = [
contents: '{\n "compilerOptions": {}\n}', contents: '{\n "compilerOptions": {}\n}',
error: null, error: null,
ext: 'json', ext: 'json',
head: '',
history: ['tsconfig.json'], history: ['tsconfig.json'],
fileKey: 'tsconfigjson', fileKey: 'tsconfigjson',
name: 'tsconfig', name: 'tsconfig',
seed: '{\n "compilerOptions": {}\n}', seed: '{\n "compilerOptions": {}\n}',
tail: '',
editableRegionBoundaries: [], editableRegionBoundaries: [],
usesMultifileEditor: true, usesMultifileEditor: true,
path: 'tsconfig.json' path: 'tsconfig.json'
@@ -242,12 +242,6 @@ exports[`challenge schema > should not be changed without informing the mobile t
"fileKey": { "fileKey": {
"type": "string", "type": "string",
}, },
"head": {
"allow": [
"",
],
"type": "string",
},
"history": { "history": {
"items": [ "items": [
{ {
@@ -277,12 +271,6 @@ exports[`challenge schema > should not be changed without informing the mobile t
], ],
"type": "string", "type": "string",
}, },
"tail": {
"allow": [
"",
],
"type": "string",
},
}, },
"type": "object", "type": "object",
}, },
@@ -2050,12 +2038,6 @@ exports[`challenge schema > should not be changed without informing the mobile t
"fileKey": { "fileKey": {
"type": "string", "type": "string",
}, },
"head": {
"allow": [
"",
],
"type": "string",
},
"history": { "history": {
"items": [ "items": [
{ {
@@ -2085,12 +2067,6 @@ exports[`challenge schema > should not be changed without informing the mobile t
], ],
"type": "string", "type": "string",
}, },
"tail": {
"allow": [
"",
],
"type": "string",
},
}, },
"type": "object", "type": "object",
}, },
-2
View File
@@ -26,8 +26,6 @@ const fileJoi = Joi.object().keys({
editableRegionBoundaries: [Joi.array().items(Joi.number())], editableRegionBoundaries: [Joi.array().items(Joi.number())],
path: Joi.string(), path: Joi.string(),
error: Joi.valid(null), error: Joi.valid(null),
head: Joi.string().allow(''),
tail: Joi.string().allow(''),
seed: Joi.string().allow(''), seed: Joi.string().allow(''),
contents: Joi.string().allow(''), contents: Joi.string().allow(''),
id: Joi.string().allow(''), id: Joi.string().allow(''),
+2 -7
View File
@@ -100,19 +100,14 @@ const dummyChallenge = {
name: 'file1', name: 'file1',
ext: 'js', ext: 'js',
history: [], history: [],
contents: 'console.log("Hello")', contents: 'console.log("Hello")'
// head and tail should not be required, but they currently are
head: '',
tail: ''
}, },
{ {
spuriousProp: '2', spuriousProp: '2',
name: 'file2', name: 'file2',
ext: 'css', ext: 'css',
history: [], history: [],
contents: 'body { background: red; }', contents: 'body { background: red; }'
head: '',
tail: ''
} }
] ]
}; };
+1 -6
View File
@@ -277,12 +277,7 @@ async function buildJSChallenge(
challengeType, challengeType,
build: toBuild build: toBuild
.reduce( .reduce(
(body, challengeFile) => [ (body, challengeFile) => [...body, challengeFile.contents],
...body,
challengeFile.head,
challengeFile.contents,
challengeFile.tail
],
[] as string[] [] as string[]
) )
.join('\n'), .join('\n'),
+8 -21
View File
@@ -11,8 +11,6 @@ import {
import { import {
transformContents, transformContents,
transformHeadTailAndContents,
compileHeadTail,
createSource createSource
} from '@freecodecamp/shared/utils/polyvinyl'; } from '@freecodecamp/shared/utils/polyvinyl';
import { version } from '@freecodecamp/browser-scripts/package.json'; import { version } from '@freecodecamp/browser-scripts/package.json';
@@ -115,20 +113,14 @@ const getJSTranspiler = loopProtectOptions => async challengeFile => {
await loadBabel(); await loadBabel();
await loadPresetEnv(); await loadPresetEnv();
const babelOptions = getBabelOptions(presetsJS, loopProtectOptions); const babelOptions = getBabelOptions(presetsJS, loopProtectOptions);
return transformHeadTailAndContents( return transformContents(babelTransformCode(babelOptions), challengeFile);
babelTransformCode(babelOptions),
challengeFile
);
}; };
const getJSXTranspiler = loopProtectOptions => async challengeFile => { const getJSXTranspiler = loopProtectOptions => async challengeFile => {
await loadBabel(); await loadBabel();
await loadPresetReact(); await loadPresetReact();
const babelOptions = getBabelOptions(presetsJSX, loopProtectOptions); const babelOptions = getBabelOptions(presetsJSX, loopProtectOptions);
return transformHeadTailAndContents( return transformContents(babelTransformCode(babelOptions), challengeFile);
babelTransformCode(babelOptions),
challengeFile
);
}; };
const getJSXModuleTranspiler = loopProtectOptions => async challengeFile => { const getJSXModuleTranspiler = loopProtectOptions => async challengeFile => {
@@ -147,8 +139,8 @@ const getTSTranspiler = loopProtectOptions => async challengeFile => {
await loadBabel(); await loadBabel();
const babelOptions = getBabelOptions(presetsJS, loopProtectOptions); const babelOptions = getBabelOptions(presetsJS, loopProtectOptions);
return flow( return flow(
partial(transformHeadTailAndContents, compileTypeScriptCode), partial(transformContents, compileTypeScriptCode),
partial(transformHeadTailAndContents, babelTransformCode(babelOptions)) partial(transformContents, babelTransformCode(babelOptions))
)(challengeFile); )(challengeFile);
}; };
@@ -162,8 +154,8 @@ const getTSXModuleTranspiler = loopProtectOptions => async challengeFile => {
moduleId: 'index' // TODO: this should be dynamic moduleId: 'index' // TODO: this should be dynamic
}; };
return flow( return flow(
partial(transformHeadTailAndContents, compileTypeScriptCode), partial(transformContents, compileTypeScriptCode),
partial(transformHeadTailAndContents, babelTransformCode(babelOptions)) partial(transformContents, babelTransformCode(babelOptions))
)(challengeFile); )(challengeFile);
}; };
@@ -413,8 +405,7 @@ const getHtmlTranspiler = scriptOptions =>
export const getTransformers = loopProtectOptions => [ export const getTransformers = loopProtectOptions => [
createSource, createSource,
replaceNBSP, replaceNBSP,
createTranspiler(loopProtectOptions), createTranspiler(loopProtectOptions)
partial(compileHeadTail, '')
]; ];
export const getMultifileJSXTransformers = loopProtectOptions => [ export const getMultifileJSXTransformers = loopProtectOptions => [
@@ -423,8 +414,4 @@ export const getMultifileJSXTransformers = loopProtectOptions => [
createModuleTransformer(loopProtectOptions) createModuleTransformer(loopProtectOptions)
]; ];
export const getPythonTransformers = () => [ export const getPythonTransformers = () => [createSource, replaceNBSP];
createSource,
replaceNBSP,
partial(compileHeadTail, '')
];
+2 -47
View File
@@ -6,8 +6,6 @@ export interface IncompleteChallengeFile {
ext: Ext; ext: Ext;
name: string; name: string;
contents: string; contents: string;
head?: string;
tail?: string;
} }
export interface ChallengeFile extends IncompleteChallengeFile { export interface ChallengeFile extends IncompleteChallengeFile {
@@ -15,8 +13,6 @@ export interface ChallengeFile extends IncompleteChallengeFile {
editableContents?: string; editableContents?: string;
usesMultifileEditor?: boolean; usesMultifileEditor?: boolean;
error?: unknown; error?: unknown;
head: string;
tail: string;
seed?: string; seed?: string;
source?: string; source?: string;
path: string; path: string;
@@ -71,8 +67,6 @@ export function isPoly(poly: unknown): poly is ChallengeFile {
'name' in poly && 'name' in poly &&
'ext' in poly && 'ext' in poly &&
'fileKey' in poly && 'fileKey' in poly &&
'head' in poly &&
'tail' in poly &&
'history' in poly 'history' in poly
); );
} }
@@ -82,8 +76,6 @@ export function isPoly(poly: unknown): poly is ChallengeFile {
typeof poly.name === 'string' && typeof poly.name === 'string' &&
exts.includes(poly.ext as Ext) && exts.includes(poly.ext as Ext) &&
typeof poly.fileKey === 'string' && typeof poly.fileKey === 'string' &&
typeof poly.head === 'string' &&
typeof poly.tail === 'string' &&
Array.isArray(poly.history); Array.isArray(poly.history);
return hasProperties(poly) && hasCorrectTypes(poly); return hasProperties(poly) && hasCorrectTypes(poly);
@@ -111,33 +103,11 @@ export function setContent(
// database. // database.
export function regenerateMissingProperties(file: IncompleteChallengeFile) { export function regenerateMissingProperties(file: IncompleteChallengeFile) {
const newPath = file.name + '.' + file.ext; const newPath = file.name + '.' + file.ext;
const newFile = { return {
...file, ...file,
path: newPath, path: newPath,
history: [newPath], history: [newPath]
head: file.head ?? '',
tail: file.tail ?? ''
}; };
return newFile;
}
async function clearHeadTail(polyP: Promise<ChallengeFile>) {
const poly = await polyP;
checkPoly(poly);
return {
...poly,
head: '',
tail: ''
};
}
export async function compileHeadTail(padding = '', poly: ChallengeFile) {
return clearHeadTail(
transformContents(
() => [poly.head, poly.contents, poly.tail].join(padding),
poly
)
);
} }
type Wrapper = (x: string) => Promise<string> | string; type Wrapper = (x: string) => Promise<string> | string;
@@ -154,21 +124,6 @@ export async function transformContents(
return newPoly; return newPoly;
} }
export async function transformHeadTailAndContents(
wrap: Wrapper,
polyP: ChallengeFile | Promise<ChallengeFile>
) {
const poly = await polyP;
const contents = await transformContents(wrap, poly);
const head = await wrap(poly.head);
const tail = await wrap(poly.tail);
return {
...contents,
head,
tail
};
}
// createSource(poly: PolyVinyl) => PolyVinyl // createSource(poly: PolyVinyl) => PolyVinyl
export function createSource<Rest>( export function createSource<Rest>(
poly: Pick<ChallengeFile, 'contents' | 'source'> & Rest poly: Pick<ChallengeFile, 'contents' | 'source'> & Rest
@@ -10,11 +10,9 @@ const props = {
contents: '', contents: '',
editableRegionBoundaries: [0, 2], editableRegionBoundaries: [0, 2],
ext: 'html', ext: 'html',
head: '',
id: '', id: '',
key: 'indexhtml', key: 'indexhtml',
name: 'index', name: 'index'
tail: ''
} }
], ],
stepNum: 5, stepNum: 5,
@@ -33,8 +33,6 @@ export interface ChallengeSeed {
contents: string; contents: string;
ext: string; ext: string;
editableRegionBoundaries: number[]; editableRegionBoundaries: number[];
head?: string;
tail?: string;
} }
// Build the base markdown for a step // Build the base markdown for a step
@@ -56,20 +54,8 @@ function getStepTemplate({
}) })
.join('\n'); .join('\n');
const seedHeads = challengeSeeds
.filter(({ head }) => head)
.map(({ ext, head }) => getCodeBlock(ext, head))
.join('\n');
const seedTails = challengeSeeds
.filter(({ tail }) => tail)
.map(({ ext, tail }) => getCodeBlock(ext, tail))
.join('\n');
const stepDescription = `step ${stepNum} instructions`; const stepDescription = `step ${stepNum} instructions`;
const seedChallengeSection = getSeedSection(seedTexts, 'seed-contents'); const seedChallengeSection = getSeedSection(seedTexts, 'seed-contents');
const seedHeadSection = getSeedSection(seedHeads, 'before-user-code');
const seedTailSection = getSeedSection(seedTails, 'after-user-code');
const demoString = isFirstChallenge const demoString = isFirstChallenge
? ` ? `
@@ -99,10 +85,7 @@ ${stepDescription}
Test 1 Test 1
${getCodeBlock('js')} ${getCodeBlock('js')}
# --seed--` + # --seed--` + seedChallengeSection
seedChallengeSection +
seedHeadSection +
seedTailSection
); );
} }
@@ -65,12 +65,6 @@ assert(
# --seed-- # --seed--
## --before-user-code--
```js
// this runs before the user's code is evaluated.
```
## --seed-contents-- ## --seed-contents--
::id{#html-key} ::id{#html-key}
@@ -1,107 +0,0 @@
# --description--
Paragraph 1
```html
code example
```
# --instructions--
Paragraph 0
```html
code example 0
```
# --hints--
First hint
```js
// test code
```
Second hint with <code>code</code>
```js
// more test code
```
Third *hint* with <code>code</code> and `inline code`
```js
// more test code
if(let x of xs) {
console.log(x);
}
```
# --seed--
## --before-user-code--
```css
body {
etc: ''
}
```
```html
<!-- comment -->
```
## --seed-contents--
```html
<html>
<body>
</body>
</html>
```
```css
body {
background: green;
}
```
```js
var x = 'y';
```
## --after-user-code--
```css
body {
background: blue;
}
```
```js
function teardown(params) {
// after
}
```
# --solutions--
::id{#html-key}
```html
<html>
<body>
</body>
</html>
```
```css
body {
background: white;
}
```
```js
var x = 'y';
```
@@ -1,17 +1,5 @@
# --seed-- # --seed--
## --before-user-code--
```css
body {
etc: ''
}
```
```html
<!-- comment -->
```
## --seed-contents-- ## --seed-contents--
```html ```html
@@ -31,20 +19,6 @@ body {
var x = 'y'; var x = 'y';
``` ```
## --after-user-code--
```css
body {
background: blue;
}
```
```js
function teardown(params) {
// after
}
```
# --solutions-- # --solutions--
@@ -1,95 +0,0 @@
# --description--
Paragraph 1
```html
code example
```
# --instructions--
Paragraph 0
```html
code example 0
```
# --hints--
First hint
```js
// test code
```
Second hint with <code>code</code>
```js
// more test code
```
Third *hint* with <code>code</code> and `inline code`
```js
// more test code
if(let x of xs) {
console.log(x);
}
```
# --seed--
## --before-user-code--
```css
body {
etc: ''
}
```
```html
<!-- comment -->
```
## --seed-contents--
```html
<html>
<body>
</body>
</html>
```
```css
body {
background: green;
}
```
```js
var x = 'y';
```
## --after-user-code--
# --solutions--
::id{#html-key}
```html
<html>
<body>
</body>
</html>
```
```css
body {
background: white;
}
```
```js
var x = 'y';
```
@@ -1,32 +0,0 @@
# --description--
Paragraph 1
```html
code example
```
# --before-all--
# --hints--
First hint
```js
// test code
```
Second hint with <code>code</code>
```js
// more test code
```
Third *hint* with <code>code</code> and `inline code`
```js
// more test code
if(let x of xs) {
console.log(x);
}
```
@@ -1,96 +0,0 @@
# --description--
Paragraph 1
```html
code example
```
# --instructions--
Paragraph 0
```html
code example 0
```
# --hints--
First hint
```js
// test code
```
Second hint with <code>code</code>
```js
// more test code
```
Third *hint* with <code>code</code> and `inline code`
```js
// more test code
if(let x of xs) {
console.log(x);
}
```
# --seed--
## --before-user-code--
## --seed-contents--
```html
<html>
<body>
</body>
</html>
```
```css
body {
background: green;
}
```
```js
var x = 'y';
```
## --after-user-code--
```css
body {
background: blue;
}
```
```js
function teardown(params) {
// after
}
```
# --solutions--
::id{#html-key}
```html
<html>
<body>
</body>
</html>
```
```css
body {
background: white;
}
```
```js
var x = 'y';
```
@@ -39,31 +39,7 @@ if(let x of xs) {
# --seed-- # --seed--
## --before-user-code-- This section intentionally has no `## --seed-contents--`.
```css
body {
etc: ''
}
```
```html
<!-- comment -->
```
## --after-user-code--
```css
body {
background: blue;
}
```
```js
function teardown(params) {
// after
}
```
# --solutions-- # --solutions--
@@ -1,104 +0,0 @@
# --description--
Paragraph 1
```html
code example
```
# --instructions--
Paragraph 0
```html
code example 0
```
# --hints--
First hint
```js
// test code
```
Second hint with <code>code</code>
```js
// more test code
```
Third *hint* with <code>code</code> and `inline code`
```js
// more test code
if(let x of xs) {
console.log(x);
}
```
# --seed--
## --before-user-code--
```css
body {
etc: ''
}
```
```html
<!-- comment -->
```
## --seed-contents--
```html
<html>
<body>
</body>
</html>
```
```css
body {
background: green;
}
```
```js
var x = 'y';
```
## --after-user-code--
```css
```
```js
function teardown(params) {
// after
}
```
# --solutions--
::id{#html-key}
```html
<html>
<body>
</body>
</html>
```
```css
body {
background: white;
}
```
```js
var x = 'y';
```
@@ -1,104 +0,0 @@
# --description--
Paragraph 1
```html
code example
```
# --instructions--
Paragraph 0
```html
code example 0
```
# --hints--
First hint
```js
// test code
```
Second hint with <code>code</code>
```js
// more test code
```
Third *hint* with <code>code</code> and `inline code`
```js
// more test code
if(let x of xs) {
console.log(x);
}
```
# --seed--
## --before-user-code--
```css
```
```html
<!-- comment -->
```
## --seed-contents--
```html
<html>
<body>
</body>
</html>
```
```css
body {
background: green;
}
```
```js
var x = 'y';
```
## --after-user-code--
```css
body {
background: blue;
}
```
```js
function teardown(params) {
// after
}
```
# --solutions--
::id{#html-key}
```html
<html>
<body>
</body>
</html>
```
```css
body {
background: white;
}
```
```js
var x = 'y';
```
@@ -1,17 +1,5 @@
# --seed-- # --seed--
## --before-user-code--
```css
body {
etc: ''
}
```
```html
<!-- comment -->
```
## --seed-contents-- ## --seed-contents--
```html ```html
@@ -31,20 +19,6 @@ body {
var x = 'y'; var x = 'y';
``` ```
## --after-user-code--
```css
body {
background: blue;
}
```
```js
function teardown(params) {
// after
}
```
# --solutions-- # --solutions--
@@ -27,17 +27,3 @@ const Button = () => {
return <button> {/* another comment! */} text </button>; return <button> {/* another comment! */} text </button>;
}; };
``` ```
## --before-user-code--
```jsx
function setup() {}
```
## --after-user-code--
```jsx
function teardown(params) {
// after
}
```
@@ -11,10 +11,8 @@ exports[`challenge parser > should import md from other files 1`] = `
</html>", </html>",
"editableRegionBoundaries": [], "editableRegionBoundaries": [],
"ext": "html", "ext": "html",
"head": "",
"id": "", "id": "",
"name": "index", "name": "index",
"tail": "",
}, },
{ {
"contents": "body { "contents": "body {
@@ -22,10 +20,8 @@ exports[`challenge parser > should import md from other files 1`] = `
}", }",
"editableRegionBoundaries": [], "editableRegionBoundaries": [],
"ext": "css", "ext": "css",
"head": "",
"id": "", "id": "",
"name": "styles", "name": "styles",
"tail": "",
}, },
{ {
"contents": "var x = 'y'; "contents": "var x = 'y';
@@ -35,10 +31,8 @@ for (let index = 0; index < array.length; index++) {
}", }",
"editableRegionBoundaries": [], "editableRegionBoundaries": [],
"ext": "js", "ext": "js",
"head": "",
"id": "custom-name", "id": "custom-name",
"name": "script", "name": "script",
"tail": "",
}, },
], ],
"description": "<section id="description"> "description": "<section id="description">
@@ -76,10 +70,8 @@ exports[`challenge parser > should not mix other YAML with the frontmatter 1`] =
</html>", </html>",
"editableRegionBoundaries": [], "editableRegionBoundaries": [],
"ext": "html", "ext": "html",
"head": "",
"id": "", "id": "",
"name": "index", "name": "index",
"tail": "",
}, },
{ {
"contents": "body { "contents": "body {
@@ -87,19 +79,15 @@ exports[`challenge parser > should not mix other YAML with the frontmatter 1`] =
}", }",
"editableRegionBoundaries": [], "editableRegionBoundaries": [],
"ext": "css", "ext": "css",
"head": "",
"id": "", "id": "",
"name": "styles", "name": "styles",
"tail": "",
}, },
{ {
"contents": "var x = 'y';", "contents": "var x = 'y';",
"editableRegionBoundaries": [], "editableRegionBoundaries": [],
"ext": "js", "ext": "js",
"head": "",
"id": "", "id": "",
"name": "script", "name": "script",
"tail": "",
}, },
], ],
"description": "<section id="description"> "description": "<section id="description">
@@ -183,10 +171,8 @@ exports[`challenge parser > should parse a more realistic md file 1`] = `
23, 23,
], ],
"ext": "html", "ext": "html",
"head": "",
"id": "html-key", "id": "html-key",
"name": "index", "name": "index",
"tail": "",
}, },
{ {
"contents": "body { "contents": "body {
@@ -206,19 +192,15 @@ a {
9, 9,
], ],
"ext": "css", "ext": "css",
"head": "",
"id": "", "id": "",
"name": "styles", "name": "styles",
"tail": "",
}, },
{ {
"contents": "var x = 'y';", "contents": "var x = 'y';",
"editableRegionBoundaries": [], "editableRegionBoundaries": [],
"ext": "js", "ext": "js",
"head": " // this runs before the user's code is evaluated.",
"id": "final-key", "id": "final-key",
"name": "script", "name": "script",
"tail": "",
}, },
], ],
"description": "<section id="description"> "description": "<section id="description">
@@ -265,10 +247,8 @@ a {
</body> </body>
</html>", </html>",
"ext": "html", "ext": "html",
"head": "",
"id": "html-key", "id": "html-key",
"name": "index", "name": "index",
"tail": "",
}, },
{ {
"contents": "body { "contents": "body {
@@ -284,18 +264,14 @@ a {
color: green; color: green;
}", }",
"ext": "css", "ext": "css",
"head": "",
"id": "", "id": "",
"name": "styles", "name": "styles",
"tail": "",
}, },
{ {
"contents": "var x = 'y';", "contents": "var x = 'y';",
"ext": "js", "ext": "js",
"head": "",
"id": "final-key", "id": "final-key",
"name": "script", "name": "script",
"tail": "",
}, },
], ],
], ],
@@ -349,10 +325,8 @@ exports[`challenge parser > should parse a simple md file 1`] = `
</html>", </html>",
"editableRegionBoundaries": [], "editableRegionBoundaries": [],
"ext": "html", "ext": "html",
"head": "",
"id": "", "id": "",
"name": "index", "name": "index",
"tail": "",
}, },
{ {
"contents": "body { "contents": "body {
@@ -360,19 +334,15 @@ exports[`challenge parser > should parse a simple md file 1`] = `
}", }",
"editableRegionBoundaries": [], "editableRegionBoundaries": [],
"ext": "css", "ext": "css",
"head": "",
"id": "", "id": "",
"name": "styles", "name": "styles",
"tail": "",
}, },
{ {
"contents": "var x = 'y';", "contents": "var x = 'y';",
"editableRegionBoundaries": [], "editableRegionBoundaries": [],
"ext": "js", "ext": "js",
"head": "",
"id": "", "id": "",
"name": "script", "name": "script",
"tail": "",
}, },
{ {
"contents": "{ "contents": "{
@@ -382,10 +352,8 @@ exports[`challenge parser > should parse a simple md file 1`] = `
}", }",
"editableRegionBoundaries": [], "editableRegionBoundaries": [],
"ext": "json", "ext": "json",
"head": "",
"id": "", "id": "",
"name": "tsconfig", "name": "tsconfig",
"tail": "",
}, },
], ],
"description": "<section id="description"> "description": "<section id="description">
@@ -406,28 +374,22 @@ exports[`challenge parser > should parse a simple md file 1`] = `
</body> </body>
</html>", </html>",
"ext": "html", "ext": "html",
"head": "",
"id": "html-key", "id": "html-key",
"name": "index", "name": "index",
"tail": "",
}, },
{ {
"contents": "body { "contents": "body {
background: white; background: white;
}", }",
"ext": "css", "ext": "css",
"head": "",
"id": "", "id": "",
"name": "styles", "name": "styles",
"tail": "",
}, },
{ {
"contents": "var x = 'y';", "contents": "var x = 'y';",
"ext": "js", "ext": "js",
"head": "",
"id": "", "id": "",
"name": "script", "name": "script",
"tail": "",
}, },
], ],
], ],
@@ -462,10 +424,8 @@ exports[`challenge parser > should parse frontmatter 1`] = `
</html>", </html>",
"editableRegionBoundaries": [], "editableRegionBoundaries": [],
"ext": "html", "ext": "html",
"head": "",
"id": "", "id": "",
"name": "index", "name": "index",
"tail": "",
}, },
{ {
"contents": "body { "contents": "body {
@@ -473,19 +433,15 @@ exports[`challenge parser > should parse frontmatter 1`] = `
}", }",
"editableRegionBoundaries": [], "editableRegionBoundaries": [],
"ext": "css", "ext": "css",
"head": "",
"id": "", "id": "",
"name": "styles", "name": "styles",
"tail": "",
}, },
{ {
"contents": "var x = 'y';", "contents": "var x = 'y';",
"editableRegionBoundaries": [], "editableRegionBoundaries": [],
"ext": "js", "ext": "js",
"head": "",
"id": "", "id": "",
"name": "script", "name": "script",
"tail": "",
}, },
], ],
"challengeType": 0, "challengeType": 0,
@@ -524,10 +480,8 @@ exports[`challenge parser > should parse gfm strikethrough and frontmatter 1`] =
</html>", </html>",
"editableRegionBoundaries": [], "editableRegionBoundaries": [],
"ext": "html", "ext": "html",
"head": "",
"id": "", "id": "",
"name": "index", "name": "index",
"tail": "",
}, },
{ {
"contents": "body { "contents": "body {
@@ -535,19 +489,15 @@ exports[`challenge parser > should parse gfm strikethrough and frontmatter 1`] =
}", }",
"editableRegionBoundaries": [], "editableRegionBoundaries": [],
"ext": "css", "ext": "css",
"head": "",
"id": "", "id": "",
"name": "styles", "name": "styles",
"tail": "",
}, },
{ {
"contents": "var x = 'y';", "contents": "var x = 'y';",
"editableRegionBoundaries": [], "editableRegionBoundaries": [],
"ext": "js", "ext": "js",
"head": "",
"id": "", "id": "",
"name": "script", "name": "script",
"tail": "",
}, },
], ],
"description": "<section id="description"> "description": "<section id="description">
@@ -582,28 +532,22 @@ exports[`challenge parser > should parse gfm strikethrough and frontmatter 1`] =
</body> </body>
</html>", </html>",
"ext": "html", "ext": "html",
"head": "",
"id": "html-key", "id": "html-key",
"name": "index", "name": "index",
"tail": "",
}, },
{ {
"contents": "body { "contents": "body {
background: white; background: white;
}", }",
"ext": "css", "ext": "css",
"head": "",
"id": "", "id": "",
"name": "styles", "name": "styles",
"tail": "",
}, },
{ {
"contents": "var x = 'y';", "contents": "var x = 'y';",
"ext": "js", "ext": "js",
"head": "",
"id": "", "id": "",
"name": "script", "name": "script",
"tail": "",
}, },
], ],
], ],
-2
View File
@@ -6,8 +6,6 @@ type ChallengeFile = {
contents: string; contents: string;
ext: string; ext: string;
editableRegionBoundaries: number[]; editableRegionBoundaries: number[];
head?: string;
tail?: string;
}; };
export interface ParsedChallenge { export interface ParsedChallenge {
id: string; id: string;
@@ -10,10 +10,8 @@ exports[`add-seed plugin > should have an output to match the snapshot 1`] = `
</html>", </html>",
"editableRegionBoundaries": [], "editableRegionBoundaries": [],
"ext": "html", "ext": "html",
"head": "",
"id": "", "id": "",
"name": "index", "name": "index",
"tail": "",
}, },
{ {
"contents": "body { "contents": "body {
@@ -21,19 +19,15 @@ exports[`add-seed plugin > should have an output to match the snapshot 1`] = `
}", }",
"editableRegionBoundaries": [], "editableRegionBoundaries": [],
"ext": "css", "ext": "css",
"head": "",
"id": "", "id": "",
"name": "styles", "name": "styles",
"tail": "",
}, },
{ {
"contents": "var x = 'y';", "contents": "var x = 'y';",
"editableRegionBoundaries": [], "editableRegionBoundaries": [],
"ext": "js", "ext": "js",
"head": "",
"id": "", "id": "",
"name": "script", "name": "script",
"tail": "",
}, },
{ {
"contents": "{ "contents": "{
@@ -43,10 +37,8 @@ exports[`add-seed plugin > should have an output to match the snapshot 1`] = `
}", }",
"editableRegionBoundaries": [], "editableRegionBoundaries": [],
"ext": "json", "ext": "json",
"head": "",
"id": "", "id": "",
"name": "tsconfig", "name": "tsconfig",
"tail": "",
}, },
], ],
} }
@@ -10,28 +10,22 @@ exports[`add solution plugin > should have an output to match the snapshot 1`] =
</body> </body>
</html>", </html>",
"ext": "html", "ext": "html",
"head": "",
"id": "html-key", "id": "html-key",
"name": "index", "name": "index",
"tail": "",
}, },
{ {
"contents": "body { "contents": "body {
background: white; background: white;
}", }",
"ext": "css", "ext": "css",
"head": "",
"id": "", "id": "",
"name": "styles", "name": "styles",
"tail": "",
}, },
{ {
"contents": "var x = 'y';", "contents": "var x = 'y';",
"ext": "js", "ext": "js",
"head": "",
"id": "", "id": "",
"name": "script", "name": "script",
"tail": "",
}, },
], ],
], ],
@@ -38,22 +38,17 @@ function addSeeds() {
// processing in these cases. // processing in these cases.
if (isEmpty(seedTree.children)) return; if (isEmpty(seedTree.children)) return;
const contentsTree = root(getSection(seedTree, `--seed-contents--`)); const contentsTree = root(getSection(seedTree, `--seed-contents--`));
const headTree = root(getSection(seedTree, `--before-user-code--`));
const tailTree = root(getSection(seedTree, `--after-user-code--`));
const seeds = {}; const seeds = {};
// While before and after code are optional, the contents are not // Seed contents are required.
if (isEmpty(contentsTree.children)) if (isEmpty(contentsTree.children))
throw Error('## --seed-contents-- must appear in # --seed-- sections'); throw Error('## --seed-contents-- must appear in # --seed-- sections');
const visitForContents = visitChildren( const visitForContents = visitChildren(
getFileVisitor(seeds, 'contents', validateEditableMarkers) getFileVisitor(seeds, 'contents', validateEditableMarkers)
); );
const visitForHead = visitChildren(getFileVisitor(seeds, 'head'));
const visitForTail = visitChildren(getFileVisitor(seeds, 'tail'));
visitForContents(contentsTree); visitForContents(contentsTree);
visitForHead(headTree);
visitForTail(tailTree);
const seedVals = Object.values(seeds); const seedVals = Object.values(seeds);
file.data = { file.data = {
...file.data, ...file.data,
@@ -7,14 +7,9 @@ import addSeed from './add-seed';
describe('add-seed plugin', () => { describe('add-seed plugin', () => {
let adjacentKeysAST, let adjacentKeysAST,
withSeedKeysAST, withSeedKeysAST,
withBeforeAfterAST,
cCodeAST, cCodeAST,
withErmsOnOneLineAST, withErmsOnOneLineAST,
withEmptyAfterAST,
withEmptyBeforeAST,
withEmptyContentsAST, withEmptyContentsAST,
withInvalidBeforeAST,
withInvalidAfterAST,
simpleAST, simpleAST,
withEditableMarkersAST, withEditableMarkersAST,
withSeedKeysOrphanAST, withSeedKeysOrphanAST,
@@ -27,16 +22,11 @@ describe('add-seed plugin', () => {
beforeAll(async () => { beforeAll(async () => {
adjacentKeysAST = await parseFixture('with-seed-keys-adjacent.md'); adjacentKeysAST = await parseFixture('with-seed-keys-adjacent.md');
withSeedKeysAST = await parseFixture('with-seed-keys.md'); withSeedKeysAST = await parseFixture('with-seed-keys.md');
withBeforeAfterAST = await parseFixture('with-before-and-after.md');
cCodeAST = await parseFixture('with-c-code.md'); cCodeAST = await parseFixture('with-c-code.md');
withErmsOnOneLineAST = await parseFixture( withErmsOnOneLineAST = await parseFixture(
'with-editable-markers-on-one-line.md' 'with-editable-markers-on-one-line.md'
); );
withEmptyAfterAST = await parseFixture('with-empty-after.md');
withEmptyBeforeAST = await parseFixture('with-empty-before.md');
withEmptyContentsAST = await parseFixture('with-empty-contents.md'); withEmptyContentsAST = await parseFixture('with-empty-contents.md');
withInvalidBeforeAST = await parseFixture('with-invalid-before.md');
withInvalidAfterAST = await parseFixture('with-invalid-after.md');
simpleAST = await parseFixture('simple.md'); simpleAST = await parseFixture('simple.md');
withEditableMarkersAST = await parseFixture('with-editable-markers.md'); withEditableMarkersAST = await parseFixture('with-editable-markers.md');
withSeedKeysOrphanAST = await parseFixture('with-seed-keys-orphan.md'); withSeedKeysOrphanAST = await parseFixture('with-seed-keys-orphan.md');
@@ -65,23 +55,19 @@ describe('add-seed plugin', () => {
}); });
it('adds test objects to the challengeFiles array following a schema', () => { it('adds test objects to the challengeFiles array following a schema', () => {
expect.assertions(15); expect.assertions(11);
plugin(simpleAST, file); plugin(simpleAST, file);
const { const {
data: { challengeFiles } data: { challengeFiles }
} = file; } = file;
const testObject = challengeFiles.find(x => x.ext === 'js'); const testObject = challengeFiles.find(x => x.ext === 'js');
expect(Object.keys(testObject).length).toEqual(7); expect(Object.keys(testObject).length).toEqual(5);
expect(testObject).toHaveProperty('ext'); expect(testObject).toHaveProperty('ext');
expect(typeof testObject['ext']).toBe('string'); expect(typeof testObject['ext']).toBe('string');
expect(testObject).toHaveProperty('name'); expect(testObject).toHaveProperty('name');
expect(typeof testObject['name']).toBe('string'); expect(typeof testObject['name']).toBe('string');
expect(testObject).toHaveProperty('contents'); expect(testObject).toHaveProperty('contents');
expect(typeof testObject['contents']).toBe('string'); expect(typeof testObject['contents']).toBe('string');
expect(testObject).toHaveProperty('head');
expect(typeof testObject['head']).toBe('string');
expect(testObject).toHaveProperty('tail');
expect(typeof testObject['tail']).toBe('string');
expect(testObject).toHaveProperty('id'); expect(testObject).toHaveProperty('id');
expect(typeof testObject['id']).toBe('string'); expect(typeof testObject['id']).toBe('string');
expect(testObject).toHaveProperty('editableRegionBoundaries'); expect(testObject).toHaveProperty('editableRegionBoundaries');
@@ -155,96 +141,14 @@ describe('add-seed plugin', () => {
expect(file).toEqual(fileTwo); expect(file).toEqual(fileTwo);
}); });
it('gets the before-user-code for each language', () => {
expect.assertions(3);
plugin(withBeforeAfterAST, file);
const {
data: { challengeFiles }
} = file;
const scriptjs = challengeFiles.find(x => x.ext === 'js');
const indexhtml = challengeFiles.find(x => x.ext === 'html');
const stylescss = challengeFiles.find(x => x.ext === 'css');
expect(scriptjs.head).toBe('');
expect(indexhtml.head).toBe(`<!-- comment -->`);
expect(stylescss.head).toBe(`body {
etc: ''
}`);
});
it('gets the after-user-code for each language', () => {
expect.assertions(3);
plugin(withBeforeAfterAST, file);
const {
data: { challengeFiles }
} = file;
const scriptjs = challengeFiles.find(x => x.ext === 'js');
const indexhtml = challengeFiles.find(x => x.ext === 'html');
const stylescss = challengeFiles.find(x => x.ext === 'css');
expect(scriptjs.tail).toBe(`function teardown(params) {
// after
}`);
expect(indexhtml.tail).toBe('');
expect(stylescss.tail).toBe(`body {
background: blue;
}`);
});
it('throws an error if there is any code of an unsupported language', () => { it('throws an error if there is any code of an unsupported language', () => {
expect.assertions(1); expect.assertions(1);
expect(() => plugin(cCodeAST, file)).toThrow( expect(() => plugin(cCodeAST, file)).toThrow(
"On line 30 'c' is not a supported language.\n" + "On line 18 'c' is not a supported language.\n" +
' Please use one of js, css, html, jsx, ts, tsx or py' ' Please use one of js, css, html, jsx, ts, tsx or py'
); );
}); });
it('throws if there is before/after code with empty blocks', () => {
expect.assertions(2);
expect(() => plugin(withInvalidBeforeAST, file)).toThrow(
'Empty code block in --before-user-code-- section'
);
expect(() => plugin(withInvalidAfterAST, file)).toThrow(
'Empty code block in --after-user-code-- section'
);
});
it('quietly ignores empty before sections', () => {
expect.assertions(6);
plugin(withEmptyBeforeAST, file);
const {
data: { challengeFiles }
} = file;
const scriptjs = challengeFiles.find(x => x.ext === 'js');
const indexhtml = challengeFiles.find(x => x.ext === 'html');
const stylescss = challengeFiles.find(x => x.ext === 'css');
expect(scriptjs.head).toBe('');
expect(scriptjs.tail).toBe('function teardown(params) {\n // after\n}');
expect(indexhtml.head).toBe('');
expect(indexhtml.tail).toBe('');
expect(stylescss.head).toBe('');
expect(stylescss.tail).toBe('body {\n background: blue;\n}');
});
it('quietly ignores empty after sections', () => {
expect.assertions(6);
plugin(withEmptyAfterAST, file);
const {
data: { challengeFiles }
} = file;
const scriptjs = challengeFiles.find(x => x.ext === 'js');
const indexhtml = challengeFiles.find(x => x.ext === 'html');
const stylescss = challengeFiles.find(x => x.ext === 'css');
expect(scriptjs.head).toBe('');
expect(scriptjs.tail).toBe('');
expect(indexhtml.head).toBe('<!-- comment -->');
expect(indexhtml.tail).toBe('');
expect(stylescss.head).toBe("body {\n etc: ''\n}");
expect(stylescss.tail).toBe('');
});
it('throws an error (with line number) if 2 markers appear on 1 line', () => { it('throws an error (with line number) if 2 markers appear on 1 line', () => {
expect.assertions(1); expect.assertions(1);
expect(() => plugin(withErmsOnOneLineAST, file)).toThrow( expect(() => plugin(withErmsOnOneLineAST, file)).toThrow(
@@ -253,17 +157,13 @@ describe('add-seed plugin', () => {
}); });
it('handles jsx', () => { it('handles jsx', () => {
expect.assertions(3); expect.assertions(1);
plugin(withSeedKeysJSXAST, file); plugin(withSeedKeysJSXAST, file);
const { const {
data: { challengeFiles } data: { challengeFiles }
} = file; } = file;
const indexjsx = challengeFiles.find(x => x.ext === 'jsx'); const indexjsx = challengeFiles.find(x => x.ext === 'jsx');
expect(indexjsx.head).toBe(`function setup() {}`);
expect(indexjsx.tail).toBe(`function teardown(params) {
// after
}`);
expect(indexjsx.contents).toBe(`var x = 'y'; expect(indexjsx.contents).toBe(`var x = 'y';
/* comment */ /* comment */
@@ -40,23 +40,19 @@ describe('add solution plugin', () => {
}); });
it('adds solution objects to the challengeFiles array following a schema', () => { it('adds solution objects to the challengeFiles array following a schema', () => {
expect.assertions(13); expect.assertions(9);
plugin(mockAST, file); plugin(mockAST, file);
const { const {
data: { solutions } data: { solutions }
} = file; } = file;
const testObject = solutions[0].find(solution => solution.ext === 'js'); const testObject = solutions[0].find(solution => solution.ext === 'js');
expect(Object.keys(testObject).length).toEqual(6); expect(Object.keys(testObject).length).toEqual(4);
expect(testObject).toHaveProperty('ext'); expect(testObject).toHaveProperty('ext');
expect(typeof testObject['ext']).toBe('string'); expect(typeof testObject['ext']).toBe('string');
expect(testObject).toHaveProperty('name'); expect(testObject).toHaveProperty('name');
expect(typeof testObject['name']).toBe('string'); expect(typeof testObject['name']).toBe('string');
expect(testObject).toHaveProperty('contents'); expect(testObject).toHaveProperty('contents');
expect(typeof testObject['contents']).toBe('string'); expect(typeof testObject['contents']).toBe('string');
expect(testObject).toHaveProperty('head');
expect(typeof testObject['head']).toBe('string');
expect(testObject).toHaveProperty('tail');
expect(typeof testObject['tail']).toBe('string');
expect(testObject).toHaveProperty('id'); expect(testObject).toHaveProperty('id');
expect(typeof testObject['id']).toBe('string'); expect(typeof testObject['id']).toBe('string');
}); });
@@ -4,10 +4,6 @@ const position = require('unist-util-position');
const getId = require('./get-id'); const getId = require('./get-id');
const keyToSection = {
head: 'before-user-code',
tail: 'after-user-code'
};
const supportedLanguages = [ const supportedLanguages = [
'js', 'js',
'css', 'css',
@@ -29,8 +25,6 @@ function defaultFile(lang, id) {
ext: lang, ext: lang,
name: getFilenames(lang), name: getFilenames(lang),
contents: '', contents: '',
head: '',
tail: '',
id id
}; };
} }
@@ -74,11 +68,6 @@ function codeToData(node, seeds, seedKey, validate) {
if (!seeds[fileId]) { if (!seeds[fileId]) {
seeds[fileId] = defaultFile(shortLang, id); seeds[fileId] = defaultFile(shortLang, id);
} }
if (isEmpty(node.value) && seedKey !== 'contents') {
const section = keyToSection[seedKey];
throw Error(`Empty code block in --${section}-- section`);
}
seeds[fileId][seedKey] = isEmpty(seeds[fileId][seedKey]) seeds[fileId][seedKey] = isEmpty(seeds[fileId][seedKey])
? node.value ? node.value
: seeds[fileId][seedKey] + '\n' + node.value; : seeds[fileId][seedKey] + '\n' + node.value;
@@ -30,9 +30,6 @@ const VALID_MARKERS = [
'## --sentence--', '## --sentence--',
'## --text--', '## --text--',
'## --video-solution--', '## --video-solution--',
// TODO: Remove these two markers when https://github.com/freeCodeCamp/freeCodeCamp/issues/57107 is resolved
'## --after-user-code--',
'## --before-user-code--',
// Level 3 // Level 3
'### --audio-id--', '### --audio-id--',
@@ -299,8 +299,6 @@ exports.createPagesStatefully = async function ({ graphql, actions }) {
name name
ext ext
contents contents
head
tail
history history
fileKey fileKey
} }