| 
									
										
										
										
											2017-01-18 15:16:23 -08:00
										 |  |  | const path = require('path'); | 
					
						
							| 
									
										
										
										
											2018-10-04 14:45:22 +01:00
										 |  |  | const { findIndex } = require('lodash'); | 
					
						
							|  |  |  | const readDirP = require('readdirp-walk'); | 
					
						
							| 
									
										
										
										
											2018-10-04 18:25:49 +01:00
										 |  |  | const { parseMarkdown } = require('@freecodecamp/challenge-md-parser'); | 
					
						
							| 
									
										
										
										
											2018-01-19 14:03:17 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-28 06:18:13 +00:00
										 |  |  | const { dasherize } = require('./utils'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-09 20:26:37 +01:00
										 |  |  | const challengesDir = path.resolve(__dirname, './challenges'); | 
					
						
							| 
									
										
										
										
											2018-11-16 18:22:52 +00:00
										 |  |  | const metaDir = path.resolve(challengesDir, '_meta'); | 
					
						
							|  |  |  | exports.challengesDir = challengesDir; | 
					
						
							|  |  |  | exports.metaDir = metaDir; | 
					
						
							| 
									
										
										
										
											2018-10-09 20:26:37 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-18 21:04:04 +03:00
										 |  |  | function getChallengesDirForLang(lang) { | 
					
						
							|  |  |  |   return path.resolve(challengesDir, `./${lang}`); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | exports.getChallengesDirForLang = getChallengesDirForLang; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-04 14:45:22 +01:00
										 |  |  | exports.getChallengesForLang = function getChallengesForLang(lang) { | 
					
						
							|  |  |  |   let curriculum = {}; | 
					
						
							| 
									
										
										
										
											2019-03-16 00:12:56 +03:00
										 |  |  |   return new Promise(resolve => { | 
					
						
							|  |  |  |     let running = 1; | 
					
						
							|  |  |  |     function done() { | 
					
						
							|  |  |  |       if (--running === 0) { | 
					
						
							|  |  |  |         resolve(curriculum); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-11-18 21:04:04 +03:00
										 |  |  |     readDirP({ root: getChallengesDirForLang(lang) }) | 
					
						
							| 
									
										
										
										
											2019-03-16 00:12:56 +03:00
										 |  |  |       .on('data', file => { | 
					
						
							|  |  |  |         running++; | 
					
						
							|  |  |  |         buildCurriculum(file, curriculum).then(done); | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |       .on('end', done); | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2018-10-04 14:45:22 +01:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function buildCurriculum(file, curriculum) { | 
					
						
							| 
									
										
										
										
											2018-11-18 21:04:04 +03:00
										 |  |  |   const { name, depth, path: filePath, fullPath, stat } = file; | 
					
						
							| 
									
										
										
										
											2018-10-04 14:45:22 +01:00
										 |  |  |   if (depth === 1 && stat.isDirectory()) { | 
					
						
							|  |  |  |     // extract the superBlock info
 | 
					
						
							|  |  |  |     const { order, name: superBlock } = superBlockInfo(name); | 
					
						
							|  |  |  |     curriculum[superBlock] = { superBlock, order, blocks: {} }; | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (depth === 2 && stat.isDirectory()) { | 
					
						
							| 
									
										
										
										
											2018-10-09 20:26:37 +01:00
										 |  |  |     const blockName = getBlockNameFromPath(filePath); | 
					
						
							|  |  |  |     const metaPath = path.resolve( | 
					
						
							|  |  |  |       __dirname, | 
					
						
							|  |  |  |       `./challenges/_meta/${blockName}/meta.json` | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     const blockMeta = require(metaPath); | 
					
						
							|  |  |  |     const { name: superBlock } = superBlockInfoFromPath(filePath); | 
					
						
							| 
									
										
										
										
											2018-10-04 14:45:22 +01:00
										 |  |  |     const blockInfo = { meta: blockMeta, challenges: [] }; | 
					
						
							|  |  |  |     curriculum[superBlock].blocks[name] = blockInfo; | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-10-10 16:20:40 -04:00
										 |  |  |   if (name === 'meta.json' || name === '.DS_Store') { | 
					
						
							| 
									
										
										
										
											2018-10-04 14:45:22 +01:00
										 |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-11-16 18:22:52 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-09 20:26:37 +01:00
										 |  |  |   const block = getBlockNameFromPath(filePath); | 
					
						
							|  |  |  |   const { name: superBlock } = superBlockInfoFromPath(filePath); | 
					
						
							| 
									
										
										
										
											2018-10-10 16:20:40 -04:00
										 |  |  |   let challengeBlock; | 
					
						
							|  |  |  |   try { | 
					
						
							|  |  |  |     challengeBlock = curriculum[superBlock].blocks[block]; | 
					
						
							|  |  |  |   } catch (e) { | 
					
						
							|  |  |  |     console.log(superBlock, block); | 
					
						
							| 
									
										
										
										
											2018-10-25 12:54:57 +03:00
										 |  |  |     // eslint-disable-next-line no-process-exit
 | 
					
						
							| 
									
										
										
										
											2018-10-10 16:20:40 -04:00
										 |  |  |     process.exit(0); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-10-04 14:45:22 +01:00
										 |  |  |   const { meta } = challengeBlock; | 
					
						
							| 
									
										
										
										
											2018-11-16 18:22:52 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-18 21:04:04 +03:00
										 |  |  |   const challenge = await createChallenge(fullPath, meta); | 
					
						
							| 
									
										
										
										
											2018-11-16 18:22:52 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |   challengeBlock.challenges = [...challengeBlock.challenges, challenge]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-18 21:04:04 +03:00
										 |  |  | async function createChallenge(fullPath, maybeMeta) { | 
					
						
							| 
									
										
										
										
											2018-11-16 18:22:52 +00:00
										 |  |  |   let meta; | 
					
						
							|  |  |  |   if (maybeMeta) { | 
					
						
							|  |  |  |     meta = maybeMeta; | 
					
						
							|  |  |  |   } else { | 
					
						
							| 
									
										
										
										
											2018-11-18 21:04:04 +03:00
										 |  |  |     const metaPath = path.resolve( | 
					
						
							|  |  |  |       metaDir, | 
					
						
							|  |  |  |       `./${getBlockNameFromFullPath(fullPath)}/meta.json` | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2018-11-16 18:22:52 +00:00
										 |  |  |     meta = require(metaPath); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-11-18 21:04:04 +03:00
										 |  |  |   const { name: superBlock } = superBlockInfoFromFullPath(fullPath); | 
					
						
							| 
									
										
										
										
											2018-11-16 18:22:52 +00:00
										 |  |  |   const challenge = await parseMarkdown(fullPath); | 
					
						
							| 
									
										
										
										
											2018-10-04 14:45:22 +01:00
										 |  |  |   const challengeOrder = findIndex( | 
					
						
							|  |  |  |     meta.challengeOrder, | 
					
						
							|  |  |  |     ([id]) => id === challenge.id | 
					
						
							|  |  |  |   ); | 
					
						
							| 
									
										
										
										
											2018-11-18 21:16:09 +03:00
										 |  |  |   const { | 
					
						
							|  |  |  |     name: blockName, | 
					
						
							|  |  |  |     order, | 
					
						
							|  |  |  |     superOrder, | 
					
						
							|  |  |  |     isPrivate, | 
					
						
							|  |  |  |     required = [], | 
					
						
							|  |  |  |     template, | 
					
						
							|  |  |  |     time | 
					
						
							|  |  |  |   } = meta; | 
					
						
							| 
									
										
										
										
											2018-10-04 14:45:22 +01:00
										 |  |  |   challenge.block = blockName; | 
					
						
							| 
									
										
										
										
											2018-10-28 06:18:13 +00:00
										 |  |  |   challenge.dashedName = dasherize(challenge.title); | 
					
						
							| 
									
										
										
										
											2018-10-04 14:45:22 +01:00
										 |  |  |   challenge.order = order; | 
					
						
							|  |  |  |   challenge.superOrder = superOrder; | 
					
						
							|  |  |  |   challenge.superBlock = superBlock; | 
					
						
							|  |  |  |   challenge.challengeOrder = challengeOrder; | 
					
						
							| 
									
										
										
										
											2018-11-18 21:16:09 +03:00
										 |  |  |   challenge.isPrivate = challenge.isPrivate || isPrivate; | 
					
						
							|  |  |  |   challenge.required = required.concat(challenge.required || []); | 
					
						
							|  |  |  |   challenge.template = template; | 
					
						
							|  |  |  |   challenge.time = time; | 
					
						
							| 
									
										
										
										
											2018-11-16 18:22:52 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |   return challenge; | 
					
						
							| 
									
										
										
										
											2018-10-04 14:45:22 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-16 18:22:52 +00:00
										 |  |  | exports.createChallenge = createChallenge; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-04 14:45:22 +01:00
										 |  |  | function superBlockInfoFromPath(filePath) { | 
					
						
							| 
									
										
										
										
											2018-10-25 12:54:57 +03:00
										 |  |  |   const [maybeSuper] = filePath.split(path.sep); | 
					
						
							| 
									
										
										
										
											2018-10-04 14:45:22 +01:00
										 |  |  |   return superBlockInfo(maybeSuper); | 
					
						
							| 
									
										
										
										
											2015-11-01 17:20:03 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-18 21:04:04 +03:00
										 |  |  | function superBlockInfoFromFullPath(fullFilePath) { | 
					
						
							| 
									
										
										
										
											2019-02-18 19:32:49 +00:00
										 |  |  |   const [, , maybeSuper] = fullFilePath.split(path.sep).reverse(); | 
					
						
							| 
									
										
										
										
											2018-11-18 21:04:04 +03:00
										 |  |  |   return superBlockInfo(maybeSuper); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-04 14:45:22 +01:00
										 |  |  | function superBlockInfo(fileName) { | 
					
						
							|  |  |  |   const [maybeOrder, ...superBlock] = fileName.split('-'); | 
					
						
							|  |  |  |   let order = parseInt(maybeOrder, 10); | 
					
						
							| 
									
										
										
										
											2018-01-19 14:03:17 -05:00
										 |  |  |   if (isNaN(order)) { | 
					
						
							| 
									
										
										
										
											2018-10-04 14:45:22 +01:00
										 |  |  |     return { order: 0, name: fileName }; | 
					
						
							| 
									
										
										
										
											2018-01-19 14:03:17 -05:00
										 |  |  |   } else { | 
					
						
							|  |  |  |     return { | 
					
						
							|  |  |  |       order: order, | 
					
						
							| 
									
										
										
										
											2018-10-04 14:45:22 +01:00
										 |  |  |       name: superBlock.join('-') | 
					
						
							| 
									
										
										
										
											2018-01-19 14:03:17 -05:00
										 |  |  |     }; | 
					
						
							| 
									
										
										
										
											2015-12-06 21:44:34 -08:00
										 |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-04 14:45:22 +01:00
										 |  |  | function getBlockNameFromPath(filePath) { | 
					
						
							| 
									
										
										
										
											2018-10-25 12:54:57 +03:00
										 |  |  |   const [, block] = filePath.split(path.sep); | 
					
						
							| 
									
										
										
										
											2018-10-04 14:45:22 +01:00
										 |  |  |   return block; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-11-16 18:22:52 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | function getBlockNameFromFullPath(fullFilePath) { | 
					
						
							|  |  |  |   const [, block] = fullFilePath.split(path.sep).reverse(); | 
					
						
							|  |  |  |   return block; | 
					
						
							|  |  |  | } |