(meta, chapter)
| 218 | } |
| 219 | |
| 220 | function chapterZipFile(meta, chapter) { |
| 221 | let spec = meta.match(/\bzip: ("(?:\\.|[^"\\])*")/); |
| 222 | if (!spec) return null; |
| 223 | if (!chapter.start_code) throw new Error("zip but no start code"); |
| 224 | let data = /(\S+)(?:\s+include=(.*))?/.exec(JSON.parse(spec[1])) |
| 225 | let name = "code/chapter/" + chapter.id + ".zip"; |
| 226 | let files = (chapter.include || []).concat(data[2] ? JSON.parse(data[2]) : []).filter(f => !/(^|\/)_/.test(f)); |
| 227 | let exists = fs.existsSync(name) && fs.statSync(name).mtime; |
| 228 | if (exists && files.every(file => fs.statSync("html/" + file).mtime < exists)) |
| 229 | return name; |
| 230 | |
| 231 | let zip = new jszip; |
| 232 | for (let file of files) { |
| 233 | zip.file(chapter.id + "/" + file, fs.readFileSync("html/" + file)); |
| 234 | } |
| 235 | if (data[1].indexOf("html") != -1) { |
| 236 | let html = chapter.start_code; |
| 237 | if (guessType(html) != "html") |
| 238 | html = prepareHTML("<body><script>\n" + html.trim() + "\n</script></body>", chapter.include); |
| 239 | zip.file(chapter.id + "/index.html", html); |
| 240 | } |
| 241 | if (data[1].indexOf("node") != -1) { |
| 242 | zip.file(chapter.id + "/code/load.js", fs.readFileSync("code/load.js", "utf8")); |
| 243 | let js = chapter.start_code; |
| 244 | if (chapter.include) js = "// load dependencies\nrequire(\"./code/load\")(" + chapter.include.map(JSON.stringify).join(", ") + ");\n\n" + js; |
| 245 | zip.file(chapter.id + "/run_with_node.js", js); |
| 246 | } |
| 247 | zip.generateAsync({type: "uint8array"}).then(content => fs.writeFileSync(name, content)); |
| 248 | return name; |
| 249 | } |
no test coverage detected