From 14c9ed8974ec4b5b24d04ebfacfec7c784136790 Mon Sep 17 00:00:00 2001
From: Alex Chaffee
Date: Fri, 23 Mar 2018 15:53:59 -0400
Subject: [PATCH] feat(seed): unpack/repack properly handles paragraph breaks
---
README.md | 5 +++-
repack.js | 25 +---------------
unpack.js | 36 ++++++++++++++---------
unpackedChallenge.js | 69 +++++++++++++++++++++++++++++++++-----------
4 files changed, 79 insertions(+), 56 deletions(-)
diff --git a/README.md b/README.md
index f038a5ab64..7999c4537d 100644
--- a/README.md
+++ b/README.md
@@ -19,8 +19,11 @@ For each challenge section, there is a JSON file (fields documented below) conta
`npm run unpack` extracts challenges into separate files for easier viewing and editing. The files are `.gitignore`d and will *not* be checked in, and all mongo seed importing will keep using the existing system; this is essentially a tool for editing `challenge.json` files. These HTML files are self-contained and run their own tests -- open a browser JS console to see the test results.
-`npm run repack` gathers up the unpacked/edited HTML files into challenge-block JSON files. Use `git diff` to see the changes
+`npm run repack` gathers up the unpacked/edited HTML files into challenge-block JSON files. Use `git diff` to see the changes.
+When editing the unpacked files, you must only edit lines between comment fences like `` and ``. In descriptions, you can insert a paragraph break with ``.
+
+Unpacked lines that begin with `//--JSON:` are parsed and inserted verbatim.
## Links
diff --git a/repack.js b/repack.js
index 9328b3e611..149d66697d 100644
--- a/repack.js
+++ b/repack.js
@@ -19,25 +19,9 @@ function directoriesIn(parentDir) {
}
let superBlocks = directoriesIn(unpackedRoot);
-console.log(superBlocks);
-
-function diffFiles(originalFilePath, changedFilePath) {
- // todo: async
- console.log(`diffing ${originalFilePath} and ${changedFilePath}`);
- let original = fs.readFileSync(originalFilePath).toString();
- let repacked = fs.readFileSync(changedFilePath).toString();
-
- let changes = jsdiff.diffLines(original, repacked, { newlineIsToken: true });
- changes.forEach((change) => {
- if (change.added || change.removed) {
- console.log(JSON.stringify(change, null, 2));
- }
- });
- console.log('');
-}
-
superBlocks.forEach(superBlock => {
let superBlockPath = path.join(unpackedRoot, superBlock);
+ console.log(`Repacking ${superBlockPath}...`);
let blocks = directoriesIn(superBlockPath);
blocks.forEach(blockName => {
let blockPath = path.join(superBlockPath, blockName);
@@ -59,13 +43,6 @@ superBlocks.forEach(superBlock => {
path.join(seedChallengesRoot, superBlock, blockName + '.json');
// todo: async
fs.writeFileSync(outputFilePath, JSON.stringify(block, null, 2));
-
- // todo: make this a command-line option instead
- let doDiff = false;
- if (doDiff) {
- diffFiles(blockFilePath, outputFilePath);
- }
-
});
});
diff --git a/unpack.js b/unpack.js
index 36410e4653..6307b19fa1 100644
--- a/unpack.js
+++ b/unpack.js
@@ -17,21 +17,29 @@ import {UnpackedChallenge, ChallengeFile} from './unpackedChallenge';
// bundle up the test-running JS
function createUnpackedBundle() {
- let unpackedFile = path.join(__dirname, 'unpacked.js');
- let b = browserify(unpackedFile).bundle();
- b.on('error', console.error);
- let unpackedBundleFile =
- path.join(__dirname, 'unpacked', 'unpacked-bundle.js');
- const bundleFileStream = fs.createWriteStream(unpackedBundleFile);
- bundleFileStream.on('finish', () => {
- console.log('Wrote bundled JS into ' + unpackedBundleFile);
+ let unpackedDir = path.join(__dirname, 'unpacked');
+ fs.mkdirp(unpackedDir, (err) => {
+ if (err && err.code !== 'EEXIST') {
+ console.log(err);
+ throw err;
+ }
+
+ let unpackedFile = path.join(__dirname, 'unpacked.js');
+ let b = browserify(unpackedFile).bundle();
+ b.on('error', console.error);
+ let unpackedBundleFile =
+ path.join(unpackedDir, 'unpacked-bundle.js');
+ const bundleFileStream = fs.createWriteStream(unpackedBundleFile);
+ bundleFileStream.on('finish', () => {
+ console.log('Wrote bundled JS into ' + unpackedBundleFile);
+ });
+ bundleFileStream.on('pipe', () => {
+ console.log('Writing bundled JS...');
+ });
+ bundleFileStream.on('error', console.error);
+ b.pipe(bundleFileStream);
+ // bundleFileStream.end(); // do not do this prematurely!
});
- bundleFileStream.on('pipe', () => {
- console.log('Writing bundled JS...');
- });
- bundleFileStream.on('error', console.error);
- b.pipe(bundleFileStream);
- // bundleFileStream.end(); // do not do this prematurely!
}
let currentlyUnpackingDir = null;
diff --git a/unpackedChallenge.js b/unpackedChallenge.js
index f419c2eb26..79a594d51d 100644
--- a/unpackedChallenge.js
+++ b/unpackedChallenge.js
@@ -4,6 +4,7 @@ import path from 'path';
import _ from 'lodash';
const jsonLinePrefix = '//--JSON:';
+const paragraphBreak = '';
class ChallengeFile {
constructor(dir, name, suffix) {
@@ -27,6 +28,7 @@ class ChallengeFile {
});
}
+
readChunks() {
// todo: make this work async
// todo: make sure it works with encodings
@@ -34,17 +36,39 @@ class ChallengeFile {
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;
}
@@ -116,15 +145,16 @@ class UnpackedChallenge {
return `${prefix}-${this.challenge.id}`;
}
- expandedDescription(description) {
+ expandedDescription() {
let out = [];
- description.forEach(part => {
+ 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 expanations...
+ // 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.
@@ -136,7 +166,10 @@ class UnpackedChallenge {
out.push(jsonLinePrefix + JSON.stringify(part));
}
});
- // indent by 2
+
+ if (out[ out.length - 1 ] === paragraphBreak) {
+ out.pop();
+ }
return out;
}
@@ -171,15 +204,17 @@ class UnpackedChallenge {
(challenge id ${this.challenge.id}
).
`);
text.push('Open the JavaScript console to see test results.
');
- // text.push(`Edit this HTML file (between <!--s only!)
- // and run npm repack ???
- // to incorporate your changes into the challenge database.
`);
+ 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('');
- text.push(this.expandedDescription(this.challenge.description).join('\n'));
+ if (this.challenge.description.length) {
+ text.push(this.expandedDescription().join('\n'));
+ }
text.push('');
text.push('
');
@@ -218,7 +253,7 @@ class UnpackedChallenge {
// Note: none of the challenges have more than one solution
// todo: should we deal with multiple solutions or not?
if (this.challenge.solutions && this.challenge.solutions.length > 0) {
- let solution = this.challenge.solutions[0];
+ let solution = this.challenge.solutions[ 0 ];
text.push(solution);
}
text.push('');