// Fragile, hacky script that finds exercises in chapters, extracts // their starting code, and collects it into a big JSON object // together with the solution code. var fs = require("fs"); var output = [], failed = false; var allSolutions = fs.readdirSync("code/solutions/").filter(function(file) { return !/^2[012]/.test(file); }); var dir = fs.readdirSync("."); dir.sort(); dir.forEach(function(file) { var match = /^((\d+).*).txt$/.exec(file), chapNum = match && match[2]; if (!match) return; var text = fs.readFileSync(file, "utf8"); var includes = text.match(/\n:load_files: (.*)/); if (includes) includes = includes = JSON.parse(includes[1]); var chapter = {number: +chapNum, id: match[1], title: text.match(/\n= (.*?) =\n/)[1], start_code: getStartCode(text, includes), exercises: [], include: includes}; var zip = chapterZipFile(text, chapter); var extraLinks = text.match(/\n:code_links: (.*)/); if (extraLinks) extraLinks = JSON.parse(extraLinks[1]); if (extraLinks || zip) chapter.links = (zip ? [zip] : []).concat(extraLinks || []); var exerciseSection = text.indexOf("\n== Exercises ==\n"); var exerciseBlock = exerciseSection ? text.slice(exerciseSection) : ""; var header = /\n=== (.*?) ===\n/g; var num = 1; while (match = header.exec(exerciseBlock)) { var nextsection = exerciseBlock.indexOf("\n==", header.lastIndex); for (var pos = header.lastIndex;;) { var ifdef = exerciseBlock.indexOf("ifdef::interactive_target[]", pos); if (ifdef == -1 || nextsection > 0 && nextsection < ifdef) break; var indef = exerciseBlock.slice(pos = ifdef + 21, exerciseBlock.indexOf("endif::interactive_target[]", ifdef)); var sourceBlock = indef.match(/\[source,([^\]]*)\]\n----\n([^]+?)\n----/); if (!sourceBlock) continue; var type = sourceBlock[1].indexOf("html") > -1 ? "html" : "js"; var file = chapNum + "_" + num + "_" + match[1].toLowerCase().replace(/[^\-\s\w]/g, "").replace(/\s/g, "_") + "." + type; try { var solution = fs.readFileSync("code/solutions/" + file, "utf8"); var extra = /^\s*\s*(\n\n\n", include: ["code/draw_graph.js", "code/chapter/22_fast.js"], exercises: [ {name: "Pathfinding", file: "code/solutions/22_1_pathfinding.js", number: 1, type: "js", code: "function findPath(a, b) {\n // Your code here...\n}\n\nvar graph = treeGraph(4, 4);\nvar root = graph[0], leaf = graph[graph.length - 1];\nconsole.log(findPath(root, leaf).length);\n// → 4\n\nleaf.connect(root);\nconsole.log(findPath(root, leaf).length);\n// → 2\n", solution: fs.readFileSync("code/solutions/22_1_pathfinding.js", "utf8") }, {name: "Timing", file: "code/solutions/22_2_timing.js", number: 2, type: "js", code: "", solution: fs.readFileSync("code/solutions/22_2_timing.js", "utf8") }, {name: "Optimizing", file: "code/solutions/22_3_optimizing.js", number: 3, type: "js", code: "", solution: fs.readFileSync("code/solutions/22_3_optimizing.js", "utf8") } ] }); if (allSolutions.length) { console.error("Solution files " + allSolutions + " were not used."); failed = true; } if (!failed) console.log("var chapterData = " + JSON.stringify(output, null, 2) + ";"); else process.exit(1); function prepareHTML(code, include) { return "\n" + (include || []).map(function(s) { return "\n"; }).join("") + "\n" + code; } function guessType(code) { return /^[\s\w\n:]*", chapter.include); zip.file(chapter.id + "/index.html", html); } if (spec[1].indexOf("node") != -1) { zip.file(chapter.id + "/code/load.js", fs.readFileSync("code/load.js", "utf8")); var js = chapter.start_code; if (chapter.include) js = "// load dependencies\nrequire(\"./code/load\")(" + chapter.include.map(JSON.stringify).join(", ") + ");\n\n" + js; zip.file(chapter.id + "/run_with_node.js", js); } fs.writeFileSync(name, zip.generate({type: "nodebuffer"})); return name; }