/* eslint-disable no-inline-comments */ import fs from 'fs-extra'; import path from 'path'; import _ from 'lodash'; import { dasherize } from '../common/utils'; const jsonLinePrefix = '//--JSON:'; const paragraphBreak = ''; class ChallengeFile { constructor(dir, name, suffix) { this.dir = dir; this.name = name; this.suffix = suffix; } filePath() { return path.join(this.dir, this.name + this.suffix); } write(contents) { if (_.isArray(contents)) { contents = contents.join('\n'); } fs.writeFile(this.filePath(), contents, err => { if (err) { throw err; } }); } readChunks() { // todo: make this work async // todo: make sure it works with encodings let data = fs.readFileSync(this.filePath()); let lines = data.toString().split(/(?:\r\n|\r|\n)/g); let chunks = {}; let readingChunk = null; let currentParagraph = []; function removeLeadingEmptyLines(array) { let emptyString = /^\s*$/; while (array && Array.isArray(array) && emptyString.test(array[0])) { array.shift(); } } lines.forEach(line => { let chunkEnd = /( { removeLeadingEmptyLines(chunks[key]); }); // console.log(JSON.stringify(chunks, null, 2)); return chunks; } } export {ChallengeFile}; class UnpackedChallenge { constructor(targetDir, challengeJson, index) { this.targetDir = targetDir; this.index = index; // todo: merge challengeJson properties into this object? this.challenge = challengeJson; // extract names of block and superblock from path // note: this is a bit redundant with the // fileName,superBlock,superOrder fields // that getChallenges() adds to the challenge JSON let targetDirPath = path.parse(targetDir); let parentDirPath = path.parse(targetDirPath.dir); // superBlockName e.g. "03-front-end-libraries" this.superBlockName = parentDirPath.base; // challengeBlockName e.g. "bootstrap" this.challengeBlockName = targetDirPath.base; } unpack() { this.challengeFile() .write(this.unpackedHTML()); } challengeFile() { return new ChallengeFile(this.targetDir, this.baseName(), '.html'); } baseName() { // eslint-disable-next-line no-nested-ternary let prefix = ((this.index < 10) ? '00' : (this.index < 100) ? '0' : '') + this.index; return `${prefix}-${dasherize(this.challenge.title)}-${this.challenge.id}`; } expandedDescription() { let out = []; this.challenge.description.forEach(part => { if (_.isString(part)) { out.push(part.toString()); out.push(paragraphBreak); } else { // Descriptions are weird since sometimes they're text and sometimes // they're "steps" which appear one at a time with optional pix and // captions and links, or "questions" with choices and explanations... // For now we preserve non-string descriptions via JSON but this is // not a great solution. // It would be better if "steps" and "description" were separate fields. // For the record, the (unnamed) fields in step are: // 0: image URL // 1: caption // 2: text // 3: link URL out.push(jsonLinePrefix + JSON.stringify(part)); } }); if (out[ out.length - 1 ] === paragraphBreak) { out.pop(); } return out; } expandedTests(tests) { if (!tests) { return []; } let out = []; tests.forEach(test => { if (_.isString(test)) { out.push(test); } else { // todo: figure out what to do about these id-title challenge links out.push(jsonLinePrefix + JSON.stringify(test)); } }); return out; } unpackedHTML() { let text = []; text.push(''); text.push(''); text.push(''); text.push(''); text.push(''); text.push(''); text.push(''); text.push(`

${this.challenge.title}

`); text.push(`

This is the unpacked version of ${this.superBlockName}/${this.challengeBlockName} (challenge id ${this.challenge.id}).

`); text.push('

Open the JavaScript console to see test results.

'); text.push(`

Edit this HTML file (between <!-- marks only!) and run npm run repack to incorporate your changes into the challenge database.

`); text.push(''); text.push('

Description

'); text.push('
'); text.push(''); if (this.challenge.description.length) { text.push(this.expandedDescription().join('\n')); } text.push(''); text.push('
'); text.push(''); text.push('

Seed

'); text.push('
');
    if (this.challenge.seed) {
      text.push(text, this.challenge.seed.join('\n'));
    }
    text.push('');
    text.push('
'); // Q: What is the difference between 'seed' and 'challengeSeed' ? text.push(''); text.push('

Challenge Seed

'); text.push('
');
    if (this.challenge.challengeSeed) {
      text.push(text, this.challenge.challengeSeed.join('\n'));
    }
    text.push('');
    text.push('
'); text.push(''); text.push('

Head

'); text.push(''); text.push(''); text.push('

Solution

'); text.push( ''); text.push(''); text.push('

Tail

'); text.push(''); text.push(''); text.push('

Tests

'); text.push(''); text.push(''); text.push(''); text.push(''); return text; } } export {UnpackedChallenge};