From e66bf09dce6de0275750dbdc6d82a6e8253efcf2 Mon Sep 17 00:00:00 2001 From: Sem Bauke Date: Tue, 17 Mar 2026 17:59:16 +0100 Subject: [PATCH] refactor(tools,client): remove head and tail logic (#66524) --- .../show-daily-coding-challenge.tsx | 4 - client/src/redux/prop-types.ts | 4 - .../classic/saved-challenges.test.ts | 6 - .../Introduction/components/block.test.tsx | 2 - client/utils/__fixtures__/challenges.ts | 14 --- .../challenge-schema.test.mjs.snap | 24 ---- curriculum/schema/challenge-schema.js | 2 - curriculum/src/build-superblock.test.js | 9 +- packages/challenge-builder/src/build.ts | 7 +- .../challenge-builder/src/transformers.js | 29 ++--- packages/shared/src/utils/polyvinyl.ts | 49 +------- .../helpers/get-step-template.test.ts | 4 +- .../helpers/get-step-template.ts | 19 +-- .../parser/__fixtures__/realistic.md | 6 - .../__fixtures__/with-before-and-after.md | 107 ----------------- .../parser/__fixtures__/with-c-code.md | 26 ----- .../parser/__fixtures__/with-empty-after.md | 95 --------------- .../__fixtures__/with-empty-before-hook.md | 32 ------ .../parser/__fixtures__/with-empty-before.md | 96 ---------------- .../__fixtures__/with-empty-contents.md | 26 +---- .../parser/__fixtures__/with-invalid-after.md | 104 ----------------- .../__fixtures__/with-invalid-before.md | 104 ----------------- .../__fixtures__/with-multiple-solns.md | 26 ----- .../parser/__fixtures__/with-seed-keys-jsx.md | 14 --- .../index.acceptance.test.js.snap | 56 --------- tools/challenge-parser/parser/index.d.ts | 2 - .../__snapshots__/add-seed.test.js.snap | 8 -- .../__snapshots__/add-solution.test.js.snap | 6 - .../parser/plugins/add-seed.js | 9 +- .../parser/plugins/add-seed.test.js | 108 +----------------- .../parser/plugins/add-solution.test.js | 8 +- .../parser/plugins/utils/get-file-visitor.js | 11 -- .../parser/plugins/validate-sections.js | 3 - .../gatsby-source-challenges/gatsby-node.js | 2 - 34 files changed, 24 insertions(+), 998 deletions(-) delete mode 100644 tools/challenge-parser/parser/__fixtures__/with-before-and-after.md delete mode 100644 tools/challenge-parser/parser/__fixtures__/with-empty-after.md delete mode 100644 tools/challenge-parser/parser/__fixtures__/with-empty-before-hook.md delete mode 100644 tools/challenge-parser/parser/__fixtures__/with-empty-before.md delete mode 100644 tools/challenge-parser/parser/__fixtures__/with-invalid-after.md delete mode 100644 tools/challenge-parser/parser/__fixtures__/with-invalid-before.md diff --git a/client/src/client-only-routes/show-daily-coding-challenge.tsx b/client/src/client-only-routes/show-daily-coding-challenge.tsx index fa09827ee4d..3ff46971130 100644 --- a/client/src/client-only-routes/show-daily-coding-challenge.tsx +++ b/client/src/client-only-routes/show-daily-coding-challenge.tsx @@ -97,8 +97,6 @@ function formatChallengeData({ name: 'script', ext: 'js', contents: javascript.challengeFiles[0].contents, - head: '', - tail: '', path: '', history: ['script.js'], fileKey: 'scriptjs' @@ -123,9 +121,7 @@ function formatChallengeData({ ext: 'py', name: 'main', contents: python.challengeFiles[0].contents, - head: '', path: '', - tail: '', editableRegionBoundaries: [], history: ['main.py'] } diff --git a/client/src/redux/prop-types.ts b/client/src/redux/prop-types.ts index 0711ee3449d..8110b9c52ef 100644 --- a/client/src/redux/prop-types.ts +++ b/client/src/redux/prop-types.ts @@ -211,7 +211,6 @@ export type ChallengeNode = { fields: Fields; fillInTheBlank: FillInTheBlank; forumTopicId: number; - head: string[]; hasEditableBoundaries: boolean; helpCategory: string; hooks?: Hooks; @@ -245,7 +244,6 @@ export type ChallengeNode = { sourceInstanceName: string; superOrder: number; superBlock: SuperBlocks; - tail: string[]; template: string; tests: Test[]; title: string; @@ -548,11 +546,9 @@ export type ExperienceData = { export type FileKeyChallenge = { contents: string; ext: Ext; - head: string; id: string; key: string; name: string; - tail: string; }; export type ChallengeFiles = ChallengeFile[] | null; diff --git a/client/src/templates/Challenges/classic/saved-challenges.test.ts b/client/src/templates/Challenges/classic/saved-challenges.test.ts index f2495996f6d..5e1d648076a 100644 --- a/client/src/templates/Challenges/classic/saved-challenges.test.ts +++ b/client/src/templates/Challenges/classic/saved-challenges.test.ts @@ -10,8 +10,6 @@ const jsChallenge = { fileKey: 'jsFileKey', name: 'name', ext: 'js' as const, - head: 'head', - tail: 'tail', history: [], seed: 'original js contents', path: 'index.js' @@ -22,8 +20,6 @@ const cssChallenge = { fileKey: 'cssFileKey', name: 'name', ext: 'css' as const, - head: 'head', - tail: 'tail', history: [], seed: 'original css contents', path: 'styles.css' @@ -34,8 +30,6 @@ const htmlChallenge = { fileKey: 'htmlFileKey', name: 'name', ext: 'html' as const, - head: 'head', - tail: 'tail', history: [], seed: 'original html contents', path: 'index.html' diff --git a/client/src/templates/Introduction/components/block.test.tsx b/client/src/templates/Introduction/components/block.test.tsx index 67c51a1eeb7..ae8f85761f0 100644 --- a/client/src/templates/Introduction/components/block.test.tsx +++ b/client/src/templates/Introduction/components/block.test.tsx @@ -39,7 +39,6 @@ const defaultProps = { fields: {} as Fields, forumTopicId: 12345, guideUrl: 'https://mockurl.com', - head: ['mockHead'], hasEditableBoundaries: false, helpCategory: 'mockHelpCategory', id: 'mockId', @@ -70,7 +69,6 @@ const defaultProps = { sourceInstanceName: 'mockSourceInstanceName', superOrder: 1, superBlock: SuperBlocks.FullStackDeveloperV9, - tail: ['mockTail'], template: 'mockTemplate', tests: [] as Test[], title: 'mockTitle', diff --git a/client/utils/__fixtures__/challenges.ts b/client/utils/__fixtures__/challenges.ts index 44931007bc8..a3aff0c9756 100644 --- a/client/utils/__fixtures__/challenges.ts +++ b/client/utils/__fixtures__/challenges.ts @@ -5,12 +5,10 @@ export const challengeFiles: ChallengeFile[] = [ contents: 'some ts', error: null, ext: 'ts', - head: '', history: ['index.ts'], fileKey: 'indexts', name: 'index', seed: 'some ts', - tail: '', editableRegionBoundaries: [], usesMultifileEditor: true, path: 'index.ts' @@ -19,12 +17,10 @@ export const challengeFiles: ChallengeFile[] = [ contents: 'some css', error: null, ext: 'css', - head: '', history: ['styles.css'], fileKey: 'stylescss', name: 'styles', seed: 'some css', - tail: '', editableRegionBoundaries: [], usesMultifileEditor: true, path: 'styles.css' @@ -33,12 +29,10 @@ export const challengeFiles: ChallengeFile[] = [ contents: 'some html', error: null, ext: 'html', - head: '', history: ['index.html'], fileKey: 'indexhtml', name: 'index', seed: 'some html', - tail: '', editableRegionBoundaries: [], usesMultifileEditor: true, path: 'index.html' @@ -47,12 +41,10 @@ export const challengeFiles: ChallengeFile[] = [ contents: 'some js', error: null, ext: 'js', - head: '', history: ['script.js'], fileKey: 'scriptjs', name: 'script', seed: 'some js', - tail: '', editableRegionBoundaries: [], usesMultifileEditor: true, path: 'script.js' @@ -61,12 +53,10 @@ export const challengeFiles: ChallengeFile[] = [ contents: 'some jsx', error: null, ext: 'jsx', - head: '', history: ['index.jsx'], fileKey: 'indexjsx', name: 'index', seed: 'some jsx', - tail: '', editableRegionBoundaries: [], usesMultifileEditor: true, path: 'index.jsx' @@ -75,12 +65,10 @@ export const challengeFiles: ChallengeFile[] = [ contents: 'some tsx', error: null, ext: 'tsx', - head: '', history: ['index.tsx'], fileKey: 'indextsx', name: 'index', seed: 'some tsx', - tail: '', editableRegionBoundaries: [], usesMultifileEditor: true, path: 'index.tsx' @@ -89,12 +77,10 @@ export const challengeFiles: ChallengeFile[] = [ contents: '{\n "compilerOptions": {}\n}', error: null, ext: 'json', - head: '', history: ['tsconfig.json'], fileKey: 'tsconfigjson', name: 'tsconfig', seed: '{\n "compilerOptions": {}\n}', - tail: '', editableRegionBoundaries: [], usesMultifileEditor: true, path: 'tsconfig.json' diff --git a/curriculum/schema/__snapshots__/challenge-schema.test.mjs.snap b/curriculum/schema/__snapshots__/challenge-schema.test.mjs.snap index 2f2a20aa6b4..34b5dde4570 100644 --- a/curriculum/schema/__snapshots__/challenge-schema.test.mjs.snap +++ b/curriculum/schema/__snapshots__/challenge-schema.test.mjs.snap @@ -242,12 +242,6 @@ exports[`challenge schema > should not be changed without informing the mobile t "fileKey": { "type": "string", }, - "head": { - "allow": [ - "", - ], - "type": "string", - }, "history": { "items": [ { @@ -277,12 +271,6 @@ exports[`challenge schema > should not be changed without informing the mobile t ], "type": "string", }, - "tail": { - "allow": [ - "", - ], - "type": "string", - }, }, "type": "object", }, @@ -2050,12 +2038,6 @@ exports[`challenge schema > should not be changed without informing the mobile t "fileKey": { "type": "string", }, - "head": { - "allow": [ - "", - ], - "type": "string", - }, "history": { "items": [ { @@ -2085,12 +2067,6 @@ exports[`challenge schema > should not be changed without informing the mobile t ], "type": "string", }, - "tail": { - "allow": [ - "", - ], - "type": "string", - }, }, "type": "object", }, diff --git a/curriculum/schema/challenge-schema.js b/curriculum/schema/challenge-schema.js index 61205a58ace..59a1e7fc79e 100644 --- a/curriculum/schema/challenge-schema.js +++ b/curriculum/schema/challenge-schema.js @@ -26,8 +26,6 @@ const fileJoi = Joi.object().keys({ editableRegionBoundaries: [Joi.array().items(Joi.number())], path: Joi.string(), error: Joi.valid(null), - head: Joi.string().allow(''), - tail: Joi.string().allow(''), seed: Joi.string().allow(''), contents: Joi.string().allow(''), id: Joi.string().allow(''), diff --git a/curriculum/src/build-superblock.test.js b/curriculum/src/build-superblock.test.js index 3cbe5c44b69..d7cabab12c0 100644 --- a/curriculum/src/build-superblock.test.js +++ b/curriculum/src/build-superblock.test.js @@ -100,19 +100,14 @@ const dummyChallenge = { name: 'file1', ext: 'js', history: [], - contents: 'console.log("Hello")', - // head and tail should not be required, but they currently are - head: '', - tail: '' + contents: 'console.log("Hello")' }, { spuriousProp: '2', name: 'file2', ext: 'css', history: [], - contents: 'body { background: red; }', - head: '', - tail: '' + contents: 'body { background: red; }' } ] }; diff --git a/packages/challenge-builder/src/build.ts b/packages/challenge-builder/src/build.ts index e0012971b75..e18a129de2e 100644 --- a/packages/challenge-builder/src/build.ts +++ b/packages/challenge-builder/src/build.ts @@ -277,12 +277,7 @@ async function buildJSChallenge( challengeType, build: toBuild .reduce( - (body, challengeFile) => [ - ...body, - challengeFile.head, - challengeFile.contents, - challengeFile.tail - ], + (body, challengeFile) => [...body, challengeFile.contents], [] as string[] ) .join('\n'), diff --git a/packages/challenge-builder/src/transformers.js b/packages/challenge-builder/src/transformers.js index d2fae43c8e0..e38892c1fde 100644 --- a/packages/challenge-builder/src/transformers.js +++ b/packages/challenge-builder/src/transformers.js @@ -11,8 +11,6 @@ import { import { transformContents, - transformHeadTailAndContents, - compileHeadTail, createSource } from '@freecodecamp/shared/utils/polyvinyl'; import { version } from '@freecodecamp/browser-scripts/package.json'; @@ -115,20 +113,14 @@ const getJSTranspiler = loopProtectOptions => async challengeFile => { await loadBabel(); await loadPresetEnv(); const babelOptions = getBabelOptions(presetsJS, loopProtectOptions); - return transformHeadTailAndContents( - babelTransformCode(babelOptions), - challengeFile - ); + return transformContents(babelTransformCode(babelOptions), challengeFile); }; const getJSXTranspiler = loopProtectOptions => async challengeFile => { await loadBabel(); await loadPresetReact(); const babelOptions = getBabelOptions(presetsJSX, loopProtectOptions); - return transformHeadTailAndContents( - babelTransformCode(babelOptions), - challengeFile - ); + return transformContents(babelTransformCode(babelOptions), challengeFile); }; const getJSXModuleTranspiler = loopProtectOptions => async challengeFile => { @@ -147,8 +139,8 @@ const getTSTranspiler = loopProtectOptions => async challengeFile => { await loadBabel(); const babelOptions = getBabelOptions(presetsJS, loopProtectOptions); return flow( - partial(transformHeadTailAndContents, compileTypeScriptCode), - partial(transformHeadTailAndContents, babelTransformCode(babelOptions)) + partial(transformContents, compileTypeScriptCode), + partial(transformContents, babelTransformCode(babelOptions)) )(challengeFile); }; @@ -162,8 +154,8 @@ const getTSXModuleTranspiler = loopProtectOptions => async challengeFile => { moduleId: 'index' // TODO: this should be dynamic }; return flow( - partial(transformHeadTailAndContents, compileTypeScriptCode), - partial(transformHeadTailAndContents, babelTransformCode(babelOptions)) + partial(transformContents, compileTypeScriptCode), + partial(transformContents, babelTransformCode(babelOptions)) )(challengeFile); }; @@ -413,8 +405,7 @@ const getHtmlTranspiler = scriptOptions => export const getTransformers = loopProtectOptions => [ createSource, replaceNBSP, - createTranspiler(loopProtectOptions), - partial(compileHeadTail, '') + createTranspiler(loopProtectOptions) ]; export const getMultifileJSXTransformers = loopProtectOptions => [ @@ -423,8 +414,4 @@ export const getMultifileJSXTransformers = loopProtectOptions => [ createModuleTransformer(loopProtectOptions) ]; -export const getPythonTransformers = () => [ - createSource, - replaceNBSP, - partial(compileHeadTail, '') -]; +export const getPythonTransformers = () => [createSource, replaceNBSP]; diff --git a/packages/shared/src/utils/polyvinyl.ts b/packages/shared/src/utils/polyvinyl.ts index 1e68cc78890..f6920e8bf02 100644 --- a/packages/shared/src/utils/polyvinyl.ts +++ b/packages/shared/src/utils/polyvinyl.ts @@ -6,8 +6,6 @@ export interface IncompleteChallengeFile { ext: Ext; name: string; contents: string; - head?: string; - tail?: string; } export interface ChallengeFile extends IncompleteChallengeFile { @@ -15,8 +13,6 @@ export interface ChallengeFile extends IncompleteChallengeFile { editableContents?: string; usesMultifileEditor?: boolean; error?: unknown; - head: string; - tail: string; seed?: string; source?: string; path: string; @@ -71,8 +67,6 @@ export function isPoly(poly: unknown): poly is ChallengeFile { 'name' in poly && 'ext' in poly && 'fileKey' in poly && - 'head' in poly && - 'tail' in poly && 'history' in poly ); } @@ -82,8 +76,6 @@ export function isPoly(poly: unknown): poly is ChallengeFile { typeof poly.name === 'string' && exts.includes(poly.ext as Ext) && typeof poly.fileKey === 'string' && - typeof poly.head === 'string' && - typeof poly.tail === 'string' && Array.isArray(poly.history); return hasProperties(poly) && hasCorrectTypes(poly); @@ -111,33 +103,11 @@ export function setContent( // database. export function regenerateMissingProperties(file: IncompleteChallengeFile) { const newPath = file.name + '.' + file.ext; - const newFile = { + return { ...file, path: newPath, - history: [newPath], - head: file.head ?? '', - tail: file.tail ?? '' + history: [newPath] }; - return newFile; -} - -async function clearHeadTail(polyP: Promise) { - 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; @@ -154,21 +124,6 @@ export async function transformContents( return newPoly; } -export async function transformHeadTailAndContents( - wrap: Wrapper, - polyP: ChallengeFile | Promise -) { - 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 export function createSource( poly: Pick & Rest diff --git a/tools/challenge-helper-scripts/helpers/get-step-template.test.ts b/tools/challenge-helper-scripts/helpers/get-step-template.test.ts index 2e70ad5889a..0262463c57e 100644 --- a/tools/challenge-helper-scripts/helpers/get-step-template.test.ts +++ b/tools/challenge-helper-scripts/helpers/get-step-template.test.ts @@ -10,11 +10,9 @@ const props = { contents: '', editableRegionBoundaries: [0, 2], ext: 'html', - head: '', id: '', key: 'indexhtml', - name: 'index', - tail: '' + name: 'index' } ], stepNum: 5, diff --git a/tools/challenge-helper-scripts/helpers/get-step-template.ts b/tools/challenge-helper-scripts/helpers/get-step-template.ts index 2304335b795..a55854757f4 100644 --- a/tools/challenge-helper-scripts/helpers/get-step-template.ts +++ b/tools/challenge-helper-scripts/helpers/get-step-template.ts @@ -33,8 +33,6 @@ export interface ChallengeSeed { contents: string; ext: string; editableRegionBoundaries: number[]; - head?: string; - tail?: string; } // Build the base markdown for a step @@ -56,20 +54,8 @@ function getStepTemplate({ }) .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 seedChallengeSection = getSeedSection(seedTexts, 'seed-contents'); - const seedHeadSection = getSeedSection(seedHeads, 'before-user-code'); - const seedTailSection = getSeedSection(seedTails, 'after-user-code'); const demoString = isFirstChallenge ? ` @@ -99,10 +85,7 @@ ${stepDescription} Test 1 ${getCodeBlock('js')} -# --seed--` + - seedChallengeSection + - seedHeadSection + - seedTailSection +# --seed--` + seedChallengeSection ); } diff --git a/tools/challenge-parser/parser/__fixtures__/realistic.md b/tools/challenge-parser/parser/__fixtures__/realistic.md index 06358426f72..553cf3db3f2 100644 --- a/tools/challenge-parser/parser/__fixtures__/realistic.md +++ b/tools/challenge-parser/parser/__fixtures__/realistic.md @@ -65,12 +65,6 @@ assert( # --seed-- -## --before-user-code-- - -```js - // this runs before the user's code is evaluated. -``` - ## --seed-contents-- ::id{#html-key} diff --git a/tools/challenge-parser/parser/__fixtures__/with-before-and-after.md b/tools/challenge-parser/parser/__fixtures__/with-before-and-after.md deleted file mode 100644 index d8a24be65e3..00000000000 --- a/tools/challenge-parser/parser/__fixtures__/with-before-and-after.md +++ /dev/null @@ -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 - -```js -// more test code -``` - -Third *hint* with code and `inline code` - -```js -// more test code -if(let x of xs) { - console.log(x); -} -``` - -# --seed-- - -## --before-user-code-- - -```css -body { - etc: '' -} -``` - -```html - -``` - -## --seed-contents-- - -```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 - - - - -``` - -```css -body { - background: white; -} -``` - -```js -var x = 'y'; -``` diff --git a/tools/challenge-parser/parser/__fixtures__/with-c-code.md b/tools/challenge-parser/parser/__fixtures__/with-c-code.md index 0dec5de4bd9..b1a40a3ff22 100644 --- a/tools/challenge-parser/parser/__fixtures__/with-c-code.md +++ b/tools/challenge-parser/parser/__fixtures__/with-c-code.md @@ -1,17 +1,5 @@ # --seed-- -## --before-user-code-- - -```css -body { - etc: '' -} -``` - -```html - -``` - ## --seed-contents-- ```html @@ -31,20 +19,6 @@ body { var x = 'y'; ``` -## --after-user-code-- - -```css -body { - background: blue; -} -``` - -```js -function teardown(params) { - // after -} -``` - # --solutions-- diff --git a/tools/challenge-parser/parser/__fixtures__/with-empty-after.md b/tools/challenge-parser/parser/__fixtures__/with-empty-after.md deleted file mode 100644 index 80687946d43..00000000000 --- a/tools/challenge-parser/parser/__fixtures__/with-empty-after.md +++ /dev/null @@ -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 - -```js -// more test code -``` - -Third *hint* with code and `inline code` - -```js -// more test code -if(let x of xs) { - console.log(x); -} -``` - -# --seed-- - -## --before-user-code-- - -```css -body { - etc: '' -} -``` - -```html - -``` - -## --seed-contents-- - -```html - - - - -``` - -```css -body { - background: green; -} -``` - -```js -var x = 'y'; -``` - -## --after-user-code-- - - -# --solutions-- - -::id{#html-key} - -```html - - - - -``` - -```css -body { - background: white; -} -``` - -```js -var x = 'y'; -``` diff --git a/tools/challenge-parser/parser/__fixtures__/with-empty-before-hook.md b/tools/challenge-parser/parser/__fixtures__/with-empty-before-hook.md deleted file mode 100644 index 2c86edc9bca..00000000000 --- a/tools/challenge-parser/parser/__fixtures__/with-empty-before-hook.md +++ /dev/null @@ -1,32 +0,0 @@ -# --description-- - -Paragraph 1 - -```html -code example -``` - -# --before-all-- - -# --hints-- - -First hint - -```js -// test code -``` - -Second hint with code - -```js -// more test code -``` - -Third *hint* with code and `inline code` - -```js -// more test code -if(let x of xs) { - console.log(x); -} -``` diff --git a/tools/challenge-parser/parser/__fixtures__/with-empty-before.md b/tools/challenge-parser/parser/__fixtures__/with-empty-before.md deleted file mode 100644 index a5eda54b331..00000000000 --- a/tools/challenge-parser/parser/__fixtures__/with-empty-before.md +++ /dev/null @@ -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 - -```js -// more test code -``` - -Third *hint* with code and `inline code` - -```js -// more test code -if(let x of xs) { - console.log(x); -} -``` - -# --seed-- - -## --before-user-code-- - -## --seed-contents-- - -```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 - - - - -``` - -```css -body { - background: white; -} -``` - -```js -var x = 'y'; -``` diff --git a/tools/challenge-parser/parser/__fixtures__/with-empty-contents.md b/tools/challenge-parser/parser/__fixtures__/with-empty-contents.md index 4af31f25eb0..7649c44e6dd 100644 --- a/tools/challenge-parser/parser/__fixtures__/with-empty-contents.md +++ b/tools/challenge-parser/parser/__fixtures__/with-empty-contents.md @@ -39,31 +39,7 @@ if(let x of xs) { # --seed-- -## --before-user-code-- - -```css -body { - etc: '' -} -``` - -```html - -``` - -## --after-user-code-- - -```css -body { - background: blue; -} -``` - -```js -function teardown(params) { - // after -} -``` +This section intentionally has no `## --seed-contents--`. # --solutions-- diff --git a/tools/challenge-parser/parser/__fixtures__/with-invalid-after.md b/tools/challenge-parser/parser/__fixtures__/with-invalid-after.md deleted file mode 100644 index c15bbedcff9..00000000000 --- a/tools/challenge-parser/parser/__fixtures__/with-invalid-after.md +++ /dev/null @@ -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 - -```js -// more test code -``` - -Third *hint* with code and `inline code` - -```js -// more test code -if(let x of xs) { - console.log(x); -} -``` - -# --seed-- - -## --before-user-code-- - -```css -body { - etc: '' -} -``` - -```html - -``` - -## --seed-contents-- - -```html - - - - -``` - -```css -body { - background: green; -} -``` - -```js -var x = 'y'; -``` - -## --after-user-code-- - -```css -``` - -```js -function teardown(params) { - // after -} -``` - - -# --solutions-- - -::id{#html-key} - -```html - - - - -``` - -```css -body { - background: white; -} -``` - -```js -var x = 'y'; -``` diff --git a/tools/challenge-parser/parser/__fixtures__/with-invalid-before.md b/tools/challenge-parser/parser/__fixtures__/with-invalid-before.md deleted file mode 100644 index ae201a6b6b2..00000000000 --- a/tools/challenge-parser/parser/__fixtures__/with-invalid-before.md +++ /dev/null @@ -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 - -```js -// more test code -``` - -Third *hint* with code and `inline code` - -```js -// more test code -if(let x of xs) { - console.log(x); -} -``` - -# --seed-- - -## --before-user-code-- - -```css -``` - -```html - -``` - -## --seed-contents-- - -```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 - - - - -``` - -```css -body { - background: white; -} -``` - -```js -var x = 'y'; -``` diff --git a/tools/challenge-parser/parser/__fixtures__/with-multiple-solns.md b/tools/challenge-parser/parser/__fixtures__/with-multiple-solns.md index 1844bc93a1c..6354951e1e9 100644 --- a/tools/challenge-parser/parser/__fixtures__/with-multiple-solns.md +++ b/tools/challenge-parser/parser/__fixtures__/with-multiple-solns.md @@ -1,17 +1,5 @@ # --seed-- -## --before-user-code-- - -```css -body { - etc: '' -} -``` - -```html - -``` - ## --seed-contents-- ```html @@ -31,20 +19,6 @@ body { var x = 'y'; ``` -## --after-user-code-- - -```css -body { - background: blue; -} -``` - -```js -function teardown(params) { - // after -} -``` - # --solutions-- diff --git a/tools/challenge-parser/parser/__fixtures__/with-seed-keys-jsx.md b/tools/challenge-parser/parser/__fixtures__/with-seed-keys-jsx.md index 5d5a90a01d0..c9461b3c575 100644 --- a/tools/challenge-parser/parser/__fixtures__/with-seed-keys-jsx.md +++ b/tools/challenge-parser/parser/__fixtures__/with-seed-keys-jsx.md @@ -27,17 +27,3 @@ const Button = () => { return ; }; ``` - -## --before-user-code-- - -```jsx -function setup() {} -``` - -## --after-user-code-- - -```jsx -function teardown(params) { - // after -} -``` diff --git a/tools/challenge-parser/parser/__snapshots__/index.acceptance.test.js.snap b/tools/challenge-parser/parser/__snapshots__/index.acceptance.test.js.snap index 01164d17e96..4aa22a9e9b7 100644 --- a/tools/challenge-parser/parser/__snapshots__/index.acceptance.test.js.snap +++ b/tools/challenge-parser/parser/__snapshots__/index.acceptance.test.js.snap @@ -11,10 +11,8 @@ exports[`challenge parser > should import md from other files 1`] = ` ", "editableRegionBoundaries": [], "ext": "html", - "head": "", "id": "", "name": "index", - "tail": "", }, { "contents": "body { @@ -22,10 +20,8 @@ exports[`challenge parser > should import md from other files 1`] = ` }", "editableRegionBoundaries": [], "ext": "css", - "head": "", "id": "", "name": "styles", - "tail": "", }, { "contents": "var x = 'y'; @@ -35,10 +31,8 @@ for (let index = 0; index < array.length; index++) { }", "editableRegionBoundaries": [], "ext": "js", - "head": "", "id": "custom-name", "name": "script", - "tail": "", }, ], "description": "
@@ -76,10 +70,8 @@ exports[`challenge parser > should not mix other YAML with the frontmatter 1`] = ", "editableRegionBoundaries": [], "ext": "html", - "head": "", "id": "", "name": "index", - "tail": "", }, { "contents": "body { @@ -87,19 +79,15 @@ exports[`challenge parser > should not mix other YAML with the frontmatter 1`] = }", "editableRegionBoundaries": [], "ext": "css", - "head": "", "id": "", "name": "styles", - "tail": "", }, { "contents": "var x = 'y';", "editableRegionBoundaries": [], "ext": "js", - "head": "", "id": "", "name": "script", - "tail": "", }, ], "description": "
@@ -183,10 +171,8 @@ exports[`challenge parser > should parse a more realistic md file 1`] = ` 23, ], "ext": "html", - "head": "", "id": "html-key", "name": "index", - "tail": "", }, { "contents": "body { @@ -206,19 +192,15 @@ a { 9, ], "ext": "css", - "head": "", "id": "", "name": "styles", - "tail": "", }, { "contents": "var x = 'y';", "editableRegionBoundaries": [], "ext": "js", - "head": " // this runs before the user's code is evaluated.", "id": "final-key", "name": "script", - "tail": "", }, ], "description": "
@@ -265,10 +247,8 @@ a { ", "ext": "html", - "head": "", "id": "html-key", "name": "index", - "tail": "", }, { "contents": "body { @@ -284,18 +264,14 @@ a { color: green; }", "ext": "css", - "head": "", "id": "", "name": "styles", - "tail": "", }, { "contents": "var x = 'y';", "ext": "js", - "head": "", "id": "final-key", "name": "script", - "tail": "", }, ], ], @@ -349,10 +325,8 @@ exports[`challenge parser > should parse a simple md file 1`] = ` ", "editableRegionBoundaries": [], "ext": "html", - "head": "", "id": "", "name": "index", - "tail": "", }, { "contents": "body { @@ -360,19 +334,15 @@ exports[`challenge parser > should parse a simple md file 1`] = ` }", "editableRegionBoundaries": [], "ext": "css", - "head": "", "id": "", "name": "styles", - "tail": "", }, { "contents": "var x = 'y';", "editableRegionBoundaries": [], "ext": "js", - "head": "", "id": "", "name": "script", - "tail": "", }, { "contents": "{ @@ -382,10 +352,8 @@ exports[`challenge parser > should parse a simple md file 1`] = ` }", "editableRegionBoundaries": [], "ext": "json", - "head": "", "id": "", "name": "tsconfig", - "tail": "", }, ], "description": "
@@ -406,28 +374,22 @@ exports[`challenge parser > should parse a simple md file 1`] = ` ", "ext": "html", - "head": "", "id": "html-key", "name": "index", - "tail": "", }, { "contents": "body { background: white; }", "ext": "css", - "head": "", "id": "", "name": "styles", - "tail": "", }, { "contents": "var x = 'y';", "ext": "js", - "head": "", "id": "", "name": "script", - "tail": "", }, ], ], @@ -462,10 +424,8 @@ exports[`challenge parser > should parse frontmatter 1`] = ` ", "editableRegionBoundaries": [], "ext": "html", - "head": "", "id": "", "name": "index", - "tail": "", }, { "contents": "body { @@ -473,19 +433,15 @@ exports[`challenge parser > should parse frontmatter 1`] = ` }", "editableRegionBoundaries": [], "ext": "css", - "head": "", "id": "", "name": "styles", - "tail": "", }, { "contents": "var x = 'y';", "editableRegionBoundaries": [], "ext": "js", - "head": "", "id": "", "name": "script", - "tail": "", }, ], "challengeType": 0, @@ -524,10 +480,8 @@ exports[`challenge parser > should parse gfm strikethrough and frontmatter 1`] = ", "editableRegionBoundaries": [], "ext": "html", - "head": "", "id": "", "name": "index", - "tail": "", }, { "contents": "body { @@ -535,19 +489,15 @@ exports[`challenge parser > should parse gfm strikethrough and frontmatter 1`] = }", "editableRegionBoundaries": [], "ext": "css", - "head": "", "id": "", "name": "styles", - "tail": "", }, { "contents": "var x = 'y';", "editableRegionBoundaries": [], "ext": "js", - "head": "", "id": "", "name": "script", - "tail": "", }, ], "description": "
@@ -582,28 +532,22 @@ exports[`challenge parser > should parse gfm strikethrough and frontmatter 1`] = ", "ext": "html", - "head": "", "id": "html-key", "name": "index", - "tail": "", }, { "contents": "body { background: white; }", "ext": "css", - "head": "", "id": "", "name": "styles", - "tail": "", }, { "contents": "var x = 'y';", "ext": "js", - "head": "", "id": "", "name": "script", - "tail": "", }, ], ], diff --git a/tools/challenge-parser/parser/index.d.ts b/tools/challenge-parser/parser/index.d.ts index d81405dcc97..fef47da9fcd 100644 --- a/tools/challenge-parser/parser/index.d.ts +++ b/tools/challenge-parser/parser/index.d.ts @@ -6,8 +6,6 @@ type ChallengeFile = { contents: string; ext: string; editableRegionBoundaries: number[]; - head?: string; - tail?: string; }; export interface ParsedChallenge { id: string; diff --git a/tools/challenge-parser/parser/plugins/__snapshots__/add-seed.test.js.snap b/tools/challenge-parser/parser/plugins/__snapshots__/add-seed.test.js.snap index 5bab92e85ac..cc64b7b109c 100644 --- a/tools/challenge-parser/parser/plugins/__snapshots__/add-seed.test.js.snap +++ b/tools/challenge-parser/parser/plugins/__snapshots__/add-seed.test.js.snap @@ -10,10 +10,8 @@ exports[`add-seed plugin > should have an output to match the snapshot 1`] = ` ", "editableRegionBoundaries": [], "ext": "html", - "head": "", "id": "", "name": "index", - "tail": "", }, { "contents": "body { @@ -21,19 +19,15 @@ exports[`add-seed plugin > should have an output to match the snapshot 1`] = ` }", "editableRegionBoundaries": [], "ext": "css", - "head": "", "id": "", "name": "styles", - "tail": "", }, { "contents": "var x = 'y';", "editableRegionBoundaries": [], "ext": "js", - "head": "", "id": "", "name": "script", - "tail": "", }, { "contents": "{ @@ -43,10 +37,8 @@ exports[`add-seed plugin > should have an output to match the snapshot 1`] = ` }", "editableRegionBoundaries": [], "ext": "json", - "head": "", "id": "", "name": "tsconfig", - "tail": "", }, ], } diff --git a/tools/challenge-parser/parser/plugins/__snapshots__/add-solution.test.js.snap b/tools/challenge-parser/parser/plugins/__snapshots__/add-solution.test.js.snap index 86d428fc07c..4292f58597d 100644 --- a/tools/challenge-parser/parser/plugins/__snapshots__/add-solution.test.js.snap +++ b/tools/challenge-parser/parser/plugins/__snapshots__/add-solution.test.js.snap @@ -10,28 +10,22 @@ exports[`add solution plugin > should have an output to match the snapshot 1`] = ", "ext": "html", - "head": "", "id": "html-key", "name": "index", - "tail": "", }, { "contents": "body { background: white; }", "ext": "css", - "head": "", "id": "", "name": "styles", - "tail": "", }, { "contents": "var x = 'y';", "ext": "js", - "head": "", "id": "", "name": "script", - "tail": "", }, ], ], diff --git a/tools/challenge-parser/parser/plugins/add-seed.js b/tools/challenge-parser/parser/plugins/add-seed.js index 0a7869c839e..9425ec74d0d 100644 --- a/tools/challenge-parser/parser/plugins/add-seed.js +++ b/tools/challenge-parser/parser/plugins/add-seed.js @@ -38,22 +38,17 @@ function addSeeds() { // processing in these cases. if (isEmpty(seedTree.children)) return; 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 = {}; - // While before and after code are optional, the contents are not + // Seed contents are required. if (isEmpty(contentsTree.children)) throw Error('## --seed-contents-- must appear in # --seed-- sections'); const visitForContents = visitChildren( getFileVisitor(seeds, 'contents', validateEditableMarkers) ); - const visitForHead = visitChildren(getFileVisitor(seeds, 'head')); - const visitForTail = visitChildren(getFileVisitor(seeds, 'tail')); visitForContents(contentsTree); - visitForHead(headTree); - visitForTail(tailTree); + const seedVals = Object.values(seeds); file.data = { ...file.data, diff --git a/tools/challenge-parser/parser/plugins/add-seed.test.js b/tools/challenge-parser/parser/plugins/add-seed.test.js index a24554e59b8..3b67e843bce 100644 --- a/tools/challenge-parser/parser/plugins/add-seed.test.js +++ b/tools/challenge-parser/parser/plugins/add-seed.test.js @@ -7,14 +7,9 @@ import addSeed from './add-seed'; describe('add-seed plugin', () => { let adjacentKeysAST, withSeedKeysAST, - withBeforeAfterAST, cCodeAST, withErmsOnOneLineAST, - withEmptyAfterAST, - withEmptyBeforeAST, withEmptyContentsAST, - withInvalidBeforeAST, - withInvalidAfterAST, simpleAST, withEditableMarkersAST, withSeedKeysOrphanAST, @@ -27,16 +22,11 @@ describe('add-seed plugin', () => { beforeAll(async () => { adjacentKeysAST = await parseFixture('with-seed-keys-adjacent.md'); withSeedKeysAST = await parseFixture('with-seed-keys.md'); - withBeforeAfterAST = await parseFixture('with-before-and-after.md'); cCodeAST = await parseFixture('with-c-code.md'); withErmsOnOneLineAST = await parseFixture( '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'); - withInvalidBeforeAST = await parseFixture('with-invalid-before.md'); - withInvalidAfterAST = await parseFixture('with-invalid-after.md'); simpleAST = await parseFixture('simple.md'); withEditableMarkersAST = await parseFixture('with-editable-markers.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', () => { - expect.assertions(15); + expect.assertions(11); plugin(simpleAST, file); const { data: { challengeFiles } } = file; 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(typeof testObject['ext']).toBe('string'); expect(testObject).toHaveProperty('name'); expect(typeof testObject['name']).toBe('string'); expect(testObject).toHaveProperty('contents'); 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(typeof testObject['id']).toBe('string'); expect(testObject).toHaveProperty('editableRegionBoundaries'); @@ -155,96 +141,14 @@ describe('add-seed plugin', () => { 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(``); - 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', () => { expect.assertions(1); 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' ); }); - 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(''); - 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', () => { expect.assertions(1); expect(() => plugin(withErmsOnOneLineAST, file)).toThrow( @@ -253,17 +157,13 @@ describe('add-seed plugin', () => { }); it('handles jsx', () => { - expect.assertions(3); + expect.assertions(1); plugin(withSeedKeysJSXAST, file); const { data: { challengeFiles } } = file; 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'; /* comment */ diff --git a/tools/challenge-parser/parser/plugins/add-solution.test.js b/tools/challenge-parser/parser/plugins/add-solution.test.js index 0739bd2ef06..56aef7b3df5 100644 --- a/tools/challenge-parser/parser/plugins/add-solution.test.js +++ b/tools/challenge-parser/parser/plugins/add-solution.test.js @@ -40,23 +40,19 @@ describe('add solution plugin', () => { }); it('adds solution objects to the challengeFiles array following a schema', () => { - expect.assertions(13); + expect.assertions(9); plugin(mockAST, file); const { data: { solutions } } = file; 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(typeof testObject['ext']).toBe('string'); expect(testObject).toHaveProperty('name'); expect(typeof testObject['name']).toBe('string'); expect(testObject).toHaveProperty('contents'); 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(typeof testObject['id']).toBe('string'); }); diff --git a/tools/challenge-parser/parser/plugins/utils/get-file-visitor.js b/tools/challenge-parser/parser/plugins/utils/get-file-visitor.js index aaf0b6f55ad..7dd0303ebf3 100644 --- a/tools/challenge-parser/parser/plugins/utils/get-file-visitor.js +++ b/tools/challenge-parser/parser/plugins/utils/get-file-visitor.js @@ -4,10 +4,6 @@ const position = require('unist-util-position'); const getId = require('./get-id'); -const keyToSection = { - head: 'before-user-code', - tail: 'after-user-code' -}; const supportedLanguages = [ 'js', 'css', @@ -29,8 +25,6 @@ function defaultFile(lang, id) { ext: lang, name: getFilenames(lang), contents: '', - head: '', - tail: '', id }; } @@ -74,11 +68,6 @@ function codeToData(node, seeds, seedKey, validate) { if (!seeds[fileId]) { 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]) ? node.value : seeds[fileId][seedKey] + '\n' + node.value; diff --git a/tools/challenge-parser/parser/plugins/validate-sections.js b/tools/challenge-parser/parser/plugins/validate-sections.js index c1f39f93f58..84c2870c84a 100644 --- a/tools/challenge-parser/parser/plugins/validate-sections.js +++ b/tools/challenge-parser/parser/plugins/validate-sections.js @@ -30,9 +30,6 @@ const VALID_MARKERS = [ '## --sentence--', '## --text--', '## --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 '### --audio-id--', diff --git a/tools/client-plugins/gatsby-source-challenges/gatsby-node.js b/tools/client-plugins/gatsby-source-challenges/gatsby-node.js index cd4df0f6828..3833f256557 100644 --- a/tools/client-plugins/gatsby-source-challenges/gatsby-node.js +++ b/tools/client-plugins/gatsby-source-challenges/gatsby-node.js @@ -299,8 +299,6 @@ exports.createPagesStatefully = async function ({ graphql, actions }) { name ext contents - head - tail history fileKey }