feat(seed): "unpack" and "repack" scripts
add "npm run seed" as alias to "node seed" unpack tests and solution into HTML file; add titles and help text; style unpacked file enable running unpacked assert tests in browser Using browserify, compile "tape", "lodash", jQuery into "unpacked-bundle.js" for use during in-browser unpacked tests feat(seed): diff after repacking feat(seed): unpacked tests use Browser TAP chrome dev tool if available
This commit is contained in:
parent
0c6141289b
commit
a8484873d8
@ -1 +1,2 @@
|
|||||||
public/**/*.js
|
public/**/*.js
|
||||||
|
seed/unpacked/**/*.js
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -35,6 +35,7 @@ coverage
|
|||||||
|
|
||||||
server/*.bundle.js
|
server/*.bundle.js
|
||||||
public/js/bundle*
|
public/js/bundle*
|
||||||
|
seed/unpacked
|
||||||
|
|
||||||
*.map
|
*.map
|
||||||
|
|
||||||
|
@ -409,7 +409,7 @@ nothing to commit, working directory clean
|
|||||||
`fix/short-fix-description` or `feature/short-feature-description`. Review
|
`fix/short-fix-description` or `feature/short-feature-description`. Review
|
||||||
the [Contribution Guidelines](#contribution-guidelines) for more detail.
|
the [Contribution Guidelines](#contribution-guidelines) for more detail.
|
||||||
|
|
||||||
3. Edit your file(s) locally with the editor of your choice
|
3. Edit your file(s) locally with the editor of your choice. To edit challenges, you may want to use `unpack` and `repack` -- see [seed/README.md](seed/README.md) for instructions.
|
||||||
|
|
||||||
4. Check your `git status` to see unstaged files.
|
4. Check your `git status` to see unstaged files.
|
||||||
|
|
||||||
|
533
package-lock.json
generated
533
package-lock.json
generated
@ -186,6 +186,24 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"acorn-node": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-efP54n3d1aLfjL2UMdaXa6DsswwzJeI5rqhbFvXMrKiJ6eJFpf+7R0zN7t8IC+XKn2YOAFAv6xbBNgHUkoHWLw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"acorn": "5.5.3",
|
||||||
|
"xtend": "4.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"acorn": {
|
||||||
|
"version": "5.5.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.5.3.tgz",
|
||||||
|
"integrity": "sha512-jd5MkIUlbbmb07nXH0DT3y7rDVtkzDi4XZOUVWAer8ajmF/DTSSbl5oNFyDOl/OXA33Bl79+ypHhl2pN20VeOQ==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"addressparser": {
|
"addressparser": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/addressparser/-/addressparser-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/addressparser/-/addressparser-1.0.1.tgz",
|
||||||
@ -473,6 +491,12 @@
|
|||||||
"integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=",
|
"integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"array-filter": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz",
|
||||||
|
"integrity": "sha1-fajPLiZijtcygDWB/SH2fKzS7uw=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"array-find-index": {
|
"array-find-index": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
|
||||||
@ -493,6 +517,18 @@
|
|||||||
"es-abstract": "1.9.0"
|
"es-abstract": "1.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"array-map": {
|
||||||
|
"version": "0.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz",
|
||||||
|
"integrity": "sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"array-reduce": {
|
||||||
|
"version": "0.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz",
|
||||||
|
"integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"array-slice": {
|
"array-slice": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz",
|
||||||
@ -575,6 +611,23 @@
|
|||||||
"integrity": "sha1-ECyenpAF0+fjgpvwxPok7oYu6bk=",
|
"integrity": "sha1-ECyenpAF0+fjgpvwxPok7oYu6bk=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"astw": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/astw/-/astw-2.2.0.tgz",
|
||||||
|
"integrity": "sha1-e9QXhNMkk5h66yOba04cV6hzuRc=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"acorn": "4.0.13"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"acorn": {
|
||||||
|
"version": "4.0.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz",
|
||||||
|
"integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"async": {
|
"async": {
|
||||||
"version": "2.1.5",
|
"version": "2.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/async/-/async-2.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/async/-/async-2.1.5.tgz",
|
||||||
@ -2122,12 +2175,43 @@
|
|||||||
"integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=",
|
"integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"browser-pack": {
|
||||||
|
"version": "6.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.0.4.tgz",
|
||||||
|
"integrity": "sha512-Q4Rvn7P6ObyWfc4stqLWHtG1MJ8vVtjgT24Zbu+8UTzxYuZouqZsmNRRTFVMY/Ux0eIKv1d+JWzsInTX+fdHPQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"JSONStream": "1.0.3",
|
||||||
|
"combine-source-map": "0.8.0",
|
||||||
|
"defined": "1.0.0",
|
||||||
|
"safe-buffer": "5.1.1",
|
||||||
|
"through2": "2.0.3",
|
||||||
|
"umd": "3.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"browser-process-hrtime": {
|
"browser-process-hrtime": {
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.2.tgz",
|
||||||
"integrity": "sha1-Ql1opY00R/AqBKqJQYf86K+Le44=",
|
"integrity": "sha1-Ql1opY00R/AqBKqJQYf86K+Le44=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"browser-resolve": {
|
||||||
|
"version": "1.11.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.2.tgz",
|
||||||
|
"integrity": "sha1-j/CbCixCFxihBRwmCzLkj0QpOM4=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"resolve": "1.1.7"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"resolve": {
|
||||||
|
"version": "1.1.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
|
||||||
|
"integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"browser-sync": {
|
"browser-sync": {
|
||||||
"version": "2.18.13",
|
"version": "2.18.13",
|
||||||
"resolved": "https://registry.npmjs.org/browser-sync/-/browser-sync-2.18.13.tgz",
|
"resolved": "https://registry.npmjs.org/browser-sync/-/browser-sync-2.18.13.tgz",
|
||||||
@ -2339,6 +2423,132 @@
|
|||||||
"weinre": "2.0.0-pre-I0Z7U9OV"
|
"weinre": "2.0.0-pre-I0Z7U9OV"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"browserify": {
|
||||||
|
"version": "16.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/browserify/-/browserify-16.1.1.tgz",
|
||||||
|
"integrity": "sha512-iSH21jK0+IApV8YHOfmGt1qsGd74oflQ1Ko/28JOkWLFNBngAQfKb6WYIJ9CufH8vycqKX1sYU3y7ZrVhwevAg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"JSONStream": "1.0.3",
|
||||||
|
"assert": "1.4.1",
|
||||||
|
"browser-pack": "6.0.4",
|
||||||
|
"browser-resolve": "1.11.2",
|
||||||
|
"browserify-zlib": "0.2.0",
|
||||||
|
"buffer": "5.1.0",
|
||||||
|
"cached-path-relative": "1.0.1",
|
||||||
|
"concat-stream": "1.6.0",
|
||||||
|
"console-browserify": "1.1.0",
|
||||||
|
"constants-browserify": "1.0.0",
|
||||||
|
"crypto-browserify": "3.12.0",
|
||||||
|
"defined": "1.0.0",
|
||||||
|
"deps-sort": "2.0.0",
|
||||||
|
"domain-browser": "1.2.0",
|
||||||
|
"duplexer2": "0.1.4",
|
||||||
|
"events": "2.0.0",
|
||||||
|
"glob": "7.1.2",
|
||||||
|
"has": "1.0.1",
|
||||||
|
"htmlescape": "1.1.1",
|
||||||
|
"https-browserify": "1.0.0",
|
||||||
|
"inherits": "2.0.3",
|
||||||
|
"insert-module-globals": "7.0.2",
|
||||||
|
"labeled-stream-splicer": "2.0.0",
|
||||||
|
"mkdirp": "0.5.1",
|
||||||
|
"module-deps": "6.0.0",
|
||||||
|
"os-browserify": "0.3.0",
|
||||||
|
"parents": "1.0.1",
|
||||||
|
"path-browserify": "0.0.0",
|
||||||
|
"process": "0.11.10",
|
||||||
|
"punycode": "1.4.1",
|
||||||
|
"querystring-es3": "0.2.1",
|
||||||
|
"read-only-stream": "2.0.0",
|
||||||
|
"readable-stream": "2.2.7",
|
||||||
|
"resolve": "1.5.0",
|
||||||
|
"shasum": "1.0.2",
|
||||||
|
"shell-quote": "1.6.1",
|
||||||
|
"stream-browserify": "2.0.1",
|
||||||
|
"stream-http": "2.7.2",
|
||||||
|
"string_decoder": "1.0.3",
|
||||||
|
"subarg": "1.0.0",
|
||||||
|
"syntax-error": "1.4.0",
|
||||||
|
"through2": "2.0.3",
|
||||||
|
"timers-browserify": "1.4.2",
|
||||||
|
"tty-browserify": "0.0.1",
|
||||||
|
"url": "0.11.0",
|
||||||
|
"util": "0.10.3",
|
||||||
|
"vm-browserify": "0.0.4",
|
||||||
|
"xtend": "4.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"base64-js": {
|
||||||
|
"version": "1.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.3.tgz",
|
||||||
|
"integrity": "sha512-MsAhsUW1GxCdgYSO6tAfZrNapmUKk7mWx/k5mFY/A1gBtkaCaNapTg+FExCw1r9yeaZhqx/xPg43xgTFH6KL5w==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"buffer": {
|
||||||
|
"version": "5.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.1.0.tgz",
|
||||||
|
"integrity": "sha512-YkIRgwsZwJWTnyQrsBTWefizHh+8GYj3kbL1BTiAQ/9pwpino0G7B2gp5tx/FUBqUlvtxV85KNR3mwfAtv15Yw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"base64-js": "1.2.3",
|
||||||
|
"ieee754": "1.1.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"crypto-browserify": {
|
||||||
|
"version": "3.12.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz",
|
||||||
|
"integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"browserify-cipher": "1.0.0",
|
||||||
|
"browserify-sign": "4.0.4",
|
||||||
|
"create-ecdh": "4.0.0",
|
||||||
|
"create-hash": "1.1.3",
|
||||||
|
"create-hmac": "1.1.6",
|
||||||
|
"diffie-hellman": "5.0.2",
|
||||||
|
"inherits": "2.0.3",
|
||||||
|
"pbkdf2": "3.0.14",
|
||||||
|
"public-encrypt": "4.0.0",
|
||||||
|
"randombytes": "2.0.5",
|
||||||
|
"randomfill": "1.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain-browser": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"events": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/events/-/events-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-r/M5YkNg9zwI8QbSf7tsDWWJvO3PGwZXyG7GpFAxtMASnHL2eblFd7iHiGPtyGKKFPZ59S63NeX10Ws6WqGDcg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"process": {
|
||||||
|
"version": "0.11.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|
||||||
|
"integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"timers-browserify": {
|
||||||
|
"version": "1.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz",
|
||||||
|
"integrity": "sha1-ycWLV1voQHN1y14kYtrO50NZ9B0=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"process": "0.11.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tty-browserify": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz",
|
||||||
|
"integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"browserify-aes": {
|
"browserify-aes": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.1.1.tgz",
|
||||||
@ -2545,6 +2755,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"cached-path-relative": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-0JxLUoAKpMB44t2BqGmqyQ0uVOc=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"cachedir": {
|
"cachedir": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/cachedir/-/cachedir-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/cachedir/-/cachedir-1.1.1.tgz",
|
||||||
@ -3147,6 +3363,26 @@
|
|||||||
"integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=",
|
"integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"combine-source-map": {
|
||||||
|
"version": "0.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.8.0.tgz",
|
||||||
|
"integrity": "sha1-pY0N8ELBhvz4IqjoAV9UUNLXmos=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"convert-source-map": "1.1.3",
|
||||||
|
"inline-source-map": "0.6.2",
|
||||||
|
"lodash.memoize": "3.0.4",
|
||||||
|
"source-map": "0.5.7"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"convert-source-map": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz",
|
||||||
|
"integrity": "sha1-SCnId+n+SbMWHzvzZziI4gRpmGA=",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"combined-stream": {
|
"combined-stream": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz",
|
||||||
@ -4257,6 +4493,18 @@
|
|||||||
"integrity": "sha1-+cmvVGSvoeepcUWKi97yqpTVuxk=",
|
"integrity": "sha1-+cmvVGSvoeepcUWKi97yqpTVuxk=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"deps-sort": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/deps-sort/-/deps-sort-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-CRckkC6EZYJg65EHSMzNGvbiH7U=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"JSONStream": "1.0.3",
|
||||||
|
"shasum": "1.0.2",
|
||||||
|
"subarg": "1.0.0",
|
||||||
|
"through2": "2.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"des.js": {
|
"des.js": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz",
|
||||||
@ -4320,9 +4568,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"diff": {
|
"diff": {
|
||||||
"version": "1.4.0",
|
"version": "3.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/diff/-/diff-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
|
||||||
"integrity": "sha1-fyjS657nsVqX79ic5j3P2qPMur8=",
|
"integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"diffie-hellman": {
|
"diffie-hellman": {
|
||||||
@ -8311,6 +8559,12 @@
|
|||||||
"integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=",
|
"integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"htmlescape": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz",
|
||||||
|
"integrity": "sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"htmlparser2": {
|
"htmlparser2": {
|
||||||
"version": "3.8.3",
|
"version": "3.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz",
|
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz",
|
||||||
@ -8541,6 +8795,15 @@
|
|||||||
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz",
|
||||||
"integrity": "sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4="
|
"integrity": "sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4="
|
||||||
},
|
},
|
||||||
|
"inline-source-map": {
|
||||||
|
"version": "0.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.2.tgz",
|
||||||
|
"integrity": "sha1-+Tk0ccGKedFyT4Y/o4tYY3Ct4qU=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"source-map": "0.5.7"
|
||||||
|
}
|
||||||
|
},
|
||||||
"inline-style-prefixer": {
|
"inline-style-prefixer": {
|
||||||
"version": "2.0.5",
|
"version": "2.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-2.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-2.0.5.tgz",
|
||||||
@ -8570,6 +8833,79 @@
|
|||||||
"through": "2.3.8"
|
"through": "2.3.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"insert-module-globals": {
|
||||||
|
"version": "7.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.0.2.tgz",
|
||||||
|
"integrity": "sha512-p3s7g96Nm62MbHRuj9ZXab0DuJNWD7qcmdUXCOQ/ZZn42DtDXfsLill7bq19lDCx3K3StypqUnuE3H2VmIJFUw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"JSONStream": "1.0.3",
|
||||||
|
"combine-source-map": "0.7.2",
|
||||||
|
"concat-stream": "1.5.2",
|
||||||
|
"is-buffer": "1.1.6",
|
||||||
|
"lexical-scope": "1.2.0",
|
||||||
|
"process": "0.11.10",
|
||||||
|
"through2": "2.0.3",
|
||||||
|
"xtend": "4.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"combine-source-map": {
|
||||||
|
"version": "0.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.7.2.tgz",
|
||||||
|
"integrity": "sha1-CHAxKFazB6h8xKxIbzqaYq7MwJ4=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"convert-source-map": "1.1.3",
|
||||||
|
"inline-source-map": "0.6.2",
|
||||||
|
"lodash.memoize": "3.0.4",
|
||||||
|
"source-map": "0.5.7"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"concat-stream": {
|
||||||
|
"version": "1.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.2.tgz",
|
||||||
|
"integrity": "sha1-cIl4Yk2FavQaWnQd790mHadSwmY=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"inherits": "2.0.3",
|
||||||
|
"readable-stream": "2.0.6",
|
||||||
|
"typedarray": "0.0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"convert-source-map": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz",
|
||||||
|
"integrity": "sha1-SCnId+n+SbMWHzvzZziI4gRpmGA=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"process": {
|
||||||
|
"version": "0.11.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|
||||||
|
"integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"readable-stream": {
|
||||||
|
"version": "2.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz",
|
||||||
|
"integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"core-util-is": "1.0.2",
|
||||||
|
"inherits": "2.0.3",
|
||||||
|
"isarray": "1.0.0",
|
||||||
|
"process-nextick-args": "1.0.7",
|
||||||
|
"string_decoder": "0.10.31",
|
||||||
|
"util-deprecate": "1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"string_decoder": {
|
||||||
|
"version": "0.10.31",
|
||||||
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
|
||||||
|
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"interpret": {
|
"interpret": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/interpret/-/interpret-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/interpret/-/interpret-1.0.4.tgz",
|
||||||
@ -9550,6 +9886,25 @@
|
|||||||
"graceful-fs": "4.1.11"
|
"graceful-fs": "4.1.11"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"labeled-stream-splicer": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-pS4dE4AkwAuGscDJH2d5GLiuClk=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"inherits": "2.0.3",
|
||||||
|
"isarray": "0.0.1",
|
||||||
|
"stream-splicer": "2.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"isarray": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
|
||||||
|
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"latest-version": {
|
"latest-version": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/latest-version/-/latest-version-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/latest-version/-/latest-version-2.0.0.tgz",
|
||||||
@ -9788,6 +10143,15 @@
|
|||||||
"type-check": "0.3.2"
|
"type-check": "0.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"lexical-scope": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lexical-scope/-/lexical-scope-1.2.0.tgz",
|
||||||
|
"integrity": "sha1-/Ope3HBKSzqHls3KQZw6CvryLfQ=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"astw": "2.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"libbase64": {
|
"libbase64": {
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/libbase64/-/libbase64-0.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/libbase64/-/libbase64-0.1.0.tgz",
|
||||||
@ -10561,6 +10925,12 @@
|
|||||||
"integrity": "sha1-dx7Hg540c9nEzeKLGTlMNWL09tM=",
|
"integrity": "sha1-dx7Hg540c9nEzeKLGTlMNWL09tM=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"lodash.memoize": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz",
|
||||||
|
"integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"lodash.merge": {
|
"lodash.merge": {
|
||||||
"version": "4.6.0",
|
"version": "4.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.0.tgz",
|
||||||
@ -11705,6 +12075,12 @@
|
|||||||
"ms": "0.7.1"
|
"ms": "0.7.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"diff": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/diff/-/diff-1.4.0.tgz",
|
||||||
|
"integrity": "sha1-fyjS657nsVqX79ic5j3P2qPMur8=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"escape-string-regexp": {
|
"escape-string-regexp": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz",
|
||||||
@ -11781,6 +12157,48 @@
|
|||||||
"integrity": "sha1-mi3sg4Bvuy2XXyK+7IWcoms5OqE=",
|
"integrity": "sha1-mi3sg4Bvuy2XXyK+7IWcoms5OqE=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"module-deps": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/module-deps/-/module-deps-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-BKsMhJJENEM4dTgqq2MDTTHXRHcNUFegoAwlG4HO4VMdUyMcJDKgfgI+MOv6tR5Iv8G3MKZFgsSiyP3ZoosRMw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"JSONStream": "1.0.3",
|
||||||
|
"browser-resolve": "1.11.2",
|
||||||
|
"cached-path-relative": "1.0.1",
|
||||||
|
"concat-stream": "1.6.0",
|
||||||
|
"defined": "1.0.0",
|
||||||
|
"detective": "5.1.0",
|
||||||
|
"duplexer2": "0.1.4",
|
||||||
|
"inherits": "2.0.3",
|
||||||
|
"parents": "1.0.1",
|
||||||
|
"readable-stream": "2.2.7",
|
||||||
|
"resolve": "1.5.0",
|
||||||
|
"stream-combiner2": "1.1.1",
|
||||||
|
"subarg": "1.0.0",
|
||||||
|
"through2": "2.0.3",
|
||||||
|
"xtend": "4.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"detective": {
|
||||||
|
"version": "5.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/detective/-/detective-5.1.0.tgz",
|
||||||
|
"integrity": "sha512-TFHMqfOvxlgrfVzTEkNBSh9SvSNX/HfF4OFI2QFGCyPm02EsyILqnUeb5P6q7JZ3SFNTBL5t2sePRgrN4epUWQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"acorn-node": "1.3.0",
|
||||||
|
"defined": "1.0.0",
|
||||||
|
"minimist": "1.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minimist": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||||
|
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"module-details-from-path": {
|
"module-details-from-path": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz",
|
||||||
@ -13287,6 +13705,15 @@
|
|||||||
"readable-stream": "2.2.7"
|
"readable-stream": "2.2.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"parents": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-/t1NK/GTp3dF/nHjcdc8MwfZx1E=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"path-platform": "0.11.15"
|
||||||
|
}
|
||||||
|
},
|
||||||
"parse-asn1": {
|
"parse-asn1": {
|
||||||
"version": "5.1.0",
|
"version": "5.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.0.tgz",
|
||||||
@ -13534,6 +13961,12 @@
|
|||||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz",
|
||||||
"integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME="
|
"integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME="
|
||||||
},
|
},
|
||||||
|
"path-platform": {
|
||||||
|
"version": "0.11.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz",
|
||||||
|
"integrity": "sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"path-root": {
|
"path-root": {
|
||||||
"version": "0.1.1",
|
"version": "0.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz",
|
||||||
@ -14383,6 +14816,15 @@
|
|||||||
"readable-stream": "2.2.7"
|
"readable-stream": "2.2.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"read-only-stream": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-JyT9aoET1zdkrCiNQ4YnDB2/F/A=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"readable-stream": "2.2.7"
|
||||||
|
}
|
||||||
|
},
|
||||||
"read-pkg": {
|
"read-pkg": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
|
||||||
@ -15486,6 +15928,27 @@
|
|||||||
"resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.0.2.tgz",
|
||||||
"integrity": "sha512-zlVXeVUKvo+HEv1e2KQF/csyeMKx2oHvatQ9l6XjCUj3agvC8XGf6R9HvIPDSmp8FNPvx7b5kaEJTRi7CqxtEw=="
|
"integrity": "sha512-zlVXeVUKvo+HEv1e2KQF/csyeMKx2oHvatQ9l6XjCUj3agvC8XGf6R9HvIPDSmp8FNPvx7b5kaEJTRi7CqxtEw=="
|
||||||
},
|
},
|
||||||
|
"shasum": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/shasum/-/shasum-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-5wEjENj0F/TetXEhUOVni4euVl8=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"json-stable-stringify": "0.0.1",
|
||||||
|
"sha.js": "2.4.9"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"json-stable-stringify": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz",
|
||||||
|
"integrity": "sha1-YRwj6BTbN1Un34URk9tZ3Sryf0U=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"jsonify": "0.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"shebang-command": {
|
"shebang-command": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
|
||||||
@ -15499,6 +15962,18 @@
|
|||||||
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
|
||||||
"integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM="
|
"integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM="
|
||||||
},
|
},
|
||||||
|
"shell-quote": {
|
||||||
|
"version": "1.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.6.1.tgz",
|
||||||
|
"integrity": "sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"array-filter": "0.0.1",
|
||||||
|
"array-map": "0.0.0",
|
||||||
|
"array-reduce": "0.0.0",
|
||||||
|
"jsonify": "0.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"shelljs": {
|
"shelljs": {
|
||||||
"version": "0.3.0",
|
"version": "0.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz",
|
||||||
@ -16458,6 +16933,16 @@
|
|||||||
"duplexer": "0.0.4"
|
"duplexer": "0.0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"stream-combiner2": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz",
|
||||||
|
"integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"duplexer2": "0.1.4",
|
||||||
|
"readable-stream": "2.2.7"
|
||||||
|
}
|
||||||
|
},
|
||||||
"stream-consume": {
|
"stream-consume": {
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/stream-consume/-/stream-consume-0.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/stream-consume/-/stream-consume-0.1.0.tgz",
|
||||||
@ -16497,6 +16982,16 @@
|
|||||||
"resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz",
|
||||||
"integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI="
|
"integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI="
|
||||||
},
|
},
|
||||||
|
"stream-splicer": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/stream-splicer/-/stream-splicer-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-G2O+Q4oTPktnHMGTUZdgAXWRDYM=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"inherits": "2.0.3",
|
||||||
|
"readable-stream": "2.2.7"
|
||||||
|
}
|
||||||
|
},
|
||||||
"stream-throttle": {
|
"stream-throttle": {
|
||||||
"version": "0.1.3",
|
"version": "0.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/stream-throttle/-/stream-throttle-0.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/stream-throttle/-/stream-throttle-0.1.3.tgz",
|
||||||
@ -16849,6 +17344,23 @@
|
|||||||
"integrity": "sha1-ISqQDfq1rgTmKOIbWABjK9DZ40c=",
|
"integrity": "sha1-ISqQDfq1rgTmKOIbWABjK9DZ40c=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"subarg": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-9izxdYHplrSPyWVpn1TAauJouNI=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"minimist": "1.2.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"minimist": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||||
|
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"sum-up": {
|
"sum-up": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/sum-up/-/sum-up-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/sum-up/-/sum-up-1.0.3.tgz",
|
||||||
@ -16898,6 +17410,15 @@
|
|||||||
"integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=",
|
"integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"syntax-error": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"acorn-node": "1.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"table": {
|
"table": {
|
||||||
"version": "4.0.2",
|
"version": "4.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz",
|
||||||
@ -17781,6 +18302,12 @@
|
|||||||
"integrity": "sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po=",
|
"integrity": "sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"umd": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/umd/-/umd-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-4IcGSufhFshvLNcMCV80UnQVlZ5pMOC8mvNPForqwA4+lzYQuetTESLDQkeLmihq8bRcnpbQa48Wb8Lh16/xow==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"unc-path-regex": {
|
"unc-path-regex": {
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz",
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
"prelint-js": "npm run create-rev",
|
"prelint-js": "npm run create-rev",
|
||||||
"pretest": "npm run create-rev && npm run lint",
|
"pretest": "npm run create-rev && npm run lint",
|
||||||
"prestart-production": "gulp build -p",
|
"prestart-production": "gulp build -p",
|
||||||
|
"seed": "node seed",
|
||||||
"snyk-protect": "snyk protect",
|
"snyk-protect": "snyk protect",
|
||||||
"start": "babel-node server/server.js",
|
"start": "babel-node server/server.js",
|
||||||
"start-production": "node pm2Start",
|
"start-production": "node pm2Start",
|
||||||
@ -31,7 +32,9 @@
|
|||||||
"test-js": "npm run test-js-client && npm run test-js-common && npm run test-js-server",
|
"test-js": "npm run test-js-client && npm run test-js-common && npm run test-js-server",
|
||||||
"test-js-client": "tape -r babel-register \"client/**/*.test.js\" | tap-spec",
|
"test-js-client": "tape -r babel-register \"client/**/*.test.js\" | tap-spec",
|
||||||
"test-js-common": "tape -r babel-register \"common/**/*.test.js\" | tap-spec",
|
"test-js-common": "tape -r babel-register \"common/**/*.test.js\" | tap-spec",
|
||||||
"test-js-server": "tape -r babel-register \"server/**/*.test.js\" | tap-spec"
|
"test-js-server": "tape -r babel-register \"server/**/*.test.js\" | tap-spec",
|
||||||
|
"unpack": "babel-node ./seed/unpack.js",
|
||||||
|
"repack": "babel-node ./seed/repack.js"
|
||||||
},
|
},
|
||||||
"license": "(BSD-3-Clause AND CC-BY-SA-4.0)",
|
"license": "(BSD-3-Clause AND CC-BY-SA-4.0)",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -156,10 +159,12 @@
|
|||||||
"babel-preset-stage-0": "^6.3.13",
|
"babel-preset-stage-0": "^6.3.13",
|
||||||
"babel-preset-stage-3": "^6.24.1",
|
"babel-preset-stage-3": "^6.24.1",
|
||||||
"browser-sync": "^2.9.12",
|
"browser-sync": "^2.9.12",
|
||||||
|
"browserify": "^16.1.1",
|
||||||
"chunk-manifest-webpack-plugin": "^1.1.2",
|
"chunk-manifest-webpack-plugin": "^1.1.2",
|
||||||
"commitizen": "^2.9.6",
|
"commitizen": "^2.9.6",
|
||||||
"cz-freecodecamp": "^1.0.1",
|
"cz-freecodecamp": "^1.0.1",
|
||||||
"del": "^2.2.0",
|
"del": "^2.2.0",
|
||||||
|
"diff": "^3.5.0",
|
||||||
"eslint": "^4.10.0",
|
"eslint": "^4.10.0",
|
||||||
"eslint-config-freecodecamp": "^1.1.1",
|
"eslint-config-freecodecamp": "^1.1.1",
|
||||||
"gulp": "^3.9.1",
|
"gulp": "^3.9.1",
|
||||||
|
@ -10,8 +10,17 @@ For each challenge section, there is a JSON file (fields documented below) conta
|
|||||||
|---|---|
|
|---|---|
|
||||||
| `npm run test-challenges` | run all challenge tests (for each challenge JSON file, run all `tests` against all `solutions`) |
|
| `npm run test-challenges` | run all challenge tests (for each challenge JSON file, run all `tests` against all `solutions`) |
|
||||||
| `npm run test` | run all JS tests in the system, including client, server, lint and challenge tests |
|
| `npm run test` | run all JS tests in the system, including client, server, lint and challenge tests |
|
||||||
| `node seed` | parses all the challenge JSON files and saves them into MongoDB (code is inside [index.js](index.js)) |
|
| `npm run seed` <br> (<small>or</small> `node seed`) | parses all the challenge JSON files and saves them into MongoDB (code is inside [index.js](index.js)) |
|
||||||
| `npm run commit` | interactive tool to help you build a good commit message |
|
| `npm run commit` | interactive tool to help you build a good commit message |
|
||||||
|
| `npm run unpack` | extract challenges from `seed/challenges` into `unpacked` subdirectory, one HTML page per challenge |
|
||||||
|
| `npm run repack` | repack challenges from `unpacked` subdirectory into `seed/challenges` |
|
||||||
|
|
||||||
|
### unpack and repack
|
||||||
|
|
||||||
|
`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
|
||||||
|
|
||||||
|
|
||||||
## Links
|
## Links
|
||||||
|
|
||||||
@ -24,7 +33,7 @@ For each challenge section, there is a JSON file (fields documented below) conta
|
|||||||
|
|
||||||
## Challenge Template
|
## Challenge Template
|
||||||
|
|
||||||
```json
|
```
|
||||||
{
|
{
|
||||||
"id": "unique identifier (alphanumerical, mongodb id)",
|
"id": "unique identifier (alphanumerical, mongodb id)",
|
||||||
"title": "Challenge Title",
|
"title": "Challenge Title",
|
||||||
|
52
seed/addAssertsToTapTest.js
Normal file
52
seed/addAssertsToTapTest.js
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
let _ = require('lodash');
|
||||||
|
|
||||||
|
function createIsAssert(tapTest, isThing) {
|
||||||
|
const { assert } = tapTest;
|
||||||
|
return function() {
|
||||||
|
const args = [...arguments];
|
||||||
|
args[0] = isThing(args[0]);
|
||||||
|
assert.apply(tapTest, args);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function addAssertsToTapTest(tapTest) {
|
||||||
|
const assert = tapTest.assert;
|
||||||
|
|
||||||
|
assert.isArray = createIsAssert(tapTest, _.isArray);
|
||||||
|
assert.isBoolean = createIsAssert(tapTest, _.isBoolean);
|
||||||
|
assert.isString = createIsAssert(tapTest, _.isString);
|
||||||
|
assert.isNumber = createIsAssert(tapTest, _.isNumber);
|
||||||
|
assert.isUndefined = createIsAssert(tapTest, _.isUndefined);
|
||||||
|
|
||||||
|
assert.deepEqual = tapTest.deepEqual;
|
||||||
|
assert.equal = tapTest.equal;
|
||||||
|
assert.strictEqual = tapTest.equal;
|
||||||
|
assert.sameMembers = function sameMembers() {
|
||||||
|
const [ first, second, ...args] = arguments;
|
||||||
|
assert.apply(
|
||||||
|
tapTest,
|
||||||
|
[
|
||||||
|
_.difference(first, second).length === 0 &&
|
||||||
|
_.difference(second, first).length === 0
|
||||||
|
].concat(args)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
assert.includeMembers = function includeMembers() {
|
||||||
|
const [ first, second, ...args] = arguments;
|
||||||
|
assert.apply(tapTest,
|
||||||
|
[
|
||||||
|
_.difference(second, first).length === 0
|
||||||
|
].concat(args));
|
||||||
|
};
|
||||||
|
assert.match = function match() {
|
||||||
|
const [value, regex, ...args] = arguments;
|
||||||
|
assert.apply(tapTest,
|
||||||
|
[
|
||||||
|
regex.test(value)
|
||||||
|
].concat(args));
|
||||||
|
};
|
||||||
|
|
||||||
|
return assert;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = addAssertsToTapTest;
|
@ -4,64 +4,54 @@ const fs = require('fs');
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
const hiddenFile = /(^(\.|\/\.))|(.md$)/g;
|
const hiddenFile = /(^(\.|\/\.))|(.md$)/g;
|
||||||
|
|
||||||
function getFilesFor(dir) {
|
function getFilesFor(dir) {
|
||||||
return fs.readdirSync(path.join(__dirname, '/' + dir))
|
let targetDir = path.join(__dirname, dir);
|
||||||
|
return fs.readdirSync(targetDir)
|
||||||
.filter(file => !hiddenFile.test(file))
|
.filter(file => !hiddenFile.test(file))
|
||||||
.map(function(file) {
|
.map(function(file) {
|
||||||
let superBlock;
|
let superBlock;
|
||||||
if (fs.statSync(path.join(__dirname, dir + '/' + file)).isFile()) {
|
if (fs.statSync(path.join(targetDir, file)).isFile()) {
|
||||||
return { file: file };
|
return {file: file};
|
||||||
}
|
}
|
||||||
superBlock = file;
|
superBlock = file;
|
||||||
return getFilesFor(dir + '/' + superBlock)
|
return getFilesFor(path.join(dir, superBlock))
|
||||||
.map(function(data) {
|
.map(function(data) {
|
||||||
return {
|
return {
|
||||||
file: superBlock + '/' + data.file,
|
file: path.join(superBlock, data.file),
|
||||||
superBlock: superBlock
|
superBlock: superBlock
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.reduce(function(files, file) {
|
.reduce(function(files, entry) {
|
||||||
if (!Array.isArray(file)) {
|
return files.concat(entry);
|
||||||
files.push(file);
|
|
||||||
return files;
|
|
||||||
}
|
|
||||||
return files.concat(file);
|
|
||||||
}, []);
|
}, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSupOrder(filePath) {
|
function superblockInfo(filePath) {
|
||||||
const order = parseInt((filePath || '').split('-')[0], 10);
|
let parts = (filePath || '').split('-');
|
||||||
// check for NaN
|
let order = parseInt(parts[0], 10);
|
||||||
if (order !== order) {
|
if (isNaN(order)) {
|
||||||
return 0;
|
return {order: 0, name: filePath};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
order: order,
|
||||||
|
name: parts.splice(1).join('-')
|
||||||
|
};
|
||||||
}
|
}
|
||||||
return order;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSupName(filePath) {
|
module.exports = function getChallenges(challengesDir) {
|
||||||
const order = parseInt((filePath || '').split('-')[0], 10);
|
if (!challengesDir) {
|
||||||
// check for NaN
|
challengesDir = 'challenges';
|
||||||
if (order !== order) {
|
|
||||||
return filePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (filePath || '').split('-').splice(1).join('-');
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = function getChallenges() {
|
|
||||||
try {
|
|
||||||
return getFilesFor('challenges')
|
|
||||||
.map(function(data) {
|
|
||||||
const challengeSpec = require('./challenges/' + data.file);
|
|
||||||
challengeSpec.fileName = data.file;
|
|
||||||
challengeSpec.superBlock = getSupName(data.superBlock);
|
|
||||||
challengeSpec.superOrder = getSupOrder(data.superBlock);
|
|
||||||
|
|
||||||
return challengeSpec;
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
console.error('error: ', e);
|
|
||||||
return [];
|
|
||||||
}
|
}
|
||||||
|
return getFilesFor(challengesDir)
|
||||||
|
.map(function(data) {
|
||||||
|
const challengeSpec = require('./' + challengesDir + '/' + data.file);
|
||||||
|
let superInfo = superblockInfo(data.superBlock);
|
||||||
|
challengeSpec.fileName = data.file;
|
||||||
|
challengeSpec.superBlock = superInfo.name;
|
||||||
|
challengeSpec.superOrder = superInfo.order;
|
||||||
|
return challengeSpec;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
24
seed/mongoIds.js
Normal file
24
seed/mongoIds.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import _ from 'lodash';
|
||||||
|
import { isMongoId } from 'validator';
|
||||||
|
|
||||||
|
class MongoIds {
|
||||||
|
constructor() {
|
||||||
|
this.knownIds = [];
|
||||||
|
}
|
||||||
|
check(id, title) {
|
||||||
|
if (!isMongoId(id)) {
|
||||||
|
throw new Error(`Expected a valid ObjectId for ${title}, but got ${id}`);
|
||||||
|
}
|
||||||
|
const idIndex = _.findIndex(this.knownIds, existing => id === existing);
|
||||||
|
if (idIndex !== -1) {
|
||||||
|
throw new Error(`
|
||||||
|
All challenges must have a unique id.
|
||||||
|
|
||||||
|
The id for ${title} is already assigned
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
this.knownIds = [ ...this.knownIds, id ];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MongoIds;
|
@ -52,16 +52,16 @@ function createNewTranslations(challenge) {
|
|||||||
newTranslation = {};
|
newTranslation = {};
|
||||||
newTranslation[matches[1]] = challenge[oldKey];
|
newTranslation[matches[1]] = challenge[oldKey];
|
||||||
translations[tag] = translations[tag] ?
|
translations[tag] = translations[tag] ?
|
||||||
Object.assign({}, translations[tag], newTranslation) :
|
({...translations[tag], ...newTranslation}) :
|
||||||
Object.assign({}, newTranslation);
|
({...newTranslation});
|
||||||
return translations;
|
return translations;
|
||||||
}
|
}
|
||||||
matches = oldKey.match(oldNameRegex);
|
matches = oldKey.match(oldNameRegex);
|
||||||
tag = normalizeLangTag(matches[1]);
|
tag = normalizeLangTag(matches[1]);
|
||||||
newTranslation = { title: challenge[oldKey] };
|
newTranslation = { title: challenge[oldKey] };
|
||||||
translations[tag] = translations[tag] ?
|
translations[tag] = translations[tag] ?
|
||||||
Object.assign({}, translations[tag], newTranslation) :
|
({...translations[tag], ...newTranslation}) :
|
||||||
Object.assign({}, newTranslation);
|
({...newTranslation});
|
||||||
return translations;
|
return translations;
|
||||||
}, {});
|
}, {});
|
||||||
}
|
}
|
||||||
@ -71,11 +71,10 @@ function normalizeChallenge(challenge) {
|
|||||||
challenge.translations = challenge.translations || {};
|
challenge.translations = challenge.translations || {};
|
||||||
var hasOldTranslations = keys.some(hasOldTranslation);
|
var hasOldTranslations = keys.some(hasOldTranslation);
|
||||||
if (hasOldTranslations) {
|
if (hasOldTranslations) {
|
||||||
challenge.translations = Object.assign(
|
challenge.translations = ({
|
||||||
{},
|
...challenge.translations,
|
||||||
challenge.translations,
|
...createNewTranslations(challenge)
|
||||||
createNewTranslations(challenge)
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
challenge.translations = sortTranslationsKeys(challenge.translations);
|
challenge.translations = sortTranslationsKeys(challenge.translations);
|
||||||
// remove old translations from the top level
|
// remove old translations from the top level
|
||||||
|
76
seed/repack.js
Normal file
76
seed/repack.js
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
/* eslint-disable no-eval, no-process-exit */
|
||||||
|
import fs from 'fs-extra';
|
||||||
|
import path from 'path';
|
||||||
|
import {UnpackedChallenge, ChallengeFile} from './unpackedChallenge';
|
||||||
|
|
||||||
|
const jsdiff = require('diff');
|
||||||
|
|
||||||
|
// Repack all challenges from all
|
||||||
|
// seed/unpacked/00-foo/bar/000-id.html files
|
||||||
|
// into
|
||||||
|
// seed/challenges/00-foo/bar.json files
|
||||||
|
|
||||||
|
let unpackedRoot = path.join(__dirname, 'unpacked');
|
||||||
|
let seedChallengesRoot = path.join(__dirname, 'challenges');
|
||||||
|
|
||||||
|
function directoriesIn(parentDir) {
|
||||||
|
return fs.readdirSync(parentDir)
|
||||||
|
.filter(entry => fs.statSync(path.join(parentDir, entry)).isDirectory());
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
let blocks = directoriesIn(superBlockPath);
|
||||||
|
blocks.forEach(blockName => {
|
||||||
|
let blockPath = path.join(superBlockPath, blockName);
|
||||||
|
let blockFilePath = path.join(blockPath, blockName + '.json');
|
||||||
|
let block = require(blockFilePath);
|
||||||
|
let index = 0;
|
||||||
|
block.challenges.forEach(challengeJson => {
|
||||||
|
let unpackedChallenge =
|
||||||
|
new UnpackedChallenge(blockPath, challengeJson, index);
|
||||||
|
let unpackedFile = unpackedChallenge.challengeFile();
|
||||||
|
let chunks = unpackedFile.readChunks();
|
||||||
|
|
||||||
|
Object.assign(block.challenges[ index ], chunks);
|
||||||
|
|
||||||
|
index += 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
let outputFilePath =
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// let challenges = getChallenges();
|
||||||
|
// challenges.forEach(challengeBlock => {
|
||||||
|
// console.log()
|
||||||
|
// });
|
@ -1,76 +1,106 @@
|
|||||||
/* eslint-disable no-eval, no-process-exit */
|
/* eslint-disable no-eval, no-process-exit, no-unused-vars */
|
||||||
import _ from 'lodash';
|
|
||||||
import { Observable } from 'rx';
|
import {Observable} from 'rx';
|
||||||
import tape from 'tape';
|
import tape from 'tape';
|
||||||
import { isMongoId } from 'validator';
|
|
||||||
|
|
||||||
import getChallenges from './getChallenges';
|
import getChallenges from './getChallenges';
|
||||||
import { modern } from '../common/app/utils/challengeTypes';
|
import { modern } from '../common/app/utils/challengeTypes';
|
||||||
|
import MongoIds from './mongoIds';
|
||||||
|
import addAssertsToTapTest from './addAssertsToTapTest';
|
||||||
|
|
||||||
const notMongoId = id => !isMongoId(id);
|
let mongoIds = new MongoIds();
|
||||||
|
|
||||||
let existingIds = [];
|
function evaluateTest(solution, assert,
|
||||||
|
react, redux, reactRedux,
|
||||||
|
head, tail,
|
||||||
|
test, tapTest) {
|
||||||
|
|
||||||
|
let code = solution;
|
||||||
|
|
||||||
|
/* NOTE: Provide dependencies for React/Redux challenges
|
||||||
|
* and configure testing environment
|
||||||
|
*/
|
||||||
|
let React,
|
||||||
|
ReactDOM,
|
||||||
|
Redux,
|
||||||
|
ReduxThunk,
|
||||||
|
ReactRedux,
|
||||||
|
Enzyme,
|
||||||
|
document;
|
||||||
|
|
||||||
|
// Fake Deep Equal dependency
|
||||||
|
const DeepEqual = (a, b) =>
|
||||||
|
JSON.stringify(a) === JSON.stringify(b);
|
||||||
|
|
||||||
|
// Hardcode Deep Freeze dependency
|
||||||
|
const DeepFreeze = (o) => {
|
||||||
|
Object.freeze(o);
|
||||||
|
Object.getOwnPropertyNames(o).forEach(function(prop) {
|
||||||
|
if (o.hasOwnProperty(prop)
|
||||||
|
&& o[ prop ] !== null
|
||||||
|
&& (
|
||||||
|
typeof o[ prop ] === 'object' ||
|
||||||
|
typeof o[ prop ] === 'function'
|
||||||
|
)
|
||||||
|
&& !Object.isFrozen(o[ prop ])) {
|
||||||
|
DeepFreeze(o[ prop ]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return o;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (react || redux || reactRedux) {
|
||||||
|
// Provide dependencies, just provide all of them
|
||||||
|
React = require('react');
|
||||||
|
ReactDOM = require('react-dom');
|
||||||
|
Redux = require('redux');
|
||||||
|
ReduxThunk = require('redux-thunk');
|
||||||
|
ReactRedux = require('react-redux');
|
||||||
|
Enzyme = require('enzyme');
|
||||||
|
const Adapter15 = require('enzyme-adapter-react-15');
|
||||||
|
Enzyme.configure({ adapter: new Adapter15() });
|
||||||
|
|
||||||
|
/* Transpile ALL the code
|
||||||
|
* (we may use JSX in head or tail or tests, too): */
|
||||||
|
const transform = require('babel-standalone').transform;
|
||||||
|
const options = { presets: [ 'es2015', 'react' ] };
|
||||||
|
|
||||||
|
head = transform(head, options).code;
|
||||||
|
solution = transform(solution, options).code;
|
||||||
|
tail = transform(tail, options).code;
|
||||||
|
test = transform(test, options).code;
|
||||||
|
|
||||||
|
const { JSDOM } = require('jsdom');
|
||||||
|
// Mock DOM document for ReactDOM.render method
|
||||||
|
const jsdom = new JSDOM(`<!doctype html>
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<div id="challenge-node"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`);
|
||||||
|
const { window } = jsdom;
|
||||||
|
|
||||||
|
// Mock DOM for ReactDOM tests
|
||||||
|
document = window.document;
|
||||||
|
global.window = window;
|
||||||
|
global.document = window.document;
|
||||||
|
|
||||||
function validateObjectId(id, title) {
|
|
||||||
if (notMongoId(id)) {
|
|
||||||
throw new Error(`Expected a vaild ObjectId for ${title}, got ${id}`);
|
|
||||||
}
|
}
|
||||||
const idIndex = _.findIndex(existingIds, existing => id === existing);
|
|
||||||
if (idIndex !== -1) {
|
|
||||||
throw new Error(`
|
|
||||||
All challenges must have a unique id.
|
|
||||||
|
|
||||||
The id for ${title} is already assigned
|
/* eslint-enable no-unused-vars */
|
||||||
`);
|
try {
|
||||||
|
(() => {
|
||||||
|
return eval(
|
||||||
|
head + '\n;;' +
|
||||||
|
solution + '\n;;' +
|
||||||
|
tail + '\n;;' +
|
||||||
|
test
|
||||||
|
);
|
||||||
|
})();
|
||||||
|
} catch (e) {
|
||||||
|
tapTest.fail(e);
|
||||||
}
|
}
|
||||||
existingIds = [ ...existingIds, id ];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
function createIsAssert(t, isThing) {
|
|
||||||
const { assert } = t;
|
|
||||||
return function() {
|
|
||||||
const args = [...arguments];
|
|
||||||
args[0] = isThing(args[0]);
|
|
||||||
assert.apply(t, args);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function fillAssert(t) {
|
|
||||||
const assert = t.assert;
|
|
||||||
|
|
||||||
assert.isArray = createIsAssert(t, _.isArray);
|
|
||||||
assert.isBoolean = createIsAssert(t, _.isBoolean);
|
|
||||||
assert.isString = createIsAssert(t, _.isString);
|
|
||||||
assert.isNumber = createIsAssert(t, _.isNumber);
|
|
||||||
assert.isUndefined = createIsAssert(t, _.isUndefined);
|
|
||||||
|
|
||||||
assert.deepEqual = t.deepEqual;
|
|
||||||
assert.equal = t.equal;
|
|
||||||
assert.strictEqual = t.equal;
|
|
||||||
|
|
||||||
assert.sameMembers = function sameMembers() {
|
|
||||||
const [ first, second, ...args] = arguments;
|
|
||||||
assert.apply(
|
|
||||||
t,
|
|
||||||
[
|
|
||||||
_.difference(first, second).length === 0 &&
|
|
||||||
_.difference(second, first).length === 0
|
|
||||||
].concat(args)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
assert.includeMembers = function includeMembers() {
|
|
||||||
const [ first, second, ...args] = arguments;
|
|
||||||
assert.apply(t, [_.difference(second, first).length === 0].concat(args));
|
|
||||||
};
|
|
||||||
|
|
||||||
assert.match = function match() {
|
|
||||||
const [value, regex, ...args] = arguments;
|
|
||||||
assert.apply(t, [regex.test(value)].concat(args));
|
|
||||||
};
|
|
||||||
|
|
||||||
return assert;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function createTest({
|
function createTest({
|
||||||
@ -84,7 +114,8 @@ function createTest({
|
|||||||
redux = false,
|
redux = false,
|
||||||
reactRedux = false
|
reactRedux = false
|
||||||
}) {
|
}) {
|
||||||
validateObjectId(id, title);
|
mongoIds.check(id, title);
|
||||||
|
|
||||||
solutions = solutions.filter(solution => !!solution);
|
solutions = solutions.filter(solution => !!solution);
|
||||||
tests = tests.filter(test => !!test);
|
tests = tests.filter(test => !!test);
|
||||||
|
|
||||||
@ -92,7 +123,10 @@ function createTest({
|
|||||||
const isAsync = s => s.includes('(async () => ');
|
const isAsync = s => s.includes('(async () => ');
|
||||||
if (isAsync(tests.join(''))) {
|
if (isAsync(tests.join(''))) {
|
||||||
console.log(`Replacing Async Tests for Challenge ${title}`);
|
console.log(`Replacing Async Tests for Challenge ${title}`);
|
||||||
tests = tests.map(t => isAsync(t) ? "assert(true, 'message: great');" : t);
|
tests = tests.map(challengeTestSource =>
|
||||||
|
isAsync(challengeTestSource) ?
|
||||||
|
"assert(true, 'message: great');" :
|
||||||
|
challengeTestSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
head = head.join('\n');
|
head = head.join('\n');
|
||||||
@ -106,116 +140,26 @@ function createTest({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Observable.fromCallback(tape)(title)
|
return Observable.fromCallback(tape)(title)
|
||||||
.doOnNext(t => solutions.length ? t.plan(plan) : t.end())
|
.doOnNext(tapTest =>
|
||||||
.flatMap(t => {
|
solutions.length ? tapTest.plan(plan) : tapTest.end())
|
||||||
|
.flatMap(tapTest => {
|
||||||
if (solutions.length <= 0) {
|
if (solutions.length <= 0) {
|
||||||
t.comment('No solutions for ' + title);
|
tapTest.comment('No solutions for ' + title);
|
||||||
return Observable.just({
|
return Observable.just({
|
||||||
title,
|
title,
|
||||||
type: 'missing'
|
type: 'missing'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return Observable.just(t)
|
return Observable.just(tapTest)
|
||||||
.map(fillAssert)
|
.map(addAssertsToTapTest)
|
||||||
/* eslint-disable no-unused-vars */
|
/* eslint-disable no-unused-vars */
|
||||||
// assert and code used within the eval
|
// assert and code used within the eval
|
||||||
.doOnNext(assert => {
|
.doOnNext(assert => {
|
||||||
solutions.forEach(solution => {
|
solutions.forEach(solution => {
|
||||||
// Original code string
|
|
||||||
const originalCode = solution;
|
|
||||||
tests.forEach(test => {
|
tests.forEach(test => {
|
||||||
let code = solution;
|
evaluateTest(solution, assert, react, redux, reactRedux,
|
||||||
|
head, tail, test, tapTest);
|
||||||
/* NOTE: Provide dependencies for React/Redux challenges
|
|
||||||
* and configure testing environment
|
|
||||||
*/
|
|
||||||
|
|
||||||
let React,
|
|
||||||
ReactDOM,
|
|
||||||
Redux,
|
|
||||||
ReduxThunk,
|
|
||||||
ReactRedux,
|
|
||||||
Enzyme,
|
|
||||||
document;
|
|
||||||
|
|
||||||
// Fake Deep Equal dependency
|
|
||||||
const DeepEqual = (a, b) =>
|
|
||||||
JSON.stringify(a) === JSON.stringify(b);
|
|
||||||
|
|
||||||
// Hardcode Deep Freeze dependency
|
|
||||||
const DeepFreeze = (o) => {
|
|
||||||
Object.freeze(o);
|
|
||||||
Object.getOwnPropertyNames(o).forEach(function(prop) {
|
|
||||||
if (o.hasOwnProperty(prop)
|
|
||||||
&& o[prop] !== null
|
|
||||||
&& (
|
|
||||||
typeof o[prop] === 'object' ||
|
|
||||||
typeof o[prop] === 'function'
|
|
||||||
)
|
|
||||||
&& !Object.isFrozen(o[prop])) {
|
|
||||||
DeepFreeze(o[prop]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return o;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (react || redux || reactRedux) {
|
|
||||||
// Provide dependencies, just provide all of them
|
|
||||||
React = require('react');
|
|
||||||
ReactDOM = require('react-dom');
|
|
||||||
Redux = require('redux');
|
|
||||||
ReduxThunk = require('redux-thunk');
|
|
||||||
ReactRedux = require('react-redux');
|
|
||||||
Enzyme = require('enzyme');
|
|
||||||
const Adapter15 = require('enzyme-adapter-react-15');
|
|
||||||
Enzyme.configure({ adapter: new Adapter15() });
|
|
||||||
|
|
||||||
/* Transpile ALL the code
|
|
||||||
* (we may use JSX in head or tail or tests, too): */
|
|
||||||
const transform = require('babel-standalone').transform;
|
|
||||||
const options = { presets: [ 'es2015', 'react' ] };
|
|
||||||
|
|
||||||
head = transform(head, options).code;
|
|
||||||
solution = transform(solution, options).code;
|
|
||||||
tail = transform(tail, options).code;
|
|
||||||
test = transform(test, options).code;
|
|
||||||
|
|
||||||
const { JSDOM } = require('jsdom');
|
|
||||||
// Mock DOM document for ReactDOM.render method
|
|
||||||
const jsdom = new JSDOM(`<!doctype html>
|
|
||||||
<html>
|
|
||||||
<body>
|
|
||||||
<div id="challenge-node"></div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
`);
|
|
||||||
const { window } = jsdom;
|
|
||||||
|
|
||||||
// Mock DOM for ReactDOM tests
|
|
||||||
document = window.document;
|
|
||||||
global.window = window;
|
|
||||||
global.document = window.document;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const editor = {
|
|
||||||
getValue() { return code; },
|
|
||||||
getOriginalCode() { return originalCode; }
|
|
||||||
};
|
|
||||||
/* eslint-enable no-unused-vars */
|
|
||||||
try {
|
|
||||||
(() => {
|
|
||||||
return eval(
|
|
||||||
head + '\n;;' +
|
|
||||||
solution + '\n;;' +
|
|
||||||
tail + '\n;;' +
|
|
||||||
test
|
|
||||||
);
|
|
||||||
})();
|
|
||||||
} catch (e) {
|
|
||||||
t.fail(e);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
@ -244,10 +188,12 @@ Observable.from(getChallenges())
|
|||||||
if (noSolutions) {
|
if (noSolutions) {
|
||||||
console.log(
|
console.log(
|
||||||
'# These challenges have no solutions\n- [ ] ' +
|
'# These challenges have no solutions\n- [ ] ' +
|
||||||
noSolutions.join('\n- [ ] ')
|
noSolutions.join('\n- [ ] ')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
err => { throw err; },
|
err => {
|
||||||
|
throw err;
|
||||||
|
},
|
||||||
() => process.exit(0)
|
() => process.exit(0)
|
||||||
);
|
);
|
||||||
|
87
seed/unpack.js
Normal file
87
seed/unpack.js
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/* eslint-disable no-eval, no-process-exit */
|
||||||
|
import fs from 'fs-extra';
|
||||||
|
import path from 'path';
|
||||||
|
import browserify from 'browserify';
|
||||||
|
import getChallenges from './getChallenges';
|
||||||
|
import {UnpackedChallenge, ChallengeFile} from './unpackedChallenge';
|
||||||
|
|
||||||
|
// Unpack all challenges
|
||||||
|
// from all seed/challenges/00-foo/bar.json files
|
||||||
|
// into seed/unpacked/00-foo/bar/000-id.html files
|
||||||
|
//
|
||||||
|
// todo: unpack translations too
|
||||||
|
// todo: use common/app/routes/Challenges/utils/index.js:15 maps
|
||||||
|
// to determine format/style for non-JS tests
|
||||||
|
// todo: figure out embedded images etc. served from elsewhere in the project
|
||||||
|
// todo: prettier/clearer CSS
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
});
|
||||||
|
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;
|
||||||
|
|
||||||
|
function unpackChallengeBlock(challengeBlock) {
|
||||||
|
let challengeBlockPath = path.parse(challengeBlock.fileName);
|
||||||
|
let unpackedChallengeBlockDir = path.join(
|
||||||
|
__dirname,
|
||||||
|
'unpacked',
|
||||||
|
challengeBlockPath.dir,
|
||||||
|
challengeBlockPath.name
|
||||||
|
);
|
||||||
|
fs.mkdirp(unpackedChallengeBlockDir, (err) => {
|
||||||
|
if (err && err.code !== 'EEXIST') {
|
||||||
|
console.log(err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentlyUnpackingDir !== challengeBlockPath.dir) {
|
||||||
|
currentlyUnpackingDir = challengeBlockPath.dir;
|
||||||
|
console.log(`Unpacking into ${currentlyUnpackingDir}:`);
|
||||||
|
}
|
||||||
|
console.log(` ${challengeBlock.name}`);
|
||||||
|
|
||||||
|
// write a copy of the challenge block into unpacked dir
|
||||||
|
delete challengeBlock.fileName;
|
||||||
|
delete challengeBlock.superBlock;
|
||||||
|
delete challengeBlock.superOrder;
|
||||||
|
let challengeBlockCopy =
|
||||||
|
new ChallengeFile(
|
||||||
|
unpackedChallengeBlockDir,
|
||||||
|
challengeBlockPath.name,
|
||||||
|
'.json');
|
||||||
|
challengeBlockCopy.write(JSON.stringify(challengeBlock, null, 2));
|
||||||
|
|
||||||
|
// unpack each challenge into an HTML file
|
||||||
|
let index = 0;
|
||||||
|
challengeBlock.challenges.forEach(challenge => {
|
||||||
|
new UnpackedChallenge(
|
||||||
|
unpackedChallengeBlockDir,
|
||||||
|
challenge,
|
||||||
|
index
|
||||||
|
).unpack();
|
||||||
|
index += 1;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
createUnpackedBundle();
|
||||||
|
let challenges = getChallenges();
|
||||||
|
challenges.forEach(challengeBlock => {
|
||||||
|
unpackChallengeBlock(challengeBlock);
|
||||||
|
});
|
22
seed/unpacked.css
Normal file
22
seed/unpacked.css
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
body {
|
||||||
|
font-family: sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
script.unpacked, pre.unpacked {
|
||||||
|
display: block;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 14px;
|
||||||
|
white-space: pre;
|
||||||
|
border: 1px solid blue;
|
||||||
|
background: #EFEFEF;
|
||||||
|
padding: .5em 1em;
|
||||||
|
margin: 1em;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.unpacked {
|
||||||
|
border: 1px solid black;
|
||||||
|
padding: .5em 1em;
|
||||||
|
margin: 1em;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
18
seed/unpacked.js
Normal file
18
seed/unpacked.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/* eslint-disable no-unused-vars,max-len */
|
||||||
|
window._ = require('lodash');
|
||||||
|
window.test = require('tape').test;
|
||||||
|
|
||||||
|
// check for Browser TAP chrome extension, available here:
|
||||||
|
// https://chrome.google.com/webstore/detail/browser-tap/ncfblaiipckncgeipgmpdioedcdmofei?hl=en
|
||||||
|
if (window.tapExtension) {
|
||||||
|
window.test = window.tapExtension(window.test);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addAssertsToTapTest = require('./addAssertsToTapTest');
|
||||||
|
window.$ = require('jquery');
|
||||||
|
|
||||||
|
test('framework', function(t) {
|
||||||
|
t.plan(1);
|
||||||
|
t.equal(1, 1, 'one equals one');
|
||||||
|
});
|
||||||
|
|
258
seed/unpackedChallenge.js
Normal file
258
seed/unpackedChallenge.js
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
/* eslint-disable no-inline-comments */
|
||||||
|
import fs from 'fs-extra';
|
||||||
|
import path from 'path';
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
const jsonLinePrefix = '//--JSON:';
|
||||||
|
|
||||||
|
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;
|
||||||
|
lines.forEach(line => {
|
||||||
|
let chunkEnd = /(<!|\/\*)--end--/;
|
||||||
|
let chunkStart = /(<!|\/\*)--(\w+)--/;
|
||||||
|
|
||||||
|
line = line.toString();
|
||||||
|
|
||||||
|
if (chunkEnd.test(line)) {
|
||||||
|
if (!readingChunk) {
|
||||||
|
throw 'Encountered --end-- without being in a chunk';
|
||||||
|
}
|
||||||
|
readingChunk = null;
|
||||||
|
} else if (chunkStart.test(line)) {
|
||||||
|
let chunkName = line.match(chunkStart)[ 2 ];
|
||||||
|
if (readingChunk) {
|
||||||
|
throw `Encountered chunk ${chunkName} start `
|
||||||
|
+ `while already reading ${readingChunk}:
|
||||||
|
${line}`;
|
||||||
|
}
|
||||||
|
readingChunk = chunkName;
|
||||||
|
} else if (readingChunk) {
|
||||||
|
if (line.startsWith(jsonLinePrefix)) {
|
||||||
|
line = JSON.parse(line.slice(jsonLinePrefix.length));
|
||||||
|
}
|
||||||
|
if (!chunks[readingChunk]) {
|
||||||
|
chunks[readingChunk] = [];
|
||||||
|
}
|
||||||
|
// don't push empty top lines
|
||||||
|
if (!(!line && chunks[readingChunk].length === 0)) {
|
||||||
|
chunks[ readingChunk ].push(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// hack to deal with solutions field being an array of a single string
|
||||||
|
// instead of an array of lines like other fields
|
||||||
|
if (chunks.solutions) {
|
||||||
|
chunks.solutions = [chunks.solutions.join('\n')];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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}-${this.challenge.id}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
expandedDescription(description) {
|
||||||
|
let out = [];
|
||||||
|
description.forEach(part => {
|
||||||
|
if (_.isString(part)) {
|
||||||
|
out.push(part.toString());
|
||||||
|
} 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...
|
||||||
|
// 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));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// indent by 2
|
||||||
|
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('<html>');
|
||||||
|
text.push('<head>');
|
||||||
|
text.push('<link rel="stylesheet" href="../../../unpacked.css">');
|
||||||
|
text.push('<!-- shim to enable running the tests in-browser -->');
|
||||||
|
text.push('<script src="../../unpacked-bundle.js"></script>');
|
||||||
|
text.push('</head>');
|
||||||
|
text.push('<body>');
|
||||||
|
text.push(`<h1>${this.challenge.title}</h1>`);
|
||||||
|
text.push(`<p>This is the <b>unpacked</b> version of
|
||||||
|
<code>${this.superBlockName}/${this.challengeBlockName}</code>
|
||||||
|
(challenge id <code>${this.challenge.id}</code>).</p>`);
|
||||||
|
text.push('<p>Open the JavaScript console to see test results.</p>');
|
||||||
|
|
||||||
|
// text.push(`<p>Edit this HTML file (between <!--s only!)
|
||||||
|
// and run <code>npm repack ???</code>
|
||||||
|
// to incorporate your changes into the challenge database.</p>`);
|
||||||
|
|
||||||
|
text.push('');
|
||||||
|
text.push('<h2>Description</h2>');
|
||||||
|
text.push('<div class="unpacked description">');
|
||||||
|
text.push('<!--description-->');
|
||||||
|
text.push(this.expandedDescription(this.challenge.description).join('\n'));
|
||||||
|
text.push('<!--end-->');
|
||||||
|
text.push('</div>');
|
||||||
|
|
||||||
|
text.push('');
|
||||||
|
text.push('<h2>Seed</h2>');
|
||||||
|
text.push('<!--seed--><pre class="unpacked">');
|
||||||
|
if (this.challenge.seed) {
|
||||||
|
text.push(text, this.challenge.seed.join('\n'));
|
||||||
|
}
|
||||||
|
text.push('<!--end-->');
|
||||||
|
text.push('</pre>');
|
||||||
|
|
||||||
|
// Q: What is the difference between 'seed' and 'challengeSeed' ?
|
||||||
|
text.push('');
|
||||||
|
text.push('<h2>Challenge Seed</h2>');
|
||||||
|
text.push('<!--challengeSeed--><pre class="unpacked">');
|
||||||
|
if (this.challenge.challengeSeed) {
|
||||||
|
text.push(text, this.challenge.challengeSeed.join('\n'));
|
||||||
|
}
|
||||||
|
text.push('<!--end-->');
|
||||||
|
text.push('</pre>');
|
||||||
|
|
||||||
|
text.push('');
|
||||||
|
text.push('<h2>Head</h2>');
|
||||||
|
text.push('<!--head--><script class="unpacked head">');
|
||||||
|
if (this.challenge.head) {
|
||||||
|
text.push(text, this.challenge.head.join('\n'));
|
||||||
|
}
|
||||||
|
text.push('</script><!--end-->');
|
||||||
|
|
||||||
|
text.push('');
|
||||||
|
text.push('<h2>Solution</h2>');
|
||||||
|
text.push(
|
||||||
|
'<!--solutions--><script class="unpacked solution" id="solution">'
|
||||||
|
);
|
||||||
|
// 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];
|
||||||
|
text.push(solution);
|
||||||
|
}
|
||||||
|
text.push('</script><!--end-->');
|
||||||
|
|
||||||
|
text.push('');
|
||||||
|
text.push('<h2>Tail</h2>');
|
||||||
|
text.push('<!--tail--><script class="unpacked tail">');
|
||||||
|
if (this.challenge.tail) {
|
||||||
|
text.push(text, this.challenge.tail.join('\n'));
|
||||||
|
}
|
||||||
|
text.push('</script><!--end-->');
|
||||||
|
|
||||||
|
text.push('');
|
||||||
|
text.push('<h2>Tests</h2>');
|
||||||
|
text.push('<script class="unpacked tests">');
|
||||||
|
text.push(`test(\'${this.challenge.title} challenge tests\', ` +
|
||||||
|
'function(t) {');
|
||||||
|
text.push('let assert = addAssertsToTapTest(t);');
|
||||||
|
text.push('let code = document.getElementById(\'solution\').innerText;');
|
||||||
|
text.push('t.plan(' +
|
||||||
|
(this.challenge.tests ? this.challenge.tests.length : 0) +
|
||||||
|
');');
|
||||||
|
text.push('/*--tests--*/');
|
||||||
|
text.push(this.expandedTests(this.challenge.tests).join('\n'));
|
||||||
|
text.push('/*--end--*/');
|
||||||
|
text.push('});')
|
||||||
|
text.push('</script>');
|
||||||
|
|
||||||
|
text.push('');
|
||||||
|
text.push('</body>');
|
||||||
|
text.push('</html>');
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {UnpackedChallenge};
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user