/* eslint-disable no-inline-comments */ import fs from 'fs-extra'; import path from 'path'; import _ from 'lodash'; import { dasherize } from './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); function removeLeadingEmptyLines(array) { let emptyString = /^\s*$/; while (array && Array.isArray(array) && emptyString.test(array[0])) { array.shift(); } } let chunkEnd = /(/; while (!endOfFile()) { let line = nextLine(); if (chunkEnd.test(line)) { return translations; } else if (langChunk.test(line)) { let langCode = line.match(langChunk)[1]; translations[langCode] = readProperties(); line = nextLine(); if (!chunkEnd.test(line)) { throw `Expected --end--: ${line}`; } } } throw `Unexpected end of the file while reading translations. ${this.filePath()}`; } function readFiles() { let files = {}; let fileChunk = //; while (!endOfFile()) { let line = nextLine(); if (chunkEnd.test(line)) { return files; } else if (fileChunk.test(line)) { let name = line.match(fileChunk)[1]; let ext = line.match(fileChunk)[2]; let key = name + ext; files[key] = {}; files[key].key = key; files[key].ext = ext; files[key].name = name; Object.assign(files[key], readProperties()); line = nextLine(); if (!chunkEnd.test(line)) { throw `Expected --end--: ${line}`; } } } throw `Unexpected end of the file while reading files. ${this.filePath()}`; } function readProperty() { let property = []; while (!endOfFile()) { let line = nextLine(); if (chunkEnd.test(line)) { removeLeadingEmptyLines(property); return property; } else if (line.startsWith(jsonLinePrefix)) { line = JSON.parse(line.slice(jsonLinePrefix.length)); property.push(line); } else { property.push(line); } } throw `Unexpected end of the file while reading a property. ${this.filePath()}`; } function readProperties() { let properties = {}; let chunkStart = /( { 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 is the unpacked version of
${this.superBlockName}/${this.challengeBlockName}
(challenge id ${this.challenge.id}
).
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.
Format of translation unit:
<!--language-code-->
<!--title-->
Title
<!--end-->
<!--description-->
Description
<!--end-->
<!--end-->
Format of file:
<!--name.ext-->
<!--contents-->
Contents
<!--end-->
<!--head-->
Head
<!--end-->
<!--tail-->
Tail
<!--end-->
<!--end-->
'); text.push(_.escape(contents.join('\n'))); text.push(''); text.push(' '); text.push('
'); text.push(_.escape(head.join('\n'))); text.push(''); text.push(' '); text.push('
'); text.push(_.escape(tail.join('\n'))); text.push(''); text.push(' '); text.push(''); }); } text.push(''); text.push('