From b8d88eb00c1bd93e96469e10d3de7df17dbbc17c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20GOUIN?= Date: Wed, 19 Jun 2019 13:44:02 +0200 Subject: [PATCH 001/120] Fixed install script (bis) --- .gitignore | 1 + README.md | 2 +- package-lock.json | 391 ++++++++---------- package.json | 5 +- src/config.js | 16 + .../default_config.json | 0 src/postinstall.js | 18 - src/server.js | 8 +- 8 files changed, 208 insertions(+), 233 deletions(-) create mode 100644 src/config.js rename config.example.json => src/default_config.json (100%) delete mode 100644 src/postinstall.js diff --git a/.gitignore b/.gitignore index d60caaf..8dd596e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /.idea /node_modules /config.json +/config.example.json /data \ No newline at end of file diff --git a/README.md b/README.md index 5d3d913..b36d91a 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ then edit the config.json file with your values : **3. Start your server** ```bash -npm start +npm run #or node src/server.js ``` diff --git a/package-lock.json b/package-lock.json index 5a5577b..790eafc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1352,12 +1352,14 @@ "arr-flatten": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true }, "arr-union": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true }, "array-equal": { "version": "1.0.0", @@ -1395,7 +1397,8 @@ "assign-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true }, "astral-regex": { "version": "1.0.0", @@ -1425,7 +1428,8 @@ "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true }, "aws-sign2": { "version": "0.7.0", @@ -2224,13 +2228,13 @@ "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "base": { "version": "0.11.2", "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, "requires": { "cache-base": "^1.0.1", "class-utils": "^0.3.5", @@ -2245,6 +2249,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, "requires": { "is-descriptor": "^1.0.0" } @@ -2253,6 +2258,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, "requires": { "kind-of": "^6.0.0" } @@ -2261,6 +2267,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, "requires": { "kind-of": "^6.0.0" } @@ -2269,6 +2276,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", "is-data-descriptor": "^1.0.0", @@ -2278,12 +2286,14 @@ "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true }, "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true } } }, @@ -2324,85 +2334,11 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, - "braces": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.1.tgz", - "integrity": "sha512-SO5lYHA3vO6gz66erVvedSCkp7AKWdv6VcQ2N4ysXfPxdAlxAMMAdwegGGcv1Bqwm7naF1hNdk5d6AAIEHV2nQ==", - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "kind-of": "^6.0.2", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" - }, - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" - } - } - }, "browser-process-hrtime": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz", @@ -2460,6 +2396,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, "requires": { "collection-visit": "^1.0.0", "component-emitter": "^1.2.1", @@ -2475,7 +2412,8 @@ "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true } } }, @@ -2569,6 +2507,7 @@ "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, "requires": { "arr-union": "^3.1.0", "define-property": "^0.2.5", @@ -2580,6 +2519,7 @@ "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, "requires": { "is-descriptor": "^0.1.0" } @@ -2587,7 +2527,8 @@ "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true } } }, @@ -2616,6 +2557,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, "requires": { "map-visit": "^1.0.0", "object-visit": "^1.0.0" @@ -2648,18 +2590,19 @@ "commander": { "version": "2.20.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", - "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==" + "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", + "dev": true }, "component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "content-disposition": { "version": "0.5.3", @@ -2702,7 +2645,8 @@ "copy-descriptor": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true }, "core-js": { "version": "2.6.9", @@ -2790,7 +2734,8 @@ "decode-uri-component": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true }, "deep-is": { "version": "0.1.3", @@ -2811,6 +2756,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, "requires": { "is-descriptor": "^1.0.2", "isobject": "^3.0.1" @@ -2820,6 +2766,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, "requires": { "kind-of": "^6.0.0" } @@ -2828,6 +2775,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, "requires": { "kind-of": "^6.0.0" } @@ -2836,6 +2784,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", "is-data-descriptor": "^1.0.0", @@ -2845,12 +2794,14 @@ "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true }, "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true } } }, @@ -2969,6 +2920,11 @@ "is-symbol": "^1.0.2" } }, + "es6-object-assign": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", + "integrity": "sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=" + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -3179,6 +3135,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, "requires": { "assign-symbols": "^1.0.0", "is-extendable": "^1.0.1" @@ -3188,6 +3145,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, "requires": { "is-plain-object": "^2.0.4" } @@ -3250,27 +3208,6 @@ "dev": true, "optional": true }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, "finalhandler": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", @@ -3296,7 +3233,8 @@ "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true }, "for-own": { "version": "0.1.5", @@ -3359,8 +3297,7 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "fsevents": { "version": "1.2.9", @@ -3929,7 +3866,8 @@ "get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true }, "getpass": { "version": "0.1.7", @@ -3944,7 +3882,6 @@ "version": "7.1.4", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", - "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -4071,6 +4008,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, "requires": { "get-value": "^2.0.6", "has-values": "^1.0.0", @@ -4080,7 +4018,8 @@ "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true } } }, @@ -4088,6 +4027,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, "requires": { "is-number": "^3.0.0", "kind-of": "^4.0.0" @@ -4097,6 +4037,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, "requires": { "kind-of": "^3.0.2" }, @@ -4105,6 +4046,7 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, "requires": { "is-buffer": "^1.1.5" } @@ -4115,6 +4057,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, "requires": { "is-buffer": "^1.1.5" } @@ -4197,7 +4140,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -4208,6 +4150,11 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, + "interpret": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", + "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==" + }, "invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", @@ -4231,6 +4178,7 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, "requires": { "kind-of": "^3.0.2" } @@ -4254,7 +4202,8 @@ "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true }, "is-callable": { "version": "1.1.4", @@ -4275,6 +4224,7 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, "requires": { "kind-of": "^3.0.2" } @@ -4289,6 +4239,7 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, "requires": { "is-accessor-descriptor": "^0.1.6", "is-data-descriptor": "^0.1.4", @@ -4298,7 +4249,8 @@ "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true } } }, @@ -4322,7 +4274,8 @@ "is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true }, "is-extglob": { "version": "1.0.0", @@ -4361,18 +4314,11 @@ "is-extglob": "^1.0.0" } }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "requires": { - "kind-of": "^3.0.2" - } - }, "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, "requires": { "isobject": "^3.0.1" }, @@ -4380,7 +4326,8 @@ "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true } } }, @@ -4442,18 +4389,14 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - }, "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -6739,6 +6682,7 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, "requires": { "is-buffer": "^1.1.5" } @@ -6869,12 +6813,14 @@ "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true }, "map-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, "requires": { "object-visit": "^1.0.0" } @@ -6981,7 +6927,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -6996,6 +6941,7 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "dev": true, "requires": { "for-in": "^1.0.2", "is-extendable": "^1.0.1" @@ -7005,6 +6951,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, "requires": { "is-plain-object": "^2.0.4" } @@ -7111,22 +7058,6 @@ "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", "dev": true }, - "node-nailgun-client": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/node-nailgun-client/-/node-nailgun-client-0.1.2.tgz", - "integrity": "sha512-OC611lR0fsDUSptwnhBf8d3sj4DZ5fiRKfS2QaGPe0kR3Dt9YoZr1MY7utK0scFPTbXuQdSBBbeoKYVbME1q5g==", - "requires": { - "commander": "^2.8.1" - } - }, - "node-nailgun-server": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/node-nailgun-server/-/node-nailgun-server-0.1.4.tgz", - "integrity": "sha512-e0Hbh6XPb/7GqATJ45BePaUEO5AwR7InRW/pGeMKHH1cqPMBFCeqdBNfvi+bkVLnsbYOOQE+pAek9nmNoD8sYw==", - "requires": { - "commander": "^2.8.1" - } - }, "node-notifier": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.0.tgz", @@ -7140,17 +7071,6 @@ "which": "^1.3.0" } }, - "node-plantuml": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/node-plantuml/-/node-plantuml-0.8.1.tgz", - "integrity": "sha512-sEI9j61MLunxkV0QTUyycanLhk9qP23iEkv4IBT+bcndR8qJ1hm9TCD21I0cK5XyBCXNQ3gywXKujHWDojwcQg==", - "requires": { - "commander": "^2.8.1", - "node-nailgun-client": "^0.1.0", - "node-nailgun-server": "^0.1.4", - "plantuml-encoder": "^1.2.5" - } - }, "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -7207,6 +7127,7 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, "requires": { "copy-descriptor": "^0.1.0", "define-property": "^0.2.5", @@ -7217,6 +7138,7 @@ "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, "requires": { "is-descriptor": "^0.1.0" } @@ -7233,6 +7155,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, "requires": { "isobject": "^3.0.0" }, @@ -7240,7 +7163,8 @@ "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true } } }, @@ -7294,7 +7218,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1" } @@ -7417,11 +7340,6 @@ "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" }, - "pako": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.3.tgz", - "integrity": "sha1-X1FbDGci4ZgpIK6ABerLC3ynPM8=" - }, "parse-glob": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", @@ -7459,7 +7377,8 @@ "pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true }, "path-exists": { "version": "3.0.0", @@ -7469,8 +7388,7 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-key": { "version": "2.0.1", @@ -7480,8 +7398,7 @@ "path-parse": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", - "dev": true + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" }, "path-to-regexp": { "version": "0.1.7", @@ -7572,15 +7489,6 @@ } } }, - "plantuml-encoder": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/plantuml-encoder/-/plantuml-encoder-1.2.5.tgz", - "integrity": "sha512-viV7Sz+BJNX/sC3iyebh2VfLyAZKuu3+JuBs2ISms8+zoTGwPqwk3/WEDw/zROmGAJ/xD4sNd8zsBw/YmTo7ng==", - "requires": { - "pako": "1.0.3", - "utf8-bytes": "0.0.1" - } - }, "pn": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", @@ -8152,6 +8060,14 @@ "util.promisify": "^1.0.0" } }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "requires": { + "resolve": "^1.1.6" + } + }, "regenerate": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", @@ -8189,6 +8105,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, "requires": { "extend-shallow": "^3.0.2", "safe-regex": "^1.1.0" @@ -8237,12 +8154,14 @@ "repeat-element": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==" + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true }, "repeat-string": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true }, "repeating": { "version": "2.0.1", @@ -8339,7 +8258,6 @@ "version": "1.11.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.0.tgz", "integrity": "sha512-WL2pBDjqT6pGUNSUzMw00o4T7If+z4H2x3Gz893WoUQ5KW8Vr9txp00ykiP16VBaZF5+j/OcXJHZ9+PCvdiDKw==", - "dev": true, "requires": { "path-parse": "^1.0.6" } @@ -8362,12 +8280,14 @@ "resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true }, "ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true }, "rimraf": { "version": "2.6.3", @@ -8393,6 +8313,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, "requires": { "ret": "~0.1.10" } @@ -8807,6 +8728,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "dev": true, "requires": { "extend-shallow": "^2.0.1", "is-extendable": "^0.1.1", @@ -8818,6 +8740,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, "requires": { "is-extendable": "^0.1.0" } @@ -8842,6 +8765,16 @@ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" }, + "shelljs": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.3.tgz", + "integrity": "sha512-fc0BKlAWiLpwZljmOvAOTE/gXawtCoNrP5oaY7KIaQbbyHeQVg01pSEuEGvGh3HEdBU4baCD7wQBwADmM/7f7A==", + "requires": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + } + }, "shellwords": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", @@ -8856,6 +8789,23 @@ "yargs": "^10.0.3" } }, + "shx": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/shx/-/shx-0.3.2.tgz", + "integrity": "sha512-aS0mWtW3T2sHAenrSrip2XGv39O9dXIFUqxAEWHEOS1ePtGIBavdPJY1kE2IHl14V/4iCbUiNDPGdyYTtmhSoA==", + "requires": { + "es6-object-assign": "^1.0.3", + "minimist": "^1.2.0", + "shelljs": "^0.8.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + } + } + }, "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", @@ -8877,6 +8827,7 @@ "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, "requires": { "base": "^0.11.1", "debug": "^2.2.0", @@ -8892,6 +8843,7 @@ "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, "requires": { "is-descriptor": "^0.1.0" } @@ -8900,6 +8852,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, "requires": { "is-extendable": "^0.1.0" } @@ -8910,6 +8863,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, "requires": { "define-property": "^1.0.0", "isobject": "^3.0.0", @@ -8920,6 +8874,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, "requires": { "is-descriptor": "^1.0.0" } @@ -8928,6 +8883,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, "requires": { "kind-of": "^6.0.0" } @@ -8936,6 +8892,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, "requires": { "kind-of": "^6.0.0" } @@ -8944,6 +8901,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", "is-data-descriptor": "^1.0.0", @@ -8953,12 +8911,14 @@ "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true }, "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true } } }, @@ -8966,6 +8926,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, "requires": { "kind-of": "^3.2.0" } @@ -8973,12 +8934,14 @@ "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true }, "source-map-resolve": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "dev": true, "requires": { "atob": "^2.1.1", "decode-uri-component": "^0.2.0", @@ -8999,7 +8962,8 @@ "source-map-url": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=" + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true }, "spdx-correct": { "version": "3.1.0", @@ -9037,6 +9001,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, "requires": { "extend-shallow": "^3.0.0" } @@ -9068,6 +9033,7 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, "requires": { "define-property": "^0.2.5", "object-copy": "^0.1.0" @@ -9077,6 +9043,7 @@ "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, "requires": { "is-descriptor": "^0.1.0" } @@ -9299,6 +9266,7 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, "requires": { "kind-of": "^3.0.2" } @@ -9307,6 +9275,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, "requires": { "define-property": "^2.0.2", "extend-shallow": "^3.0.2", @@ -9318,6 +9287,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, "requires": { "is-number": "^3.0.0", "repeat-string": "^1.6.1" @@ -9327,6 +9297,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, "requires": { "kind-of": "^3.0.2" } @@ -9420,6 +9391,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "dev": true, "requires": { "arr-union": "^3.1.0", "get-value": "^2.0.6", @@ -9431,6 +9403,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, "requires": { "is-extendable": "^0.1.0" } @@ -9439,6 +9412,7 @@ "version": "0.4.3", "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", + "dev": true, "requires": { "extend-shallow": "^2.0.1", "is-extendable": "^0.1.1", @@ -9457,6 +9431,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, "requires": { "has-value": "^0.3.1", "isobject": "^3.0.0" @@ -9466,6 +9441,7 @@ "version": "0.3.1", "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, "requires": { "get-value": "^2.0.3", "has-values": "^0.1.4", @@ -9476,6 +9452,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, "requires": { "isarray": "1.0.0" } @@ -9485,12 +9462,14 @@ "has-values": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true }, "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true } } }, @@ -9506,12 +9485,14 @@ "urix": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true }, "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true }, "user-home": { "version": "1.1.1", @@ -9519,11 +9500,6 @@ "integrity": "sha1-K1viOjK2Onyd640PKNSFcko98ZA=", "dev": true }, - "utf8-bytes": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/utf8-bytes/-/utf8-bytes-0.0.1.tgz", - "integrity": "sha1-EWsCVEjJtQAIHN+/H01sbDfYg30=" - }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -9700,8 +9676,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "write-file-atomic": { "version": "2.4.1", diff --git a/package.json b/package.json index 63e60c2..3e519c2 100644 --- a/package.json +++ b/package.json @@ -1,4 +1,5 @@ { + "nodePort": 5000, "name": "gitblog.md", "version": "1.0.0", "description": "A static blog using Markdown pulled from your git repository.", @@ -7,6 +8,7 @@ "express": "^4.17.1", "ncp": "^2.0.0", "showdown": "^1.9.0", + "shx": "^0.3.2", "xml": "^1.0.1" }, "devDependencies": { @@ -17,8 +19,9 @@ "supertest": "^4.0.2" }, "scripts": { + "start": "node src/server.js", "test": "jest", - "install": "node src/postinstall.js" + "install": "shx mkdir -p ./data/$(date +%Y)/$(date +%m)/$(date +%d)/ && shx cp -rf ./sample_data/* ./data/$(date +%Y)/$(date +%m)/$(date +%d) && shx cp -f ./src/default_config.json ./config.example.json" }, "repository": { "type": "git", diff --git a/src/config.js b/src/config.js new file mode 100644 index 0000000..b64d0c1 --- /dev/null +++ b/src/config.js @@ -0,0 +1,16 @@ +const refConfig = require('./default_config.json'); +const srcConfig = require('../config.json'); + +const merge = function (ref, src) { + if (typeof ref !== typeof src) { + return ref; + } else if (typeof ref === 'object') { + const out = {}; + Object.keys(ref).forEach(key => out[key] = merge(ref[key], src[key])); + return out; + } else { + return src; + } +}; + +module.exports = merge(refConfig, srcConfig); \ No newline at end of file diff --git a/config.example.json b/src/default_config.json similarity index 100% rename from config.example.json rename to src/default_config.json diff --git a/src/postinstall.js b/src/postinstall.js deleted file mode 100644 index 3b68c2b..0000000 --- a/src/postinstall.js +++ /dev/null @@ -1,18 +0,0 @@ -const fs = require('fs'); -const ncp = require('ncp').ncp; - -const pad0 = n => ('0'+n).substr(-2); - -const datetime = new Date(); -const dir = `./data/${datetime.getFullYear()}/${pad0(datetime.getMonth())}/${pad0(datetime.getDay())}/`; - -if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, {recursive: true}); -} - -ncp('./sample_data/',dir, function(err){ - if(err) - console.error(err); - else - console.log(`sample data copied to ${dir}`); -}); \ No newline at end of file diff --git a/src/server.js b/src/server.js index db33f45..f52bfce 100644 --- a/src/server.js +++ b/src/server.js @@ -1,9 +1,7 @@ -const config = require('../config.json'); +const config = require('./config'); const app = require('./app')(config); -const port = config.nodePort|3000; - -app.listen(config.nodePort|3000, () => { - console.log(`gitblog.md server listening on port ${port}`); +app.listen(config.nodePort, () => { + console.log(`gitblog.md server listening on port ${config.nodePort}`); }); From 2dd87a3d9b07f385c9dcf0f45165e28824cda7e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20GOUIN?= Date: Wed, 19 Jun 2019 13:51:02 +0200 Subject: [PATCH 002/120] Separated sample article and home page --- package.json | 2 +- sample_data/{ => article}/birthday-cake.png | Bin sample_data/{ => article}/index.md | 0 sample_data/home/index.ejs | 10 ++++++++++ 4 files changed, 11 insertions(+), 1 deletion(-) rename sample_data/{ => article}/birthday-cake.png (100%) rename sample_data/{ => article}/index.md (100%) create mode 100644 sample_data/home/index.ejs diff --git a/package.json b/package.json index 3e519c2..4c4b2ab 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "scripts": { "start": "node src/server.js", "test": "jest", - "install": "shx mkdir -p ./data/$(date +%Y)/$(date +%m)/$(date +%d)/ && shx cp -rf ./sample_data/* ./data/$(date +%Y)/$(date +%m)/$(date +%d) && shx cp -f ./src/default_config.json ./config.example.json" + "install": "shx mkdir ./data && shx cp -rf ./sample_data/home/* ./data && shx mkdir -p ./data/$(date +%Y)/$(date +%m)/$(date +%d) && shx cp -rf ./sample_data/article/* ./data/$(date +%Y)/$(date +%m)/$(date +%d) && shx cp -f ./src/default_config.json ./config.example.json" }, "repository": { "type": "git", diff --git a/sample_data/birthday-cake.png b/sample_data/article/birthday-cake.png similarity index 100% rename from sample_data/birthday-cake.png rename to sample_data/article/birthday-cake.png diff --git a/sample_data/index.md b/sample_data/article/index.md similarity index 100% rename from sample_data/index.md rename to sample_data/article/index.md diff --git a/sample_data/home/index.ejs b/sample_data/home/index.ejs new file mode 100644 index 0000000..566549b --- /dev/null +++ b/sample_data/home/index.ejs @@ -0,0 +1,10 @@ + + + + + Title + + + + + \ No newline at end of file From 611d88d08863332519239a1c6e7ab9daba3448fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20GOUIN?= Date: Wed, 19 Jun 2019 14:33:12 +0200 Subject: [PATCH 003/120] config.js tests --- package-lock.json | 5 ---- package.json | 1 - src/config.js | 15 ++++++++++-- src/default_config.json | 4 +-- src/server.js | 6 ++--- test/config.test.js | 54 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 72 insertions(+), 13 deletions(-) create mode 100644 test/config.test.js diff --git a/package-lock.json b/package-lock.json index 790eafc..a5b988b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7024,11 +7024,6 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, - "ncp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", - "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=" - }, "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", diff --git a/package.json b/package.json index 4c4b2ab..08df023 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,6 @@ "main": "src/server.js", "dependencies": { "express": "^4.17.1", - "ncp": "^2.0.0", "showdown": "^1.9.0", "shx": "^0.3.2", "xml": "^1.0.1" diff --git a/src/config.js b/src/config.js index b64d0c1..f0b1273 100644 --- a/src/config.js +++ b/src/config.js @@ -1,16 +1,27 @@ const refConfig = require('./default_config.json'); -const srcConfig = require('../config.json'); +const fs = require('fs'); const merge = function (ref, src) { if (typeof ref !== typeof src) { + console.log(ref, src, ref); return ref; } else if (typeof ref === 'object') { const out = {}; Object.keys(ref).forEach(key => out[key] = merge(ref[key], src[key])); return out; } else { + console.log(ref, src, src); return src; } }; -module.exports = merge(refConfig, srcConfig); \ No newline at end of file +module.exports = function () { + try { + let configData = fs.readFileSync('./config.json'); + let config = JSON.parse(configData); + return merge(refConfig, config); + } catch (error) { + console.error('Failed to load config.json'); + return refConfig; + } +}; \ No newline at end of file diff --git a/src/default_config.json b/src/default_config.json index 5ab3b35..f62a4ed 100644 --- a/src/default_config.json +++ b/src/default_config.json @@ -1,6 +1,6 @@ { - "nodePort": 3000, - "dataDir": "data", + "node_port": 3000, + "data_dir": "data", "modules" : { "plantuml" : false, "rss": true, diff --git a/src/server.js b/src/server.js index f52bfce..7e68e9c 100644 --- a/src/server.js +++ b/src/server.js @@ -1,7 +1,7 @@ -const config = require('./config'); +const config = require('./config')(); const app = require('./app')(config); -app.listen(config.nodePort, () => { - console.log(`gitblog.md server listening on port ${config.nodePort}`); +app.listen(config['node_port'], () => { + console.log(`gitblog.md server listening on port ${config['node_port']}`); }); diff --git a/test/config.test.js b/test/config.test.js new file mode 100644 index 0000000..5f61b3f --- /dev/null +++ b/test/config.test.js @@ -0,0 +1,54 @@ +/* jshint -W117 */ +const fs = require('fs'); + +const configFile = './config.json'; +const tmpConfigFile = './config.temp.json'; + +beforeAll(() => { + if (fs.existsSync(configFile)) { + fs.renameSync(configFile, tmpConfigFile); + } + expect(fs.existsSync(configFile)).toBeFalsy(); +}); + +afterAll(() => { + if (fs.existsSync(tmpConfigFile)) { + fs.renameSync(tmpConfigFile, configFile); + } else if (fs.existsSync(configFile)) { + fs.unlinkSync(configFile); //remove config file if remaining + } +}); + +test('no config', () => { + if (fs.existsSync(configFile)) + fs.unlinkSync(configFile); + expect(fs.existsSync(configFile)).toBeFalsy(); + const config = require('../src/config')(); + expect(config).toBeDefined(); + expect(config['node_port']).toBe(3000); + expect(config['data_dir']).toBe('data'); +}); + +test('invalid config ignored', () => { + fs.writeFileSync(configFile, 'invalid JSON'); + const config = require('../src/config')(); + expect(config).toBeDefined(); + expect(config['node_port']).toBe(3000); + expect(config['data_dir']).toBe('data'); +}); + +test('good config merged', () => { + fs.writeFileSync(configFile, '{"node_port":5000}'); + const config = require('../src/config')(); + expect(config).toBeDefined(); + expect(config['node_port']).toBe(5000); + expect(config['data_dir']).toBe('data'); +}); + +test('wrong config fixed', () => { + fs.writeFileSync(configFile, '{"node_port":"hello","data_dir":"data2"}'); + const config = require('../src/config')(); + expect(config).toBeDefined(); + expect(config['node_port']).toBe(3000); + expect(config['data_dir']).toBe('data2'); +}); \ No newline at end of file From d7fb67401b20022ffcc54d86933bd23fdb0649c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20GOUIN?= Date: Wed, 19 Jun 2019 14:35:56 +0200 Subject: [PATCH 004/120] updated package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 08df023..b05eae5 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ }, "scripts": { "start": "node src/server.js", - "test": "jest", + "test": "jest --silent", "install": "shx mkdir ./data && shx cp -rf ./sample_data/home/* ./data && shx mkdir -p ./data/$(date +%Y)/$(date +%m)/$(date +%d) && shx cp -rf ./sample_data/article/* ./data/$(date +%Y)/$(date +%m)/$(date +%d) && shx cp -f ./src/default_config.json ./config.example.json" }, "repository": { From d6a493dac6b831a82255735b677747715da79377 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20GOUIN?= Date: Wed, 19 Jun 2019 14:36:16 +0200 Subject: [PATCH 005/120] removed debug log --- src/config.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/config.js b/src/config.js index f0b1273..a768790 100644 --- a/src/config.js +++ b/src/config.js @@ -3,14 +3,12 @@ const fs = require('fs'); const merge = function (ref, src) { if (typeof ref !== typeof src) { - console.log(ref, src, ref); return ref; } else if (typeof ref === 'object') { const out = {}; Object.keys(ref).forEach(key => out[key] = merge(ref[key], src[key])); return out; } else { - console.log(ref, src, src); return src; } }; From 15761551e756a62e8b58eeebc350be728a28f5d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20GOUIN?= Date: Wed, 19 Jun 2019 14:39:20 +0200 Subject: [PATCH 006/120] minor error log --- src/config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.js b/src/config.js index a768790..42f18b3 100644 --- a/src/config.js +++ b/src/config.js @@ -19,7 +19,7 @@ module.exports = function () { let config = JSON.parse(configData); return merge(refConfig, config); } catch (error) { - console.error('Failed to load config.json'); + console.error('Failed to load config.json : '+error); return refConfig; } }; \ No newline at end of file From 20b4d0353f087c7fe9c26fb1909410d0f5ebccf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20GOUIN?= Date: Wed, 19 Jun 2019 16:27:49 +0200 Subject: [PATCH 007/120] file_walker.js detect articles in data file tree --- .gitignore | 3 +- package.json | 2 +- ...efault_config.json => config.default.json} | 0 src/config.js | 2 +- src/file_walker.js | 55 ++++++ test/app.test.js | 5 +- test/config.test.json | 3 - test/file_walker.test.js | 165 ++++++++++++++++++ 8 files changed, 227 insertions(+), 8 deletions(-) rename src/{default_config.json => config.default.json} (100%) create mode 100644 src/file_walker.js delete mode 100644 test/config.test.json create mode 100644 test/file_walker.test.js diff --git a/.gitignore b/.gitignore index 8dd596e..aba5ca9 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ /node_modules /config.json /config.example.json -/data \ No newline at end of file +/data +/test_data \ No newline at end of file diff --git a/package.json b/package.json index b05eae5..ee88950 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "scripts": { "start": "node src/server.js", "test": "jest --silent", - "install": "shx mkdir ./data && shx cp -rf ./sample_data/home/* ./data && shx mkdir -p ./data/$(date +%Y)/$(date +%m)/$(date +%d) && shx cp -rf ./sample_data/article/* ./data/$(date +%Y)/$(date +%m)/$(date +%d) && shx cp -f ./src/default_config.json ./config.example.json" + "install": "shx mkdir ./data && shx cp -rf ./sample_data/home/* ./data && shx mkdir -p ./data/$(date +%Y)/$(date +%m)/$(date +%d) && shx cp -rf ./sample_data/article/* ./data/$(date +%Y)/$(date +%m)/$(date +%d) && shx cp -f src/config.default.json ./config.example.json" }, "repository": { "type": "git", diff --git a/src/default_config.json b/src/config.default.json similarity index 100% rename from src/default_config.json rename to src/config.default.json diff --git a/src/config.js b/src/config.js index 42f18b3..30ec53d 100644 --- a/src/config.js +++ b/src/config.js @@ -1,4 +1,4 @@ -const refConfig = require('./default_config.json'); +const refConfig = require('./config.default.json'); const fs = require('fs'); const merge = function (ref, src) { diff --git a/src/file_walker.js b/src/file_walker.js new file mode 100644 index 0000000..134a170 --- /dev/null +++ b/src/file_walker.js @@ -0,0 +1,55 @@ +const fs = require('fs'); + +const fileTree = (path, cb) => { + let list = []; + let remaining = 0; + fs.readdir(path, {withFileTypes: true}, (err, items) => { + if (err) + return cb(err); + items.forEach((item) => { + if (item.isDirectory()) { + remaining++; + fileTree(`${path}/${item.name}`, (err, out) => { + if (err) + return cb(err); + list.push(...out); + remaining--; + if (remaining === 0) + cb(null, list); + }); + } else { + list.push(`${path}/${item.name}`); + } + }); + if (remaining === 0) + cb(null, list); + }); +}; + +module.exports = (config) => { + return { + fileTree: config['test'] ? fileTree : undefined, + fetchArticles: (cb) => { + fileTree(config['data_dir'], (err, fileList) => { + if (err) + return cb(err); + const paths = fileList + .map(path => path.substr(config['data_dir'].length)) + .filter(path => path.indexOf(config['article']['index']) === path.length - config['article']['index'].length) + .map(path => path.substr(0, path.length - config['article']['index'].length)) + .map(path => path.match(/^\/(\d{4})\/(\d{2})\/(\d{2})\/$/)) + .filter(path => path && path.length > 1); + const list = []; + paths.forEach(path => { + list.push({ + path: config['data_dir'] + path[0] + config['article']['index'], + year: parseInt(path[1]), + month: parseInt(path[2]), + day: parseInt(path[3]) + }); + }); + cb(null, list); + }); + } + }; +}; \ No newline at end of file diff --git a/test/app.test.js b/test/app.test.js index 6f24b00..8e33e19 100644 --- a/test/app.test.js +++ b/test/app.test.js @@ -1,7 +1,8 @@ /* jshint -W117 */ const request = require('supertest'); -const config = require('./config.test.json'); -const app = require('../src/app')(config); +const app = require('../src/app')({ + +}); describe('Test root path', () => { test('GET / 200', done => { diff --git a/test/config.test.json b/test/config.test.json deleted file mode 100644 index 0e0dcd2..0000000 --- a/test/config.test.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - -} \ No newline at end of file diff --git a/test/file_walker.test.js b/test/file_walker.test.js new file mode 100644 index 0000000..10da942 --- /dev/null +++ b/test/file_walker.test.js @@ -0,0 +1,165 @@ +/* jshint -W117 */ +const fs = require('fs'); + +const dataDir = './test_data'; +const testIndex = 'testindex.md'; + +const config = { + 'test': true, + 'data_dir': dataDir, + 'article': { + 'index': testIndex + } +}; + +const fw = require('../src/file_walker')(config); + +const deleteFolderSync = (path) => { + if(!fs.existsSync(path)) + return; + fs.readdirSync(path, {withFileTypes: true}).forEach((item) => { + if (item.isDirectory()) + deleteFolderSync(`${path}/${item.name}`); + else + fs.unlinkSync(`${path}/${item.name}`); + }); + fs.rmdirSync(path); +}; + +beforeEach(() => { + deleteFolderSync(dataDir); + fs.mkdirSync(dataDir); +}); + +afterAll(() => { + if (fs.existsSync(dataDir)) { + deleteFolderSync(dataDir); + } +}); + +const createEmptyDirs = list => list.forEach(path => fs.mkdirSync(path, {recursive: true})); +const createEmptyFiles = list => list.forEach(file => fs.writeFileSync(file, '')); + +describe('Test function fileTree', () => { + test('empty root', (done) => { + fw.fileTree(dataDir, (err, list) => { + expect(err).toBeNull(); + expect(list).toBeDefined(); + expect(list.length).toBe(0); + done(); + }); + }); + test('empty folders', (done) => { + createEmptyDirs([ + `${dataDir}/test/test`, + `${dataDir}/test/test2`, + `${dataDir}/test2` + ]); + fw.fileTree(dataDir, (err, list) => { + expect(err).toBeNull(); + expect(list).toBeDefined(); + expect(list.length).toBe(0); + done(); + }); + }); + test('simple files', (done) => { + const fileList = [ + `${dataDir}/f1.txt`, + `${dataDir}/f2.txt` + ]; + createEmptyFiles(fileList); + fw.fileTree(dataDir, (err, list) => { + expect(err).toBeNull(); + expect(list).toBeDefined(); + expect(list.length).toBe(fileList.length); + expect(list).toEqual(expect.arrayContaining(fileList)); + done(); + }); + }); + test('nested files', (done) => { + createEmptyDirs([ + `${dataDir}/test/test`, + `${dataDir}/test2` + ]); + const fileList = [ + `${dataDir}/f1.txt`, + `${dataDir}/test/f2.txt`, + `${dataDir}/test/test/f3.txt`, + `${dataDir}/test2/f4.txt` + ]; + createEmptyFiles(fileList); + fw.fileTree(dataDir, (err, list) => { + expect(err).toBeNull(); + expect(list).toBeDefined(); + expect(list.length).toBe(fileList.length); + expect(list).toEqual(expect.arrayContaining(fileList)); + done(); + }); + }); + test('invalid root', (done) => { + fw.fileTree('invalid root', (err, list) => { + expect(err).not.toBeNull(); + expect(list).not.toBeDefined(); + done(); + }); + }); +}); + +describe('Test article fetching', () => { + test('invalid data dir', (done) => { + config['data_dir'] = 'invalid root'; + fw.fetchArticles((err,list) => { + expect(err).not.toBeNull(); + expect(list).not.toBeDefined(); + config['data_dir'] = dataDir; + done(); + }); + }); + test('empty data dir', (done) => { + fw.fetchArticles((err,list) => { + expect(err).toBeNull(); + expect(list).toBeDefined(); + expect(list.length).toBe(0); + done(); + }); + }); + test('misplaced index file', (done) => { + createEmptyDirs([ + `${dataDir}/test/test`, + `${dataDir}/2019/05/05`, + ]); + createEmptyFiles([ + `${dataDir}/${testIndex}`, + `${dataDir}/test/test/${testIndex}`, + `${dataDir}/2019/05/${testIndex}`, + ]); + fw.fetchArticles((err,list) => { + expect(err).toBeNull(); + expect(list).toBeDefined(); + expect(list.length).toBe(0); + done(); + }); + }); + test('correct index file', (done) => { + createEmptyDirs([ + `${dataDir}/2019/05/05`, + ]); + const fileList = [ + `${dataDir}/2019/05/05/${testIndex}`, + ]; + createEmptyFiles(fileList); + fw.fetchArticles((err,list) => { + expect(err).toBeNull(); + expect(list).toBeDefined(); + expect(list.length).toBe(1); + expect(list[0]).toEqual({ + path: fileList[0], + year:2019, + month:5, + day:5 + }); + done(); + }); + }); +}); + From f17bcbd3c927b6043bd2eab55ff6a35793d3598b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20GOUIN?= Date: Wed, 19 Jun 2019 16:28:53 +0200 Subject: [PATCH 008/120] code 'function' --- src/app.js | 2 +- src/config.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app.js b/src/app.js index c0f527e..ed30e1e 100644 --- a/src/app.js +++ b/src/app.js @@ -1,7 +1,7 @@ const express = require('express'); const app = express(); -module.exports = function(/*config*/){ +module.exports = (/*config*/) => { app.get('/', (req,res) => { res.status(200).send('Hello World!'); }); diff --git a/src/config.js b/src/config.js index 30ec53d..f698325 100644 --- a/src/config.js +++ b/src/config.js @@ -1,7 +1,7 @@ const refConfig = require('./config.default.json'); const fs = require('fs'); -const merge = function (ref, src) { +const merge = (ref, src) => { if (typeof ref !== typeof src) { return ref; } else if (typeof ref === 'object') { @@ -13,7 +13,7 @@ const merge = function (ref, src) { } }; -module.exports = function () { +module.exports = () => { try { let configData = fs.readFileSync('./config.json'); let config = JSON.parse(configData); From 1c30f95677f9635e2847f7a6e111d57cee102371 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20GOUIN?= Date: Wed, 19 Jun 2019 16:33:14 +0200 Subject: [PATCH 009/120] [skip ci]wip file_walker reading article info --- sample_data/article/index.md | 2 +- src/file_walker.js | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/sample_data/article/index.md b/sample_data/article/index.md index 8212cc1..e45f07a 100644 --- a/sample_data/article/index.md +++ b/sample_data/article/index.md @@ -2,5 +2,5 @@ ## If you see this page, that means it's working -![missing image](./birthday-cake.png) +![thumbnail](./birthday-cake.png) diff --git a/src/file_walker.js b/src/file_walker.js index 134a170..2d2c9ad 100644 --- a/src/file_walker.js +++ b/src/file_walker.js @@ -26,9 +26,14 @@ const fileTree = (path, cb) => { }); }; +const readIndexFile = (path,cb) => { + //TODO reading page title and possibly ![thumbnail](url) +}; + module.exports = (config) => { return { fileTree: config['test'] ? fileTree : undefined, + readIndexFile: config['test'] ? readIndexFile : undefined, fetchArticles: (cb) => { fileTree(config['data_dir'], (err, fileList) => { if (err) From 9300bfbb784adbc80398bcf98b6c22da8a079b87 Mon Sep 17 00:00:00 2001 From: Klemek Date: Wed, 19 Jun 2019 19:41:29 +0200 Subject: [PATCH 010/120] file walker now read title and thumbnail infos --- README.md | 28 +------- src/config.default.json | 5 +- src/config.js | 2 +- src/file_walker.js | 44 +++++++++--- test/file_walker.test.js | 141 +++++++++++++++++++++++++++++++++++---- 5 files changed, 169 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index b36d91a..0d18322 100644 --- a/README.md +++ b/README.md @@ -26,33 +26,7 @@ npm install cd gitblog.md cp config.example.json config.json ``` -then edit the config.json file with your values : -> default values for config.json -````json -{ - "nodePort": 3000, - "dataDir": "data", - "modules" : { - "plantuml" : false, - "rss": true, - "webhook": true - }, - "home" : { - "index" : "index.ejs" - }, - "article" : { - "index" : "index.md" - }, - "rss" : { - "endpoint" : "/rss", - "length" : 10 - }, - "webhook" : { - "endpoint": "/webhook", - "secretFile": "git_secret" - } -} -```` +then edit the config.json file with your custom values. **3. Start your server** diff --git a/src/config.default.json b/src/config.default.json index f62a4ed..2ee6f02 100644 --- a/src/config.default.json +++ b/src/config.default.json @@ -10,7 +10,10 @@ "index" : "index.ejs" }, "article" : { - "index" : "index.md" + "index" : "index.md", + "thumbnail_tag" : "thumbnail", + "default_title": "Untitled", + "default_thumbnail" : null }, "rss" : { "endpoint" : "/rss", diff --git a/src/config.js b/src/config.js index f698325..ac75ed5 100644 --- a/src/config.js +++ b/src/config.js @@ -15,7 +15,7 @@ const merge = (ref, src) => { module.exports = () => { try { - let configData = fs.readFileSync('./config.json'); + let configData = fs.readFileSync('./config.json', {encoding:'UTF-8'}); let config = JSON.parse(configData); return merge(refConfig, config); } catch (error) { diff --git a/src/file_walker.js b/src/file_walker.js index 2d2c9ad..1b1766b 100644 --- a/src/file_walker.js +++ b/src/file_walker.js @@ -1,6 +1,6 @@ const fs = require('fs'); -const fileTree = (path, cb) => { +const getFileTree = (path, cb) => { let list = []; let remaining = 0; fs.readdir(path, {withFileTypes: true}, (err, items) => { @@ -9,7 +9,7 @@ const fileTree = (path, cb) => { items.forEach((item) => { if (item.isDirectory()) { remaining++; - fileTree(`${path}/${item.name}`, (err, out) => { + getFileTree(`${path}/${item.name}`, (err, out) => { if (err) return cb(err); list.push(...out); @@ -26,16 +26,30 @@ const fileTree = (path, cb) => { }); }; -const readIndexFile = (path,cb) => { - //TODO reading page title and possibly ![thumbnail](url) +const readIndexFile = (path, thumbnailTag, cb) => { + fs.readFile(path, {encoding: 'UTF-8'}, (err, data) => { + if (err) + return cb(err); + + let info = {}; + + const regRes1 = data.match(/(^|[^#])#([^#\r\n]*)\r?\n?$/m); + info.title = regRes1 ? regRes1[2].trim() : undefined; + + const thumbnailRegEx = new RegExp(`!\\[${thumbnailTag}]\\(([^)]*)\\)`, 'i'); + const regRes2 = data.match(thumbnailRegEx); + info.thumbnail = regRes2 ? regRes2[1].trim() : undefined; + + cb(null, info); + }); }; module.exports = (config) => { return { - fileTree: config['test'] ? fileTree : undefined, + fileTree: config['test'] ? getFileTree : undefined, readIndexFile: config['test'] ? readIndexFile : undefined, fetchArticles: (cb) => { - fileTree(config['data_dir'], (err, fileList) => { + getFileTree(config['data_dir'], (err, fileList) => { if (err) return cb(err); const paths = fileList @@ -44,16 +58,30 @@ module.exports = (config) => { .map(path => path.substr(0, path.length - config['article']['index'].length)) .map(path => path.match(/^\/(\d{4})\/(\d{2})\/(\d{2})\/$/)) .filter(path => path && path.length > 1); + if (paths.length === 0) + cb(null, []); const list = []; + let remaining = 0; paths.forEach(path => { - list.push({ + const article = { path: config['data_dir'] + path[0] + config['article']['index'], year: parseInt(path[1]), month: parseInt(path[2]), day: parseInt(path[3]) + }; + remaining++; + readIndexFile(article.path, config['article']['thumbnail_tag'], (err, info) => { + if (err) + return cb(err); + article.title = info.title || config['article']['default_title']; + article.thumbnail = info.thumbnail ? (config['data_dir'] + path[0] + info.thumbnail) : config['article']['default_thumbnail']; + list.push(article); + remaining--; + if (remaining === 0) + cb(null, list); }); }); - cb(null, list); + }); } }; diff --git a/test/file_walker.test.js b/test/file_walker.test.js index 10da942..e1a1c1a 100644 --- a/test/file_walker.test.js +++ b/test/file_walker.test.js @@ -8,14 +8,17 @@ const config = { 'test': true, 'data_dir': dataDir, 'article': { - 'index': testIndex + 'index': testIndex, + 'default_title': 'Untitled', + 'default_thumbnail': 'default.png', + 'thumbnail_tag': 'thumbnail' } }; const fw = require('../src/file_walker')(config); const deleteFolderSync = (path) => { - if(!fs.existsSync(path)) + if (!fs.existsSync(path)) return; fs.readdirSync(path, {withFileTypes: true}).forEach((item) => { if (item.isDirectory()) @@ -105,10 +108,95 @@ describe('Test function fileTree', () => { }); }); +describe('Test index article reading', () => { + const file = `${dataDir}/${testIndex}`; + + test('invalid file', (done) => { + fw.readIndexFile('invalid file', 'thumbnail', (err, info) => { + expect(err).not.toBeNull(); + expect(info).not.toBeDefined(); + done(); + }); + + }); + + test('correct file', (done) => { + fs.writeFileSync(file, ` + # This is an awesome title !?¤ + ![custom_thumbnail](./thumbnail.jpg) + this is some text + `); + fw.readIndexFile(file, 'custom_thumbnail', (err, info) => { + expect(err).toBeNull(); + expect(info).toBeDefined(); + expect(info.title).toBe('This is an awesome title !?¤'); + expect(info.thumbnail).toBe('./thumbnail.jpg'); + done(); + }); + }); + + test('no title', (done) => { + fs.writeFileSync(file, ` + ## This is an awesome title !?¤ + ![custom_thumbnail](./thumbnail.jpg) + ### this is some text + `); + fw.readIndexFile(file, 'custom_thumbnail', (err, info) => { + expect(err).toBeNull(); + expect(info).toBeDefined(); + expect(info.title).not.toBeDefined(); + expect(info.thumbnail).toBe('./thumbnail.jpg'); + done(); + }); + }); + + test('title at beginning', (done) => { + fs.writeFileSync(file, '#title'); + fw.readIndexFile(file, 'custom_thumbnail', (err, info) => { + expect(err).toBeNull(); + expect(info).toBeDefined(); + expect(info.title).toBe('title'); + expect(info.thumbnail).not.toBeDefined(); + done(); + }); + }); + + test('no thumbnail', (done) => { + fs.writeFileSync(file, ` + # This is an awesome title !?¤ + ![custom_thumbnail](./thumbnail.jpg) + this is some text + `); + fw.readIndexFile(file, 'thumbnail', (err, info) => { + expect(err).toBeNull(); + expect(info).toBeDefined(); + expect(info.title).toBe('This is an awesome title !?¤'); + expect(info.thumbnail).not.toBeDefined(); + done(); + }); + }); + + test('multiple thumbnails', (done) => { + fs.writeFileSync(file, ` + # This is an awesome title !?¤ + ![custom_thumbnail](./thumbnail.jpg) + this is some text + ![custom_thumbnail](./thumbnail2.jpg) + `); + fw.readIndexFile(file, 'custom_thumbnail', (err, info) => { + expect(err).toBeNull(); + expect(info).toBeDefined(); + expect(info.title).toBe('This is an awesome title !?¤'); + expect(info.thumbnail).toBe('./thumbnail.jpg'); + done(); + }); + }); +}); + describe('Test article fetching', () => { test('invalid data dir', (done) => { config['data_dir'] = 'invalid root'; - fw.fetchArticles((err,list) => { + fw.fetchArticles((err, list) => { expect(err).not.toBeNull(); expect(list).not.toBeDefined(); config['data_dir'] = dataDir; @@ -116,7 +204,7 @@ describe('Test article fetching', () => { }); }); test('empty data dir', (done) => { - fw.fetchArticles((err,list) => { + fw.fetchArticles((err, list) => { expect(err).toBeNull(); expect(list).toBeDefined(); expect(list.length).toBe(0); @@ -133,30 +221,55 @@ describe('Test article fetching', () => { `${dataDir}/test/test/${testIndex}`, `${dataDir}/2019/05/${testIndex}`, ]); - fw.fetchArticles((err,list) => { + fw.fetchArticles((err, list) => { expect(err).toBeNull(); expect(list).toBeDefined(); expect(list.length).toBe(0); done(); }); }); + test('empty index file', (done) => { + createEmptyDirs([ + `${dataDir}/2019/05/05`, + ]); + const file = `${dataDir}/2019/05/05/${testIndex}`; + createEmptyFiles([file]); + fw.fetchArticles((err, list) => { + expect(err).toBeNull(); + expect(list).toBeDefined(); + expect(list.length).toBe(1); + expect(list[0]).toEqual({ + path: file, + year: 2019, + month: 5, + day: 5, + title:'Untitled', + thumbnail:'default.png' + }); + done(); + }); + }); test('correct index file', (done) => { createEmptyDirs([ `${dataDir}/2019/05/05`, ]); - const fileList = [ - `${dataDir}/2019/05/05/${testIndex}`, - ]; - createEmptyFiles(fileList); - fw.fetchArticles((err,list) => { + const file = `${dataDir}/2019/05/05/${testIndex}`; + fs.writeFileSync(file, ` + # Title + ![thumbnail](./thumbnail.jpg) + this is some text + `); + fw.fetchArticles((err, list) => { expect(err).toBeNull(); expect(list).toBeDefined(); expect(list.length).toBe(1); expect(list[0]).toEqual({ - path: fileList[0], - year:2019, - month:5, - day:5 + path: file, + year: 2019, + month: 5, + day: 5, + title:'Title', + thumbnail:`${dataDir}/2019/05/05/./thumbnail.jpg` }); done(); }); From f335cb9f139e6c089e084081258c5026430f6446 Mon Sep 17 00:00:00 2001 From: Klemek Date: Wed, 19 Jun 2019 19:49:54 +0200 Subject: [PATCH 011/120] nothing beat a js postinstall --- package-lock.json | 74 ++++++++++++++-------------------------------- package.json | 4 +-- src/postinstall.js | 28 ++++++++++++++++++ 3 files changed, 53 insertions(+), 53 deletions(-) create mode 100644 src/postinstall.js diff --git a/package-lock.json b/package-lock.json index a5b988b..27426e9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2228,7 +2228,8 @@ "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true }, "base": { "version": "0.11.2", @@ -2334,6 +2335,7 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2602,7 +2604,8 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true }, "content-disposition": { "version": "0.5.3", @@ -2920,11 +2923,6 @@ "is-symbol": "^1.0.2" } }, - "es6-object-assign": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", - "integrity": "sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=" - }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -3297,7 +3295,8 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true }, "fsevents": { "version": "1.2.9", @@ -3882,6 +3881,7 @@ "version": "7.1.4", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -4140,6 +4140,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -4150,11 +4151,6 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, - "interpret": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", - "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==" - }, "invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", @@ -6927,6 +6923,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -7024,6 +7021,11 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "ncp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=" + }, "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", @@ -7213,6 +7215,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, "requires": { "wrappy": "1" } @@ -7383,7 +7386,8 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true }, "path-key": { "version": "2.0.1", @@ -7393,7 +7397,8 @@ "path-parse": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true }, "path-to-regexp": { "version": "0.1.7", @@ -8055,14 +8060,6 @@ "util.promisify": "^1.0.0" } }, - "rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", - "requires": { - "resolve": "^1.1.6" - } - }, "regenerate": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", @@ -8253,6 +8250,7 @@ "version": "1.11.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.0.tgz", "integrity": "sha512-WL2pBDjqT6pGUNSUzMw00o4T7If+z4H2x3Gz893WoUQ5KW8Vr9txp00ykiP16VBaZF5+j/OcXJHZ9+PCvdiDKw==", + "dev": true, "requires": { "path-parse": "^1.0.6" } @@ -8760,16 +8758,6 @@ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" }, - "shelljs": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.3.tgz", - "integrity": "sha512-fc0BKlAWiLpwZljmOvAOTE/gXawtCoNrP5oaY7KIaQbbyHeQVg01pSEuEGvGh3HEdBU4baCD7wQBwADmM/7f7A==", - "requires": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - } - }, "shellwords": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", @@ -8784,23 +8772,6 @@ "yargs": "^10.0.3" } }, - "shx": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/shx/-/shx-0.3.2.tgz", - "integrity": "sha512-aS0mWtW3T2sHAenrSrip2XGv39O9dXIFUqxAEWHEOS1ePtGIBavdPJY1kE2IHl14V/4iCbUiNDPGdyYTtmhSoA==", - "requires": { - "es6-object-assign": "^1.0.3", - "minimist": "^1.2.0", - "shelljs": "^0.8.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" - } - } - }, "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", @@ -9671,7 +9642,8 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true }, "write-file-atomic": { "version": "2.4.1", diff --git a/package.json b/package.json index ee88950..4f8859b 100644 --- a/package.json +++ b/package.json @@ -6,8 +6,8 @@ "main": "src/server.js", "dependencies": { "express": "^4.17.1", + "ncp": "^2.0.0", "showdown": "^1.9.0", - "shx": "^0.3.2", "xml": "^1.0.1" }, "devDependencies": { @@ -20,7 +20,7 @@ "scripts": { "start": "node src/server.js", "test": "jest --silent", - "install": "shx mkdir ./data && shx cp -rf ./sample_data/home/* ./data && shx mkdir -p ./data/$(date +%Y)/$(date +%m)/$(date +%d) && shx cp -rf ./sample_data/article/* ./data/$(date +%Y)/$(date +%m)/$(date +%d) && shx cp -f src/config.default.json ./config.example.json" + "install": "node src/postinstall.js" }, "repository": { "type": "git", diff --git a/src/postinstall.js b/src/postinstall.js new file mode 100644 index 0000000..2181c14 --- /dev/null +++ b/src/postinstall.js @@ -0,0 +1,28 @@ +const fs = require('fs'); +const ncp = require('ncp').ncp; + +const copy = (src,dest) => { + ncp(src,dest, function(err){ + if(err) + console.error(err); + else + console.log(`copied ${src} to ${dest}`); + }); +}; + +copy('./src/config.default.json','./config.example.json'); + +if (!fs.existsSync('./data')) + fs.mkdirSync('./data'); + +copy('./sample_data/home','./data'); + +const pad0 = n => ('0'+n).substr(-2); + +const datetime = new Date(); +const dir = `./data/${datetime.getFullYear()}/${pad0(datetime.getMonth())}/${pad0(datetime.getDay())}`; + +if (!fs.existsSync(dir)) + fs.mkdirSync(dir, {recursive: true}); + +copy('./sample_data/article',dir); \ No newline at end of file From 94d96df26fff1ccf40d3ae1e5b3bb33ac93efbcb Mon Sep 17 00:00:00 2001 From: Klemek Date: Wed, 19 Jun 2019 20:17:00 +0200 Subject: [PATCH 012/120] main app loading articles in memory --- src/app.js | 38 ++++++++++++++++++++++++++++++++++++-- src/server.js | 5 +---- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/src/app.js b/src/app.js index ed30e1e..3ff4815 100644 --- a/src/app.js +++ b/src/app.js @@ -1,11 +1,45 @@ const express = require('express'); const app = express(); -module.exports = (/*config*/) => { - app.get('/', (req,res) => { +const cons = { + ok: '\x1b[32m✔\x1b[0m %s', + warn: '\x1b[33m⚠\x1b[0m %s', + error: '\x1b[31m✘\x1b[0m %s', +}; + +module.exports = (config) => { + const fw = require('./file_walker')(config); + + const articles = []; + + const reload = (callback) => { + fw.fetchArticles((err, list) => { + if (err) { + callback(false); + return console.error(cons.error, 'loading articles : ' + err); + } + articles.splice(0, articles.length, ...list); + if (articles.length > 0) + console.log(cons.ok, `loaded ${articles.length} article${articles.length > 1 ? 's' : ''}`); + else + console.log(cons.warn, `no articles loaded, check your configuration`); + callback(true); + }); + }; + + app.get('/', (req, res) => { res.status(200).send('Hello World!'); }); + app.start = () => { + reload((res) => { + if (res) + app.listen(config['node_port'], () => { + console.log(cons.ok, `gitblog.md server listening on port ${config['node_port']}`); + }); + }); + }; + return app; }; diff --git a/src/server.js b/src/server.js index 7e68e9c..3a9e0ef 100644 --- a/src/server.js +++ b/src/server.js @@ -1,7 +1,4 @@ const config = require('./config')(); const app = require('./app')(config); -app.listen(config['node_port'], () => { - console.log(`gitblog.md server listening on port ${config['node_port']}`); -}); - +app.start(); \ No newline at end of file From 4047f9dd570555372f334e18ab3064616c04df66 Mon Sep 17 00:00:00 2001 From: Klemek Date: Thu, 20 Jun 2019 00:09:02 +0200 Subject: [PATCH 013/120] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 5d3d913..d00d5ad 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,9 @@ You need to [create a new repository](https://github.com/new) on your favorite G ```bash #gitblog.md/ cd data +git init +git add . +git commit -m "initial commit" git remote add origin git push -u origin master ``` From 7710eaad252a7f997fa34ef8128f250aadf01ab6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20GOUIN?= Date: Thu, 20 Jun 2019 09:06:47 +0200 Subject: [PATCH 014/120] Fixed wrong date on postinstall.js --- src/postinstall.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/postinstall.js b/src/postinstall.js index 2181c14..7aeedcf 100644 --- a/src/postinstall.js +++ b/src/postinstall.js @@ -12,17 +12,18 @@ const copy = (src,dest) => { copy('./src/config.default.json','./config.example.json'); -if (!fs.existsSync('./data')) +if (!fs.existsSync('./data')) { fs.mkdirSync('./data'); -copy('./sample_data/home','./data'); + copy('./sample_data/home','./data'); -const pad0 = n => ('0'+n).substr(-2); + const pad0 = n => ('0'+n).substr(-2); -const datetime = new Date(); -const dir = `./data/${datetime.getFullYear()}/${pad0(datetime.getMonth())}/${pad0(datetime.getDay())}`; + const datetime = new Date(); + const dir = `./data/${datetime.getFullYear()}/${pad0(datetime.getMonth()+1)}/${pad0(datetime.getDate())}`; -if (!fs.existsSync(dir)) - fs.mkdirSync(dir, {recursive: true}); + if (!fs.existsSync(dir)) + fs.mkdirSync(dir, {recursive: true}); -copy('./sample_data/article',dir); \ No newline at end of file + copy('./sample_data/article',dir); +} \ No newline at end of file From fe6cca6453ecf2318c2f0caad2cc04d6ca3d4c9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20GOUIN?= Date: Thu, 20 Jun 2019 10:07:22 +0200 Subject: [PATCH 015/120] app default endpoints --- package-lock.json | 5 ++ package.json | 1 + src/app.js | 52 ++++++++++++++++++-- src/config.default.json | 26 +++++----- test/app.test.js | 100 ++++++++++++++++++++++++++++++++++++--- test/file_walker.test.js | 38 +++++---------- test/test_utils.js | 19 ++++++++ 7 files changed, 193 insertions(+), 48 deletions(-) create mode 100644 test/test_utils.js diff --git a/package-lock.json b/package-lock.json index 27426e9..7967736 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2869,6 +2869,11 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, + "ejs": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.6.2.tgz", + "integrity": "sha512-PcW2a0tyTuPHz3tWyYqtK6r1fZ3gp+3Sop8Ph+ZYN81Ob5rwmbHEzaqs10N3BEsaGTkh/ooniXK+WwszGlc2+Q==" + }, "electron-to-chromium": { "version": "1.3.165", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.165.tgz", diff --git a/package.json b/package.json index 4f8859b..7ca4d39 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "description": "A static blog using Markdown pulled from your git repository.", "main": "src/server.js", "dependencies": { + "ejs": "^2.6.2", "express": "^4.17.1", "ncp": "^2.0.0", "showdown": "^1.9.0", diff --git a/src/app.js b/src/app.js index 3ff4815..06308eb 100644 --- a/src/app.js +++ b/src/app.js @@ -1,5 +1,7 @@ const express = require('express'); const app = express(); +const fs = require('fs'); +const path = require('path'); const cons = { ok: '\x1b[32m✔\x1b[0m %s', @@ -10,8 +12,17 @@ const cons = { module.exports = (config) => { const fw = require('./file_walker')(config); + app.set('view engine', config['view_engine']); + app.set('views', path.join(__dirname, '..')); + const articles = []; + const log = (status, msg) => { + if (config['test']) + return; + console.log(status, msg); + }; + const reload = (callback) => { fw.fetchArticles((err, list) => { if (err) { @@ -20,22 +31,55 @@ module.exports = (config) => { } articles.splice(0, articles.length, ...list); if (articles.length > 0) - console.log(cons.ok, `loaded ${articles.length} article${articles.length > 1 ? 's' : ''}`); + log(cons.ok, `loaded ${articles.length} article${articles.length > 1 ? 's' : ''}`); else - console.log(cons.warn, `no articles loaded, check your configuration`); + log(cons.warn, `no articles loaded, check your configuration`); callback(true); }); }; + const render = (res, path, data, code = 200) => { + res.render(path, data, (err, html) => { + if (err) { + res.sendStatus(500); + log(cons.error, `failed to render ${path} : ${err}`); + } else + res.status(code).send(html); + }); + }; + + const showError = (path, code, res) => { + const errorPath = `${config['data_dir']}/${config['home']['error']}`; + if (fs.existsSync(errorPath)) + render(res, errorPath, {error: code, path: path}, code); + else + res.sendStatus(code); + }; + app.get('/', (req, res) => { - res.status(200).send('Hello World!'); + const homePath = `${config['data_dir']}/${config['home']['index']}`; + if (fs.existsSync(homePath)) + render(res, homePath, {articles: articles}); + else { + showError(req.path, 404, res); + } + }); + + app.get('*', express.static(config['data_dir'])); + + app.get('*', (req, res) => { + showError(req.path, 404, res); + }); + + app.all('*', (req, res) => { + res.status(400).send('bad request'); }); app.start = () => { reload((res) => { if (res) app.listen(config['node_port'], () => { - console.log(cons.ok, `gitblog.md server listening on port ${config['node_port']}`); + log(cons.ok, `gitblog.md server listening on port ${config['node_port']}`); }); }); }; diff --git a/src/config.default.json b/src/config.default.json index 2ee6f02..7427b2c 100644 --- a/src/config.default.json +++ b/src/config.default.json @@ -1,25 +1,27 @@ { "node_port": 3000, "data_dir": "data", - "modules" : { - "plantuml" : false, + "view_engine": "ejs", + "modules": { + "plantuml": false, "rss": true, "webhook": true }, - "home" : { - "index" : "index.ejs" + "home": { + "index": "index.ejs", + "error": "error.ejs" }, - "article" : { - "index" : "index.md", - "thumbnail_tag" : "thumbnail", + "article": { + "index": "index.md", + "thumbnail_tag": "thumbnail", "default_title": "Untitled", - "default_thumbnail" : null + "default_thumbnail": null }, - "rss" : { - "endpoint" : "/rss", - "length" : 10 + "rss": { + "endpoint": "/rss", + "length": 10 }, - "webhook" : { + "webhook": { "endpoint": "/webhook", "secretFile": "git_secret" } diff --git a/test/app.test.js b/test/app.test.js index 8e33e19..03cf8a3 100644 --- a/test/app.test.js +++ b/test/app.test.js @@ -1,15 +1,103 @@ /* jshint -W117 */ const request = require('supertest'); -const app = require('../src/app')({ +const fs = require('fs'); +const utils = require('./test_utils'); +const dataDir = './test_data'; +const testIndex = 'testindex.ejs'; +const testError = 'testerror.ejs'; + +const config = { + 'test': true, + 'data_dir': dataDir, + 'view_engine': 'ejs', + 'home': { + 'index': testIndex, + 'error': testError + } +}; + +const app = require('../src/app')(config); + +beforeEach(() => { + utils.deleteFolderSync(dataDir); + fs.mkdirSync(dataDir); +}); + +afterAll(() => { + if (fs.existsSync(dataDir)) { + utils.deleteFolderSync(dataDir); + } }); describe('Test root path', () => { - test('GET / 200', done => { - request(app).get('/').then(response => { - expect(response.statusCode).toBe(200); - done(); - }); + test('404 no index no error', done => { + request(app).get('/').then(response => { + expect(response.statusCode).toBe(404); + done(); + }); + }); + test('404 no index but error page', done => { + fs.writeFileSync(`${dataDir}/${testError}`, 'error <%= error %> at <%= path %>'); + request(app).get('/').then(response => { + expect(response.statusCode).toBe(404); + expect(response.text).toBe('error 404 at /'); + done(); + }); + }); + test('200 index page', done => { + fs.writeFileSync(`${dataDir}/${testIndex}`, 'hello there'); + request(app).get('/').then(response => { + expect(response.statusCode).toBe(200); + expect(response.text).toBe('hello there'); + done(); + }); + }); + //TODO test articles list +}); + +describe('Test static files', () => { + test('404 invalid file no error page', done => { + request(app).get('/somefile.txt').then(response => { + expect(response.statusCode).toBe(404); + done(); + }); + }); + test('404 invalid file but error page', done => { + fs.writeFileSync(`${dataDir}/${testError}`, 'error <%= error %> at <%= path %>'); + request(app).get('/somefile.txt').then(response => { + expect(response.statusCode).toBe(404); + expect(response.text).toBe('error 404 at /somefile.txt'); + done(); + }); + }); + test('200 valid file', done => { + fs.writeFileSync(`${dataDir}/somefile.txt`, 'filecontent'); + request(app).get('/somefile.txt').then(response => { + expect(response.statusCode).toBe(200); + expect(response.text).toBe('filecontent'); + done(); + }); }); }); +describe('Test other requests', () => { + test('400 POST', done => { + request(app).post('/').then(response => { + expect(response.statusCode).toBe(400); + done(); + }); + }); + test('400 PUT', done => { + request(app).put('/').then(response => { + expect(response.statusCode).toBe(400); + done(); + }); + }); + test('400 DELETE', done => { + request(app).delete('/').then(response => { + expect(response.statusCode).toBe(400); + done(); + }); + }); +}); diff --git a/test/file_walker.test.js b/test/file_walker.test.js index e1a1c1a..4500d87 100644 --- a/test/file_walker.test.js +++ b/test/file_walker.test.js @@ -1,5 +1,6 @@ /* jshint -W117 */ const fs = require('fs'); +const utils = require('./test_utils'); const dataDir = './test_data'; const testIndex = 'testindex.md'; @@ -17,32 +18,17 @@ const config = { const fw = require('../src/file_walker')(config); -const deleteFolderSync = (path) => { - if (!fs.existsSync(path)) - return; - fs.readdirSync(path, {withFileTypes: true}).forEach((item) => { - if (item.isDirectory()) - deleteFolderSync(`${path}/${item.name}`); - else - fs.unlinkSync(`${path}/${item.name}`); - }); - fs.rmdirSync(path); -}; - beforeEach(() => { - deleteFolderSync(dataDir); + utils.deleteFolderSync(dataDir); fs.mkdirSync(dataDir); }); afterAll(() => { if (fs.existsSync(dataDir)) { - deleteFolderSync(dataDir); + utils.deleteFolderSync(dataDir); } }); -const createEmptyDirs = list => list.forEach(path => fs.mkdirSync(path, {recursive: true})); -const createEmptyFiles = list => list.forEach(file => fs.writeFileSync(file, '')); - describe('Test function fileTree', () => { test('empty root', (done) => { fw.fileTree(dataDir, (err, list) => { @@ -53,7 +39,7 @@ describe('Test function fileTree', () => { }); }); test('empty folders', (done) => { - createEmptyDirs([ + utils.createEmptyDirs([ `${dataDir}/test/test`, `${dataDir}/test/test2`, `${dataDir}/test2` @@ -70,7 +56,7 @@ describe('Test function fileTree', () => { `${dataDir}/f1.txt`, `${dataDir}/f2.txt` ]; - createEmptyFiles(fileList); + utils.createEmptyFiles(fileList); fw.fileTree(dataDir, (err, list) => { expect(err).toBeNull(); expect(list).toBeDefined(); @@ -80,7 +66,7 @@ describe('Test function fileTree', () => { }); }); test('nested files', (done) => { - createEmptyDirs([ + utils.createEmptyDirs([ `${dataDir}/test/test`, `${dataDir}/test2` ]); @@ -90,7 +76,7 @@ describe('Test function fileTree', () => { `${dataDir}/test/test/f3.txt`, `${dataDir}/test2/f4.txt` ]; - createEmptyFiles(fileList); + utils.createEmptyFiles(fileList); fw.fileTree(dataDir, (err, list) => { expect(err).toBeNull(); expect(list).toBeDefined(); @@ -212,11 +198,11 @@ describe('Test article fetching', () => { }); }); test('misplaced index file', (done) => { - createEmptyDirs([ + utils.createEmptyDirs([ `${dataDir}/test/test`, `${dataDir}/2019/05/05`, ]); - createEmptyFiles([ + utils.createEmptyFiles([ `${dataDir}/${testIndex}`, `${dataDir}/test/test/${testIndex}`, `${dataDir}/2019/05/${testIndex}`, @@ -229,11 +215,11 @@ describe('Test article fetching', () => { }); }); test('empty index file', (done) => { - createEmptyDirs([ + utils.createEmptyDirs([ `${dataDir}/2019/05/05`, ]); const file = `${dataDir}/2019/05/05/${testIndex}`; - createEmptyFiles([file]); + utils.createEmptyFiles([file]); fw.fetchArticles((err, list) => { expect(err).toBeNull(); expect(list).toBeDefined(); @@ -250,7 +236,7 @@ describe('Test article fetching', () => { }); }); test('correct index file', (done) => { - createEmptyDirs([ + utils.createEmptyDirs([ `${dataDir}/2019/05/05`, ]); const file = `${dataDir}/2019/05/05/${testIndex}`; diff --git a/test/test_utils.js b/test/test_utils.js new file mode 100644 index 0000000..e03ef7a --- /dev/null +++ b/test/test_utils.js @@ -0,0 +1,19 @@ +const fs = require('fs'); + +const deleteFolderSync = (path) => { + if (!fs.existsSync(path)) + return; + fs.readdirSync(path, {withFileTypes: true}).forEach((item) => { + if (item.isDirectory()) + deleteFolderSync(`${path}/${item.name}`); + else + fs.unlinkSync(`${path}/${item.name}`); + }); + fs.rmdirSync(path); +}; + +module.exports = { + deleteFolderSync: deleteFolderSync, + createEmptyDirs: list => list.forEach(path => fs.mkdirSync(path, {recursive: true})), + createEmptyFiles: list => list.forEach(file => fs.writeFileSync(file, '')), +}; \ No newline at end of file From 07dd06a338ba0e06e31e6fd6822b0262847a901c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20GOUIN?= Date: Thu, 20 Jun 2019 10:40:48 +0200 Subject: [PATCH 016/120] use of path.join everywhere --- src/app.js | 19 +++++-------- src/config.js | 2 +- src/file_walker.js | 24 ++++++++-------- src/postinstall.js | 21 +++++++------- test/app.test.js | 9 +++--- test/config.test.js | 4 +-- test/file_walker.test.js | 61 ++++++++++++++++++++-------------------- test/test_utils.js | 13 +++++---- 8 files changed, 77 insertions(+), 76 deletions(-) diff --git a/src/app.js b/src/app.js index 06308eb..e825e6d 100644 --- a/src/app.js +++ b/src/app.js @@ -17,11 +17,6 @@ module.exports = (config) => { const articles = []; - const log = (status, msg) => { - if (config['test']) - return; - console.log(status, msg); - }; const reload = (callback) => { fw.fetchArticles((err, list) => { @@ -31,9 +26,9 @@ module.exports = (config) => { } articles.splice(0, articles.length, ...list); if (articles.length > 0) - log(cons.ok, `loaded ${articles.length} article${articles.length > 1 ? 's' : ''}`); + console.log(cons.ok, `loaded ${articles.length} article${articles.length > 1 ? 's' : ''}`); else - log(cons.warn, `no articles loaded, check your configuration`); + console.log(cons.warn, `no articles loaded, check your configuration`); callback(true); }); }; @@ -42,16 +37,16 @@ module.exports = (config) => { res.render(path, data, (err, html) => { if (err) { res.sendStatus(500); - log(cons.error, `failed to render ${path} : ${err}`); + console.log(cons.error, `failed to render ${path} : ${err}`); } else res.status(code).send(html); }); }; - const showError = (path, code, res) => { - const errorPath = `${config['data_dir']}/${config['home']['error']}`; + const showError = (resPath, code, res) => { + const errorPath = path.join(config['data_dir'],config['home']['error']); if (fs.existsSync(errorPath)) - render(res, errorPath, {error: code, path: path}, code); + render(res, errorPath, {error: code, path: resPath}, code); else res.sendStatus(code); }; @@ -79,7 +74,7 @@ module.exports = (config) => { reload((res) => { if (res) app.listen(config['node_port'], () => { - log(cons.ok, `gitblog.md server listening on port ${config['node_port']}`); + console.log(cons.ok, `gitblog.md server listening on port ${config['node_port']}`); }); }); }; diff --git a/src/config.js b/src/config.js index ac75ed5..fd44034 100644 --- a/src/config.js +++ b/src/config.js @@ -15,7 +15,7 @@ const merge = (ref, src) => { module.exports = () => { try { - let configData = fs.readFileSync('./config.json', {encoding:'UTF-8'}); + let configData = fs.readFileSync('config.json', {encoding:'UTF-8'}); let config = JSON.parse(configData); return merge(refConfig, config); } catch (error) { diff --git a/src/file_walker.js b/src/file_walker.js index 1b1766b..52ac51c 100644 --- a/src/file_walker.js +++ b/src/file_walker.js @@ -1,15 +1,16 @@ const fs = require('fs'); +const path = require('path'); -const getFileTree = (path, cb) => { +const getFileTree = (dir, cb) => { let list = []; let remaining = 0; - fs.readdir(path, {withFileTypes: true}, (err, items) => { + fs.readdir(dir, {withFileTypes: true}, (err, items) => { if (err) return cb(err); items.forEach((item) => { if (item.isDirectory()) { remaining++; - getFileTree(`${path}/${item.name}`, (err, out) => { + getFileTree(path.join(dir, item.name), (err, out) => { if (err) return cb(err); list.push(...out); @@ -18,7 +19,7 @@ const getFileTree = (path, cb) => { cb(null, list); }); } else { - list.push(`${path}/${item.name}`); + list.push(path.join(dir, item.name)); } }); if (remaining === 0) @@ -57,24 +58,25 @@ module.exports = (config) => { .filter(path => path.indexOf(config['article']['index']) === path.length - config['article']['index'].length) .map(path => path.substr(0, path.length - config['article']['index'].length)) .map(path => path.match(/^\/(\d{4})\/(\d{2})\/(\d{2})\/$/)) - .filter(path => path && path.length > 1); + .filter(matches => matches && matches.length > 1); if (paths.length === 0) cb(null, []); const list = []; let remaining = 0; - paths.forEach(path => { + paths.forEach(matches => { const article = { - path: config['data_dir'] + path[0] + config['article']['index'], - year: parseInt(path[1]), - month: parseInt(path[2]), - day: parseInt(path[3]) + path: path.join(config['data_dir'], matches[1], matches[2], matches[3], config['article']['index']), + parent: path.join(config['data_dir'], matches[1], matches[2], matches[3]), + year: parseInt(matches[1]), + month: parseInt(matches[2]), + day: parseInt(matches[3]) }; remaining++; readIndexFile(article.path, config['article']['thumbnail_tag'], (err, info) => { if (err) return cb(err); article.title = info.title || config['article']['default_title']; - article.thumbnail = info.thumbnail ? (config['data_dir'] + path[0] + info.thumbnail) : config['article']['default_thumbnail']; + article.thumbnail = info.thumbnail ? path.join(article.parent, info.thumbnail) : config['article']['default_thumbnail']; list.push(article); remaining--; if (remaining === 0) diff --git a/src/postinstall.js b/src/postinstall.js index 7aeedcf..8654aa5 100644 --- a/src/postinstall.js +++ b/src/postinstall.js @@ -1,29 +1,30 @@ const fs = require('fs'); +const path = require('path'); const ncp = require('ncp').ncp; -const copy = (src,dest) => { - ncp(src,dest, function(err){ - if(err) +const copy = (src, dest) => { + ncp(src, dest, function (err) { + if (err) console.error(err); else console.log(`copied ${src} to ${dest}`); }); }; -copy('./src/config.default.json','./config.example.json'); +copy(path.join('src', 'config.default.json'), 'config.example.json'); -if (!fs.existsSync('./data')) { - fs.mkdirSync('./data'); +if (!fs.existsSync('data')) { + fs.mkdirSync('data'); - copy('./sample_data/home','./data'); + copy(path.join('sample_data','home'), 'data'); - const pad0 = n => ('0'+n).substr(-2); + const pad0 = n => ('0' + n).substr(-2); const datetime = new Date(); - const dir = `./data/${datetime.getFullYear()}/${pad0(datetime.getMonth()+1)}/${pad0(datetime.getDate())}`; + const dir = path.join('data', datetime.getFullYear().toString(), pad0(datetime.getMonth() + 1), pad0(datetime.getDate())); if (!fs.existsSync(dir)) fs.mkdirSync(dir, {recursive: true}); - copy('./sample_data/article',dir); + copy(path.join('sample_data','article'), dir); } \ No newline at end of file diff --git a/test/app.test.js b/test/app.test.js index 03cf8a3..f9f2eae 100644 --- a/test/app.test.js +++ b/test/app.test.js @@ -1,9 +1,10 @@ /* jshint -W117 */ const request = require('supertest'); const fs = require('fs'); +const path = require('path'); const utils = require('./test_utils'); -const dataDir = './test_data'; +const dataDir = 'test_data'; const testIndex = 'testindex.ejs'; const testError = 'testerror.ejs'; @@ -38,7 +39,7 @@ describe('Test root path', () => { }); }); test('404 no index but error page', done => { - fs.writeFileSync(`${dataDir}/${testError}`, 'error <%= error %> at <%= path %>'); + fs.writeFileSync(path.join(dataDir,testError), 'error <%= error %> at <%= path %>'); request(app).get('/').then(response => { expect(response.statusCode).toBe(404); expect(response.text).toBe('error 404 at /'); @@ -46,7 +47,7 @@ describe('Test root path', () => { }); }); test('200 index page', done => { - fs.writeFileSync(`${dataDir}/${testIndex}`, 'hello there'); + fs.writeFileSync(path.join(dataDir,testIndex), 'hello there'); request(app).get('/').then(response => { expect(response.statusCode).toBe(200); expect(response.text).toBe('hello there'); @@ -64,7 +65,7 @@ describe('Test static files', () => { }); }); test('404 invalid file but error page', done => { - fs.writeFileSync(`${dataDir}/${testError}`, 'error <%= error %> at <%= path %>'); + fs.writeFileSync(path.join(dataDir,testError), 'error <%= error %> at <%= path %>'); request(app).get('/somefile.txt').then(response => { expect(response.statusCode).toBe(404); expect(response.text).toBe('error 404 at /somefile.txt'); diff --git a/test/config.test.js b/test/config.test.js index 5f61b3f..ad60ea4 100644 --- a/test/config.test.js +++ b/test/config.test.js @@ -1,8 +1,8 @@ /* jshint -W117 */ const fs = require('fs'); -const configFile = './config.json'; -const tmpConfigFile = './config.temp.json'; +const configFile = 'config.json'; +const tmpConfigFile = 'config.temp.json'; beforeAll(() => { if (fs.existsSync(configFile)) { diff --git a/test/file_walker.test.js b/test/file_walker.test.js index 4500d87..b60131a 100644 --- a/test/file_walker.test.js +++ b/test/file_walker.test.js @@ -1,8 +1,9 @@ /* jshint -W117 */ const fs = require('fs'); +const path = require('path'); const utils = require('./test_utils'); -const dataDir = './test_data'; +const dataDir = 'test_data'; const testIndex = 'testindex.md'; const config = { @@ -40,9 +41,9 @@ describe('Test function fileTree', () => { }); test('empty folders', (done) => { utils.createEmptyDirs([ - `${dataDir}/test/test`, - `${dataDir}/test/test2`, - `${dataDir}/test2` + path.join(dataDir, 'test', 'test'), + path.join(dataDir, 'test', 'test2'), + path.join(dataDir, 'test2') ]); fw.fileTree(dataDir, (err, list) => { expect(err).toBeNull(); @@ -53,8 +54,8 @@ describe('Test function fileTree', () => { }); test('simple files', (done) => { const fileList = [ - `${dataDir}/f1.txt`, - `${dataDir}/f2.txt` + path.join(dataDir, 'f1.txt'), + path.join(dataDir, 'f2.txt') ]; utils.createEmptyFiles(fileList); fw.fileTree(dataDir, (err, list) => { @@ -67,14 +68,14 @@ describe('Test function fileTree', () => { }); test('nested files', (done) => { utils.createEmptyDirs([ - `${dataDir}/test/test`, - `${dataDir}/test2` + path.join(dataDir, 'test', 'test'), + path.join(dataDir, 'test2') ]); const fileList = [ - `${dataDir}/f1.txt`, - `${dataDir}/test/f2.txt`, - `${dataDir}/test/test/f3.txt`, - `${dataDir}/test2/f4.txt` + path.join(dataDir, 'f1.txt'), + path.join(dataDir, 'test', 'f2.txt'), + path.join(dataDir, 'test', 'test', 'f3.txt'), + path.join(dataDir, 'test2', 'f4.txt') ]; utils.createEmptyFiles(fileList); fw.fileTree(dataDir, (err, list) => { @@ -95,7 +96,7 @@ describe('Test function fileTree', () => { }); describe('Test index article reading', () => { - const file = `${dataDir}/${testIndex}`; + const file = path.join(dataDir, testIndex); test('invalid file', (done) => { fw.readIndexFile('invalid file', 'thumbnail', (err, info) => { @@ -199,13 +200,13 @@ describe('Test article fetching', () => { }); test('misplaced index file', (done) => { utils.createEmptyDirs([ - `${dataDir}/test/test`, - `${dataDir}/2019/05/05`, + path.join(dataDir, 'test', 'test'), + path.join(dataDir, '2019', '05', '05') ]); utils.createEmptyFiles([ - `${dataDir}/${testIndex}`, - `${dataDir}/test/test/${testIndex}`, - `${dataDir}/2019/05/${testIndex}`, + path.join(dataDir, testIndex), + path.join(dataDir, 'test', 'test', testIndex), + path.join(dataDir, '2019', '05', testIndex) ]); fw.fetchArticles((err, list) => { expect(err).toBeNull(); @@ -215,10 +216,9 @@ describe('Test article fetching', () => { }); }); test('empty index file', (done) => { - utils.createEmptyDirs([ - `${dataDir}/2019/05/05`, - ]); - const file = `${dataDir}/2019/05/05/${testIndex}`; + const dir = path.join(dataDir, '2019', '05', '05'); + const file = path.join(dir, testIndex); + utils.createEmptyDirs([dir]); utils.createEmptyFiles([file]); fw.fetchArticles((err, list) => { expect(err).toBeNull(); @@ -226,20 +226,20 @@ describe('Test article fetching', () => { expect(list.length).toBe(1); expect(list[0]).toEqual({ path: file, + parent:dir, year: 2019, month: 5, day: 5, - title:'Untitled', - thumbnail:'default.png' + title: 'Untitled', + thumbnail: 'default.png' }); done(); }); }); test('correct index file', (done) => { - utils.createEmptyDirs([ - `${dataDir}/2019/05/05`, - ]); - const file = `${dataDir}/2019/05/05/${testIndex}`; + const dir = path.join(dataDir, '2019', '05', '05'); + const file = path.join(dir, testIndex); + utils.createEmptyDirs([dir]); fs.writeFileSync(file, ` # Title ![thumbnail](./thumbnail.jpg) @@ -251,11 +251,12 @@ describe('Test article fetching', () => { expect(list.length).toBe(1); expect(list[0]).toEqual({ path: file, + parent:dir, year: 2019, month: 5, day: 5, - title:'Title', - thumbnail:`${dataDir}/2019/05/05/./thumbnail.jpg` + title: 'Title', + thumbnail: path.join(dataDir, '2019', '05', '05', './thumbnail.jpg') }); done(); }); diff --git a/test/test_utils.js b/test/test_utils.js index e03ef7a..5b39983 100644 --- a/test/test_utils.js +++ b/test/test_utils.js @@ -1,15 +1,16 @@ const fs = require('fs'); +const path = require('path'); -const deleteFolderSync = (path) => { - if (!fs.existsSync(path)) +const deleteFolderSync = (dir) => { + if (!fs.existsSync(dir)) return; - fs.readdirSync(path, {withFileTypes: true}).forEach((item) => { + fs.readdirSync(dir, {withFileTypes: true}).forEach((item) => { if (item.isDirectory()) - deleteFolderSync(`${path}/${item.name}`); + deleteFolderSync(path.join(dir,item.name)); else - fs.unlinkSync(`${path}/${item.name}`); + fs.unlinkSync(path.join(dir,item.name)); }); - fs.rmdirSync(path); + fs.rmdirSync(dir); }; module.exports = { From f9a2db38d2210454f7665123309701f41e5fc642 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20GOUIN?= Date: Thu, 20 Jun 2019 10:49:51 +0200 Subject: [PATCH 017/120] code convention --- src/app.js | 21 ++++++++++++--------- src/config.js | 2 +- src/file_walker.js | 12 ++++++------ src/postinstall.js | 2 +- test/app.test.js | 36 ++++++++++++++++++------------------ test/test_utils.js | 4 ++-- 6 files changed, 40 insertions(+), 37 deletions(-) diff --git a/src/app.js b/src/app.js index e825e6d..e51e450 100644 --- a/src/app.js +++ b/src/app.js @@ -45,19 +45,22 @@ module.exports = (config) => { const showError = (resPath, code, res) => { const errorPath = path.join(config['data_dir'],config['home']['error']); - if (fs.existsSync(errorPath)) - render(res, errorPath, {error: code, path: resPath}, code); - else - res.sendStatus(code); + fs.access(errorPath, (err) => { + if(err) + res.sendStatus(code); + else + render(res, errorPath, {error: code, path: resPath}, code); + }); }; app.get('/', (req, res) => { const homePath = `${config['data_dir']}/${config['home']['index']}`; - if (fs.existsSync(homePath)) - render(res, homePath, {articles: articles}); - else { - showError(req.path, 404, res); - } + fs.access(homePath,(err)=>{ + if(err) + showError(req.path, 404, res); + else + render(res, homePath, {articles: articles}); + }); }); app.get('*', express.static(config['data_dir'])); diff --git a/src/config.js b/src/config.js index fd44034..143af88 100644 --- a/src/config.js +++ b/src/config.js @@ -6,7 +6,7 @@ const merge = (ref, src) => { return ref; } else if (typeof ref === 'object') { const out = {}; - Object.keys(ref).forEach(key => out[key] = merge(ref[key], src[key])); + Object.keys(ref).forEach((key) =>out[key] = merge(ref[key], src[key])); return out; } else { return src; diff --git a/src/file_walker.js b/src/file_walker.js index 52ac51c..5ce2e57 100644 --- a/src/file_walker.js +++ b/src/file_walker.js @@ -54,16 +54,16 @@ module.exports = (config) => { if (err) return cb(err); const paths = fileList - .map(path => path.substr(config['data_dir'].length)) - .filter(path => path.indexOf(config['article']['index']) === path.length - config['article']['index'].length) - .map(path => path.substr(0, path.length - config['article']['index'].length)) - .map(path => path.match(/^\/(\d{4})\/(\d{2})\/(\d{2})\/$/)) - .filter(matches => matches && matches.length > 1); + .map((path) =>path.substr(config['data_dir'].length)) + .filter((path) =>path.indexOf(config['article']['index']) === path.length - config['article']['index'].length) + .map((path) =>path.substr(0, path.length - config['article']['index'].length)) + .map((path) =>path.match(/^\/(\d{4})\/(\d{2})\/(\d{2})\/$/)) + .filter((matches) =>matches && matches.length > 1); if (paths.length === 0) cb(null, []); const list = []; let remaining = 0; - paths.forEach(matches => { + paths.forEach((matches) =>{ const article = { path: path.join(config['data_dir'], matches[1], matches[2], matches[3], config['article']['index']), parent: path.join(config['data_dir'], matches[1], matches[2], matches[3]), diff --git a/src/postinstall.js b/src/postinstall.js index 8654aa5..e9acd6e 100644 --- a/src/postinstall.js +++ b/src/postinstall.js @@ -18,7 +18,7 @@ if (!fs.existsSync('data')) { copy(path.join('sample_data','home'), 'data'); - const pad0 = n => ('0' + n).substr(-2); + const pad0 = (n) =>('0' + n).substr(-2); const datetime = new Date(); const dir = path.join('data', datetime.getFullYear().toString(), pad0(datetime.getMonth() + 1), pad0(datetime.getDate())); diff --git a/test/app.test.js b/test/app.test.js index f9f2eae..62e4832 100644 --- a/test/app.test.js +++ b/test/app.test.js @@ -32,23 +32,23 @@ afterAll(() => { }); describe('Test root path', () => { - test('404 no index no error', done => { - request(app).get('/').then(response => { + test('404 no index no error', (done) =>{ + request(app).get('/').then((response) =>{ expect(response.statusCode).toBe(404); done(); }); }); - test('404 no index but error page', done => { + test('404 no index but error page', (done) =>{ fs.writeFileSync(path.join(dataDir,testError), 'error <%= error %> at <%= path %>'); - request(app).get('/').then(response => { + request(app).get('/').then((response) =>{ expect(response.statusCode).toBe(404); expect(response.text).toBe('error 404 at /'); done(); }); }); - test('200 index page', done => { + test('200 index page', (done) =>{ fs.writeFileSync(path.join(dataDir,testIndex), 'hello there'); - request(app).get('/').then(response => { + request(app).get('/').then((response) =>{ expect(response.statusCode).toBe(200); expect(response.text).toBe('hello there'); done(); @@ -58,23 +58,23 @@ describe('Test root path', () => { }); describe('Test static files', () => { - test('404 invalid file no error page', done => { - request(app).get('/somefile.txt').then(response => { + test('404 invalid file no error page', (done) =>{ + request(app).get('/somefile.txt').then((response) =>{ expect(response.statusCode).toBe(404); done(); }); }); - test('404 invalid file but error page', done => { + test('404 invalid file but error page', (done) =>{ fs.writeFileSync(path.join(dataDir,testError), 'error <%= error %> at <%= path %>'); - request(app).get('/somefile.txt').then(response => { + request(app).get('/somefile.txt').then((response) =>{ expect(response.statusCode).toBe(404); expect(response.text).toBe('error 404 at /somefile.txt'); done(); }); }); - test('200 valid file', done => { + test('200 valid file', (done) =>{ fs.writeFileSync(`${dataDir}/somefile.txt`, 'filecontent'); - request(app).get('/somefile.txt').then(response => { + request(app).get('/somefile.txt').then((response) =>{ expect(response.statusCode).toBe(200); expect(response.text).toBe('filecontent'); done(); @@ -83,20 +83,20 @@ describe('Test static files', () => { }); describe('Test other requests', () => { - test('400 POST', done => { - request(app).post('/').then(response => { + test('400 POST', (done) =>{ + request(app).post('/').then((response) =>{ expect(response.statusCode).toBe(400); done(); }); }); - test('400 PUT', done => { - request(app).put('/').then(response => { + test('400 PUT', (done) =>{ + request(app).put('/').then((response) =>{ expect(response.statusCode).toBe(400); done(); }); }); - test('400 DELETE', done => { - request(app).delete('/').then(response => { + test('400 DELETE', (done) =>{ + request(app).delete('/').then((response) =>{ expect(response.statusCode).toBe(400); done(); }); diff --git a/test/test_utils.js b/test/test_utils.js index 5b39983..a80a886 100644 --- a/test/test_utils.js +++ b/test/test_utils.js @@ -15,6 +15,6 @@ const deleteFolderSync = (dir) => { module.exports = { deleteFolderSync: deleteFolderSync, - createEmptyDirs: list => list.forEach(path => fs.mkdirSync(path, {recursive: true})), - createEmptyFiles: list => list.forEach(file => fs.writeFileSync(file, '')), + createEmptyDirs: (list) =>list.forEach((path) =>fs.mkdirSync(path, {recursive: true})), + createEmptyFiles: (list) =>list.forEach((file) =>fs.writeFileSync(file, '')), }; \ No newline at end of file From dcb4e0144703853aeabe8bd54e884eb286837706 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20GOUIN?= Date: Thu, 20 Jun 2019 10:50:48 +0200 Subject: [PATCH 018/120] added readability check for fs.access --- src/app.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/app.js b/src/app.js index e51e450..3c645b2 100644 --- a/src/app.js +++ b/src/app.js @@ -44,9 +44,9 @@ module.exports = (config) => { }; const showError = (resPath, code, res) => { - const errorPath = path.join(config['data_dir'],config['home']['error']); - fs.access(errorPath, (err) => { - if(err) + const errorPath = path.join(config['data_dir'], config['home']['error']); + fs.access(errorPath, fs.constants.R_OK, (err) => { + if (err) res.sendStatus(code); else render(res, errorPath, {error: code, path: resPath}, code); @@ -55,8 +55,8 @@ module.exports = (config) => { app.get('/', (req, res) => { const homePath = `${config['data_dir']}/${config['home']['index']}`; - fs.access(homePath,(err)=>{ - if(err) + fs.access(homePath, fs.constants.R_OK, (err) => { + if (err) showError(req.path, 404, res); else render(res, homePath, {articles: articles}); From e88eb94d78a7e2d65ae673e326468f058beefbb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20GOUIN?= Date: Thu, 20 Jun 2019 11:42:01 +0200 Subject: [PATCH 019/120] Home minimal EJS template --- sample_data/home/error.ejs | 12 ++++++++++++ sample_data/home/index.ejs | 17 +++++++++++++++-- sample_data/home/style.css | 18 ++++++++++++++++++ src/file_walker.js | 23 +++++++++++++---------- test/file_walker.test.js | 22 ++++++++++++++-------- 5 files changed, 72 insertions(+), 20 deletions(-) create mode 100644 sample_data/home/error.ejs create mode 100644 sample_data/home/style.css diff --git a/sample_data/home/error.ejs b/sample_data/home/error.ejs new file mode 100644 index 0000000..c2471f6 --- /dev/null +++ b/sample_data/home/error.ejs @@ -0,0 +1,12 @@ + + + + + Error <%= error %> + + +
+

Error <%= error %> at path <%= path %>

+
+ + \ No newline at end of file diff --git a/sample_data/home/index.ejs b/sample_data/home/index.ejs index 566549b..25c46d0 100644 --- a/sample_data/home/index.ejs +++ b/sample_data/home/index.ejs @@ -2,9 +2,22 @@ - Title + GitBlog.md - Home + - +
+

GitBlog.md

+ A static blog using Markdown pulled from your git repository +

Articles in this blog :

+ <% articles.forEach((article) => { %> +
+

<%- `${article.title}`%> (<%= `${article.day}/${article.month}/${article.year}`%>)

+ <% if(article.thumbnail){%> + <%- `thumbnail`%> + <% }%> +
+ <% }); %> +
\ No newline at end of file diff --git a/sample_data/home/style.css b/sample_data/home/style.css new file mode 100644 index 0000000..979ab2a --- /dev/null +++ b/sample_data/home/style.css @@ -0,0 +1,18 @@ +main { + max-width: 70ch; + padding: 2ch; + margin: auto; +} + +.article a, .article a:visited{ + color:black; +} + +.article a:visited{ + color:black; +} + +.article img{ + max-width:100%; + max-height:10vh; +} \ No newline at end of file diff --git a/src/file_walker.js b/src/file_walker.js index 5ce2e57..76647ba 100644 --- a/src/file_walker.js +++ b/src/file_walker.js @@ -54,29 +54,32 @@ module.exports = (config) => { if (err) return cb(err); const paths = fileList - .map((path) =>path.substr(config['data_dir'].length)) - .filter((path) =>path.indexOf(config['article']['index']) === path.length - config['article']['index'].length) - .map((path) =>path.substr(0, path.length - config['article']['index'].length)) - .map((path) =>path.match(/^\/(\d{4})\/(\d{2})\/(\d{2})\/$/)) - .filter((matches) =>matches && matches.length > 1); + .map((path) => path.substr(config['data_dir'].length)) + .filter((path) => path.indexOf(config['article']['index']) === path.length - config['article']['index'].length) + .map((path) => path.substr(0, path.length - config['article']['index'].length)) + .map((path) => path.match(/^\/(\d{4})\/(\d{2})\/(\d{2})\/$/)) + .filter((matches) => matches && matches.length > 1); if (paths.length === 0) cb(null, []); const list = []; let remaining = 0; - paths.forEach((matches) =>{ + paths.forEach((matches) => { const article = { - path: path.join(config['data_dir'], matches[1], matches[2], matches[3], config['article']['index']), - parent: path.join(config['data_dir'], matches[1], matches[2], matches[3]), + path: path.join(matches[1], matches[2], matches[3]), + realPath: path.join(config['data_dir'], matches[1], matches[2], matches[3]), year: parseInt(matches[1]), month: parseInt(matches[2]), day: parseInt(matches[3]) }; + article.date = new Date(article.year, article.month, article.day); remaining++; - readIndexFile(article.path, config['article']['thumbnail_tag'], (err, info) => { + readIndexFile(path.join(article.realPath, config['article']['index']), config['article']['thumbnail_tag'], (err, info) => { if (err) return cb(err); article.title = info.title || config['article']['default_title']; - article.thumbnail = info.thumbnail ? path.join(article.parent, info.thumbnail) : config['article']['default_thumbnail']; + article.thumbnail = info.thumbnail ? path.join(article.path, info.thumbnail) : config['article']['default_thumbnail']; + article.escapedTitle = article.title.toLowerCase().replace(/[^\w]/gm, ' ').trim().replace(/ /gm, '_'); + article.url = path.join(article.path, article.escapedTitle); list.push(article); remaining--; if (remaining === 0) diff --git a/test/file_walker.test.js b/test/file_walker.test.js index b60131a..c36a58e 100644 --- a/test/file_walker.test.js +++ b/test/file_walker.test.js @@ -225,13 +225,16 @@ describe('Test article fetching', () => { expect(list).toBeDefined(); expect(list.length).toBe(1); expect(list[0]).toEqual({ - path: file, - parent:dir, + path: path.join('2019', '05', '05'), + realPath: dir, year: 2019, month: 5, day: 5, + date : new Date(2019,5,5), title: 'Untitled', - thumbnail: 'default.png' + thumbnail: 'default.png', + escapedTitle: 'untitled', + url: path.join('2019', '05', '05', 'untitled'), }); done(); }); @@ -241,7 +244,7 @@ describe('Test article fetching', () => { const file = path.join(dir, testIndex); utils.createEmptyDirs([dir]); fs.writeFileSync(file, ` - # Title + # Title with : info ! ![thumbnail](./thumbnail.jpg) this is some text `); @@ -250,13 +253,16 @@ describe('Test article fetching', () => { expect(list).toBeDefined(); expect(list.length).toBe(1); expect(list[0]).toEqual({ - path: file, - parent:dir, + path: path.join('2019', '05', '05'), + realPath: dir, year: 2019, month: 5, day: 5, - title: 'Title', - thumbnail: path.join(dataDir, '2019', '05', '05', './thumbnail.jpg') + date : new Date(2019,5,5), + title: 'Title with : info !', + thumbnail: path.join('2019', '05', '05', './thumbnail.jpg'), + escapedTitle: 'title_with___info', + url: path.join('2019', '05', '05', 'title_with___info'), }); done(); }); From c755ea939d44004bb425f6fd94f77499dd6ea95f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20GOUIN?= Date: Thu, 20 Jun 2019 13:05:18 +0200 Subject: [PATCH 020/120] Hide given file extensions --- src/app.js | 10 ++++++++- src/config.default.json | 3 ++- test/app.test.js | 45 +++++++++++++++++++++++++++++++++++------ 3 files changed, 50 insertions(+), 8 deletions(-) diff --git a/src/app.js b/src/app.js index 3c645b2..b7d432b 100644 --- a/src/app.js +++ b/src/app.js @@ -17,7 +17,6 @@ module.exports = (config) => { const articles = []; - const reload = (callback) => { fw.fetchArticles((err, list) => { if (err) { @@ -32,6 +31,8 @@ module.exports = (config) => { callback(true); }); }; + if (config['test']) + app.reload = reload; const render = (res, path, data, code = 200) => { res.render(path, data, (err, html) => { @@ -63,6 +64,13 @@ module.exports = (config) => { }); }); + app.get('*', (req, res, next) => { + if (config['home']['hidden'].includes(path.extname(req.path))) + showError(req.path, 404, res); + else + next(); + }); + app.get('*', express.static(config['data_dir'])); app.get('*', (req, res) => { diff --git a/src/config.default.json b/src/config.default.json index 7427b2c..ece0152 100644 --- a/src/config.default.json +++ b/src/config.default.json @@ -9,7 +9,8 @@ }, "home": { "index": "index.ejs", - "error": "error.ejs" + "error": "error.ejs", + "hidden": [".ejs"] }, "article": { "index": "index.md", diff --git a/test/app.test.js b/test/app.test.js index 62e4832..ae3b5de 100644 --- a/test/app.test.js +++ b/test/app.test.js @@ -14,8 +14,15 @@ const config = { 'view_engine': 'ejs', 'home': { 'index': testIndex, - 'error': testError - } + 'error': testError, + 'hidden': ['.ejs','.test'] + }, + 'article': { + 'index': 'index.md', + 'thumbnail_tag': 'thumbnail', + 'default_title': 'Untitled', + 'default_thumbnail': null + }, }; const app = require('../src/app')(config); @@ -46,15 +53,34 @@ describe('Test root path', () => { done(); }); }); - test('200 index page', (done) =>{ - fs.writeFileSync(path.join(dataDir,testIndex), 'hello there'); + test('200 no articles', (done) =>{ + fs.writeFileSync(path.join(dataDir,testIndex), 'articles <%= articles.length %>'); request(app).get('/').then((response) =>{ expect(response.statusCode).toBe(200); - expect(response.text).toBe('hello there'); + expect(response.text).toBe('articles 0'); done(); }); }); - //TODO test articles list + test('200 2 articles', (done) =>{ + utils.createEmptyDirs([ + path.join(dataDir, '2019', '05', '05'), + path.join(dataDir, '2018', '05', '05') + ]); + utils.createEmptyFiles([ + path.join(dataDir, '2019', '05', '05','index.md'), + path.join(dataDir, '2018', '05', '05','index.md') + ]); + fs.writeFileSync(path.join(dataDir,testIndex), 'articles <%= articles.length %>'); + app.reload((res) => { + expect(res).toBe(true); + request(app).get('/').then((response) =>{ + expect(response.statusCode).toBe(200); + expect(response.text).toBe('articles 2'); + done(); + }); + }); + + }); }); describe('Test static files', () => { @@ -72,6 +98,13 @@ describe('Test static files', () => { done(); }); }); + test('404 hidden file', (done) =>{ + fs.writeFileSync(path.join(dataDir,'somefile.test'), ''); + request(app).get('/somefile.test').then((response) =>{ + expect(response.statusCode).toBe(404); + done(); + }); + }); test('200 valid file', (done) =>{ fs.writeFileSync(`${dataDir}/somefile.txt`, 'filecontent'); request(app).get('/somefile.txt').then((response) =>{ From 69d05e39002dbd2fb67f7b7072b541c716430328 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20GOUIN?= Date: Thu, 20 Jun 2019 13:15:51 +0200 Subject: [PATCH 021/120] Comments are good --- src/app.js | 30 +++++++++++++++++++++++++++++- src/config.js | 6 ++++++ src/file_walker.js | 15 +++++++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/src/app.js b/src/app.js index b7d432b..257490c 100644 --- a/src/app.js +++ b/src/app.js @@ -3,6 +3,10 @@ const app = express(); const fs = require('fs'); const path = require('path'); +/** + * Terminal colors and symbols to display status messages + * @type {{warn: string, ok: string, error: string}} + */ const cons = { ok: '\x1b[32m✔\x1b[0m %s', warn: '\x1b[33m⚠\x1b[0m %s', @@ -12,11 +16,17 @@ const cons = { module.exports = (config) => { const fw = require('./file_walker')(config); + // set view engine from configuration app.set('view engine', config['view_engine']); + // reroute the views folder to the root folder app.set('views', path.join(__dirname, '..')); const articles = []; + /** + * Fetch articles from the data folder and send success as a response + * @param callback + */ const reload = (callback) => { fw.fetchArticles((err, list) => { if (err) { @@ -34,6 +44,13 @@ module.exports = (config) => { if (config['test']) app.reload = reload; + /** + * Render the page with the view engine and catch errors + * @param res + * @param path - path of the view + * @param data - data to pass to the view + * @param code - code to send along the page + */ const render = (res, path, data, code = 200) => { res.render(path, data, (err, html) => { if (err) { @@ -44,6 +61,12 @@ module.exports = (config) => { }); }; + /** + * Show an error with the correct page + * @param resPath - the page of the original error + * @param code - error code + * @param res + */ const showError = (resPath, code, res) => { const errorPath = path.join(config['data_dir'], config['home']['error']); fs.access(errorPath, fs.constants.R_OK, (err) => { @@ -54,6 +77,7 @@ module.exports = (config) => { }); }; + // home endpoint : send the correct index page or error if not existing app.get('/', (req, res) => { const homePath = `${config['data_dir']}/${config['home']['index']}`; fs.access(homePath, fs.constants.R_OK, (err) => { @@ -64,6 +88,7 @@ module.exports = (config) => { }); }); + // catch all gets and return 404 if it's an hidden file type app.get('*', (req, res, next) => { if (config['home']['hidden'].includes(path.extname(req.path))) showError(req.path, 404, res); @@ -71,16 +96,19 @@ module.exports = (config) => { next(); }); + // serve all static files via get app.get('*', express.static(config['data_dir'])); - + // catch express.static errors (mostly not found) by displaying 404 app.get('*', (req, res) => { showError(req.path, 404, res); }); + // catch all other methods and return 400 app.all('*', (req, res) => { res.status(400).send('bad request'); }); + // must be use in a server.js to start the server app.start = () => { reload((res) => { if (res) diff --git a/src/config.js b/src/config.js index 143af88..3869386 100644 --- a/src/config.js +++ b/src/config.js @@ -1,6 +1,12 @@ const refConfig = require('./config.default.json'); const fs = require('fs'); +/** + * Merge resources by reading object keys and keeping reference value only if it's type is different from the source + * @param ref - reference object/value + * @param src - source object/value + * @returns {*} + */ const merge = (ref, src) => { if (typeof ref !== typeof src) { return ref; diff --git a/src/file_walker.js b/src/file_walker.js index 76647ba..98a2126 100644 --- a/src/file_walker.js +++ b/src/file_walker.js @@ -1,6 +1,11 @@ const fs = require('fs'); const path = require('path'); +/** + * Get all files path inside a given folder path + * @param dir + * @param cb + */ const getFileTree = (dir, cb) => { let list = []; let remaining = 0; @@ -27,6 +32,12 @@ const getFileTree = (dir, cb) => { }); }; +/** + * Tries to read a markdown file and match a title and a thumbnail + * @param path + * @param thumbnailTag - how the thumbnail image desc is given as ![thumbnailTag](url) + * @param cb + */ const readIndexFile = (path, thumbnailTag, cb) => { fs.readFile(path, {encoding: 'UTF-8'}, (err, data) => { if (err) @@ -49,6 +60,10 @@ module.exports = (config) => { return { fileTree: config['test'] ? getFileTree : undefined, readIndexFile: config['test'] ? readIndexFile : undefined, + /** + * find and read all articles inside the data directory + * @param cb + */ fetchArticles: (cb) => { getFileTree(config['data_dir'], (err, fileList) => { if (err) From b7a2fd0740aca50fecad454140ddfa5f1e14cf7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20GOUIN?= Date: Thu, 20 Jun 2019 13:49:44 +0200 Subject: [PATCH 022/120] Articles rendering system --- sample_data/home/template.ejs | 13 +++++++++ src/app.js | 40 ++++++++++++++++++++++---- src/config.default.json | 9 ++++++ src/file_walker.js | 8 +++--- src/renderer.js | 16 +++++++++++ test/file_walker.test.js | 4 +-- test/renderer.test.js | 53 +++++++++++++++++++++++++++++++++++ 7 files changed, 131 insertions(+), 12 deletions(-) create mode 100644 sample_data/home/template.ejs create mode 100644 src/renderer.js create mode 100644 test/renderer.test.js diff --git a/sample_data/home/template.ejs b/sample_data/home/template.ejs new file mode 100644 index 0000000..6cd69bf --- /dev/null +++ b/sample_data/home/template.ejs @@ -0,0 +1,13 @@ + + + + + GitBlog.md - Home + + + +
+ <%- article.content %> +
+ + \ No newline at end of file diff --git a/src/app.js b/src/app.js index 257490c..4b3d7bb 100644 --- a/src/app.js +++ b/src/app.js @@ -15,13 +15,14 @@ const cons = { module.exports = (config) => { const fw = require('./file_walker')(config); + const renderer = require('./renderer')(config); // set view engine from configuration app.set('view engine', config['view_engine']); // reroute the views folder to the root folder app.set('views', path.join(__dirname, '..')); - const articles = []; + const articles = {}; /** * Fetch articles from the data folder and send success as a response @@ -34,8 +35,9 @@ module.exports = (config) => { return console.error(cons.error, 'loading articles : ' + err); } articles.splice(0, articles.length, ...list); - if (articles.length > 0) - console.log(cons.ok, `loaded ${articles.length} article${articles.length > 1 ? 's' : ''}`); + const nb = Object.keys(articles).length; + if (nb > 0) + console.log(cons.ok, `loaded ${nb} article${nb > 1 ? 's' : ''}`); else console.log(cons.warn, `no articles loaded, check your configuration`); callback(true); @@ -79,16 +81,42 @@ module.exports = (config) => { // home endpoint : send the correct index page or error if not existing app.get('/', (req, res) => { - const homePath = `${config['data_dir']}/${config['home']['index']}`; + const homePath = path.join(config['data_dir'], config['home']['index']); fs.access(homePath, fs.constants.R_OK, (err) => { if (err) showError(req.path, 404, res); else - render(res, homePath, {articles: articles}); + render(res, homePath, {articles: Object.values(articles)}); }); }); - // catch all gets and return 404 if it's an hidden file type + // catch all article urls and render them + app.get('*', (req, res, next) => { + if (req.path.test(/^\/\d{4}\/\d{2}\/\d{2}\/(\w*\/)?$/)) { + const articlePath = req.path.substr(0, 11); + const article = articles[articlePath]; + if (!article) + showError(req.path, 404, res); + else { + renderer.render(article.realPath, (err, html) => { + if (err) + return showError(req.path, 500, res); + article.content = html; + const templatePath = path.join(config['data_dir'], config['home']['index']); + fs.access(templatePath, fs.constants.R_OK, (err) => { + if (err) + showError(req.path, 404, res); + else + render(res, templatePath, {article: article}); + }); + }); + } + } else { + next(); + } + }); + + // catch all hidden file type and return 404 app.get('*', (req, res, next) => { if (config['home']['hidden'].includes(path.extname(req.path))) showError(req.path, 404, res); diff --git a/src/config.default.json b/src/config.default.json index ece0152..065a961 100644 --- a/src/config.default.json +++ b/src/config.default.json @@ -14,6 +14,7 @@ }, "article": { "index": "index.md", + "template": "template.ejs", "thumbnail_tag": "thumbnail", "default_title": "Untitled", "default_thumbnail": null @@ -25,5 +26,13 @@ "webhook": { "endpoint": "/webhook", "secretFile": "git_secret" + }, + "showdown": { + "parseImgDimensions": true, + "strikethrough": true, + "tables": true, + "tasklists": true, + "openLinksInNewWindow": true, + "emoji": true } } \ No newline at end of file diff --git a/src/file_walker.js b/src/file_walker.js index 98a2126..ac30e12 100644 --- a/src/file_walker.js +++ b/src/file_walker.js @@ -76,7 +76,7 @@ module.exports = (config) => { .filter((matches) => matches && matches.length > 1); if (paths.length === 0) cb(null, []); - const list = []; + const articles = {}; let remaining = 0; paths.forEach((matches) => { const article = { @@ -94,11 +94,11 @@ module.exports = (config) => { article.title = info.title || config['article']['default_title']; article.thumbnail = info.thumbnail ? path.join(article.path, info.thumbnail) : config['article']['default_thumbnail']; article.escapedTitle = article.title.toLowerCase().replace(/[^\w]/gm, ' ').trim().replace(/ /gm, '_'); - article.url = path.join(article.path, article.escapedTitle); - list.push(article); + article.url = path.join(article.path, article.escapedTitle)+'/'; + articles[article.path]=article; remaining--; if (remaining === 0) - cb(null, list); + cb(null, articles); }); }); diff --git a/src/renderer.js b/src/renderer.js new file mode 100644 index 0000000..139956b --- /dev/null +++ b/src/renderer.js @@ -0,0 +1,16 @@ +const fs = require('fs'); +const showdown = require('showdown'); + +module.exports = (config) => { + const converter = new showdown.Converter(config['showdown']); + return { + render : (file, cb) => { + fs.readFile(file, {encoding:'UTF-8'}, (err, data) => { + if(err) + return cb(err); + const html = converter.makeHtml(data); + cb(null,html); + }); + } + }; +}; \ No newline at end of file diff --git a/test/file_walker.test.js b/test/file_walker.test.js index c36a58e..70abf86 100644 --- a/test/file_walker.test.js +++ b/test/file_walker.test.js @@ -234,7 +234,7 @@ describe('Test article fetching', () => { title: 'Untitled', thumbnail: 'default.png', escapedTitle: 'untitled', - url: path.join('2019', '05', '05', 'untitled'), + url: path.join('2019', '05', '05', 'untitled')+'/', }); done(); }); @@ -262,7 +262,7 @@ describe('Test article fetching', () => { title: 'Title with : info !', thumbnail: path.join('2019', '05', '05', './thumbnail.jpg'), escapedTitle: 'title_with___info', - url: path.join('2019', '05', '05', 'title_with___info'), + url: path.join('2019', '05', '05', 'title_with___info')+'/', }); done(); }); diff --git a/test/renderer.test.js b/test/renderer.test.js new file mode 100644 index 0000000..1a42d27 --- /dev/null +++ b/test/renderer.test.js @@ -0,0 +1,53 @@ +/* jshint -W117 */ +const fs = require('fs'); +const path = require('path'); +const utils = require('./test_utils'); + +const dataDir = 'test_data'; +const file = path.join(dataDir, 'test.md'); + +const config = { + 'showdown': { + 'simplifiedAutoLink': true, + 'smartIndentationFix': true + } +}; + +const renderer = require('../src/renderer')(config); + +beforeEach(() => { + utils.deleteFolderSync(dataDir); + fs.mkdirSync(dataDir); +}); + +afterAll(() => { + if (fs.existsSync(dataDir)) { + utils.deleteFolderSync(dataDir); + } +}); + +test('invalid file', (done) => { + renderer.render('invalid file', (err, html) => { + expect(err).not.toBeNull(); + expect(html).not.toBeDefined(); + done(); + }); +}); + +test('normal file', (done) => { + fs.writeFileSync(file, `# Hello`); + renderer.render(file, (err, html) => { + expect(err).toBeNull(); + expect(html).toBe('

Hello

'); + done(); + }); +}); + +test('custom rules', (done) => { + fs.writeFileSync(file, `www.google.com`); + renderer.render(file, (err, html) => { + expect(err).toBeNull(); + expect(html).toBe('

www.google.com

'); + done(); + }); +}); \ No newline at end of file From 3df43b4872faecf9731e8553f6ceb9fc56cdeb67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20GOUIN?= Date: Thu, 20 Jun 2019 14:18:49 +0200 Subject: [PATCH 023/120] Article rendering tests --- src/app.js | 38 ++++++++++++++-------- src/file_walker.js | 6 ++-- test/app.test.js | 69 ++++++++++++++++++++++++++++++++++++++++ test/file_walker.test.js | 32 +++++++++---------- 4 files changed, 112 insertions(+), 33 deletions(-) diff --git a/src/app.js b/src/app.js index 4b3d7bb..f7c2495 100644 --- a/src/app.js +++ b/src/app.js @@ -29,12 +29,13 @@ module.exports = (config) => { * @param callback */ const reload = (callback) => { - fw.fetchArticles((err, list) => { + fw.fetchArticles((err, dict) => { if (err) { callback(false); return console.error(cons.error, 'loading articles : ' + err); } - articles.splice(0, articles.length, ...list); + Object.keys(articles).forEach((key) => delete articles[key]); + Object.keys(dict).forEach((key) => articles[key] = dict[key]); const nb = Object.keys(articles).length; if (nb > 0) console.log(cons.ok, `loaded ${nb} article${nb > 1 ? 's' : ''}`); @@ -49,15 +50,15 @@ module.exports = (config) => { /** * Render the page with the view engine and catch errors * @param res - * @param path - path of the view + * @param vPath - path of the view * @param data - data to pass to the view * @param code - code to send along the page */ - const render = (res, path, data, code = 200) => { - res.render(path, data, (err, html) => { + const render = (res, vPath, data, code = 200) => { + res.render(vPath, data, (err, html) => { if (err) { res.sendStatus(500); - console.log(cons.error, `failed to render ${path} : ${err}`); + console.log(cons.error, `failed to render ${vPath} : ${err}`); } else res.status(code).send(html); }); @@ -92,21 +93,24 @@ module.exports = (config) => { // catch all article urls and render them app.get('*', (req, res, next) => { - if (req.path.test(/^\/\d{4}\/\d{2}\/\d{2}\/(\w*\/)?$/)) { - const articlePath = req.path.substr(0, 11); + if (/^\/\d{4}\/\d{2}\/\d{2}\/(\w*\/)?$/.test(req.path)) { + const articlePath = req.path.substr(1, 10); const article = articles[articlePath]; if (!article) showError(req.path, 404, res); else { - renderer.render(article.realPath, (err, html) => { - if (err) + renderer.render(path.join(article.realPath, config['article']['index']), (err, html) => { + if (err) { + console.log(cons.error, `failed to render article ${req.path} : ${err}`); return showError(req.path, 500, res); + } article.content = html; - const templatePath = path.join(config['data_dir'], config['home']['index']); + const templatePath = path.join(config['data_dir'], config['article']['template']); fs.access(templatePath, fs.constants.R_OK, (err) => { - if (err) - showError(req.path, 404, res); - else + if (err) { + console.log(cons.error, `no template found at ${templatePath}`); + showError(req.path, 500, res); + } else render(res, templatePath, {article: article}); }); }); @@ -136,6 +140,12 @@ module.exports = (config) => { res.status(400).send('bad request'); }); + app.use((err, req, res, next) => { + console.log(cons.error, `error when handling ${req.path} request : ${err}`); + console.error(err.stack); + next(err); + }); + // must be use in a server.js to start the server app.start = () => { reload((res) => { diff --git a/src/file_walker.js b/src/file_walker.js index ac30e12..d336b7d 100644 --- a/src/file_walker.js +++ b/src/file_walker.js @@ -75,7 +75,7 @@ module.exports = (config) => { .map((path) => path.match(/^\/(\d{4})\/(\d{2})\/(\d{2})\/$/)) .filter((matches) => matches && matches.length > 1); if (paths.length === 0) - cb(null, []); + cb(null, {}); const articles = {}; let remaining = 0; paths.forEach((matches) => { @@ -94,8 +94,8 @@ module.exports = (config) => { article.title = info.title || config['article']['default_title']; article.thumbnail = info.thumbnail ? path.join(article.path, info.thumbnail) : config['article']['default_thumbnail']; article.escapedTitle = article.title.toLowerCase().replace(/[^\w]/gm, ' ').trim().replace(/ /gm, '_'); - article.url = path.join(article.path, article.escapedTitle)+'/'; - articles[article.path]=article; + article.url = '/' + path.join(article.path, article.escapedTitle) + '/'; + articles[article.path] = article; remaining--; if (remaining === 0) cb(null, articles); diff --git a/test/app.test.js b/test/app.test.js index ae3b5de..ccc5953 100644 --- a/test/app.test.js +++ b/test/app.test.js @@ -7,6 +7,7 @@ const utils = require('./test_utils'); const dataDir = 'test_data'; const testIndex = 'testindex.ejs'; const testError = 'testerror.ejs'; +const testTemplate = 'testtemplate.ejs'; const config = { 'test': true, @@ -19,10 +20,12 @@ const config = { }, 'article': { 'index': 'index.md', + 'template' : testTemplate, 'thumbnail_tag': 'thumbnail', 'default_title': 'Untitled', 'default_thumbnail': null }, + 'showdown' : {} }; const app = require('../src/app')(config); @@ -83,6 +86,72 @@ describe('Test root path', () => { }); }); +describe('Test articles rendering', () => { + test('404 article not found', (done) =>{ + request(app).get('/2019/05/06/untitled/').then((response) =>{ + expect(response.statusCode).toBe(404); + done(); + }); + }); + + test('500 no template', (done) =>{ + utils.createEmptyDirs([path.join(dataDir, '2019', '05', '05'),]); + fs.writeFileSync(path.join(dataDir, '2019', '05', '05','index.md'), '# Hello'); + app.reload((res) => { + expect(res).toBe(true); + request(app).get('/2019/05/05/hello/').then((response) =>{ + expect(response.statusCode).toBe(500); + done(); + }); + }); + }); + + test('200 rendered article', (done) =>{ + utils.createEmptyDirs([path.join(dataDir, '2019', '05', '05'),]); + fs.writeFileSync(path.join(dataDir, '2019', '05', '05','index.md'), '# Hello'); + fs.writeFileSync(path.join(dataDir, testTemplate), '<%- article.content %><%- `reload` %>'); + app.reload((res) => { + expect(res).toBe(true); + request(app).get('/2019/05/05/hello/').then((response) =>{ + expect(response.statusCode).toBe(200); + expect(response.text).toBe('

Hello

reload'); + done(); + }); + }); + }); + + test('200 other url', (done) =>{ + utils.createEmptyDirs([path.join(dataDir, '2019', '05', '05'),]); + utils.createEmptyFiles([ + path.join(dataDir, '2019', '05', '05','index.md'), + path.join(dataDir, testTemplate) + ]); + app.reload((res) => { + expect(res).toBe(true); + request(app).get('/2019/05/05/anything/').then((response) =>{ + expect(response.statusCode).toBe(200); + done(); + }); + }); + }); + + test('200 other url 2', (done) =>{ + utils.createEmptyDirs([path.join(dataDir, '2019', '05', '05'),]); + utils.createEmptyFiles([ + path.join(dataDir, '2019', '05', '05','index.md'), + path.join(dataDir, testTemplate) + ]); + app.reload((res) => { + expect(res).toBe(true); + request(app).get('/2019/05/05/').then((response) =>{ + expect(response.statusCode).toBe(200); + done(); + }); + }); + }); +}); + + describe('Test static files', () => { test('404 invalid file no error page', (done) =>{ request(app).get('/somefile.txt').then((response) =>{ diff --git a/test/file_walker.test.js b/test/file_walker.test.js index 70abf86..a3e9cd5 100644 --- a/test/file_walker.test.js +++ b/test/file_walker.test.js @@ -191,10 +191,10 @@ describe('Test article fetching', () => { }); }); test('empty data dir', (done) => { - fw.fetchArticles((err, list) => { + fw.fetchArticles((err, dict) => { expect(err).toBeNull(); - expect(list).toBeDefined(); - expect(list.length).toBe(0); + expect(dict).toBeDefined(); + expect(Object.keys(dict).length).toBe(0); done(); }); }); @@ -208,10 +208,10 @@ describe('Test article fetching', () => { path.join(dataDir, 'test', 'test', testIndex), path.join(dataDir, '2019', '05', testIndex) ]); - fw.fetchArticles((err, list) => { + fw.fetchArticles((err, dict) => { expect(err).toBeNull(); - expect(list).toBeDefined(); - expect(list.length).toBe(0); + expect(dict).toBeDefined(); + expect(Object.keys(dict).length).toBe(0); done(); }); }); @@ -220,11 +220,11 @@ describe('Test article fetching', () => { const file = path.join(dir, testIndex); utils.createEmptyDirs([dir]); utils.createEmptyFiles([file]); - fw.fetchArticles((err, list) => { + fw.fetchArticles((err, dict) => { expect(err).toBeNull(); - expect(list).toBeDefined(); - expect(list.length).toBe(1); - expect(list[0]).toEqual({ + expect(dict).toBeDefined(); + expect(Object.keys(dict).length).toBe(1); + expect(dict[path.join('2019', '05', '05')]).toEqual({ path: path.join('2019', '05', '05'), realPath: dir, year: 2019, @@ -234,7 +234,7 @@ describe('Test article fetching', () => { title: 'Untitled', thumbnail: 'default.png', escapedTitle: 'untitled', - url: path.join('2019', '05', '05', 'untitled')+'/', + url: '/'+path.join('2019', '05', '05', 'untitled')+'/', }); done(); }); @@ -248,11 +248,11 @@ describe('Test article fetching', () => { ![thumbnail](./thumbnail.jpg) this is some text `); - fw.fetchArticles((err, list) => { + fw.fetchArticles((err, dict) => { expect(err).toBeNull(); - expect(list).toBeDefined(); - expect(list.length).toBe(1); - expect(list[0]).toEqual({ + expect(dict).toBeDefined(); + expect(Object.keys(dict).length).toBe(1); + expect(dict[path.join('2019', '05', '05')]).toEqual({ path: path.join('2019', '05', '05'), realPath: dir, year: 2019, @@ -262,7 +262,7 @@ describe('Test article fetching', () => { title: 'Title with : info !', thumbnail: path.join('2019', '05', '05', './thumbnail.jpg'), escapedTitle: 'title_with___info', - url: path.join('2019', '05', '05', 'title_with___info')+'/', + url: '/'+path.join('2019', '05', '05', 'title_with___info')+'/', }); done(); }); From 92021164c98fb4ac0b12db2260bd46fd503966b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20GOUIN?= Date: Thu, 20 Jun 2019 14:24:34 +0200 Subject: [PATCH 024/120] Code formatting with 2 space indent --- src/app.js | 264 +++++++++++++------------- src/config.default.json | 4 +- src/config.js | 34 ++-- src/file_walker.js | 156 +++++++-------- src/postinstall.js | 28 +-- src/renderer.js | 22 +-- test/app.test.js | 304 ++++++++++++++--------------- test/config.test.js | 62 +++--- test/file_walker.test.js | 400 +++++++++++++++++++-------------------- test/renderer.test.js | 52 ++--- test/test_utils.js | 24 +-- 11 files changed, 676 insertions(+), 674 deletions(-) diff --git a/src/app.js b/src/app.js index f7c2495..b4c129b 100644 --- a/src/app.js +++ b/src/app.js @@ -8,155 +8,155 @@ const path = require('path'); * @type {{warn: string, ok: string, error: string}} */ const cons = { - ok: '\x1b[32m✔\x1b[0m %s', - warn: '\x1b[33m⚠\x1b[0m %s', - error: '\x1b[31m✘\x1b[0m %s', + ok: '\x1b[32m✔\x1b[0m %s', + warn: '\x1b[33m⚠\x1b[0m %s', + error: '\x1b[31m✘\x1b[0m %s', }; module.exports = (config) => { - const fw = require('./file_walker')(config); - const renderer = require('./renderer')(config); + const fw = require('./file_walker')(config); + const renderer = require('./renderer')(config); - // set view engine from configuration - app.set('view engine', config['view_engine']); - // reroute the views folder to the root folder - app.set('views', path.join(__dirname, '..')); + // set view engine from configuration + app.set('view engine', config['view_engine']); + // reroute the views folder to the root folder + app.set('views', path.join(__dirname, '..')); - const articles = {}; + const articles = {}; - /** - * Fetch articles from the data folder and send success as a response - * @param callback - */ - const reload = (callback) => { - fw.fetchArticles((err, dict) => { - if (err) { - callback(false); - return console.error(cons.error, 'loading articles : ' + err); - } - Object.keys(articles).forEach((key) => delete articles[key]); - Object.keys(dict).forEach((key) => articles[key] = dict[key]); - const nb = Object.keys(articles).length; - if (nb > 0) - console.log(cons.ok, `loaded ${nb} article${nb > 1 ? 's' : ''}`); - else - console.log(cons.warn, `no articles loaded, check your configuration`); - callback(true); - }); - }; - if (config['test']) - app.reload = reload; - - /** - * Render the page with the view engine and catch errors - * @param res - * @param vPath - path of the view - * @param data - data to pass to the view - * @param code - code to send along the page - */ - const render = (res, vPath, data, code = 200) => { - res.render(vPath, data, (err, html) => { - if (err) { - res.sendStatus(500); - console.log(cons.error, `failed to render ${vPath} : ${err}`); - } else - res.status(code).send(html); - }); - }; - - /** - * Show an error with the correct page - * @param resPath - the page of the original error - * @param code - error code - * @param res - */ - const showError = (resPath, code, res) => { - const errorPath = path.join(config['data_dir'], config['home']['error']); - fs.access(errorPath, fs.constants.R_OK, (err) => { - if (err) - res.sendStatus(code); - else - render(res, errorPath, {error: code, path: resPath}, code); - }); - }; - - // home endpoint : send the correct index page or error if not existing - app.get('/', (req, res) => { - const homePath = path.join(config['data_dir'], config['home']['index']); - fs.access(homePath, fs.constants.R_OK, (err) => { - if (err) - showError(req.path, 404, res); - else - render(res, homePath, {articles: Object.values(articles)}); - }); + /** + * Fetch articles from the data folder and send success as a response + * @param callback + */ + const reload = (callback) => { + fw.fetchArticles((err, dict) => { + if (err) { + callback(false); + return console.error(cons.error, 'loading articles : ' + err); + } + Object.keys(articles).forEach((key) => delete articles[key]); + Object.keys(dict).forEach((key) => articles[key] = dict[key]); + const nb = Object.keys(articles).length; + if (nb > 0) + console.log(cons.ok, `loaded ${nb} article${nb > 1 ? 's' : ''}`); + else + console.log(cons.warn, `no articles loaded, check your configuration`); + callback(true); }); + }; + if (config['test']) + app.reload = reload; - // catch all article urls and render them - app.get('*', (req, res, next) => { - if (/^\/\d{4}\/\d{2}\/\d{2}\/(\w*\/)?$/.test(req.path)) { - const articlePath = req.path.substr(1, 10); - const article = articles[articlePath]; - if (!article) - showError(req.path, 404, res); - else { - renderer.render(path.join(article.realPath, config['article']['index']), (err, html) => { - if (err) { - console.log(cons.error, `failed to render article ${req.path} : ${err}`); - return showError(req.path, 500, res); - } - article.content = html; - const templatePath = path.join(config['data_dir'], config['article']['template']); - fs.access(templatePath, fs.constants.R_OK, (err) => { - if (err) { - console.log(cons.error, `no template found at ${templatePath}`); - showError(req.path, 500, res); - } else - render(res, templatePath, {article: article}); - }); - }); - } - } else { - next(); - } + /** + * Render the page with the view engine and catch errors + * @param res + * @param vPath - path of the view + * @param data - data to pass to the view + * @param code - code to send along the page + */ + const render = (res, vPath, data, code = 200) => { + res.render(vPath, data, (err, html) => { + if (err) { + res.sendStatus(500); + console.log(cons.error, `failed to render ${vPath} : ${err}`); + } else + res.status(code).send(html); }); + }; - // catch all hidden file type and return 404 - app.get('*', (req, res, next) => { - if (config['home']['hidden'].includes(path.extname(req.path))) - showError(req.path, 404, res); - else - next(); + /** + * Show an error with the correct page + * @param resPath - the page of the original error + * @param code - error code + * @param res + */ + const showError = (resPath, code, res) => { + const errorPath = path.join(config['data_dir'], config['home']['error']); + fs.access(errorPath, fs.constants.R_OK, (err) => { + if (err) + res.sendStatus(code); + else + render(res, errorPath, {error: code, path: resPath}, code); }); + }; - // serve all static files via get - app.get('*', express.static(config['data_dir'])); - // catch express.static errors (mostly not found) by displaying 404 - app.get('*', (req, res) => { + // home endpoint : send the correct index page or error if not existing + app.get('/', (req, res) => { + const homePath = path.join(config['data_dir'], config['home']['index']); + fs.access(homePath, fs.constants.R_OK, (err) => { + if (err) showError(req.path, 404, res); + else + render(res, homePath, {articles: Object.values(articles)}); }); + }); - // catch all other methods and return 400 - app.all('*', (req, res) => { - res.status(400).send('bad request'); - }); - - app.use((err, req, res, next) => { - console.log(cons.error, `error when handling ${req.path} request : ${err}`); - console.error(err.stack); - next(err); - }); - - // must be use in a server.js to start the server - app.start = () => { - reload((res) => { - if (res) - app.listen(config['node_port'], () => { - console.log(cons.ok, `gitblog.md server listening on port ${config['node_port']}`); - }); + // catch all article urls and render them + app.get('*', (req, res, next) => { + if (/^\/\d{4}\/\d{2}\/\d{2}\/(\w*\/)?$/.test(req.path)) { + const articlePath = req.path.substr(1, 10); + const article = articles[articlePath]; + if (!article) + showError(req.path, 404, res); + else { + renderer.render(path.join(article.realPath, config['article']['index']), (err, html) => { + if (err) { + console.log(cons.error, `failed to render article ${req.path} : ${err}`); + return showError(req.path, 500, res); + } + article.content = html; + const templatePath = path.join(config['data_dir'], config['article']['template']); + fs.access(templatePath, fs.constants.R_OK, (err) => { + if (err) { + console.log(cons.error, `no template found at ${templatePath}`); + showError(req.path, 500, res); + } else + render(res, templatePath, {article: article}); + }); }); - }; + } + } else { + next(); + } + }); - return app; + // catch all hidden file type and return 404 + app.get('*', (req, res, next) => { + if (config['home']['hidden'].includes(path.extname(req.path))) + showError(req.path, 404, res); + else + next(); + }); + + // serve all static files via get + app.get('*', express.static(config['data_dir'])); + // catch express.static errors (mostly not found) by displaying 404 + app.get('*', (req, res) => { + showError(req.path, 404, res); + }); + + // catch all other methods and return 400 + app.all('*', (req, res) => { + res.status(400).send('bad request'); + }); + + app.use((err, req, res, next) => { + console.log(cons.error, `error when handling ${req.path} request : ${err}`); + console.error(err.stack); + next(err); + }); + + // must be use in a server.js to start the server + app.start = () => { + reload((res) => { + if (res) + app.listen(config['node_port'], () => { + console.log(cons.ok, `gitblog.md server listening on port ${config['node_port']}`); + }); + }); + }; + + return app; }; diff --git a/src/config.default.json b/src/config.default.json index 065a961..25acebc 100644 --- a/src/config.default.json +++ b/src/config.default.json @@ -10,7 +10,9 @@ "home": { "index": "index.ejs", "error": "error.ejs", - "hidden": [".ejs"] + "hidden": [ + ".ejs" + ] }, "article": { "index": "index.md", diff --git a/src/config.js b/src/config.js index 3869386..a51b3c7 100644 --- a/src/config.js +++ b/src/config.js @@ -8,24 +8,24 @@ const fs = require('fs'); * @returns {*} */ const merge = (ref, src) => { - if (typeof ref !== typeof src) { - return ref; - } else if (typeof ref === 'object') { - const out = {}; - Object.keys(ref).forEach((key) =>out[key] = merge(ref[key], src[key])); - return out; - } else { - return src; - } + if (typeof ref !== typeof src) { + return ref; + } else if (typeof ref === 'object') { + const out = {}; + Object.keys(ref).forEach((key) => out[key] = merge(ref[key], src[key])); + return out; + } else { + return src; + } }; module.exports = () => { - try { - let configData = fs.readFileSync('config.json', {encoding:'UTF-8'}); - let config = JSON.parse(configData); - return merge(refConfig, config); - } catch (error) { - console.error('Failed to load config.json : '+error); - return refConfig; - } + try { + let configData = fs.readFileSync('config.json', {encoding: 'UTF-8'}); + let config = JSON.parse(configData); + return merge(refConfig, config); + } catch (error) { + console.error('Failed to load config.json : ' + error); + return refConfig; + } }; \ No newline at end of file diff --git a/src/file_walker.js b/src/file_walker.js index d336b7d..d545359 100644 --- a/src/file_walker.js +++ b/src/file_walker.js @@ -7,29 +7,29 @@ const path = require('path'); * @param cb */ const getFileTree = (dir, cb) => { - let list = []; - let remaining = 0; - fs.readdir(dir, {withFileTypes: true}, (err, items) => { - if (err) + let list = []; + let remaining = 0; + fs.readdir(dir, {withFileTypes: true}, (err, items) => { + if (err) + return cb(err); + items.forEach((item) => { + if (item.isDirectory()) { + remaining++; + getFileTree(path.join(dir, item.name), (err, out) => { + if (err) return cb(err); - items.forEach((item) => { - if (item.isDirectory()) { - remaining++; - getFileTree(path.join(dir, item.name), (err, out) => { - if (err) - return cb(err); - list.push(...out); - remaining--; - if (remaining === 0) - cb(null, list); - }); - } else { - list.push(path.join(dir, item.name)); - } - }); - if (remaining === 0) + list.push(...out); + remaining--; + if (remaining === 0) cb(null, list); + }); + } else { + list.push(path.join(dir, item.name)); + } }); + if (remaining === 0) + cb(null, list); + }); }; /** @@ -39,70 +39,70 @@ const getFileTree = (dir, cb) => { * @param cb */ const readIndexFile = (path, thumbnailTag, cb) => { - fs.readFile(path, {encoding: 'UTF-8'}, (err, data) => { - if (err) - return cb(err); + fs.readFile(path, {encoding: 'UTF-8'}, (err, data) => { + if (err) + return cb(err); - let info = {}; + let info = {}; - const regRes1 = data.match(/(^|[^#])#([^#\r\n]*)\r?\n?$/m); - info.title = regRes1 ? regRes1[2].trim() : undefined; + const regRes1 = data.match(/(^|[^#])#([^#\r\n]*)\r?\n?$/m); + info.title = regRes1 ? regRes1[2].trim() : undefined; - const thumbnailRegEx = new RegExp(`!\\[${thumbnailTag}]\\(([^)]*)\\)`, 'i'); - const regRes2 = data.match(thumbnailRegEx); - info.thumbnail = regRes2 ? regRes2[1].trim() : undefined; + const thumbnailRegEx = new RegExp(`!\\[${thumbnailTag}]\\(([^)]*)\\)`, 'i'); + const regRes2 = data.match(thumbnailRegEx); + info.thumbnail = regRes2 ? regRes2[1].trim() : undefined; - cb(null, info); - }); + cb(null, info); + }); }; module.exports = (config) => { - return { - fileTree: config['test'] ? getFileTree : undefined, - readIndexFile: config['test'] ? readIndexFile : undefined, - /** - * find and read all articles inside the data directory - * @param cb - */ - fetchArticles: (cb) => { - getFileTree(config['data_dir'], (err, fileList) => { - if (err) - return cb(err); - const paths = fileList - .map((path) => path.substr(config['data_dir'].length)) - .filter((path) => path.indexOf(config['article']['index']) === path.length - config['article']['index'].length) - .map((path) => path.substr(0, path.length - config['article']['index'].length)) - .map((path) => path.match(/^\/(\d{4})\/(\d{2})\/(\d{2})\/$/)) - .filter((matches) => matches && matches.length > 1); - if (paths.length === 0) - cb(null, {}); - const articles = {}; - let remaining = 0; - paths.forEach((matches) => { - const article = { - path: path.join(matches[1], matches[2], matches[3]), - realPath: path.join(config['data_dir'], matches[1], matches[2], matches[3]), - year: parseInt(matches[1]), - month: parseInt(matches[2]), - day: parseInt(matches[3]) - }; - article.date = new Date(article.year, article.month, article.day); - remaining++; - readIndexFile(path.join(article.realPath, config['article']['index']), config['article']['thumbnail_tag'], (err, info) => { - if (err) - return cb(err); - article.title = info.title || config['article']['default_title']; - article.thumbnail = info.thumbnail ? path.join(article.path, info.thumbnail) : config['article']['default_thumbnail']; - article.escapedTitle = article.title.toLowerCase().replace(/[^\w]/gm, ' ').trim().replace(/ /gm, '_'); - article.url = '/' + path.join(article.path, article.escapedTitle) + '/'; - articles[article.path] = article; - remaining--; - if (remaining === 0) - cb(null, articles); - }); - }); + return { + fileTree: config['test'] ? getFileTree : undefined, + readIndexFile: config['test'] ? readIndexFile : undefined, + /** + * find and read all articles inside the data directory + * @param cb + */ + fetchArticles: (cb) => { + getFileTree(config['data_dir'], (err, fileList) => { + if (err) + return cb(err); + const paths = fileList + .map((path) => path.substr(config['data_dir'].length)) + .filter((path) => path.indexOf(config['article']['index']) === path.length - config['article']['index'].length) + .map((path) => path.substr(0, path.length - config['article']['index'].length)) + .map((path) => path.match(/^\/(\d{4})\/(\d{2})\/(\d{2})\/$/)) + .filter((matches) => matches && matches.length > 1); + if (paths.length === 0) + cb(null, {}); + const articles = {}; + let remaining = 0; + paths.forEach((matches) => { + const article = { + path: path.join(matches[1], matches[2], matches[3]), + realPath: path.join(config['data_dir'], matches[1], matches[2], matches[3]), + year: parseInt(matches[1]), + month: parseInt(matches[2]), + day: parseInt(matches[3]) + }; + article.date = new Date(article.year, article.month, article.day); + remaining++; + readIndexFile(path.join(article.realPath, config['article']['index']), config['article']['thumbnail_tag'], (err, info) => { + if (err) + return cb(err); + article.title = info.title || config['article']['default_title']; + article.thumbnail = info.thumbnail ? path.join(article.path, info.thumbnail) : config['article']['default_thumbnail']; + article.escapedTitle = article.title.toLowerCase().replace(/[^\w]/gm, ' ').trim().replace(/ /gm, '_'); + article.url = '/' + path.join(article.path, article.escapedTitle) + '/'; + articles[article.path] = article; + remaining--; + if (remaining === 0) + cb(null, articles); + }); + }); - }); - } - }; + }); + } + }; }; \ No newline at end of file diff --git a/src/postinstall.js b/src/postinstall.js index e9acd6e..e5466ac 100644 --- a/src/postinstall.js +++ b/src/postinstall.js @@ -3,28 +3,28 @@ const path = require('path'); const ncp = require('ncp').ncp; const copy = (src, dest) => { - ncp(src, dest, function (err) { - if (err) - console.error(err); - else - console.log(`copied ${src} to ${dest}`); - }); + ncp(src, dest, function (err) { + if (err) + console.error(err); + else + console.log(`copied ${src} to ${dest}`); + }); }; copy(path.join('src', 'config.default.json'), 'config.example.json'); if (!fs.existsSync('data')) { - fs.mkdirSync('data'); + fs.mkdirSync('data'); - copy(path.join('sample_data','home'), 'data'); + copy(path.join('sample_data', 'home'), 'data'); - const pad0 = (n) =>('0' + n).substr(-2); + const pad0 = (n) => ('0' + n).substr(-2); - const datetime = new Date(); - const dir = path.join('data', datetime.getFullYear().toString(), pad0(datetime.getMonth() + 1), pad0(datetime.getDate())); + const datetime = new Date(); + const dir = path.join('data', datetime.getFullYear().toString(), pad0(datetime.getMonth() + 1), pad0(datetime.getDate())); - if (!fs.existsSync(dir)) - fs.mkdirSync(dir, {recursive: true}); + if (!fs.existsSync(dir)) + fs.mkdirSync(dir, {recursive: true}); - copy(path.join('sample_data','article'), dir); + copy(path.join('sample_data', 'article'), dir); } \ No newline at end of file diff --git a/src/renderer.js b/src/renderer.js index 139956b..236d931 100644 --- a/src/renderer.js +++ b/src/renderer.js @@ -2,15 +2,15 @@ const fs = require('fs'); const showdown = require('showdown'); module.exports = (config) => { - const converter = new showdown.Converter(config['showdown']); - return { - render : (file, cb) => { - fs.readFile(file, {encoding:'UTF-8'}, (err, data) => { - if(err) - return cb(err); - const html = converter.makeHtml(data); - cb(null,html); - }); - } - }; + const converter = new showdown.Converter(config['showdown']); + return { + render: (file, cb) => { + fs.readFile(file, {encoding: 'UTF-8'}, (err, data) => { + if (err) + return cb(err); + const html = converter.makeHtml(data); + cb(null, html); + }); + } + }; }; \ No newline at end of file diff --git a/test/app.test.js b/test/app.test.js index ccc5953..bbf98df 100644 --- a/test/app.test.js +++ b/test/app.test.js @@ -10,197 +10,197 @@ const testError = 'testerror.ejs'; const testTemplate = 'testtemplate.ejs'; const config = { - 'test': true, - 'data_dir': dataDir, - 'view_engine': 'ejs', - 'home': { - 'index': testIndex, - 'error': testError, - 'hidden': ['.ejs','.test'] - }, - 'article': { - 'index': 'index.md', - 'template' : testTemplate, - 'thumbnail_tag': 'thumbnail', - 'default_title': 'Untitled', - 'default_thumbnail': null - }, - 'showdown' : {} + 'test': true, + 'data_dir': dataDir, + 'view_engine': 'ejs', + 'home': { + 'index': testIndex, + 'error': testError, + 'hidden': ['.ejs', '.test'] + }, + 'article': { + 'index': 'index.md', + 'template': testTemplate, + 'thumbnail_tag': 'thumbnail', + 'default_title': 'Untitled', + 'default_thumbnail': null + }, + 'showdown': {} }; const app = require('../src/app')(config); beforeEach(() => { - utils.deleteFolderSync(dataDir); - fs.mkdirSync(dataDir); + utils.deleteFolderSync(dataDir); + fs.mkdirSync(dataDir); }); afterAll(() => { - if (fs.existsSync(dataDir)) { - utils.deleteFolderSync(dataDir); - } + if (fs.existsSync(dataDir)) { + utils.deleteFolderSync(dataDir); + } }); describe('Test root path', () => { - test('404 no index no error', (done) =>{ - request(app).get('/').then((response) =>{ - expect(response.statusCode).toBe(404); - done(); - }); + test('404 no index no error', (done) => { + request(app).get('/').then((response) => { + expect(response.statusCode).toBe(404); + done(); }); - test('404 no index but error page', (done) =>{ - fs.writeFileSync(path.join(dataDir,testError), 'error <%= error %> at <%= path %>'); - request(app).get('/').then((response) =>{ - expect(response.statusCode).toBe(404); - expect(response.text).toBe('error 404 at /'); - done(); - }); + }); + test('404 no index but error page', (done) => { + fs.writeFileSync(path.join(dataDir, testError), 'error <%= error %> at <%= path %>'); + request(app).get('/').then((response) => { + expect(response.statusCode).toBe(404); + expect(response.text).toBe('error 404 at /'); + done(); }); - test('200 no articles', (done) =>{ - fs.writeFileSync(path.join(dataDir,testIndex), 'articles <%= articles.length %>'); - request(app).get('/').then((response) =>{ - expect(response.statusCode).toBe(200); - expect(response.text).toBe('articles 0'); - done(); - }); + }); + test('200 no articles', (done) => { + fs.writeFileSync(path.join(dataDir, testIndex), 'articles <%= articles.length %>'); + request(app).get('/').then((response) => { + expect(response.statusCode).toBe(200); + expect(response.text).toBe('articles 0'); + done(); + }); + }); + test('200 2 articles', (done) => { + utils.createEmptyDirs([ + path.join(dataDir, '2019', '05', '05'), + path.join(dataDir, '2018', '05', '05') + ]); + utils.createEmptyFiles([ + path.join(dataDir, '2019', '05', '05', 'index.md'), + path.join(dataDir, '2018', '05', '05', 'index.md') + ]); + fs.writeFileSync(path.join(dataDir, testIndex), 'articles <%= articles.length %>'); + app.reload((res) => { + expect(res).toBe(true); + request(app).get('/').then((response) => { + expect(response.statusCode).toBe(200); + expect(response.text).toBe('articles 2'); + done(); + }); }); - test('200 2 articles', (done) =>{ - utils.createEmptyDirs([ - path.join(dataDir, '2019', '05', '05'), - path.join(dataDir, '2018', '05', '05') - ]); - utils.createEmptyFiles([ - path.join(dataDir, '2019', '05', '05','index.md'), - path.join(dataDir, '2018', '05', '05','index.md') - ]); - fs.writeFileSync(path.join(dataDir,testIndex), 'articles <%= articles.length %>'); - app.reload((res) => { - expect(res).toBe(true); - request(app).get('/').then((response) =>{ - expect(response.statusCode).toBe(200); - expect(response.text).toBe('articles 2'); - done(); - }); - }); - }); + }); }); describe('Test articles rendering', () => { - test('404 article not found', (done) =>{ - request(app).get('/2019/05/06/untitled/').then((response) =>{ - expect(response.statusCode).toBe(404); - done(); - }); + test('404 article not found', (done) => { + request(app).get('/2019/05/06/untitled/').then((response) => { + expect(response.statusCode).toBe(404); + done(); }); + }); - test('500 no template', (done) =>{ - utils.createEmptyDirs([path.join(dataDir, '2019', '05', '05'),]); - fs.writeFileSync(path.join(dataDir, '2019', '05', '05','index.md'), '# Hello'); - app.reload((res) => { - expect(res).toBe(true); - request(app).get('/2019/05/05/hello/').then((response) =>{ - expect(response.statusCode).toBe(500); - done(); - }); - }); + test('500 no template', (done) => { + utils.createEmptyDirs([path.join(dataDir, '2019', '05', '05'),]); + fs.writeFileSync(path.join(dataDir, '2019', '05', '05', 'index.md'), '# Hello'); + app.reload((res) => { + expect(res).toBe(true); + request(app).get('/2019/05/05/hello/').then((response) => { + expect(response.statusCode).toBe(500); + done(); + }); }); + }); - test('200 rendered article', (done) =>{ - utils.createEmptyDirs([path.join(dataDir, '2019', '05', '05'),]); - fs.writeFileSync(path.join(dataDir, '2019', '05', '05','index.md'), '# Hello'); - fs.writeFileSync(path.join(dataDir, testTemplate), '<%- article.content %><%- `reload` %>'); - app.reload((res) => { - expect(res).toBe(true); - request(app).get('/2019/05/05/hello/').then((response) =>{ - expect(response.statusCode).toBe(200); - expect(response.text).toBe('

Hello

reload'); - done(); - }); - }); + test('200 rendered article', (done) => { + utils.createEmptyDirs([path.join(dataDir, '2019', '05', '05'),]); + fs.writeFileSync(path.join(dataDir, '2019', '05', '05', 'index.md'), '# Hello'); + fs.writeFileSync(path.join(dataDir, testTemplate), '<%- article.content %><%- `reload` %>'); + app.reload((res) => { + expect(res).toBe(true); + request(app).get('/2019/05/05/hello/').then((response) => { + expect(response.statusCode).toBe(200); + expect(response.text).toBe('

Hello

reload'); + done(); + }); }); + }); - test('200 other url', (done) =>{ - utils.createEmptyDirs([path.join(dataDir, '2019', '05', '05'),]); - utils.createEmptyFiles([ - path.join(dataDir, '2019', '05', '05','index.md'), - path.join(dataDir, testTemplate) - ]); - app.reload((res) => { - expect(res).toBe(true); - request(app).get('/2019/05/05/anything/').then((response) =>{ - expect(response.statusCode).toBe(200); - done(); - }); - }); + test('200 other url', (done) => { + utils.createEmptyDirs([path.join(dataDir, '2019', '05', '05'),]); + utils.createEmptyFiles([ + path.join(dataDir, '2019', '05', '05', 'index.md'), + path.join(dataDir, testTemplate) + ]); + app.reload((res) => { + expect(res).toBe(true); + request(app).get('/2019/05/05/anything/').then((response) => { + expect(response.statusCode).toBe(200); + done(); + }); }); + }); - test('200 other url 2', (done) =>{ - utils.createEmptyDirs([path.join(dataDir, '2019', '05', '05'),]); - utils.createEmptyFiles([ - path.join(dataDir, '2019', '05', '05','index.md'), - path.join(dataDir, testTemplate) - ]); - app.reload((res) => { - expect(res).toBe(true); - request(app).get('/2019/05/05/').then((response) =>{ - expect(response.statusCode).toBe(200); - done(); - }); - }); + test('200 other url 2', (done) => { + utils.createEmptyDirs([path.join(dataDir, '2019', '05', '05'),]); + utils.createEmptyFiles([ + path.join(dataDir, '2019', '05', '05', 'index.md'), + path.join(dataDir, testTemplate) + ]); + app.reload((res) => { + expect(res).toBe(true); + request(app).get('/2019/05/05/').then((response) => { + expect(response.statusCode).toBe(200); + done(); + }); }); + }); }); describe('Test static files', () => { - test('404 invalid file no error page', (done) =>{ - request(app).get('/somefile.txt').then((response) =>{ - expect(response.statusCode).toBe(404); - done(); - }); + test('404 invalid file no error page', (done) => { + request(app).get('/somefile.txt').then((response) => { + expect(response.statusCode).toBe(404); + done(); }); - test('404 invalid file but error page', (done) =>{ - fs.writeFileSync(path.join(dataDir,testError), 'error <%= error %> at <%= path %>'); - request(app).get('/somefile.txt').then((response) =>{ - expect(response.statusCode).toBe(404); - expect(response.text).toBe('error 404 at /somefile.txt'); - done(); - }); + }); + test('404 invalid file but error page', (done) => { + fs.writeFileSync(path.join(dataDir, testError), 'error <%= error %> at <%= path %>'); + request(app).get('/somefile.txt').then((response) => { + expect(response.statusCode).toBe(404); + expect(response.text).toBe('error 404 at /somefile.txt'); + done(); }); - test('404 hidden file', (done) =>{ - fs.writeFileSync(path.join(dataDir,'somefile.test'), ''); - request(app).get('/somefile.test').then((response) =>{ - expect(response.statusCode).toBe(404); - done(); - }); + }); + test('404 hidden file', (done) => { + fs.writeFileSync(path.join(dataDir, 'somefile.test'), ''); + request(app).get('/somefile.test').then((response) => { + expect(response.statusCode).toBe(404); + done(); }); - test('200 valid file', (done) =>{ - fs.writeFileSync(`${dataDir}/somefile.txt`, 'filecontent'); - request(app).get('/somefile.txt').then((response) =>{ - expect(response.statusCode).toBe(200); - expect(response.text).toBe('filecontent'); - done(); - }); + }); + test('200 valid file', (done) => { + fs.writeFileSync(`${dataDir}/somefile.txt`, 'filecontent'); + request(app).get('/somefile.txt').then((response) => { + expect(response.statusCode).toBe(200); + expect(response.text).toBe('filecontent'); + done(); }); + }); }); describe('Test other requests', () => { - test('400 POST', (done) =>{ - request(app).post('/').then((response) =>{ - expect(response.statusCode).toBe(400); - done(); - }); + test('400 POST', (done) => { + request(app).post('/').then((response) => { + expect(response.statusCode).toBe(400); + done(); }); - test('400 PUT', (done) =>{ - request(app).put('/').then((response) =>{ - expect(response.statusCode).toBe(400); - done(); - }); + }); + test('400 PUT', (done) => { + request(app).put('/').then((response) => { + expect(response.statusCode).toBe(400); + done(); }); - test('400 DELETE', (done) =>{ - request(app).delete('/').then((response) =>{ - expect(response.statusCode).toBe(400); - done(); - }); + }); + test('400 DELETE', (done) => { + request(app).delete('/').then((response) => { + expect(response.statusCode).toBe(400); + done(); }); + }); }); diff --git a/test/config.test.js b/test/config.test.js index ad60ea4..fa72475 100644 --- a/test/config.test.js +++ b/test/config.test.js @@ -5,50 +5,50 @@ const configFile = 'config.json'; const tmpConfigFile = 'config.temp.json'; beforeAll(() => { - if (fs.existsSync(configFile)) { - fs.renameSync(configFile, tmpConfigFile); - } - expect(fs.existsSync(configFile)).toBeFalsy(); + if (fs.existsSync(configFile)) { + fs.renameSync(configFile, tmpConfigFile); + } + expect(fs.existsSync(configFile)).toBeFalsy(); }); afterAll(() => { - if (fs.existsSync(tmpConfigFile)) { - fs.renameSync(tmpConfigFile, configFile); - } else if (fs.existsSync(configFile)) { - fs.unlinkSync(configFile); //remove config file if remaining - } + if (fs.existsSync(tmpConfigFile)) { + fs.renameSync(tmpConfigFile, configFile); + } else if (fs.existsSync(configFile)) { + fs.unlinkSync(configFile); //remove config file if remaining + } }); test('no config', () => { - if (fs.existsSync(configFile)) - fs.unlinkSync(configFile); - expect(fs.existsSync(configFile)).toBeFalsy(); - const config = require('../src/config')(); - expect(config).toBeDefined(); - expect(config['node_port']).toBe(3000); - expect(config['data_dir']).toBe('data'); + if (fs.existsSync(configFile)) + fs.unlinkSync(configFile); + expect(fs.existsSync(configFile)).toBeFalsy(); + const config = require('../src/config')(); + expect(config).toBeDefined(); + expect(config['node_port']).toBe(3000); + expect(config['data_dir']).toBe('data'); }); test('invalid config ignored', () => { - fs.writeFileSync(configFile, 'invalid JSON'); - const config = require('../src/config')(); - expect(config).toBeDefined(); - expect(config['node_port']).toBe(3000); - expect(config['data_dir']).toBe('data'); + fs.writeFileSync(configFile, 'invalid JSON'); + const config = require('../src/config')(); + expect(config).toBeDefined(); + expect(config['node_port']).toBe(3000); + expect(config['data_dir']).toBe('data'); }); test('good config merged', () => { - fs.writeFileSync(configFile, '{"node_port":5000}'); - const config = require('../src/config')(); - expect(config).toBeDefined(); - expect(config['node_port']).toBe(5000); - expect(config['data_dir']).toBe('data'); + fs.writeFileSync(configFile, '{"node_port":5000}'); + const config = require('../src/config')(); + expect(config).toBeDefined(); + expect(config['node_port']).toBe(5000); + expect(config['data_dir']).toBe('data'); }); test('wrong config fixed', () => { - fs.writeFileSync(configFile, '{"node_port":"hello","data_dir":"data2"}'); - const config = require('../src/config')(); - expect(config).toBeDefined(); - expect(config['node_port']).toBe(3000); - expect(config['data_dir']).toBe('data2'); + fs.writeFileSync(configFile, '{"node_port":"hello","data_dir":"data2"}'); + const config = require('../src/config')(); + expect(config).toBeDefined(); + expect(config['node_port']).toBe(3000); + expect(config['data_dir']).toBe('data2'); }); \ No newline at end of file diff --git a/test/file_walker.test.js b/test/file_walker.test.js index a3e9cd5..1285863 100644 --- a/test/file_walker.test.js +++ b/test/file_walker.test.js @@ -7,265 +7,265 @@ const dataDir = 'test_data'; const testIndex = 'testindex.md'; const config = { - 'test': true, - 'data_dir': dataDir, - 'article': { - 'index': testIndex, - 'default_title': 'Untitled', - 'default_thumbnail': 'default.png', - 'thumbnail_tag': 'thumbnail' - } + 'test': true, + 'data_dir': dataDir, + 'article': { + 'index': testIndex, + 'default_title': 'Untitled', + 'default_thumbnail': 'default.png', + 'thumbnail_tag': 'thumbnail' + } }; const fw = require('../src/file_walker')(config); beforeEach(() => { - utils.deleteFolderSync(dataDir); - fs.mkdirSync(dataDir); + utils.deleteFolderSync(dataDir); + fs.mkdirSync(dataDir); }); afterAll(() => { - if (fs.existsSync(dataDir)) { - utils.deleteFolderSync(dataDir); - } + if (fs.existsSync(dataDir)) { + utils.deleteFolderSync(dataDir); + } }); describe('Test function fileTree', () => { - test('empty root', (done) => { - fw.fileTree(dataDir, (err, list) => { - expect(err).toBeNull(); - expect(list).toBeDefined(); - expect(list.length).toBe(0); - done(); - }); + test('empty root', (done) => { + fw.fileTree(dataDir, (err, list) => { + expect(err).toBeNull(); + expect(list).toBeDefined(); + expect(list.length).toBe(0); + done(); }); - test('empty folders', (done) => { - utils.createEmptyDirs([ - path.join(dataDir, 'test', 'test'), - path.join(dataDir, 'test', 'test2'), - path.join(dataDir, 'test2') - ]); - fw.fileTree(dataDir, (err, list) => { - expect(err).toBeNull(); - expect(list).toBeDefined(); - expect(list.length).toBe(0); - done(); - }); + }); + test('empty folders', (done) => { + utils.createEmptyDirs([ + path.join(dataDir, 'test', 'test'), + path.join(dataDir, 'test', 'test2'), + path.join(dataDir, 'test2') + ]); + fw.fileTree(dataDir, (err, list) => { + expect(err).toBeNull(); + expect(list).toBeDefined(); + expect(list.length).toBe(0); + done(); }); - test('simple files', (done) => { - const fileList = [ - path.join(dataDir, 'f1.txt'), - path.join(dataDir, 'f2.txt') - ]; - utils.createEmptyFiles(fileList); - fw.fileTree(dataDir, (err, list) => { - expect(err).toBeNull(); - expect(list).toBeDefined(); - expect(list.length).toBe(fileList.length); - expect(list).toEqual(expect.arrayContaining(fileList)); - done(); - }); + }); + test('simple files', (done) => { + const fileList = [ + path.join(dataDir, 'f1.txt'), + path.join(dataDir, 'f2.txt') + ]; + utils.createEmptyFiles(fileList); + fw.fileTree(dataDir, (err, list) => { + expect(err).toBeNull(); + expect(list).toBeDefined(); + expect(list.length).toBe(fileList.length); + expect(list).toEqual(expect.arrayContaining(fileList)); + done(); }); - test('nested files', (done) => { - utils.createEmptyDirs([ - path.join(dataDir, 'test', 'test'), - path.join(dataDir, 'test2') - ]); - const fileList = [ - path.join(dataDir, 'f1.txt'), - path.join(dataDir, 'test', 'f2.txt'), - path.join(dataDir, 'test', 'test', 'f3.txt'), - path.join(dataDir, 'test2', 'f4.txt') - ]; - utils.createEmptyFiles(fileList); - fw.fileTree(dataDir, (err, list) => { - expect(err).toBeNull(); - expect(list).toBeDefined(); - expect(list.length).toBe(fileList.length); - expect(list).toEqual(expect.arrayContaining(fileList)); - done(); - }); + }); + test('nested files', (done) => { + utils.createEmptyDirs([ + path.join(dataDir, 'test', 'test'), + path.join(dataDir, 'test2') + ]); + const fileList = [ + path.join(dataDir, 'f1.txt'), + path.join(dataDir, 'test', 'f2.txt'), + path.join(dataDir, 'test', 'test', 'f3.txt'), + path.join(dataDir, 'test2', 'f4.txt') + ]; + utils.createEmptyFiles(fileList); + fw.fileTree(dataDir, (err, list) => { + expect(err).toBeNull(); + expect(list).toBeDefined(); + expect(list.length).toBe(fileList.length); + expect(list).toEqual(expect.arrayContaining(fileList)); + done(); }); - test('invalid root', (done) => { - fw.fileTree('invalid root', (err, list) => { - expect(err).not.toBeNull(); - expect(list).not.toBeDefined(); - done(); - }); + }); + test('invalid root', (done) => { + fw.fileTree('invalid root', (err, list) => { + expect(err).not.toBeNull(); + expect(list).not.toBeDefined(); + done(); }); + }); }); describe('Test index article reading', () => { - const file = path.join(dataDir, testIndex); - - test('invalid file', (done) => { - fw.readIndexFile('invalid file', 'thumbnail', (err, info) => { - expect(err).not.toBeNull(); - expect(info).not.toBeDefined(); - done(); - }); + const file = path.join(dataDir, testIndex); + test('invalid file', (done) => { + fw.readIndexFile('invalid file', 'thumbnail', (err, info) => { + expect(err).not.toBeNull(); + expect(info).not.toBeDefined(); + done(); }); - test('correct file', (done) => { - fs.writeFileSync(file, ` + }); + + test('correct file', (done) => { + fs.writeFileSync(file, ` # This is an awesome title !?¤ ![custom_thumbnail](./thumbnail.jpg) this is some text `); - fw.readIndexFile(file, 'custom_thumbnail', (err, info) => { - expect(err).toBeNull(); - expect(info).toBeDefined(); - expect(info.title).toBe('This is an awesome title !?¤'); - expect(info.thumbnail).toBe('./thumbnail.jpg'); - done(); - }); + fw.readIndexFile(file, 'custom_thumbnail', (err, info) => { + expect(err).toBeNull(); + expect(info).toBeDefined(); + expect(info.title).toBe('This is an awesome title !?¤'); + expect(info.thumbnail).toBe('./thumbnail.jpg'); + done(); }); + }); - test('no title', (done) => { - fs.writeFileSync(file, ` + test('no title', (done) => { + fs.writeFileSync(file, ` ## This is an awesome title !?¤ ![custom_thumbnail](./thumbnail.jpg) ### this is some text `); - fw.readIndexFile(file, 'custom_thumbnail', (err, info) => { - expect(err).toBeNull(); - expect(info).toBeDefined(); - expect(info.title).not.toBeDefined(); - expect(info.thumbnail).toBe('./thumbnail.jpg'); - done(); - }); + fw.readIndexFile(file, 'custom_thumbnail', (err, info) => { + expect(err).toBeNull(); + expect(info).toBeDefined(); + expect(info.title).not.toBeDefined(); + expect(info.thumbnail).toBe('./thumbnail.jpg'); + done(); }); + }); - test('title at beginning', (done) => { - fs.writeFileSync(file, '#title'); - fw.readIndexFile(file, 'custom_thumbnail', (err, info) => { - expect(err).toBeNull(); - expect(info).toBeDefined(); - expect(info.title).toBe('title'); - expect(info.thumbnail).not.toBeDefined(); - done(); - }); + test('title at beginning', (done) => { + fs.writeFileSync(file, '#title'); + fw.readIndexFile(file, 'custom_thumbnail', (err, info) => { + expect(err).toBeNull(); + expect(info).toBeDefined(); + expect(info.title).toBe('title'); + expect(info.thumbnail).not.toBeDefined(); + done(); }); + }); - test('no thumbnail', (done) => { - fs.writeFileSync(file, ` + test('no thumbnail', (done) => { + fs.writeFileSync(file, ` # This is an awesome title !?¤ ![custom_thumbnail](./thumbnail.jpg) this is some text `); - fw.readIndexFile(file, 'thumbnail', (err, info) => { - expect(err).toBeNull(); - expect(info).toBeDefined(); - expect(info.title).toBe('This is an awesome title !?¤'); - expect(info.thumbnail).not.toBeDefined(); - done(); - }); + fw.readIndexFile(file, 'thumbnail', (err, info) => { + expect(err).toBeNull(); + expect(info).toBeDefined(); + expect(info.title).toBe('This is an awesome title !?¤'); + expect(info.thumbnail).not.toBeDefined(); + done(); }); + }); - test('multiple thumbnails', (done) => { - fs.writeFileSync(file, ` + test('multiple thumbnails', (done) => { + fs.writeFileSync(file, ` # This is an awesome title !?¤ ![custom_thumbnail](./thumbnail.jpg) this is some text ![custom_thumbnail](./thumbnail2.jpg) `); - fw.readIndexFile(file, 'custom_thumbnail', (err, info) => { - expect(err).toBeNull(); - expect(info).toBeDefined(); - expect(info.title).toBe('This is an awesome title !?¤'); - expect(info.thumbnail).toBe('./thumbnail.jpg'); - done(); - }); + fw.readIndexFile(file, 'custom_thumbnail', (err, info) => { + expect(err).toBeNull(); + expect(info).toBeDefined(); + expect(info.title).toBe('This is an awesome title !?¤'); + expect(info.thumbnail).toBe('./thumbnail.jpg'); + done(); }); + }); }); describe('Test article fetching', () => { - test('invalid data dir', (done) => { - config['data_dir'] = 'invalid root'; - fw.fetchArticles((err, list) => { - expect(err).not.toBeNull(); - expect(list).not.toBeDefined(); - config['data_dir'] = dataDir; - done(); - }); + test('invalid data dir', (done) => { + config['data_dir'] = 'invalid root'; + fw.fetchArticles((err, list) => { + expect(err).not.toBeNull(); + expect(list).not.toBeDefined(); + config['data_dir'] = dataDir; + done(); }); - test('empty data dir', (done) => { - fw.fetchArticles((err, dict) => { - expect(err).toBeNull(); - expect(dict).toBeDefined(); - expect(Object.keys(dict).length).toBe(0); - done(); - }); + }); + test('empty data dir', (done) => { + fw.fetchArticles((err, dict) => { + expect(err).toBeNull(); + expect(dict).toBeDefined(); + expect(Object.keys(dict).length).toBe(0); + done(); }); - test('misplaced index file', (done) => { - utils.createEmptyDirs([ - path.join(dataDir, 'test', 'test'), - path.join(dataDir, '2019', '05', '05') - ]); - utils.createEmptyFiles([ - path.join(dataDir, testIndex), - path.join(dataDir, 'test', 'test', testIndex), - path.join(dataDir, '2019', '05', testIndex) - ]); - fw.fetchArticles((err, dict) => { - expect(err).toBeNull(); - expect(dict).toBeDefined(); - expect(Object.keys(dict).length).toBe(0); - done(); - }); + }); + test('misplaced index file', (done) => { + utils.createEmptyDirs([ + path.join(dataDir, 'test', 'test'), + path.join(dataDir, '2019', '05', '05') + ]); + utils.createEmptyFiles([ + path.join(dataDir, testIndex), + path.join(dataDir, 'test', 'test', testIndex), + path.join(dataDir, '2019', '05', testIndex) + ]); + fw.fetchArticles((err, dict) => { + expect(err).toBeNull(); + expect(dict).toBeDefined(); + expect(Object.keys(dict).length).toBe(0); + done(); }); - test('empty index file', (done) => { - const dir = path.join(dataDir, '2019', '05', '05'); - const file = path.join(dir, testIndex); - utils.createEmptyDirs([dir]); - utils.createEmptyFiles([file]); - fw.fetchArticles((err, dict) => { - expect(err).toBeNull(); - expect(dict).toBeDefined(); - expect(Object.keys(dict).length).toBe(1); - expect(dict[path.join('2019', '05', '05')]).toEqual({ - path: path.join('2019', '05', '05'), - realPath: dir, - year: 2019, - month: 5, - day: 5, - date : new Date(2019,5,5), - title: 'Untitled', - thumbnail: 'default.png', - escapedTitle: 'untitled', - url: '/'+path.join('2019', '05', '05', 'untitled')+'/', - }); - done(); - }); + }); + test('empty index file', (done) => { + const dir = path.join(dataDir, '2019', '05', '05'); + const file = path.join(dir, testIndex); + utils.createEmptyDirs([dir]); + utils.createEmptyFiles([file]); + fw.fetchArticles((err, dict) => { + expect(err).toBeNull(); + expect(dict).toBeDefined(); + expect(Object.keys(dict).length).toBe(1); + expect(dict[path.join('2019', '05', '05')]).toEqual({ + path: path.join('2019', '05', '05'), + realPath: dir, + year: 2019, + month: 5, + day: 5, + date: new Date(2019, 5, 5), + title: 'Untitled', + thumbnail: 'default.png', + escapedTitle: 'untitled', + url: '/' + path.join('2019', '05', '05', 'untitled') + '/', + }); + done(); }); - test('correct index file', (done) => { - const dir = path.join(dataDir, '2019', '05', '05'); - const file = path.join(dir, testIndex); - utils.createEmptyDirs([dir]); - fs.writeFileSync(file, ` + }); + test('correct index file', (done) => { + const dir = path.join(dataDir, '2019', '05', '05'); + const file = path.join(dir, testIndex); + utils.createEmptyDirs([dir]); + fs.writeFileSync(file, ` # Title with : info ! ![thumbnail](./thumbnail.jpg) this is some text `); - fw.fetchArticles((err, dict) => { - expect(err).toBeNull(); - expect(dict).toBeDefined(); - expect(Object.keys(dict).length).toBe(1); - expect(dict[path.join('2019', '05', '05')]).toEqual({ - path: path.join('2019', '05', '05'), - realPath: dir, - year: 2019, - month: 5, - day: 5, - date : new Date(2019,5,5), - title: 'Title with : info !', - thumbnail: path.join('2019', '05', '05', './thumbnail.jpg'), - escapedTitle: 'title_with___info', - url: '/'+path.join('2019', '05', '05', 'title_with___info')+'/', - }); - done(); - }); + fw.fetchArticles((err, dict) => { + expect(err).toBeNull(); + expect(dict).toBeDefined(); + expect(Object.keys(dict).length).toBe(1); + expect(dict[path.join('2019', '05', '05')]).toEqual({ + path: path.join('2019', '05', '05'), + realPath: dir, + year: 2019, + month: 5, + day: 5, + date: new Date(2019, 5, 5), + title: 'Title with : info !', + thumbnail: path.join('2019', '05', '05', './thumbnail.jpg'), + escapedTitle: 'title_with___info', + url: '/' + path.join('2019', '05', '05', 'title_with___info') + '/', + }); + done(); }); + }); }); diff --git a/test/renderer.test.js b/test/renderer.test.js index 1a42d27..37aa81f 100644 --- a/test/renderer.test.js +++ b/test/renderer.test.js @@ -7,47 +7,47 @@ const dataDir = 'test_data'; const file = path.join(dataDir, 'test.md'); const config = { - 'showdown': { - 'simplifiedAutoLink': true, - 'smartIndentationFix': true - } + 'showdown': { + 'simplifiedAutoLink': true, + 'smartIndentationFix': true + } }; const renderer = require('../src/renderer')(config); beforeEach(() => { - utils.deleteFolderSync(dataDir); - fs.mkdirSync(dataDir); + utils.deleteFolderSync(dataDir); + fs.mkdirSync(dataDir); }); afterAll(() => { - if (fs.existsSync(dataDir)) { - utils.deleteFolderSync(dataDir); - } + if (fs.existsSync(dataDir)) { + utils.deleteFolderSync(dataDir); + } }); test('invalid file', (done) => { - renderer.render('invalid file', (err, html) => { - expect(err).not.toBeNull(); - expect(html).not.toBeDefined(); - done(); - }); + renderer.render('invalid file', (err, html) => { + expect(err).not.toBeNull(); + expect(html).not.toBeDefined(); + done(); + }); }); test('normal file', (done) => { - fs.writeFileSync(file, `# Hello`); - renderer.render(file, (err, html) => { - expect(err).toBeNull(); - expect(html).toBe('

Hello

'); - done(); - }); + fs.writeFileSync(file, `# Hello`); + renderer.render(file, (err, html) => { + expect(err).toBeNull(); + expect(html).toBe('

Hello

'); + done(); + }); }); test('custom rules', (done) => { - fs.writeFileSync(file, `www.google.com`); - renderer.render(file, (err, html) => { - expect(err).toBeNull(); - expect(html).toBe('

www.google.com

'); - done(); - }); + fs.writeFileSync(file, `www.google.com`); + renderer.render(file, (err, html) => { + expect(err).toBeNull(); + expect(html).toBe('

www.google.com

'); + done(); + }); }); \ No newline at end of file diff --git a/test/test_utils.js b/test/test_utils.js index a80a886..6b509c5 100644 --- a/test/test_utils.js +++ b/test/test_utils.js @@ -2,19 +2,19 @@ const fs = require('fs'); const path = require('path'); const deleteFolderSync = (dir) => { - if (!fs.existsSync(dir)) - return; - fs.readdirSync(dir, {withFileTypes: true}).forEach((item) => { - if (item.isDirectory()) - deleteFolderSync(path.join(dir,item.name)); - else - fs.unlinkSync(path.join(dir,item.name)); - }); - fs.rmdirSync(dir); + if (!fs.existsSync(dir)) + return; + fs.readdirSync(dir, {withFileTypes: true}).forEach((item) => { + if (item.isDirectory()) + deleteFolderSync(path.join(dir, item.name)); + else + fs.unlinkSync(path.join(dir, item.name)); + }); + fs.rmdirSync(dir); }; module.exports = { - deleteFolderSync: deleteFolderSync, - createEmptyDirs: (list) =>list.forEach((path) =>fs.mkdirSync(path, {recursive: true})), - createEmptyFiles: (list) =>list.forEach((file) =>fs.writeFileSync(file, '')), + deleteFolderSync: deleteFolderSync, + createEmptyDirs: (list) => list.forEach((path) => fs.mkdirSync(path, {recursive: true})), + createEmptyFiles: (list) => list.forEach((file) => fs.writeFileSync(file, '')), }; \ No newline at end of file From 7937985c3d728a564be980364f5b35c7741c277f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20GOUIN?= Date: Thu, 20 Jun 2019 14:26:29 +0200 Subject: [PATCH 025/120] Code formatting --- sample_data/home/error.ejs | 2 +- sample_data/home/index.ejs | 11 +++++++---- sample_data/home/style.css | 14 +++++++------- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/sample_data/home/error.ejs b/sample_data/home/error.ejs index c2471f6..e4b1427 100644 --- a/sample_data/home/error.ejs +++ b/sample_data/home/error.ejs @@ -6,7 +6,7 @@
-

Error <%= error %> at path <%= path %>

+

Error <%= error %> at path <%= path %>

\ No newline at end of file diff --git a/sample_data/home/index.ejs b/sample_data/home/index.ejs index 25c46d0..680f203 100644 --- a/sample_data/home/index.ejs +++ b/sample_data/home/index.ejs @@ -12,10 +12,13 @@

Articles in this blog :

<% articles.forEach((article) => { %>
-

<%- `${article.title}`%> (<%= `${article.day}/${article.month}/${article.year}`%>)

- <% if(article.thumbnail){%> - <%- `thumbnail`%> - <% }%> +

<%- `${article.title}` %> ( + <%= `${article.day}/${article.month}/${article.year}` %> + ) +

+ <% if(article.thumbnail){ %> + <%- `thumbnail` %> + <% } %>
<% }); %> diff --git a/sample_data/home/style.css b/sample_data/home/style.css index 979ab2a..6d915f0 100644 --- a/sample_data/home/style.css +++ b/sample_data/home/style.css @@ -4,15 +4,15 @@ main { margin: auto; } -.article a, .article a:visited{ - color:black; +.article a, .article a:visited { + color: black; } -.article a:visited{ - color:black; +.article a:visited { + color: black; } -.article img{ - max-width:100%; - max-height:10vh; +.article img { + max-width: 100%; + max-height: 10vh; } \ No newline at end of file From 6175bc5074c3c5ce6e868fb326d56edf54617882 Mon Sep 17 00:00:00 2001 From: Klemek Date: Thu, 20 Jun 2019 14:29:36 +0200 Subject: [PATCH 026/120] Update README.md --- README.md | 86 +------------------------------------------------------ 1 file changed, 1 insertion(+), 85 deletions(-) diff --git a/README.md b/README.md index d00d5ad..fc37e8f 100644 --- a/README.md +++ b/README.md @@ -1,90 +1,6 @@ # GitBlog.md (WIP) -> This is a work in progress, some information written here might not be true yet. +> This is a work in progress, development are in the branch 'dev' [![Build Status](https://img.shields.io/travis/Klemek/GitBlog.md.svg?branch=master)](https://travis-ci.org/Klemek/GitBlog.md) A static blog using Markdown pulled from your git repository. - -## Flow - -![root](./uml/root.png) - -![article](./uml/article.png) - -![webhook](./uml/webhook.png) - -![rss](./uml/rss.png) - -## Installation -**1. Download and install the latest version from the repo** -```bash -git clone https://github.com/klemek/gitblog.md.git -npm install -``` -**2. Create your config file** -```bash -cd gitblog.md -cp config.example.json config.json -``` -then edit the config.json file with your values : -> default values for config.json -````json -{ - "nodePort": 3000, - "dataDir": "data", - "modules" : { - "plantuml" : false, - "rss": true, - "webhook": true - }, - "home" : { - "index" : "index.ejs" - }, - "article" : { - "index" : "index.md" - }, - "rss" : { - "endpoint" : "/rss", - "length" : 10 - }, - "webhook" : { - "endpoint": "/webhook", - "secretFile": "git_secret" - } -} -```` - -**3. Start your server** - -```bash -npm start -#or -node src/server.js -``` - -You might want to use something like screen to separate the process from your current terminal session. - -**4. Create and init your git source** - -You need to [create a new repository](https://github.com/new) on your favorite Git service. - -```bash -#gitblog.md/ -cd data -git init -git add . -git commit -m "initial commit" -git remote add origin -git push -u origin master -``` - -**5. Refresh content with a webhook (optional)** - -At first start, a `git_secret` file will be generated, use it to create a new webhook as following : - -* Payload URL : `https:///webhook` -* Content type : `application/json` -* Secret : `` -* Events : Just the push event - -On GitHub, webhooks can be created in the `Settings/Webhooks` part of the repository. From 1fa8007e0e037fd15fcc74679b0494e345d00906 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20GOUIN?= Date: Thu, 20 Jun 2019 15:08:22 +0200 Subject: [PATCH 027/120] RSS function --- package-lock.json | 14 +++--- package.json | 2 +- src/config.default.json | 3 ++ src/rss.js | 40 ++++++++++++++++ test/rss.test.js | 100 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 152 insertions(+), 7 deletions(-) create mode 100644 src/rss.js create mode 100644 test/rss.test.js diff --git a/package-lock.json b/package-lock.json index 7967736..56e9b3a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8670,8 +8670,7 @@ "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, "semver": { "version": "5.7.0", @@ -9670,10 +9669,13 @@ "async-limiter": "~1.0.0" } }, - "xml": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", - "integrity": "sha1-eLpyAgApxbyHuKgaPPzXS0ovweU=" + "xml-js": { + "version": "1.6.11", + "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", + "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==", + "requires": { + "sax": "^1.2.4" + } }, "xml-name-validator": { "version": "3.0.0", diff --git a/package.json b/package.json index 7ca4d39..5136935 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "express": "^4.17.1", "ncp": "^2.0.0", "showdown": "^1.9.0", - "xml": "^1.0.1" + "xml-js": "^1.6.11" }, "devDependencies": { "babel-cli": "^6.26.0", diff --git a/src/config.default.json b/src/config.default.json index 25acebc..88c5156 100644 --- a/src/config.default.json +++ b/src/config.default.json @@ -2,6 +2,7 @@ "node_port": 3000, "data_dir": "data", "view_engine": "ejs", + "loopback_url": "http://localhost:3000", "modules": { "plantuml": false, "rss": true, @@ -22,6 +23,8 @@ "default_thumbnail": null }, "rss": { + "title": "mygitblog RSS feed", + "description": "a generated RSS feed from my articles", "endpoint": "/rss", "length": 10 }, diff --git a/src/rss.js b/src/rss.js new file mode 100644 index 0000000..efd36aa --- /dev/null +++ b/src/rss.js @@ -0,0 +1,40 @@ +const convert = require('xml-js'); + +const mapArticle = (url, article) => { + return { + 'title': {'_text': article.title}, + 'link': {'_text': (url + article.url).replace(/([^:])\/\//g, '$1/')}, + 'pubDate': {'_text': article.date.toString()}, + }; +}; + +module.exports = (config) => { + return { + get: (dict) => { + const items = Object.values(dict) + .sort((a, b) => ('' + b.path).localeCompare(a.path)) + .slice(0, config['rss']['length']); + const data = { + '_declaration': { + '_attributes': { + 'version': '1.0', + 'encoding': 'UTF-8' + } + }, + 'rss': { + '_attributes': { + 'version': '2.0' + }, + 'title': {'_text': config['rss']['title']}, + 'description': {'_text': config['rss']['description']}, + 'link': {'_text': config['loopback_url']}, + 'lastBuildDate': {'_text': new Date().toString()}, + 'lastPubDate': {'_text': new Date().toString()}, + 'ttl': {'_text': '60'}, + 'item': items.map((a) => mapArticle(config['loopback_url'], a)) + } + }; + return convert.js2xml(data, {compact: true}); + } + }; +}; \ No newline at end of file diff --git a/test/rss.test.js b/test/rss.test.js new file mode 100644 index 0000000..087a2eb --- /dev/null +++ b/test/rss.test.js @@ -0,0 +1,100 @@ +/* jshint -W117 */ +const config = { + 'loopback_url': 'http://test.test/', + 'rss': { + 'title': 'test rss', + 'description': 'description', + 'endpoint': '/rss', + 'length': 2 + }, +}; + +const rss = require('../src/rss')(config); + +test('empty rss', () => { + const xml = rss.get({}); + expect(xml).toBe('' + + '' + + 'test rss' + + 'description' + + 'http://test.test/' + + '' + new Date().toString() + '' + + '' + new Date().toString() + '' + + '60' + + ''); +}); + +test('1 item', () => { + const data = { + 'a': { + path: 'a', + realPath: 'b', + year: 2019, + month: 5, + day: 5, + date: new Date(2019, 5, 5), + title: 'Title with : info !', + thumbnail: '/2019/05/05/thumbnail.jpg/', + escapedTitle: 'title_with___info', + url: '/2019/05/05/title_with___info/', + } + }; + const xml = rss.get(data); + expect(xml).toEqual('' + + '' + + 'test rss' + + 'description' + + 'http://test.test/' + + '' + new Date().toString() + '' + + '' + new Date().toString() + '' + + '60' + + '' + + 'Title with : info !' + + 'http://test.test/2019/05/05/title_with___info/' + + '' + new Date(2019, 5, 5).toString() + '' + + '' + + ''); +}); + +test('3 items only 2 shown sorted', () => { + const data = { + 'a': { + path: '2019/05/05', + date: new Date(2019, 5, 5), + title: 'a', + url: 'a', + }, + 'b': { + path: '2018/05/05', + date: new Date(2018, 5, 5), + title: 'b', + url: 'b', + }, + 'c': { + path: '2020/05/05', + date: new Date(2020, 5, 5), + title: 'c', + url: 'c', + } + }; + const xml = rss.get(data); + expect(xml).toEqual('' + + '' + + 'test rss' + + 'description' + + 'http://test.test/' + + '' + new Date().toString() + '' + + '' + new Date().toString() + '' + + '60' + + '' + + 'c' + + 'http://test.test/c' + + '' + new Date(2020, 5, 5).toString() + '' + + '' + + '' + + 'a' + + 'http://test.test/a' + + '' + new Date(2019, 5, 5).toString() + '' + + '' + + ''); +}); \ No newline at end of file From 9cb601528e148695afb933c4923b67e79a492511 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20GOUIN?= Date: Thu, 20 Jun 2019 16:13:50 +0200 Subject: [PATCH 028/120] RSS feed in app --- package-lock.json | 38 +++++++++++---- package.json | 4 +- src/app.js | 34 ++++++++++++- src/config.default.json | 2 +- src/file_walker.js | 1 + src/rss.js | 40 ---------------- test/app.test.js | 93 ++++++++++++++++++++++++++++-------- test/file_walker.test.js | 8 +++- test/rss.test.js | 100 --------------------------------------- 9 files changed, 147 insertions(+), 173 deletions(-) delete mode 100644 src/rss.js delete mode 100644 test/rss.test.js diff --git a/package-lock.json b/package-lock.json index 56e9b3a..3667629 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8296,6 +8296,30 @@ "glob": "^7.1.3" } }, + "rss": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/rss/-/rss-1.2.2.tgz", + "integrity": "sha1-UKFpiHYTgTOnT5oF0r3I240nqSE=", + "requires": { + "mime-types": "2.1.13", + "xml": "1.0.1" + }, + "dependencies": { + "mime-db": { + "version": "1.25.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.25.0.tgz", + "integrity": "sha1-wY29fHOl2/b0SgJNwNFloeexw5I=" + }, + "mime-types": { + "version": "2.1.13", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.13.tgz", + "integrity": "sha1-4HqqnGxrmnyjASxpADrSWjnpKog=", + "requires": { + "mime-db": "~1.25.0" + } + } + } + }, "rsvp": { "version": "4.8.5", "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", @@ -8670,7 +8694,8 @@ "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true }, "semver": { "version": "5.7.0", @@ -9669,13 +9694,10 @@ "async-limiter": "~1.0.0" } }, - "xml-js": { - "version": "1.6.11", - "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", - "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==", - "requires": { - "sax": "^1.2.4" - } + "xml": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", + "integrity": "sha1-eLpyAgApxbyHuKgaPPzXS0ovweU=" }, "xml-name-validator": { "version": "3.0.0", diff --git a/package.json b/package.json index 5136935..04dee11 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,8 @@ "ejs": "^2.6.2", "express": "^4.17.1", "ncp": "^2.0.0", - "showdown": "^1.9.0", - "xml-js": "^1.6.11" + "rss": "^1.2.2", + "showdown": "^1.9.0" }, "devDependencies": { "babel-cli": "^6.26.0", diff --git a/src/app.js b/src/app.js index b4c129b..4319149 100644 --- a/src/app.js +++ b/src/app.js @@ -2,6 +2,7 @@ const express = require('express'); const app = express(); const fs = require('fs'); const path = require('path'); +const Rss = require('rss'); /** * Terminal colors and symbols to display status messages @@ -23,6 +24,7 @@ module.exports = (config) => { app.set('views', path.join(__dirname, '..')); const articles = {}; + let lastRSS = ''; /** * Fetch articles from the data folder and send success as a response @@ -41,6 +43,9 @@ module.exports = (config) => { console.log(cons.ok, `loaded ${nb} article${nb > 1 ? 's' : ''}`); else console.log(cons.warn, `no articles loaded, check your configuration`); + + lastRSS = ''; + callback(true); }); }; @@ -87,10 +92,37 @@ module.exports = (config) => { if (err) showError(req.path, 404, res); else - render(res, homePath, {articles: Object.values(articles)}); + render(res, homePath, {articles: Object.values(articles).sort((a, b) => ('' + b.path).localeCompare(a.path))}); }); }); + //RSS endpoint + app.get(config['rss']['endpoint'], (req, res) => { + if (config['modules']['rss']) { + if (!lastRSS) { + const feed = new Rss({ + 'title': config['rss']['title'], + 'description': config['rss']['description'], + 'feed_url': 'http://' + req.headers.host + req.url, + 'site_url': 'http://' + req.headers.host + }); + Object.values(articles) + .slice(0, config['rss']['length']) + .forEach((article) => { + feed.item({ + title: article.title, + url: 'http://' + req.headers.host + article.url, + date: article.date + }); + }); + lastRSS = feed.xml(); + } + res.type('rss').send(lastRSS); + } else { + showError(req.path, 404, res); + } + }); + // catch all article urls and render them app.get('*', (req, res, next) => { if (/^\/\d{4}\/\d{2}\/\d{2}\/(\w*\/)?$/.test(req.path)) { diff --git a/src/config.default.json b/src/config.default.json index 88c5156..d5ac59a 100644 --- a/src/config.default.json +++ b/src/config.default.json @@ -2,7 +2,7 @@ "node_port": 3000, "data_dir": "data", "view_engine": "ejs", - "loopback_url": "http://localhost:3000", + "language": "en-us", "modules": { "plantuml": false, "rss": true, diff --git a/src/file_walker.js b/src/file_walker.js index d545359..b9bc8a6 100644 --- a/src/file_walker.js +++ b/src/file_walker.js @@ -87,6 +87,7 @@ module.exports = (config) => { day: parseInt(matches[3]) }; article.date = new Date(article.year, article.month, article.day); + article.date.setUTCHours(0); remaining++; readIndexFile(path.join(article.realPath, config['article']['index']), config['article']['thumbnail_tag'], (err, info) => { if (err) diff --git a/src/rss.js b/src/rss.js deleted file mode 100644 index efd36aa..0000000 --- a/src/rss.js +++ /dev/null @@ -1,40 +0,0 @@ -const convert = require('xml-js'); - -const mapArticle = (url, article) => { - return { - 'title': {'_text': article.title}, - 'link': {'_text': (url + article.url).replace(/([^:])\/\//g, '$1/')}, - 'pubDate': {'_text': article.date.toString()}, - }; -}; - -module.exports = (config) => { - return { - get: (dict) => { - const items = Object.values(dict) - .sort((a, b) => ('' + b.path).localeCompare(a.path)) - .slice(0, config['rss']['length']); - const data = { - '_declaration': { - '_attributes': { - 'version': '1.0', - 'encoding': 'UTF-8' - } - }, - 'rss': { - '_attributes': { - 'version': '2.0' - }, - 'title': {'_text': config['rss']['title']}, - 'description': {'_text': config['rss']['description']}, - 'link': {'_text': config['loopback_url']}, - 'lastBuildDate': {'_text': new Date().toString()}, - 'lastPubDate': {'_text': new Date().toString()}, - 'ttl': {'_text': '60'}, - 'item': items.map((a) => mapArticle(config['loopback_url'], a)) - } - }; - return convert.js2xml(data, {compact: true}); - } - }; -}; \ No newline at end of file diff --git a/test/app.test.js b/test/app.test.js index bbf98df..2342920 100644 --- a/test/app.test.js +++ b/test/app.test.js @@ -9,30 +9,26 @@ const testIndex = 'testindex.ejs'; const testError = 'testerror.ejs'; const testTemplate = 'testtemplate.ejs'; -const config = { - 'test': true, - 'data_dir': dataDir, - 'view_engine': 'ejs', - 'home': { - 'index': testIndex, - 'error': testError, - 'hidden': ['.ejs', '.test'] - }, - 'article': { - 'index': 'index.md', - 'template': testTemplate, - 'thumbnail_tag': 'thumbnail', - 'default_title': 'Untitled', - 'default_thumbnail': null - }, - 'showdown': {} -}; +const config = require('../src/config')(); + +config['test'] = true; +config['data_dir'] = dataDir; +config['home']['index'] = testIndex; +config['home']['error'] = testError; +config['article']['template'] = testTemplate; +config['home']['hidden'].push('.test'); +config['rss']['endpoint'] = '/rsstest'; +config['rss']['length'] = 2; const app = require('../src/app')(config); -beforeEach(() => { +beforeEach((done) => { utils.deleteFolderSync(dataDir); fs.mkdirSync(dataDir); + app.reload((res) => { + expect(res).toBe(true); + done(); + }); }); afterAll(() => { @@ -86,6 +82,65 @@ describe('Test root path', () => { }); }); +describe('Test RSS feed', () => { + test('404 rss deactivated', (done) => { + config['modules']['rss'] = false; + request(app).get('/rsstest').then((response) => { + expect(response.statusCode).toBe(404); + config['modules']['rss'] = true; + done(); + }); + }); + test('200 empty rss', (done) => { + request(app).get('/rsstest').then((response) => { + expect(response.statusCode).toBe(200); + expect(response.text.length).toBeGreaterThan(0); + expect(response.text.split('').length).toBe(1); + done(); + }); + }); + test('200 2 rss items', (done) => { + utils.createEmptyDirs([ + path.join(dataDir, '2019', '05', '05'), + path.join(dataDir, '2018', '05', '05') + ]); + utils.createEmptyFiles([ + path.join(dataDir, '2019', '05', '05', 'index.md'), + path.join(dataDir, '2018', '05', '05', 'index.md') + ]); + app.reload((res) => { + expect(res).toBe(true); + request(app).get('/rsstest').then((response) => { + expect(response.statusCode).toBe(200); + expect(response.text.length).toBeGreaterThan(0); + expect(response.text.split('').length).toBe(3); + done(); + }); + }); + }); + test('200 max rss items', (done) => { + utils.createEmptyDirs([ + path.join(dataDir, '2019', '05', '05'), + path.join(dataDir, '2018', '05', '05'), + path.join(dataDir, '2017', '05', '05') + ]); + utils.createEmptyFiles([ + path.join(dataDir, '2019', '05', '05', 'index.md'), + path.join(dataDir, '2018', '05', '05', 'index.md'), + path.join(dataDir, '2017', '05', '05', 'index.md') + ]); + app.reload((res) => { + expect(res).toBe(true); + request(app).get('/rsstest').then((response) => { + expect(response.statusCode).toBe(200); + expect(response.text.length).toBeGreaterThan(0); + expect(response.text.split('').length).toBe(3); + done(); + }); + }); + }); +}); + describe('Test articles rendering', () => { test('404 article not found', (done) => { request(app).get('/2019/05/06/untitled/').then((response) => { diff --git a/test/file_walker.test.js b/test/file_walker.test.js index 1285863..20ec67b 100644 --- a/test/file_walker.test.js +++ b/test/file_walker.test.js @@ -220,6 +220,8 @@ describe('Test article fetching', () => { const file = path.join(dir, testIndex); utils.createEmptyDirs([dir]); utils.createEmptyFiles([file]); + const date = new Date(2019, 5, 5); + date.setUTCHours(0); fw.fetchArticles((err, dict) => { expect(err).toBeNull(); expect(dict).toBeDefined(); @@ -230,7 +232,7 @@ describe('Test article fetching', () => { year: 2019, month: 5, day: 5, - date: new Date(2019, 5, 5), + date: date, title: 'Untitled', thumbnail: 'default.png', escapedTitle: 'untitled', @@ -248,6 +250,8 @@ describe('Test article fetching', () => { ![thumbnail](./thumbnail.jpg) this is some text `); + const date = new Date(2019, 5, 5); + date.setUTCHours(0); fw.fetchArticles((err, dict) => { expect(err).toBeNull(); expect(dict).toBeDefined(); @@ -258,7 +262,7 @@ describe('Test article fetching', () => { year: 2019, month: 5, day: 5, - date: new Date(2019, 5, 5), + date: date, title: 'Title with : info !', thumbnail: path.join('2019', '05', '05', './thumbnail.jpg'), escapedTitle: 'title_with___info', diff --git a/test/rss.test.js b/test/rss.test.js deleted file mode 100644 index 087a2eb..0000000 --- a/test/rss.test.js +++ /dev/null @@ -1,100 +0,0 @@ -/* jshint -W117 */ -const config = { - 'loopback_url': 'http://test.test/', - 'rss': { - 'title': 'test rss', - 'description': 'description', - 'endpoint': '/rss', - 'length': 2 - }, -}; - -const rss = require('../src/rss')(config); - -test('empty rss', () => { - const xml = rss.get({}); - expect(xml).toBe('' + - '' + - 'test rss' + - 'description' + - 'http://test.test/' + - '' + new Date().toString() + '' + - '' + new Date().toString() + '' + - '60' + - ''); -}); - -test('1 item', () => { - const data = { - 'a': { - path: 'a', - realPath: 'b', - year: 2019, - month: 5, - day: 5, - date: new Date(2019, 5, 5), - title: 'Title with : info !', - thumbnail: '/2019/05/05/thumbnail.jpg/', - escapedTitle: 'title_with___info', - url: '/2019/05/05/title_with___info/', - } - }; - const xml = rss.get(data); - expect(xml).toEqual('' + - '' + - 'test rss' + - 'description' + - 'http://test.test/' + - '' + new Date().toString() + '' + - '' + new Date().toString() + '' + - '60' + - '' + - 'Title with : info !' + - 'http://test.test/2019/05/05/title_with___info/' + - '' + new Date(2019, 5, 5).toString() + '' + - '' + - ''); -}); - -test('3 items only 2 shown sorted', () => { - const data = { - 'a': { - path: '2019/05/05', - date: new Date(2019, 5, 5), - title: 'a', - url: 'a', - }, - 'b': { - path: '2018/05/05', - date: new Date(2018, 5, 5), - title: 'b', - url: 'b', - }, - 'c': { - path: '2020/05/05', - date: new Date(2020, 5, 5), - title: 'c', - url: 'c', - } - }; - const xml = rss.get(data); - expect(xml).toEqual('' + - '' + - 'test rss' + - 'description' + - 'http://test.test/' + - '' + new Date().toString() + '' + - '' + new Date().toString() + '' + - '60' + - '' + - 'c' + - 'http://test.test/c' + - '' + new Date(2020, 5, 5).toString() + '' + - '' + - '' + - 'a' + - 'http://test.test/a' + - '' + new Date(2019, 5, 5).toString() + '' + - '' + - ''); -}); \ No newline at end of file From ca642f1cab90a2a7baade1aceffeab02904322da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20GOUIN?= Date: Thu, 20 Jun 2019 16:29:04 +0200 Subject: [PATCH 029/120] code coverage with coveralls --- .travis.yml | 2 +- README.md | 1 + package-lock.json | 73 +++++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + 4 files changed, 76 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index aa98b10..3f71628 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,5 +16,5 @@ install: before_script: - npm install -g jshint script: - - npm test + - jest --silent --coverage --coverageReporters=text-lcov | coveralls - jshint ./src diff --git a/README.md b/README.md index 0d18322..4d4045e 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ > This is a work in progress, some information written here might not be true yet. [![Build Status](https://img.shields.io/travis/Klemek/GitBlog.md.svg?branch=master)](https://travis-ci.org/Klemek/GitBlog.md) +[![Coverage Status](https://img.shields.io/coveralls/github/Klemek/GitBlog.md.svg?branch=master)](https://coveralls.io/github/Klemek/GitBlog.md?branch=master) A static blog using Markdown pulled from your git repository. diff --git a/package-lock.json b/package-lock.json index 3667629..0862e4d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1339,6 +1339,15 @@ "normalize-path": "^2.0.0" } }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, "arr-diff": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", @@ -2663,6 +2672,28 @@ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true }, + "coveralls": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/coveralls/-/coveralls-3.0.4.tgz", + "integrity": "sha512-eyqUWA/7RT0JagiL0tThVhjbIjoiEUyWCjtUJoOPcWoeofP5WK/jb2OJYoBFrR6DvplR+AxOyuBqk4JHkk5ykA==", + "dev": true, + "requires": { + "growl": "~> 1.10.0", + "js-yaml": "^3.11.0", + "lcov-parse": "^0.0.10", + "log-driver": "^1.2.7", + "minimist": "^1.2.0", + "request": "^2.86.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, "cross-spawn": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", @@ -3929,6 +3960,12 @@ "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", "dev": true }, + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true + }, "growly": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", @@ -6591,6 +6628,24 @@ "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", "dev": true }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "dependencies": { + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + } + } + }, "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", @@ -6702,6 +6757,12 @@ "invert-kv": "^1.0.0" } }, + "lcov-parse": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/lcov-parse/-/lcov-parse-0.0.10.tgz", + "integrity": "sha1-GwuP+ayceIklBYK3C3ExXZ2m2aM=", + "dev": true + }, "left-pad": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", @@ -6757,6 +6818,12 @@ "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", "dev": true }, + "log-driver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz", + "integrity": "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg==", + "dev": true + }, "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -9001,6 +9068,12 @@ "extend-shallow": "^3.0.0" } }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, "sshpk": { "version": "1.16.1", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", diff --git a/package.json b/package.json index 04dee11..e309b2a 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "devDependencies": { "babel-cli": "^6.26.0", "babel-preset-env": "^1.7.0", + "coveralls": "^3.0.4", "jest": "^24.8.0", "superagent": "^5.1.0", "supertest": "^4.0.2" From 2552ac0ea2cd5a1faa6fecbb8c6910a17e263d3c Mon Sep 17 00:00:00 2001 From: Klemek Date: Thu, 20 Jun 2019 18:17:37 +0200 Subject: [PATCH 030/120] fixed path separator on multi-system --- src/file_walker.js | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/file_walker.js b/src/file_walker.js index b9bc8a6..d62b9e6 100644 --- a/src/file_walker.js +++ b/src/file_walker.js @@ -69,22 +69,20 @@ module.exports = (config) => { if (err) return cb(err); const paths = fileList - .map((path) => path.substr(config['data_dir'].length)) - .filter((path) => path.indexOf(config['article']['index']) === path.length - config['article']['index'].length) - .map((path) => path.substr(0, path.length - config['article']['index'].length)) - .map((path) => path.match(/^\/(\d{4})\/(\d{2})\/(\d{2})\/$/)) - .filter((matches) => matches && matches.length > 1); + .map((p) => p.substr(config['data_dir'].length+1).split(path.sep)) + .filter((p) => p.length === 4 && p[3] === config['article']['index'] && + /^\d{4}$/.test(p[0]) && /^\d{2}$/.test(p[1]) && /^\d{2}$/.test(p[2])); if (paths.length === 0) cb(null, {}); const articles = {}; let remaining = 0; - paths.forEach((matches) => { + paths.forEach((p) => { const article = { - path: path.join(matches[1], matches[2], matches[3]), - realPath: path.join(config['data_dir'], matches[1], matches[2], matches[3]), - year: parseInt(matches[1]), - month: parseInt(matches[2]), - day: parseInt(matches[3]) + path: path.join(p[0], p[1], p[2]), + realPath: path.join(config['data_dir'], p[0], p[1], p[2]), + year: parseInt(p[0]), + month: parseInt(p[1]), + day: parseInt(p[2]) }; article.date = new Date(article.year, article.month, article.day); article.date.setUTCHours(0); From 61308c20e79efb0bc1ad701f5f4a876f309e8189 Mon Sep 17 00:00:00 2001 From: Klemek Date: Thu, 20 Jun 2019 18:23:10 +0200 Subject: [PATCH 031/120] fixed path separator on multi-system --- src/file_walker.js | 8 +++++--- test/file_walker.test.js | 16 +++++++++------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/file_walker.js b/src/file_walker.js index d62b9e6..b0bdcae 100644 --- a/src/file_walker.js +++ b/src/file_walker.js @@ -1,6 +1,8 @@ const fs = require('fs'); const path = require('path'); +const joinUrl = (...paths) => path.join(...paths).replace(/\\/g,'/'); + /** * Get all files path inside a given folder path * @param dir @@ -78,7 +80,7 @@ module.exports = (config) => { let remaining = 0; paths.forEach((p) => { const article = { - path: path.join(p[0], p[1], p[2]), + path: joinUrl(p[0], p[1], p[2]), realPath: path.join(config['data_dir'], p[0], p[1], p[2]), year: parseInt(p[0]), month: parseInt(p[1]), @@ -91,9 +93,9 @@ module.exports = (config) => { if (err) return cb(err); article.title = info.title || config['article']['default_title']; - article.thumbnail = info.thumbnail ? path.join(article.path, info.thumbnail) : config['article']['default_thumbnail']; + article.thumbnail = info.thumbnail ? joinUrl(article.path, info.thumbnail) : config['article']['default_thumbnail']; article.escapedTitle = article.title.toLowerCase().replace(/[^\w]/gm, ' ').trim().replace(/ /gm, '_'); - article.url = '/' + path.join(article.path, article.escapedTitle) + '/'; + article.url = '/' + joinUrl(article.path, article.escapedTitle) + '/'; articles[article.path] = article; remaining--; if (remaining === 0) diff --git a/test/file_walker.test.js b/test/file_walker.test.js index 20ec67b..4eb6c2c 100644 --- a/test/file_walker.test.js +++ b/test/file_walker.test.js @@ -6,6 +6,8 @@ const utils = require('./test_utils'); const dataDir = 'test_data'; const testIndex = 'testindex.md'; +const joinUrl = (...paths) => path.join(...paths).replace(/\\/g,'/'); + const config = { 'test': true, 'data_dir': dataDir, @@ -226,8 +228,8 @@ describe('Test article fetching', () => { expect(err).toBeNull(); expect(dict).toBeDefined(); expect(Object.keys(dict).length).toBe(1); - expect(dict[path.join('2019', '05', '05')]).toEqual({ - path: path.join('2019', '05', '05'), + expect(dict[joinUrl('2019', '05', '05')]).toEqual({ + path: joinUrl('2019', '05', '05'), realPath: dir, year: 2019, month: 5, @@ -236,7 +238,7 @@ describe('Test article fetching', () => { title: 'Untitled', thumbnail: 'default.png', escapedTitle: 'untitled', - url: '/' + path.join('2019', '05', '05', 'untitled') + '/', + url: '/' + joinUrl('2019', '05', '05', 'untitled') + '/', }); done(); }); @@ -256,17 +258,17 @@ describe('Test article fetching', () => { expect(err).toBeNull(); expect(dict).toBeDefined(); expect(Object.keys(dict).length).toBe(1); - expect(dict[path.join('2019', '05', '05')]).toEqual({ - path: path.join('2019', '05', '05'), + expect(dict[joinUrl('2019', '05', '05')]).toEqual({ + path: joinUrl('2019', '05', '05'), realPath: dir, year: 2019, month: 5, day: 5, date: date, title: 'Title with : info !', - thumbnail: path.join('2019', '05', '05', './thumbnail.jpg'), + thumbnail: joinUrl('2019', '05', '05', './thumbnail.jpg'), escapedTitle: 'title_with___info', - url: '/' + path.join('2019', '05', '05', 'title_with___info') + '/', + url: '/' + joinUrl('2019', '05', '05', 'title_with___info') + '/', }); done(); }); From 553aa40fb3c702a414fbd3785cb7b9c03e6ae095 Mon Sep 17 00:00:00 2001 From: Klemek Date: Thu, 20 Jun 2019 19:15:49 +0200 Subject: [PATCH 032/120] generating git_secret at start --- .gitignore | 3 +- src/app.js | 56 +++++++++++++++++++--- src/config.default.json | 2 +- test/app.test.js | 101 +++++++++++++++++++++++++++------------- 4 files changed, 120 insertions(+), 42 deletions(-) diff --git a/.gitignore b/.gitignore index aba5ca9..606eb9f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ /config.json /config.example.json /data -/test_data \ No newline at end of file +/test_data +/git_secret \ No newline at end of file diff --git a/src/app.js b/src/app.js index 4319149..05d62e1 100644 --- a/src/app.js +++ b/src/app.js @@ -14,6 +14,16 @@ const cons = { error: '\x1b[31m✘\x1b[0m %s', }; +const randStr = (length) => { + let result = ''; + const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + const charactersLength = characters.length; + for (let i = 0; i < length; i++) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + } + return result; +}; + module.exports = (config) => { const fw = require('./file_walker')(config); const renderer = require('./renderer')(config); @@ -25,16 +35,18 @@ module.exports = (config) => { const articles = {}; let lastRSS = ''; + let webhookSecret; /** * Fetch articles from the data folder and send success as a response - * @param callback + * @param success + * @param error */ - const reload = (callback) => { + const reload = (success, error) => { fw.fetchArticles((err, dict) => { if (err) { - callback(false); - return console.error(cons.error, 'loading articles : ' + err); + console.error(cons.error, 'error loading articles : ' + err); + return error ? error() : null; } Object.keys(articles).forEach((key) => delete articles[key]); Object.keys(dict).forEach((key) => articles[key] = dict[key]); @@ -46,12 +58,41 @@ module.exports = (config) => { lastRSS = ''; - callback(true); + success(); }); }; if (config['test']) app.reload = reload; + /** + * Fetch or create secret token for git webhook + * @param success + * @param error + */ + const checkSecret = (success, error) => { + if (!config['modules']['webhook']) + success(); + fs.readFile(config['webhook']['secret_file'], {encoding: 'UTF-8'}, (err, data) => { + if (err) { + webhookSecret = randStr(32); + fs.writeFile(config['webhook']['secret_file'], webhookSecret, {encoding: 'UTF-8'}, (err) => { + if (err) { + console.error(cons.error, 'error creating secret : ' + err); + return error ? error() : null; + } + console.log(cons.ok,'created git secret at '+config['webhook']['secret_file']); + success(); + }); + } else { + webhookSecret = data; + console.log(cons.ok,'loaded git secret from '+config['webhook']['secret_file']); + success(); + } + }); + }; + if (config['test']) + app.checkSecret = checkSecret; + /** * Render the page with the view engine and catch errors * @param res @@ -180,11 +221,12 @@ module.exports = (config) => { // must be use in a server.js to start the server app.start = () => { - reload((res) => { - if (res) + reload(() => { + checkSecret(() => { app.listen(config['node_port'], () => { console.log(cons.ok, `gitblog.md server listening on port ${config['node_port']}`); }); + }); }); }; diff --git a/src/config.default.json b/src/config.default.json index d5ac59a..027b7d2 100644 --- a/src/config.default.json +++ b/src/config.default.json @@ -30,7 +30,7 @@ }, "webhook": { "endpoint": "/webhook", - "secretFile": "git_secret" + "secret_file": "git_secret" }, "showdown": { "parseImgDimensions": true, diff --git a/test/app.test.js b/test/app.test.js index 2342920..900da2a 100644 --- a/test/app.test.js +++ b/test/app.test.js @@ -22,13 +22,10 @@ config['rss']['length'] = 2; const app = require('../src/app')(config); -beforeEach((done) => { +beforeEach((done, fail) => { utils.deleteFolderSync(dataDir); fs.mkdirSync(dataDir); - app.reload((res) => { - expect(res).toBe(true); - done(); - }); + app.reload(done, fail); }); afterAll(() => { @@ -60,7 +57,7 @@ describe('Test root path', () => { done(); }); }); - test('200 2 articles', (done) => { + test('200 2 articles', (done, fail) => { utils.createEmptyDirs([ path.join(dataDir, '2019', '05', '05'), path.join(dataDir, '2018', '05', '05') @@ -70,18 +67,62 @@ describe('Test root path', () => { path.join(dataDir, '2018', '05', '05', 'index.md') ]); fs.writeFileSync(path.join(dataDir, testIndex), 'articles <%= articles.length %>'); - app.reload((res) => { - expect(res).toBe(true); + app.reload(() => { request(app).get('/').then((response) => { expect(response.statusCode).toBe(200); expect(response.text).toBe('articles 2'); done(); }); - }); + }, fail); }); }); +describe('Test check secret', () => { + const secretFile = 'git_secret'; + const tmpSecretFile = 'tmp_git_secret'; + beforeEach(() => { + if (fs.existsSync(secretFile)) + fs.renameSync(secretFile, tmpSecretFile); + }); + + afterEach(() => { + if (fs.existsSync(tmpSecretFile)) { + fs.renameSync(tmpSecretFile, secretFile); + } else if (fs.existsSync(secretFile)) { + fs.unlinkSync(secretFile); //remove secret file if remaining + } + }); + + test('no check if not activated', (done, fail) => { + config['modules']['webhook'] = false; + app.checkSecret(() => { + config['modules']['webhook'] = true; + done(); + }, () => { + config['modules']['webhook'] = true; + fail(); + }); + }); + test('create if not exists', (done, fail) => { + if (fs.existsSync(secretFile)) + fs.unlinkSync(secretFile); + app.checkSecret(() => { + expect(fs.existsSync(secretFile)).toBe(true); + expect(fs.readFileSync(secretFile).length).toBeGreaterThan(0); + done(); + }, fail); + }); + test('read if exists', (done, fail) => { + fs.writeFileSync(secretFile,'secret value'); + app.checkSecret(() => { + expect(fs.existsSync(secretFile)).toBe(true); + expect(fs.readFileSync(secretFile, {encoding:'UTF-8'})).toBe('secret value'); + done(); + }, fail); + }); +}); + describe('Test RSS feed', () => { test('404 rss deactivated', (done) => { config['modules']['rss'] = false; @@ -99,7 +140,7 @@ describe('Test RSS feed', () => { done(); }); }); - test('200 2 rss items', (done) => { + test('200 2 rss items', (done, fail) => { utils.createEmptyDirs([ path.join(dataDir, '2019', '05', '05'), path.join(dataDir, '2018', '05', '05') @@ -108,17 +149,16 @@ describe('Test RSS feed', () => { path.join(dataDir, '2019', '05', '05', 'index.md'), path.join(dataDir, '2018', '05', '05', 'index.md') ]); - app.reload((res) => { - expect(res).toBe(true); + app.reload(() => { request(app).get('/rsstest').then((response) => { expect(response.statusCode).toBe(200); expect(response.text.length).toBeGreaterThan(0); expect(response.text.split('').length).toBe(3); done(); - }); + }, fail); }); }); - test('200 max rss items', (done) => { + test('200 max rss items', (done, fail) => { utils.createEmptyDirs([ path.join(dataDir, '2019', '05', '05'), path.join(dataDir, '2018', '05', '05'), @@ -129,14 +169,13 @@ describe('Test RSS feed', () => { path.join(dataDir, '2018', '05', '05', 'index.md'), path.join(dataDir, '2017', '05', '05', 'index.md') ]); - app.reload((res) => { - expect(res).toBe(true); + app.reload(() => { request(app).get('/rsstest').then((response) => { expect(response.statusCode).toBe(200); expect(response.text.length).toBeGreaterThan(0); expect(response.text.split('').length).toBe(3); done(); - }); + }, fail); }); }); }); @@ -149,59 +188,55 @@ describe('Test articles rendering', () => { }); }); - test('500 no template', (done) => { + test('500 no template', (done, fail) => { utils.createEmptyDirs([path.join(dataDir, '2019', '05', '05'),]); fs.writeFileSync(path.join(dataDir, '2019', '05', '05', 'index.md'), '# Hello'); - app.reload((res) => { - expect(res).toBe(true); + app.reload(() => { request(app).get('/2019/05/05/hello/').then((response) => { expect(response.statusCode).toBe(500); done(); - }); + }, fail); }); }); - test('200 rendered article', (done) => { + test('200 rendered article', (done, fail) => { utils.createEmptyDirs([path.join(dataDir, '2019', '05', '05'),]); fs.writeFileSync(path.join(dataDir, '2019', '05', '05', 'index.md'), '# Hello'); fs.writeFileSync(path.join(dataDir, testTemplate), '<%- article.content %><%- `reload` %>'); - app.reload((res) => { - expect(res).toBe(true); + app.reload(() => { request(app).get('/2019/05/05/hello/').then((response) => { expect(response.statusCode).toBe(200); expect(response.text).toBe('

Hello

reload'); done(); - }); + }, fail); }); }); - test('200 other url', (done) => { + test('200 other url', (done, fail) => { utils.createEmptyDirs([path.join(dataDir, '2019', '05', '05'),]); utils.createEmptyFiles([ path.join(dataDir, '2019', '05', '05', 'index.md'), path.join(dataDir, testTemplate) ]); - app.reload((res) => { - expect(res).toBe(true); + app.reload(() => { request(app).get('/2019/05/05/anything/').then((response) => { expect(response.statusCode).toBe(200); done(); - }); + }, fail); }); }); - test('200 other url 2', (done) => { + test('200 other url 2', (done, fail) => { utils.createEmptyDirs([path.join(dataDir, '2019', '05', '05'),]); utils.createEmptyFiles([ path.join(dataDir, '2019', '05', '05', 'index.md'), path.join(dataDir, testTemplate) ]); - app.reload((res) => { - expect(res).toBe(true); + app.reload(() => { request(app).get('/2019/05/05/').then((response) => { expect(response.statusCode).toBe(200); done(); - }); + }, fail); }); }); }); From c1c8672380595d1eb239abd78ad5ab4888086b27 Mon Sep 17 00:00:00 2001 From: Klemek Date: Thu, 20 Jun 2019 19:55:59 +0200 Subject: [PATCH 033/120] Webhook endpoint with secret in config --- README.md | 24 ++++++++++-- src/app.js | 60 ++++++++---------------------- src/config.default.json | 3 +- test/app.test.js | 82 ++++++++++++++++++----------------------- 4 files changed, 75 insertions(+), 94 deletions(-) diff --git a/README.md b/README.md index 4d4045e..8837c18 100644 --- a/README.md +++ b/README.md @@ -52,11 +52,29 @@ git push -u origin master **5. Refresh content with a webhook (optional)** -At first start, a `git_secret` file will be generated, use it to create a new webhook as following : +Create a webhook on your git source (On GitHub, in the `Settings/Webhooks` part of the repository.) with the following parameters : * Payload URL : `https:///webhook` * Content type : `application/json` -* Secret : `` * Events : Just the push event -On GitHub, webhooks can be created in the `Settings/Webhooks` part of the repository. +**6. Securize your webhook (optional)** + +Here are the steps for Github, if you use another platform adapt it your way (header format on the config) : + +* Create a password or random secret +* Calculate it's SHA1 +* Edit your configuration to add webhook info +```json +{ +... +"webhook": { + "secret_value": "sha1=", + "secret_header": "X-Hub-Signature" + }, +... +} +``` +* Launch the server +* Update your webhook on github to include the secret +* Check if Github successfully reached the endpoint \ No newline at end of file diff --git a/src/app.js b/src/app.js index 05d62e1..1ce5022 100644 --- a/src/app.js +++ b/src/app.js @@ -14,16 +14,6 @@ const cons = { error: '\x1b[31m✘\x1b[0m %s', }; -const randStr = (length) => { - let result = ''; - const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; - const charactersLength = characters.length; - for (let i = 0; i < length; i++) { - result += characters.charAt(Math.floor(Math.random() * charactersLength)); - } - return result; -}; - module.exports = (config) => { const fw = require('./file_walker')(config); const renderer = require('./renderer')(config); @@ -35,7 +25,6 @@ module.exports = (config) => { const articles = {}; let lastRSS = ''; - let webhookSecret; /** * Fetch articles from the data folder and send success as a response @@ -64,35 +53,6 @@ module.exports = (config) => { if (config['test']) app.reload = reload; - /** - * Fetch or create secret token for git webhook - * @param success - * @param error - */ - const checkSecret = (success, error) => { - if (!config['modules']['webhook']) - success(); - fs.readFile(config['webhook']['secret_file'], {encoding: 'UTF-8'}, (err, data) => { - if (err) { - webhookSecret = randStr(32); - fs.writeFile(config['webhook']['secret_file'], webhookSecret, {encoding: 'UTF-8'}, (err) => { - if (err) { - console.error(cons.error, 'error creating secret : ' + err); - return error ? error() : null; - } - console.log(cons.ok,'created git secret at '+config['webhook']['secret_file']); - success(); - }); - } else { - webhookSecret = data; - console.log(cons.ok,'loaded git secret from '+config['webhook']['secret_file']); - success(); - } - }); - }; - if (config['test']) - app.checkSecret = checkSecret; - /** * Render the page with the view engine and catch errors * @param res @@ -164,6 +124,20 @@ module.exports = (config) => { } }); + //webhook endpoint + app.post(config['webhook']['endpoint'], (req, res) => { + if (config['modules']['webhook']) { + if (config['webhook']['secret_header'] && req.get(config['webhook']['secret_header']) !== config['webhook']['secret_value']) { + res.sendStatus(403); + } else { + res.sendStatus(200); + //TODO reload + } + } else { + res.sendStatus(400); + } + }); + // catch all article urls and render them app.get('*', (req, res, next) => { if (/^\/\d{4}\/\d{2}\/\d{2}\/(\w*\/)?$/.test(req.path)) { @@ -222,10 +196,8 @@ module.exports = (config) => { // must be use in a server.js to start the server app.start = () => { reload(() => { - checkSecret(() => { - app.listen(config['node_port'], () => { - console.log(cons.ok, `gitblog.md server listening on port ${config['node_port']}`); - }); + app.listen(config['node_port'], () => { + console.log(cons.ok, `gitblog.md server listening on port ${config['node_port']}`); }); }); }; diff --git a/src/config.default.json b/src/config.default.json index 027b7d2..9e30297 100644 --- a/src/config.default.json +++ b/src/config.default.json @@ -30,7 +30,8 @@ }, "webhook": { "endpoint": "/webhook", - "secret_file": "git_secret" + "secret_value": "", + "secret_header": "" }, "showdown": { "parseImgDimensions": true, diff --git a/test/app.test.js b/test/app.test.js index 900da2a..3174ce5 100644 --- a/test/app.test.js +++ b/test/app.test.js @@ -19,6 +19,7 @@ config['article']['template'] = testTemplate; config['home']['hidden'].push('.test'); config['rss']['endpoint'] = '/rsstest'; config['rss']['length'] = 2; +config['webhook']['endpoint'] = '/webhooktest'; const app = require('../src/app')(config); @@ -74,52 +75,6 @@ describe('Test root path', () => { done(); }); }, fail); - - }); -}); - -describe('Test check secret', () => { - const secretFile = 'git_secret'; - const tmpSecretFile = 'tmp_git_secret'; - beforeEach(() => { - if (fs.existsSync(secretFile)) - fs.renameSync(secretFile, tmpSecretFile); - }); - - afterEach(() => { - if (fs.existsSync(tmpSecretFile)) { - fs.renameSync(tmpSecretFile, secretFile); - } else if (fs.existsSync(secretFile)) { - fs.unlinkSync(secretFile); //remove secret file if remaining - } - }); - - test('no check if not activated', (done, fail) => { - config['modules']['webhook'] = false; - app.checkSecret(() => { - config['modules']['webhook'] = true; - done(); - }, () => { - config['modules']['webhook'] = true; - fail(); - }); - }); - test('create if not exists', (done, fail) => { - if (fs.existsSync(secretFile)) - fs.unlinkSync(secretFile); - app.checkSecret(() => { - expect(fs.existsSync(secretFile)).toBe(true); - expect(fs.readFileSync(secretFile).length).toBeGreaterThan(0); - done(); - }, fail); - }); - test('read if exists', (done, fail) => { - fs.writeFileSync(secretFile,'secret value'); - app.checkSecret(() => { - expect(fs.existsSync(secretFile)).toBe(true); - expect(fs.readFileSync(secretFile, {encoding:'UTF-8'})).toBe('secret value'); - done(); - }, fail); }); }); @@ -180,6 +135,41 @@ describe('Test RSS feed', () => { }); }); +describe('Test webhook', () => { + test('400 webhook deactivated', (done) => { + config['modules']['webhook'] = false; + request(app).post('/webhooktest').then((response) => { + expect(response.statusCode).toBe(400); + config['modules']['webhook'] = true; + done(); + }); + }); + test('200 no secret', (done) => { + request(app).post('/webhooktest').then((response) => { + expect(response.statusCode).toBe(200); + //TODO test reload + done(); + }); + }); + test('403 wrong secret', (done) => { + config['webhook']['secret_header'] = 'testheader'; + config['webhook']['secret_value'] = 'testvalue'; + request(app).post('/webhooktest').set('testheader','testvalue2').then((response) => { + expect(response.statusCode).toBe(403); + done(); + }); + }); + test('200 valid secret', (done) => { + config['webhook']['secret_header'] = 'testheader'; + config['webhook']['secret_value'] = 'testvalue'; + request(app).post('/webhooktest').set('testheader','testvalue').then((response) => { + expect(response.statusCode).toBe(200); + //TODO test reload + done(); + }); + }); +}); + describe('Test articles rendering', () => { test('404 article not found', (done) => { request(app).get('/2019/05/06/untitled/').then((response) => { From 0f5b3f138d07cd985322a1bb411aa95eeabf936f Mon Sep 17 00:00:00 2001 From: Klemek Date: Thu, 20 Jun 2019 20:19:03 +0200 Subject: [PATCH 034/120] valid github signature check --- README.md | 6 +++--- package-lock.json | 5 +++++ package.json | 2 ++ src/app.js | 21 ++++++++++++++++----- src/config.default.json | 4 ++-- test/app.test.js | 23 +++++++++++++++++------ 6 files changed, 45 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 8837c18..3d30a55 100644 --- a/README.md +++ b/README.md @@ -63,14 +63,14 @@ Create a webhook on your git source (On GitHub, in the `Settings/Webhooks` part Here are the steps for Github, if you use another platform adapt it your way (header format on the config) : * Create a password or random secret -* Calculate it's SHA1 * Edit your configuration to add webhook info ```json { ... "webhook": { - "secret_value": "sha1=", - "secret_header": "X-Hub-Signature" + "endpoint": "/webhook", + "secret": "sha1=", + "signature_header": "X-Hub-Signature" }, ... } diff --git a/package-lock.json b/package-lock.json index 0862e4d..4328a5a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2704,6 +2704,11 @@ "which": "^1.2.9" } }, + "crypto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", + "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==" + }, "cssom": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.6.tgz", diff --git a/package.json b/package.json index e309b2a..9acd811 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,8 @@ "description": "A static blog using Markdown pulled from your git repository.", "main": "src/server.js", "dependencies": { + "body-parser": "^1.19.0", + "crypto": "^1.0.1", "ejs": "^2.6.2", "express": "^4.17.1", "ncp": "^2.0.0", diff --git a/src/app.js b/src/app.js index 1ce5022..f5f1ef2 100644 --- a/src/app.js +++ b/src/app.js @@ -3,6 +3,9 @@ const app = express(); const fs = require('fs'); const path = require('path'); const Rss = require('rss'); +const bodyParser = require('body-parser'); +const crypto = require('crypto'); +app.use(bodyParser.json()); /** * Terminal colors and symbols to display status messages @@ -127,12 +130,20 @@ module.exports = (config) => { //webhook endpoint app.post(config['webhook']['endpoint'], (req, res) => { if (config['modules']['webhook']) { - if (config['webhook']['secret_header'] && req.get(config['webhook']['secret_header']) !== config['webhook']['secret_value']) { - res.sendStatus(403); - } else { - res.sendStatus(200); - //TODO reload + if (config['webhook']['signature_header'] && config['webhook']['secret']) { + const payload = JSON.stringify(req.body); + if (!payload) { + return res.sendStatus(403); + } + const hmac = crypto.createHmac('sha1', config['webhook']['secret']); + const digest = 'sha1=' + hmac.update(payload).digest('hex'); + const checksum = req.headers[config['webhook']['signature_header']]; + if (!checksum || !digest || checksum !== digest) { + return res.sendStatus(403); + } } + res.sendStatus(200); + //TODO reload } else { res.sendStatus(400); } diff --git a/src/config.default.json b/src/config.default.json index 9e30297..f138c22 100644 --- a/src/config.default.json +++ b/src/config.default.json @@ -30,8 +30,8 @@ }, "webhook": { "endpoint": "/webhook", - "secret_value": "", - "secret_header": "" + "secret": "", + "signature_header": "" }, "showdown": { "parseImgDimensions": true, diff --git a/test/app.test.js b/test/app.test.js index 3174ce5..1ca5f06 100644 --- a/test/app.test.js +++ b/test/app.test.js @@ -151,18 +151,29 @@ describe('Test webhook', () => { done(); }); }); + test('403 no payload', (done) => { + config['webhook']['signature_header'] = 'testheader'; + config['webhook']['secret'] = 'testvalue'; + request(app).post('/webhooktest').then((response) => { + expect(response.statusCode).toBe(403); + done(); + }); + }); test('403 wrong secret', (done) => { - config['webhook']['secret_header'] = 'testheader'; - config['webhook']['secret_value'] = 'testvalue'; - request(app).post('/webhooktest').set('testheader','testvalue2').then((response) => { + config['webhook']['signature_header'] = 'testheader'; + config['webhook']['secret'] = 'testvalue'; + request(app).post('/webhooktest').set('testheader', 'sha1=invalid').then((response) => { expect(response.statusCode).toBe(403); done(); }); }); test('200 valid secret', (done) => { - config['webhook']['secret_header'] = 'testheader'; - config['webhook']['secret_value'] = 'testvalue'; - request(app).post('/webhooktest').set('testheader','testvalue').then((response) => { + config['webhook']['signature_header'] = 'testheader'; + config['webhook']['secret'] = 'testvalue'; + request(app).post('/webhooktest') + .send({}) + .set('testheader', 'sha1=d924d5bd4b36faf9d572844ac9c12a09ce3e7134') + .then((response) => { expect(response.statusCode).toBe(200); //TODO test reload done(); From 00d1d6c4e198e2ff089f5b287982a9b460d9647c Mon Sep 17 00:00:00 2001 From: Klemek Date: Thu, 20 Jun 2019 20:37:35 +0200 Subject: [PATCH 035/120] webhook : pull command then reload articles --- src/app.js | 16 ++++++++++++-- src/config.default.json | 3 ++- test/app.test.js | 48 +++++++++++++++++++++++++++++------------ 3 files changed, 50 insertions(+), 17 deletions(-) diff --git a/src/app.js b/src/app.js index f5f1ef2..2f34a07 100644 --- a/src/app.js +++ b/src/app.js @@ -2,9 +2,14 @@ const express = require('express'); const app = express(); const fs = require('fs'); const path = require('path'); + +//rss const Rss = require('rss'); + +///webhook const bodyParser = require('body-parser'); const crypto = require('crypto'); +const cp = require('child_process'); app.use(bodyParser.json()); /** @@ -142,8 +147,15 @@ module.exports = (config) => { return res.sendStatus(403); } } - res.sendStatus(200); - //TODO reload + cp.exec(config['webhook']['pull_command'], {cwd: path.join(__dirname, '..', config['data_dir'])}, (err) => { + if (err) { + console.log(cons.error, `command '${config['webhook']['pull_command']}' failed : ${err}`); + return res.sendStatus(500); + } + reload(() => { + res.sendStatus(200); + }); + }); } else { res.sendStatus(400); } diff --git a/src/config.default.json b/src/config.default.json index f138c22..a959260 100644 --- a/src/config.default.json +++ b/src/config.default.json @@ -31,7 +31,8 @@ "webhook": { "endpoint": "/webhook", "secret": "", - "signature_header": "" + "signature_header": "", + "pull_command": "git pull" }, "showdown": { "parseImgDimensions": true, diff --git a/test/app.test.js b/test/app.test.js index 1ca5f06..730d9e8 100644 --- a/test/app.test.js +++ b/test/app.test.js @@ -110,8 +110,8 @@ describe('Test RSS feed', () => { expect(response.text.length).toBeGreaterThan(0); expect(response.text.split('').length).toBe(3); done(); - }, fail); - }); + }); + }, fail); }); test('200 max rss items', (done, fail) => { utils.createEmptyDirs([ @@ -130,8 +130,8 @@ describe('Test RSS feed', () => { expect(response.text.length).toBeGreaterThan(0); expect(response.text.split('').length).toBe(3); done(); - }, fail); - }); + }); + }, fail); }); }); @@ -145,9 +145,29 @@ describe('Test webhook', () => { }); }); test('200 no secret', (done) => { + utils.createEmptyDirs([path.join(dataDir, '2019', '05', '05'),]); + utils.createEmptyFiles([ + path.join(dataDir, '2019', '05', '05', 'index.md'), + path.join(dataDir, testTemplate) + ]); + config['webhook']['pull_command'] = 'git --help'; request(app).post('/webhooktest').then((response) => { expect(response.statusCode).toBe(200); - //TODO test reload + request(app).get('/2019/05/05/').then((response) => { + expect(response.statusCode).toBe(200); + done(); + }); + }); + }); + test('500 command failed', (done) => { + utils.createEmptyDirs([path.join(dataDir, '2019', '05', '05'),]); + utils.createEmptyFiles([ + path.join(dataDir, '2019', '05', '05', 'index.md'), + path.join(dataDir, testTemplate) + ]); + config['webhook']['pull_command'] = 'qzgfqgqz'; + request(app).post('/webhooktest').then((response) => { + expect(response.statusCode).toBe(500); done(); }); }); @@ -170,12 +190,12 @@ describe('Test webhook', () => { test('200 valid secret', (done) => { config['webhook']['signature_header'] = 'testheader'; config['webhook']['secret'] = 'testvalue'; + config['webhook']['pull_command'] = 'git --help'; request(app).post('/webhooktest') .send({}) .set('testheader', 'sha1=d924d5bd4b36faf9d572844ac9c12a09ce3e7134') .then((response) => { expect(response.statusCode).toBe(200); - //TODO test reload done(); }); }); @@ -196,8 +216,8 @@ describe('Test articles rendering', () => { request(app).get('/2019/05/05/hello/').then((response) => { expect(response.statusCode).toBe(500); done(); - }, fail); - }); + }); + }, fail); }); test('200 rendered article', (done, fail) => { @@ -209,8 +229,8 @@ describe('Test articles rendering', () => { expect(response.statusCode).toBe(200); expect(response.text).toBe('

Hello

reload'); done(); - }, fail); - }); + }); + }, fail); }); test('200 other url', (done, fail) => { @@ -223,8 +243,8 @@ describe('Test articles rendering', () => { request(app).get('/2019/05/05/anything/').then((response) => { expect(response.statusCode).toBe(200); done(); - }, fail); - }); + }); + }, fail); }); test('200 other url 2', (done, fail) => { @@ -237,8 +257,8 @@ describe('Test articles rendering', () => { request(app).get('/2019/05/05/').then((response) => { expect(response.statusCode).toBe(200); done(); - }, fail); - }); + }); + }, fail); }); }); From 5fb8e0fa4523c47202bde07e97b7fdce95e5536c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20GOUIN?= Date: Fri, 21 Jun 2019 09:09:08 +0200 Subject: [PATCH 036/120] Add rewrite url for articles relative resources --- src/app.js | 9 ++++++++- test/app.test.js | 11 ++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/app.js b/src/app.js index 2f34a07..f6802d2 100644 --- a/src/app.js +++ b/src/app.js @@ -161,9 +161,16 @@ module.exports = (config) => { } }); + //rewrite urls to hide articles titles : /2019/05/05/sometitle/img.png => /2019/05/05/img.png + app.use((req, res, next) => { + if (/^\/\d{4}\/\d{2}\/\d{2}\//.test(req.url)) + req.url = req.url.slice(0, 11) + req.url.slice(req.url.lastIndexOf('/')); + next(); + }); + // catch all article urls and render them app.get('*', (req, res, next) => { - if (/^\/\d{4}\/\d{2}\/\d{2}\/(\w*\/)?$/.test(req.path)) { + if (/^\/\d{4}\/\d{2}\/\d{2}\/$/.test(req.path)) { const articlePath = req.path.substr(1, 10); const article = articles[articlePath]; if (!article) diff --git a/test/app.test.js b/test/app.test.js index 730d9e8..c694be9 100644 --- a/test/app.test.js +++ b/test/app.test.js @@ -286,13 +286,22 @@ describe('Test static files', () => { }); }); test('200 valid file', (done) => { - fs.writeFileSync(`${dataDir}/somefile.txt`, 'filecontent'); + fs.writeFileSync(path.join(dataDir, 'somefile.txt'), 'filecontent'); request(app).get('/somefile.txt').then((response) => { expect(response.statusCode).toBe(200); expect(response.text).toBe('filecontent'); done(); }); }); + test('200 valid resource of article', (done) => { + utils.createEmptyDirs([path.join(dataDir, '2019', '05', '05'),]); + fs.writeFileSync(path.join(dataDir, '2019', '05', '05', 'somefile.txt'), 'filecontent'); + request(app).get('/2019/05/05/title/somefile.txt').then((response) => { + expect(response.statusCode).toBe(200); + expect(response.text).toBe('filecontent'); + done(); + }); + }); }); describe('Test other requests', () => { From 193e7f62cb260a7879cb4b1f90f6c1547adc5341 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20GOUIN?= Date: Fri, 21 Jun 2019 11:31:26 +0200 Subject: [PATCH 037/120] Prism code rendering --- package-lock.json | 75 +++++++++++++++++++++++++++++++++++++++++ package.json | 2 ++ src/config.default.json | 3 +- src/renderer.js | 23 ++++++++++++- test/renderer.test.js | 43 +++++++++++++++++++++++ 5 files changed, 144 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4328a5a..9fb4096 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2543,6 +2543,17 @@ } } }, + "clipboard": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.4.tgz", + "integrity": "sha512-Vw26VSLRpJfBofiVaFb/I8PVfdI1OxKcYShe6fm0sP/DtmiWQNCjhM/okTvdCo0G+lMMm1rMYbk4IK4x1X+kgQ==", + "optional": true, + "requires": { + "good-listener": "^1.2.2", + "select": "^1.1.2", + "tiny-emitter": "^2.0.0" + } + }, "cliui": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", @@ -2850,6 +2861,12 @@ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "dev": true }, + "delegate": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", + "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==", + "optional": true + }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -3959,6 +3976,15 @@ "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", "dev": true }, + "good-listener": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz", + "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=", + "optional": true, + "requires": { + "delegate": "^3.1.2" + } + }, "graceful-fs": { "version": "4.1.15", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", @@ -7145,6 +7171,35 @@ "which": "^1.3.0" } }, + "node-prismjs": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/node-prismjs/-/node-prismjs-0.1.2.tgz", + "integrity": "sha512-WKb6ZbUlPWarzS8jR2UdIbV4lYpt6sOTkIx3u5Ldz55K1Zzs982KyF6aj1zjZbrrx/UGZSZ1e0j28lIzcm3ceg==", + "requires": { + "prismjs": "~1.6.0" + }, + "dependencies": { + "clipboard": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-1.7.1.tgz", + "integrity": "sha1-Ng1taUbpmnof7zleQrqStem1oWs=", + "optional": true, + "requires": { + "good-listener": "^1.2.2", + "select": "^1.1.2", + "tiny-emitter": "^2.0.0" + } + }, + "prismjs": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.6.0.tgz", + "integrity": "sha1-EY2V+3pm26InLjQ7NF9SNmWds2U=", + "requires": { + "clipboard": "^1.5.5" + } + } + } + }, "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -7620,6 +7675,14 @@ } } }, + "prismjs": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.16.0.tgz", + "integrity": "sha512-OA4MKxjFZHSvZcisLGe14THYsug/nF6O1f0pAJc0KN0wTyAcLqmsbE+lTGKSpyh+9pEW57+k6pg2AfYR+coyHA==", + "requires": { + "clipboard": "^2.0.0" + } + }, "private": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", @@ -8769,6 +8832,12 @@ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true }, + "select": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz", + "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=", + "optional": true + }, "semver": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", @@ -9323,6 +9392,12 @@ "integrity": "sha1-iQN8vJLFarGJJua6TLsgDhVnKmo=", "dev": true }, + "tiny-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", + "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==", + "optional": true + }, "tmpl": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", diff --git a/package.json b/package.json index 9acd811..a09266c 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,8 @@ "ejs": "^2.6.2", "express": "^4.17.1", "ncp": "^2.0.0", + "node-prismjs": "^0.1.2", + "prismjs": "^1.16.0", "rss": "^1.2.2", "showdown": "^1.9.0" }, diff --git a/src/config.default.json b/src/config.default.json index a959260..6c9827d 100644 --- a/src/config.default.json +++ b/src/config.default.json @@ -6,7 +6,8 @@ "modules": { "plantuml": false, "rss": true, - "webhook": true + "webhook": true, + "prism": true }, "home": { "index": "index.ejs", diff --git a/src/renderer.js b/src/renderer.js index 236d931..f24a81e 100644 --- a/src/renderer.js +++ b/src/renderer.js @@ -1,5 +1,6 @@ const fs = require('fs'); const showdown = require('showdown'); +const Prism = require('node-prismjs'); module.exports = (config) => { const converter = new showdown.Converter(config['showdown']); @@ -8,9 +9,29 @@ module.exports = (config) => { fs.readFile(file, {encoding: 'UTF-8'}, (err, data) => { if (err) return cb(err); + + if (config['modules']['prism']) { + const codeRegex = /```([\w-]+)\n((?:(?!```)[\s\S])*)\n```/m; + let match; + while ((match = codeRegex.exec(data))) { + const lang = match[1].trim(); + const code = match[2].trim(); + try { + const block = Prism.highlight(code, Prism.languages[lang] || Prism.languages.autoit, lang); + data = data.slice(0, match.index) + `
` + block + '
' + data.slice(match.index + match[0].length); + } catch (err) { + console.error(err); + } + } + } + + const html = converter.makeHtml(data); + + cb(null, html); }); } }; -}; \ No newline at end of file +}; + diff --git a/test/renderer.test.js b/test/renderer.test.js index 37aa81f..c2912eb 100644 --- a/test/renderer.test.js +++ b/test/renderer.test.js @@ -7,6 +7,9 @@ const dataDir = 'test_data'; const file = path.join(dataDir, 'test.md'); const config = { + 'modules': { + 'prism': true, + }, 'showdown': { 'simplifiedAutoLink': true, 'smartIndentationFix': true @@ -50,4 +53,44 @@ test('custom rules', (done) => { expect(html).toBe('

www.google.com

'); done(); }); +}); + +test('no prism', (done) => { + config['modules']['prism'] = false; + fs.writeFileSync(file, '```python\nprint("hello")\n```\n\n```python\nprint("hello")\n```'); + renderer.render(file, (err, html) => { + expect(err).toBeNull(); + expect(html).toBe('
print("hello")\n
\n
print("hello")\n
'); + config['modules']['prism'] = true; + done(); + }); +}); + +test('prism correct', (done) => { + fs.writeFileSync(file, '```python\nprint("hello")\n```'); + renderer.render(file, (err, html) => { + expect(err).toBeNull(); + expect(html).not.toBe('
print("hello")\n
'); + expect(html.indexOf('
')).toBe(0);
+    done();
+  });
+});
+
+test('prism invalid lang', (done) => {
+  fs.writeFileSync(file, '```pythdon\nprint("hello")\n```');
+  renderer.render(file, (err, html) => {
+    expect(err).toBeNull();
+    expect(html).not.toBe('
print("hello")\n
'); + expect(html.indexOf('
')).toBe(0);
+    done();
+  });
+});
+
+test('prism mutliple code blocks', (done) => {
+  fs.writeFileSync(file, '```python\n\n```\n\n```python\n\n```');
+  renderer.render(file, (err, html) => {
+    expect(err).toBeNull();
+    expect(html).toBe('
\n
'); + done(); + }); }); \ No newline at end of file From 17db174926ded2f8354a5c33edb94a2db13617a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20GOUIN?= Date: Fri, 21 Jun 2019 11:38:13 +0200 Subject: [PATCH 038/120] Sample templates --- sample_data/article/birthday-cake.png | Bin 919584 -> 0 bytes sample_data/article/index.md | 169 +++++++++++++++++++++++++- sample_data/home/error.ejs | 8 +- sample_data/home/prism.css | 143 ++++++++++++++++++++++ sample_data/home/style.css | 138 ++++++++++++++++++++- sample_data/home/template.ejs | 16 ++- 6 files changed, 461 insertions(+), 13 deletions(-) delete mode 100644 sample_data/article/birthday-cake.png create mode 100644 sample_data/home/prism.css diff --git a/sample_data/article/birthday-cake.png b/sample_data/article/birthday-cake.png deleted file mode 100644 index c5d5d9fc153968394bda9cb2561669732efcdd2e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 919584 zcmeI*37A|}ng8)33kgfY(qRvz*=1E{*by)g)Bplv5FAk$70?lILAJ(0U}*mej~gH; z;KB?d$^gnbia>%Rpb`N=A%TI2AVQigfb8=By(HCL)m_zftLol+w(ohKH>vgBb3X5> zdaG`os{YEpd%t_qvTH6|sZ=J-e9w&iDwU;XS1MyF6PMN{t8Dnof6;%-eC9nLJi1a@ zuE%1j%6S*8Ua9m}X3m&C>-eQAJ^cfHV|62%JAnz>^+Lt_6mMU0`Z4`aOGAMCr;+Do zqa^>$WVk#Dfhij08^hvGoqtafqi2xhKkYd{{?pEX@}pl@0>cYn_57u}Zmr~n;szl9 zap$+y=*WL7A+C>FU`vgzTJReEZ-+FjIpY4u@{I@H{?V}RdU!xgb=tevLb={sK7|#?S|MBFqlaY}BPSm(hGy-;*bF_lj z=(=y}J`}+?rU3bmBafYqg8X+X$Ni!ZI9H=+RODJ+c3c#rXAtB+?KwdH)6ReCqu=3( zhc@$dEx;UUv?_efu0No3Np_gG0Qt{bG_38B|FH5!Z*vr|Lz)AMW(BU*g!qn=0dr1fxUSqaI^Z8<=tME+t@G4{Ih4{qdTJukpIzrV!xLB$DU!v*Hl1% z!>aPSB1>`0dYiknz41-=$Q3>bkbj@DQGQ>@fBEL@(IbI_)R(Mf=L-b_-g&w|_wUm=J(RYF#A% zsb#!=yyU;$gL8=q9H{ZOTg*DW{n3fphA>?6A3~Oj>m&Iu&Xz5EE?^(j%~f<-(AvG< zGuqdRo|~l9J@TJYwnvV&{QSRo`?XIFtp(t@U}R3X#F0Qz>!L?1k|#MDE4v*jK>kOX zq;v`SPbu4swN7A)&Mj1oR;-G+?rH6Fg}R<_#)Sa+ccJdjKFPm7xv1;O*t&C^DN5iR zZFq;GHa^OBC+fQA6~7wgHe6M_0QoQ8*ylZvf1k5M-wG9&p{*D3?k?*#^^riuzKle3 zBS8M$xcjnK^6yI~=tt=SYio~>E3y`=qHgzz(ns!ihkLy3mW_ti0yyr>;@vYByhwwy zPLO|R^z7KRz_RK7o@?VT=C|f1}p=64F<}P4=zWqmvtcA$c?QY9`oI{F8qk|7p*G zWJhnm`**9tWijhy-*+UNv3v*aWBymV?Cx8tYXP#%4dy~VbHc~DK>qW|En8_JV22dv zE3!;DA8*4d(H04AaW(mG0TlVwXx-nhm;8x*}lks zWC?9^IOM-gFm4`?z+M{43yQ3T#@E|j5>HA>9SQj_mD{_Wk$>+}LWjx{I7~aTZ<_mB z)Y|FSQyRy(vfv`w5BZNIksS_${C5b)-HH~N_V@FqRo4O(y{}5;GulNv#U_N?zO{I^ zX!bz7)R;+;i@x68n zODMNLzgW5`wod+|NMpx?ApafHamUaF?9V^P^7&t*qI_rQ2`ae%tb zD^6ftZTd+?qrwr(Wv^*`%N0i$^p?qgDCzslnLpM_mEOwC8PjJK(`V*FF|OWi)&AxO ztzKZF&R(Hdx&Gql&W+WGHi~Xk-4BTTcW)T-7RZ0d*=w`!6u^;S$LgX~R!jDd9Deeb zkpHx&0QpZS{}jObWZR*S6Y4LD`kalIHY7m)hb&S&Mg9}ZIt8#k;os0%I|PzSqX5=t zn*7&$q;`({Czf#v;84Q1(aF7s&ia39?jBowKgYEI6vLsE2R9dny58|A@;^jJ{)a4* zIYluXO7-DGCzCm~YBD2jJy7!B+9>8*kpGyo+hlAMz{M~t=)VJ-7{z??@-Yvtjj@sc zHn1YRDfy2usab|Z0bH!~A^m565Y<#+g?&%%#&9YSU4Y z|E4raTtWVmNOR*s@@}wF*;F(s7C)O2yZ|Eqjh`9Pxs=C$I=LP>(w_c-KB{1)vfW6U zWG;CznUOXhDEV)0lfpIRKZPte86yR-Qn4=trP?Tf7&is{A`j0Zh99F3f*Q1-HeNp8&}}kN@Bvuz7Zv0+{v#;L$uMQ=r>Qxd4)X9{+(p zZ1d?h1&{|oJ~JuYtxy2N&0QWnC;xdU7QT%;4&A)3(6s;+ieV*bgTEF&hyC9{%8vo^ z&*R_!M`(dPO!>iIWqq!CBEkRvoZy&;51ITA7fI_p`A;j~b)%&KRw|3?MAABQXIjH; zI9l@GkR_Q5$$v7b9yw47V5KrJk|l{t?1BGK5+fabkmP@KpV+S@|FLIyl<`pjE0qUE z@rn7WyEM3e#5}-^WAi`%2b^}w`o*pVU@k14EdI}hjFY;}n~{HkQ4A}U`E5>|SZ+Qy zmVtCR4)Wh6VBmKr|A8m4o9Wd3K%r=cyUqw#a{YIm@lb{N0?z zzZaU?((uz>7^!_LR>E(QI#JmHnR34MYy@MiYoVZ29r1&k) zHTh3t&Nmw+`EMqZz~$Z-z;PG9{8F)N0Y+r``Vp7JeZh5c53lXfk^i>IqPhe5k1CxE zrd@&Ub%!?+30o05wOyddZ%_UsPjHLjk^dGzTphQ7eJ*;xB84LO^SD#l(dfv3M{LpD ziTp>C$~Fhm(?8JX{V~xt$JFV~t@{0-(>n)tzf{KAPFl!+J6iGGmi)(;Q%2OLKy{g9 z%x?wl@8nEq(w+bkm^|5=o{P1FKM zXml|az@wthT7H8g|M^*lxFzx*V%F$$ZUW=9r8^Wc6~W(YJT@*`YXS1#+9>8*kpGyo z%h)mrY_41VUE!bzu6EGmod(wer zKBjTyU5s38?~F+EQ}nRNf6>BzZ=d}8ogaEvuz-DY_5y{AqLtS^Z&v@u7wi=4=E;Ao znawsX@}JF#TSY8jpAB58$g3cgxgGm8;58!7O&LQY|79rqyASg3Z*J&WNdopYf;oyZ zi&wY%dRqN`Ye_z_?}q%xp5f8QS7QFpxNyo3a$gI;bHV8O<=Qp{>;Z9=qT3=>#{Czm zpDciBGr=uxPX1c}MSeB;k37K)ueE?38u9(}!6(#5`w4tn{sQDbf76h*Nd8007JV&5 zz&<4ZsKU`#Cq{h-dZ+reP9dXaqg4U&UzJGnfAXJZ-Wl~U0sGF7PhX-xU;SBbSRXlC zoB;VR&NhTClm8I1L>~(k7^|%xp>X+|q7P~RGYTDLSsUMK@^OC}TnkXvPIKE2b0Ig% zAU8z*gG?Kp&RbwrZSc#A=M-fuc(Hv9D2`F^4p0b=8~Jyf0LQy0|Be&D?)?y0N!>V6 zQRJVapRes7tC*yq`6WR9{mMt#y&?Z)i?cs31?)rf?G)cs{87>EyTW1o@h z`7J>H{mxI(y(9lc3+K80p8kP8o>lV{ZM-e}sg|7;n|^Lw#Oh zf4$S*M%vrGdlhpOk0@y23XuPBbC*ZY$^R&dJ3MpZY2mH~7-e8wg#ZEwAbVCJ&02umS=k^02;?n5G0a<(?IC~w0tf^l zKmiOQO>`0g1Q0+VZvhHm-lA*|0R#|0AP4~pU=V4dlL#PyKu`jE?)B^$L9GR#7zQ=J z=qv&VAb>z#0u;c!B-suE2q1t!Py!UdpwdNW5kLR|1o9G~0Olphb`U@S0R(~)pa2Gy zE;<{AzCp<@UjfB*tT3glVy-0u;a^RM{p1i3zNF*&kPC zEkI&Pk8HRU!^jfKa1cNM0R*xMPyn+zaVrE6KmdWr1So)!C6wVHfB*srWD}qOW^>|J z2q1s}0+9(&0J~4ozb&n1Q0*~0ha<4K$rUL8vz7D7P$PWY4@`hAmj|z^qpc@gTYAz5I_Kdgamr} z2l}Xj2_>ANB7gt_2n-2O0Ea9%g#ZEwAdrv%1u&sxGgJf+KmdUu0Se%d1*ftKY`OC% ze$QHftXkX-0R#dNpcn>_B)W(I0tg_GzW@a=e^a)I00Iag5P$#$Fn}b{MFbE)0D=4k zD1iB!vPA?CKp;+mG55aj!;!89pcuwEWf>I$2q1t!3jqpX3m~pW009ILh*N+97-wD? z6#@t#fItfY3SbK$u0{X>1Q3W*fC3n2UKv#Z0t>(Qja?#L3s8U-n?xWi0g7Q*`J%T7 zAb``y+r^41Q5tcfC88kB%4710R#{TOCW0jyl97G zR%9(eSh=OQ2q1s}0vQFe7Q>92<7NmTfB*sr1R~JWKhQ@N3?x}}69EJeKpzR0u;b> za?MB)KmY**Y6K{NH5i<%RQCP+h4--*fSG^*0;vg53{%TE<3#`g1Q2*rfC9)wKmY** z5J*#?r+=W2Dwt;O88re3AdtKO1u*$30006ABrb5pxoh(`0w{)wKMEK=0tg_GssIHr z)yy+)1Q0+VaRCZo;uC=3BY*$`sR~d4Q_Vc%MgRc>5*MHVCO!ce{tEBi?sH-wQ)Mo(WM*sl?5*45TCYpGLjQ|1&q%J@KOnnA0egqIeAW;DdV4{g<*a#qiK-vNy z*?R6-Lu&yjhH1|RMvnjj2qY^&0Zcaa3>*Oj5J+2q0+@FG89f3BAdsv81u)svGjIeD zKp<@a3Sip#XY{EG?D~%9w;5Uskg5XXMqp5YVmL^_2?P*80D+VQD1a$to3SE*00IaM z3QzzCDL8=u0tg_Glt54aKp#~wsgyHV1Q0*~fvP}i1@Qb&?7xt;0M+dCKLQ9KfWVsq zsTD)gjsOA(Ab`M|0u(?d0s;sifIyN06u>0Y&Y%%M009IjfXo2|5J*8_nR$KtuofVN zOm{d&ieZOv+zkN)5I`Vo0SaK)`J?v;AbufxcyS zdN*qULdzQcMZlc^#n7ESdqw~O1Q2j2Kml}!&#nmwE!;r%)SxuLV#lEMH=V? z0tg_0K-mHmz_P{J9|8y69^!HKwtt96~n;f=`I2YAb@~V0Scf~es+!k0tg`BN`M0BN}l~9fB*sr zI2E7(I^}2Q2q4hCz%rXHc$l>S-8&|}g`WO_J}P43lYrqPfB*ui3Qz!3%{=2q009IN z7oY$pJ^>g$0tg_GssIHr)yy+)1Q0+VaRCZoz!TuCD?e~GYXJgIAYDfQ0dE8-hTf!t zjv#;l0tl2TKmjaMn|&dG00IbjBR~Q4CKYr90R#|0piBV@V42$N3jqWW$U)$3Cm-?( zYXNeAtl7j;HK%<$xq>>h@sikR$^TRJ|D<6tUgv2DAbETX?5MhY&yj0R%b`xLbF+L2;wvW`!38(RBf|qPM@|BZ?gr z9G(>d4FLoYh*;ns8r!cG|EaiC;j9?CD1cVvc2^vt*jF)8K|=rm1Q1A1;3WifZp;}sheG!8b400Iag5Qo468pf9u_73s2B8OCT0o-1lKUeYAA_u{?5kLR| z1fmnTOC$QE;?|-@R@4HNDeCYAidz&EzoN3ob`d}Tfp`V1c32HLU(r*=!5)g+yV+5?2rfagMz)4nKP!(YJF>Nfk27^%WKe9kL=KB zN3GLUF}A~t6`kI{qt0?C1Q0*~0R(CUtOmVbF;8(%&E=U+cJg_kz5TSRJ3iAMxDf&f zAbJN*yDuDKO@;*f&KiM6MBK<@F0R#{TPQZR?`z;FljId#AbqLgc^?JvK z_vAtZ5I~?nfzO}($`PyuC=fWUP1_;Sl@(3j5svn5(EbAMEopf%S_BY40D;H^td`x@ zXiUut;5gmbzGJ~(z-^?)We6aE00N;1+^LS*S0-K?-r;6nk2qL2r1%Zj;yeNfAb>z< z0&i1?Khmhvjoty;yT5xC8#KBDmmz=v0tg@wn7{+-?yM<69^!H00NN+Y^HI{tR2hn0(eO6m7GQZ0R#|0AR>Xf)iJflLG_oa zD}Pf=jA*b71px#Q2t?rVgT8wWXJ?Gr-^p{dXiKmY**VicI5 zK@I)|h}r^J_ck%ckuxR)5I_Kd0tg`BNMNcR0-fY&=j;{%1Q0*~0apT(D1ff$ z*)IYTn7H$8=eb`CKq?SOUVvhl{1gBH0R#|8Q(%&PLH-rR_%z4Qs1ZN_0R$2ic%{!^0`{RG4FLoYKmdW{1RNE>$M#-yH){csn{5mj0R#~6RKQU& z^i+dxA%Fk^2qYsw0Zb;<3={zb5I~?-fC5;n!D$2#KmdVc1So*Xq?&;u;Df;Hm%RUI z)&lsD3HnekKryWM;2Z)7Ab>z30u;bR63s9XKmY**>IEo(^&Xr<009ILNJM}Fm`I`- zCISc`fIz(f1+doA z0tg_`fdB=t12pb}00Iag5WWBfF#H@c0t687UEps|Kl|^j1@Jmc6hp7mL+21c009Jw z5}*JUCChdZKmY**ycVDUdYv9RhX4WyAW)b!{}4Y01-d{0R$QePyidLa2Wy!Ab>#h0u;dLQ_KJnKmY** z8VOJU8>w&^0tg_0KsN$=?)B^$L9GR#7?KmqJVkNYCfP+-5!9z8p#wEzuA!G#E1Q0*~fs_O&fGK60u_Ax~0tgHWPyh!hIDr5H2q2J>K>h`= z_k=I6!CHWn<`82=009Jo7s$UD22a8O5I_I{1X2*70H%;-#)tp{2p}**fC4yzgbNTr z009J&5a{V2=%WfIk!l8sK(PXor~cO*)&dl3%+`w)pcoeG%hnM<009I%6`%lmni#r; z00IagP^)|OK=npcx`d9R{6r#9AUn`gAN1YXl{A5>JIt5kt23l+`g z)JwW<8AYQxWtXk0*iccOSL^EZSVeMeDUJAzL`Hq5#(AUSHibPgJ)p3nR#Y?FH5E3= zwopu0yi>8Ff)HqU5r2)wFc+^Vp7 zb-Tjm)B=TFUQ}CJ8@3nk_5y1wh0UpL6ww#Om`Hm5B9S5f2(b8S9T zv!f68{AABlHm}~Qh_YD%VhMZo{Z+-5fn>@KeSKJA6F-(5#5N8qaKBJ|P+^Cad~bG` zehSao`a;)jYC6`{|oUznA+yicp`R)b)mK z2VMGeMTj{Gz0Xg`{TPKUMv19S(O~|c!agPlHAlg9+zQ_}6>BPDZdM5QQ`kbDP$ySN z{kC^W?^VQB_%x)tX74_(RfLqAQ2S~h&m66=k3C{;)k6531{qqeg6Xh*F7q8l?1gXm z1EpFB1363@%zUaoQ127?o{HED-|%^5EB=0^s5f3+IGPaLpG^E;g z(~nNpv(b{Z0JT>%I=!VXyhY)E4tx3s>V9F(j^7=j zU=4hG4=(%ALJM1IO$2t*<&P+Qe!}vxZ{L<#2;ZM+lJ-I`UQJuSUg2ZjtWREc>?`Wm zci6=W4Y5dDEF&1+z|eOKw>sV24EQvr<;f)IYfF7pnKKaq8;iX{*_HyE3i?=xR@1 z-uWHs6^BRb=a)UP+ER56e|9!y*VNtSC`Kz#WnSk+N9=db?3Y_<-3i#b_XUbFCy=lE zx?K+ID`?#b*x}J@6u#!U%ziyDhYxkf$-O0D3mm_sC^L7>_T{t_PYYom1uZ`TTiy3F zMYDvbaQT@Uf6Cu|$Zs|+-jW?2JywzO;nBM_{1XEkK@~{JI8h$Af9*2>i2l^Qa=v9Qw8``^(5{l*7qh zBrsWVmBP1qkY|7FMWnr$q?IFJU!kzC#pRj9EZZt=hp%dIA1}v}y$lnuRan1OWSJ15 z-R44#i?2QmOLz%q?FsA4iqPgumj2q2g?}o+kX?)vI9M0GtjLn5JZ@9Ujs`TKT}FdexEfzf%D#7XPlu^=MmvbVIeP5(8a5SMdv+hn$ z)2{7D2=d75Xxk`s2bX97_F*T@F9G{ILH2c&(I$qk*WIChZ{Qba)VRlnvcx}88} z-v4~{mtW@abV8|H;}k9yHFPrvRp~1`_)D66&#Z=gQc=`rgtLHgP(lcx*`c3v z72Q06lyTqZ)W<^L z@Mi$CX;>HEt@HLuxX}c0ec27_SFeH=mcVN2uCG7jzDRvLRKXFdu;!E<5_;Ruk0&P}c{zdVWqMNyp=Y4;vzD!fl z!WEdVu0N#6V_tN%5ql-|1;z4;2-^=1_}KCG_G9LZ>9ZmnUh5(4q?_EL$YY{)vW?fZ zZ~NSDEd?zOfhiis*Azv4rjuo!&C~wv5zRY5Aw8pxPUtzWN1UFwh(wiiHlS&3DH2W!#nTksluP8b#Y@^-pNp1HViY*khGz8Yy zF#8pcDn^?d-CSoMCfo7YWfZjd1y+1hj^z6eRn^3mCnT9~EfIV?-uCR$_>y>Q@pdE@>c}Yyx z|Mr2g&8KSEt9_eKzgFC@uqQVf0<{8b>&*5Fn^(OGn^U#(>Ls18K1WsO)g8Lbo~!06 z?76J_cJt=DJf8GgfbI!B-GaS{-%erkYh#5yUsdN<5AW`zv80(rufm`VY?@*aN~c#+ z41X}%3xzus_F%kSkfpI!rnR4 z5U3Tfk147zMz_)F)fKf*?89G-+T5}iqrXwui_!ZOyciWi>TQAmW-0z#@tC5W!!N^c z`>O756YM_}9F7q}!tE^@`&Sh9PUUsQ@VU~?c`Jq&D0Wfsf>j8KHY;#PDC|2P&neoO zL&I-t@1(C(9HdxAK}$?v3yt^diUGy&$&uIj8@25ZDVShFNTAs|?SmD+Qsgy$OSgj_~mOJd;<1Pe-A|&e>S0ZE?%pBZW$jZBeDo+Ninj`IQ z*A_d>440FhOTZRReOu9P?u)v;J!jh2q3k)77NEd->ho+xQ4^$_?b-+0hbg>%XQ=+s z>ILk{;FAh_=haRAi@NUx>emViT37>Rv+Jyda?o;{{;2(Oofm0sJ2h`T>8;KOq1@Wt;+kp zqBeQHpXM)j`aflNQ5S7tlkan>+P~M-*Uu?9!XN}s+cfp-w~A`cod18q3jHd`gKGg= z_2;|lJFDG2om?BLUuCbRYBgc9UT>@Yj#BXC<=5o0!=J|~TrR50I)@tF&c64`Vi~{Y zl=a3wyS4A=HOiUqm)Un-*H_Ry60iyNtit!n)2e?LX$%uRa!t7_%WLqLD_Z5u_v`J~ zKBynrk?AO?~&h0{DZX zQGP?w_9R|UPGPS1Zw+gdvL43`XGt0;e^=rm~| z+|Ry~Gpl6RRCo92xSwBEvlie@hQsx*eKs4yTUfZ_cOw1!+A#B6G=CM+No@{!tfc=8d zi;58DM7Mo>P~)_pqYSI{^bgnvv_Z9f)LZ+)O1Bvc?fyU3nC<%?wEPA18*!Bz6`{?E zZu>h=W8Nr#PPQlkTTXncqT3vWcK<~h^Ii&C&H{Gare6`-gvhhM_MH!&u=1T)+i7!u zPIz9~QtJKkB~5;7_JZnaMV^oSkhWz@Uak0#RnT$}SWDZuSrO7CD5|ga`)UW~V4C_S z>=pVkir7E&%Q_FgAq!SEbpNQ>L7T7@ZCTx8zTHi7@O*ju-0vQwr5A(03($T33*ExI zwDl(xF;ALK#&)hu_%dNzEFCdBC930TWs}fnXTQJH2F*I1=Z&i z9P(`Mam69gCJ&uebombyJo$8PaKHTQGgI1P0eg}AEvMxkYw9lqvlB}pUE`*iQYaZJX*0# zVl!(sjrUANOuwQ&Jh!gS+rozP6sf(qvhTTlNb!imm3_?nwHjcxAepwKM*0gy%t`fS zY>&zLvx?Y%U1oKS(O#X~53TxQA8S8elCizWSW6Les}{a{HON?V>C?Ebm$R+dj<)Tj zk^NHP6M8Cru@9o{J4?$cVrsU+%)Z?Hq9T=C`Z7){gl8+JDq?E(dFMfjc?w^wQ|iYJ za^G1IMVq2g*dLdgtMDd?7vD4PBSUKe{OMQ8M$c$}=PI^UETsss*^`z1CZYX_^j8$# z%&|oK@~Xytg~EQzcf2ChwvjsCui*Qmemth^Gw4q%teynbs!vdtsni4RJd;T;(RMbbx!T5(-T~Giv4F6cv`o-Nl|^Sx=*LAaCY3Mk@- z1@2P6?1{_fmVMUtfZ_#3QEiMiY|l&fTxIV_?Rjb!#UuqS4gp*6XLD+f!j5R!bJc(% zb5XP>n57lVHwimL(5tXXRE?E%nwFNp>l*HZ3Y%aS_bcob{qs%27E9RIF`CV*)pVT| zyXu^prqdKVAtcrw(RizK%KrbWqS>6XVrhrKn$0PDURqyKol_g?^oXwn-FW2cN7_@` z%o)>X>3Uk+0=DL%c783;`9+F`bIhKAGFMLBmWBWV2q1t!6ase0fQA492q1t!dICND z1ASD%^zzP#5kLR|1SCKKWEvoV00IakDL?^CBJJaro4Xfl0g^~HgG2y@KmqKCjXNQL00IbvEcY@uozdiiU0x#AmF(G1<>;Z(LDqZKmdVa z1So*TxUy9Q5I_I{&jl!eo+pUzA%Fk^2(&4%+iPE0q0O}b6vH-+xj6y|Ab>y|0u;bF z^2jIeAG0R#|0pge(y3g9UVpWKwS0Od^`_JjZe2*e{0Q8A1sDU1XG1Q0-=cmWDv z@y6@{0R#|0z(WBFpodAJO9&u<00PAePymZJW)BD;P^iE%n=N>lwE%?@C$MpfVFF2J zhzKBn00JWgD1akPxC8+N5I`US0SaIONoI%$Ab>dFG5J13<00q#EJ9|X{0R#|mEI#R0u;c=6U^`sKmY**8VgVW8@q6+zXEgDx%NiZ0{ELBdWJxd z0u;j_(?+KeKmY**x)q=Rb}PvJ5kLR|1cDTx00x;hI*kAV2q4g{00ppHLGF(L0tg^b zhQQ2cE}2r;S^$b+87X8R2q1s}0^SKw0KH2I9YO#B1P~}gfC5;CGW$RP0R#~6PJjaF zT}tQ>0tg_0Kp6rQz%rEC2LfFSocONe7ZtV^pzGeT0R%!9pcsapLHdsX0tg_`kpKm- zBR1}Y00Iag5V`;bF!T)4e*_Rf0D+DKD1aTYaVG>2KmdW@1vWfo{z=|f!5J5=If1nR z!Ot}YfB*srcrD<4G4xuG&LMyR0tf^vKmiOkb#xp71Q0-=8vzPnH+tL`0R#|0AXotk zV6ds9;|Rnp@ViaNpUGN)xO42&=qQFhWrMyTfB*srlqNs{ERC8SA%Fk^2>2vG0rV*w z^aTL~5I~?b0SaJg)a(cW1Q0;LCjknePuUpiOW)=v?#fz#ArDR=fB*u83Q!CSE1Q765fCA`iX6P3J2&5=*&CFX)V=aJ# z%u@^k2q1s}0uBTyfDX{vB?1T_fPi}e3ZVNO&;tYzKmY*; z0u(?8=z20u;bPSlK872q1uf?*bG+-!nx25I_I{1PT$L02ac^MiIy+u;($? zobAC{fNY)RRtQ8aKrxIsxeN^f1Q0-=xc~*QxeeDKfB*srL@Yo7j5xUr4FLoYK%lt* z1+cjd*C2oZ0tiGb;IaUI;IUUHuofWV$;HqRKmdVI1Y8!wP>kp&0tg_0K!gGmzzCDd zkPtus0R&nLPykySaSH?xKmdUV1t@?KCY2!};JLtRlP_7mQWXdDGRUDr4Sw h-J9Of5hnt-&D;H%S+mAWwVlu0eeW5U?|R6o{~ti$^#K3? diff --git a/sample_data/article/index.md b/sample_data/article/index.md index e45f07a..04e93ea 100644 --- a/sample_data/article/index.md +++ b/sample_data/article/index.md @@ -1,6 +1,171 @@ # Welcome to your new blog -## If you see this page, that means it's working +If you see this page, that means it's working -![thumbnail](./birthday-cake.png) +## Guide to Markdown formatting +### Headers + +# H1 +## H2 +### H3 +#### H4 +##### H5 +###### H6 + +Alternatively, for H1 and H2, an underline-ish style: + +Alt-H1 +====== + +Alt-H2 +------ + +### Emphasis + +Emphasis, aka italics, with *asterisks* or _underscores_. + +Strong emphasis, aka bold, with **asterisks** or __underscores__. + +Combined emphasis with **asterisks and _underscores_**. + +Strikethrough uses two tildes. ~~Scratch this.~~ + +### Lists + +1. First ordered list item +2. Another item +â‹…â‹…* Unordered sub-list. +1. Actual numbers don't matter, just that it's a number +â‹…â‹…1. Ordered sub-list +4. And another item. + +â‹…â‹…â‹…You can have properly indented paragraphs within list items. Notice the blank line above, and the leading spaces (at least one, but we'll use three here to also align the raw Markdown). + +â‹…â‹…â‹…To have a line break without a paragraph, you will need to use two trailing spaces.â‹…â‹… +â‹…â‹…â‹…Note that this line is separate, but within the same paragraph.â‹…â‹… +â‹…â‹…â‹…(This is contrary to the typical GFM line break behaviour, where trailing spaces are not required.) + +* Unordered list can use asterisks +- Or minuses ++ Or pluses + +### Links + +[I'm an inline-style link](https://www.google.com) + +[I'm an inline-style link with title](https://www.google.com "Google's Homepage") + +[I'm a reference-style link][Arbitrary case-insensitive reference text] + +[I'm a relative reference to a repository file](../blob/master/LICENSE) + +[You can use numbers for reference-style link definitions][1] + +Or leave it empty and use the [link text itself]. + +URLs and URLs in angle brackets will automatically get turned into links. +http://www.example.com or and sometimes +example.com (but not on Github, for example). + +Some text to show that the reference links can follow later. + +[arbitrary case-insensitive reference text]: https://www.mozilla.org +[1]: http://slashdot.org +[link text itself]: http://www.reddit.com + +### Images + +Here's our logo (hover to see the title text): + +Inline-style: +![alt text](https://github.com/adam-p/markdown-here/raw/master/src/common/images/icon48.png "Logo Title Text 1") + +Reference-style: +![alt text][logo] + +[logo]: https://github.com/adam-p/markdown-here/raw/master/src/common/images/icon48.png "Logo Title Text 2" + + +### Code and Syntax Highlighting + +Inline `code` has `back-ticks around` it. + +```javascript +var s = "JavaScript syntax highlighting"; +alert(s); +``` + +```python +s = "Python syntax highlighting" +print s +``` + +``` +No language indicated, so no syntax highlighting. +But let's throw in a tag. +``` + + +### Tables + +Colons can be used to align columns. + +| Tables | Are | Cool | +| ------------- |:-------------:| -----:| +| col 3 is | right-aligned | $1600 | +| col 2 is | centered | $12 | +| zebra stripes | are neat | $1 | + +There must be at least 3 dashes separating each header cell. +The outer pipes (|) are optional, and you don't need to make the +raw Markdown line up prettily. You can also use inline Markdown. + +Markdown | Less | Pretty +--- | --- | --- +*Still* | `renders` | **nicely** +1 | 2 | 3 + +### Blockquotes + +> Blockquotes are very handy in email to emulate reply text. +> This line is part of the same quote. + +Quote break. + +> This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can *put* **Markdown** into a blockquote. + +### Inline HTML + +
+
Definition list
+
Is something people use sometimes.
+ +
Markdown in HTML
+
Does *not* work **very** well. Use HTML tags.
+
+ +### Horizontal Rule + +Three or more... + +--- + +Hyphens + +*** + +Asterisks + +___ + +Underscores + +### Line Breaks + +Here's a line for us to start with. + +This line is separated from the one above by two newlines, so it will be a *separate paragraph*. + +This line is also a separate paragraph, but... +This line is only separated by a single newline, so it's a separate line in the *same paragraph*. diff --git a/sample_data/home/error.ejs b/sample_data/home/error.ejs index e4b1427..3d343ba 100644 --- a/sample_data/home/error.ejs +++ b/sample_data/home/error.ejs @@ -3,10 +3,16 @@ Error <%= error %> +
-

Error <%= error %> at path <%= path %>

+

Somehing went wrong + (Error <%= error %>) +

+ It means the resource you're trying to access is unavailable right now.
+ We're terribly sorry that you encountered this error.

+
Back to home
\ No newline at end of file diff --git a/sample_data/home/prism.css b/sample_data/home/prism.css new file mode 100644 index 0000000..80fb936 --- /dev/null +++ b/sample_data/home/prism.css @@ -0,0 +1,143 @@ +/* PrismJS 1.16.0 +https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript+abap+abnf+actionscript+ada+apacheconf+apl+applescript+c+arff+asciidoc+asm6502+csharp+autohotkey+autoit+bash+basic+batch+bison+bnf+brainfuck+bro+cpp+aspnet+arduino+cil+coffeescript+cmake+clojure+ruby+csp+css-extras+d+dart+diff+markup-templating+docker+ebnf+eiffel+ejs+elixir+elm+erb+erlang+fsharp+flow+fortran+gcode+gedcom+gherkin+git+glsl+gml+go+graphql+groovy+less+handlebars+haskell+haxe+hcl+http+hpkp+hsts+ichigojam+icon+inform7+ini+io+j+java+scala+php+javastacktrace+jolie+jq+javadoclike+n4js+json+jsonp+json5+julia+keyman+kotlin+latex+markdown+liquid+lisp+livescript+lolcode+lua+makefile+crystal+django+matlab+mel+mizar+monkey+n1ql+typescript+nand2tetris-hdl+nasm+nginx+nim+nix+nsis+objectivec+ocaml+opencl+oz+parigp+parser+pascal+perl+jsdoc+phpdoc+php-extras+sql+powershell+processing+prolog+properties+protobuf+scss+puppet+pure+python+q+qore+r+js-extras+jsx+renpy+reason+vala+rest+rip+roboconf+textile+rust+sas+sass+stylus+javadoc+scheme+shell-session+smalltalk+smarty+plsql+soy+twig+swift+yaml+tcl+haml+toml+tt2+pug+tsx+t4-templating+visual-basic+t4-cs+regex+vbnet+velocity+verilog+vhdl+vim+t4-vb+wasm+wiki+xeora+xojo+xquery+tap */ +/** + * prism.js default theme for JavaScript, CSS and HTML + * Based on dabblet (http://dabblet.com) + * @author Lea Verou + */ + +code[class*="language-"], +pre[class*="language-"] { + color: black; + background: none; + text-shadow: 0 1px white; + font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; + font-size: 1em; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + line-height: 1.5; + + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; +} + +pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection, +code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection { + text-shadow: none; + background: #b3d4fc; +} + +pre[class*="language-"]::selection, pre[class*="language-"] ::selection, +code[class*="language-"]::selection, code[class*="language-"] ::selection { + text-shadow: none; + background: #b3d4fc; +} + +@media print { + code[class*="language-"], + pre[class*="language-"] { + text-shadow: none; + } +} + +/* Code blocks */ +pre[class*="language-"] { + padding: 1em; + margin: .5em 0; + overflow: auto; +} + +:not(pre) > code[class*="language-"], +pre[class*="language-"] { + background: #f5f2f0; +} + +/* Inline code */ +:not(pre) > code[class*="language-"] { + padding: .1em; + border-radius: .3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: slategray; +} + +.token.punctuation { + color: #999; +} + +.namespace { + opacity: .7; +} + +.token.property, +.token.tag, +.token.boolean, +.token.number, +.token.constant, +.token.symbol, +.token.deleted { + color: #905; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #690; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #9a6e3a; + background: hsla(0, 0%, 100%, .5); +} + +.token.atrule, +.token.attr-value, +.token.keyword { + color: #07a; +} + +.token.function, +.token.class-name { + color: #DD4A68; +} + +.token.regex, +.token.important, +.token.variable { + color: #e90; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} + diff --git a/sample_data/home/style.css b/sample_data/home/style.css index 6d915f0..396d21f 100644 --- a/sample_data/home/style.css +++ b/sample_data/home/style.css @@ -1,18 +1,144 @@ +body, html { + padding: 0; + margin: 0; +} + +* { + box-sizing: border-box; +} + +body { + font: 14px/1.45 -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif; + color: #111; + -webkit-text-size-adjust: none; + + background-color: #F5F5F5; + + height: 100vh; +} + main { max-width: 70ch; padding: 2ch; margin: auto; + + background-color: #F0F0F0; + + min-height: 100%; } -.article a, .article a:visited { - color: black; +q:before { + content: open-quote; } -.article a:visited { - color: black; +q:after { + content: close-quote; } -.article img { +hr { + background: #e1e4e8; + border: 0; + height: 0.25em; + margin: 1em 0; +} + +a { + color: #3C3CA1; +} + +a:hover { + color: #8484C6; +} + +pre, code { + font-size: 96%; + background: #f8f8f8; +} + +pre { + overflow-x: auto; + -webkit-overflow-scrolling: touch; + padding: 10px 16px; +} + +table td { + vertical-align: baseline; + padding-left: 8px; +} + +table td:first-of-type { + padding-left: 0; +} + +div.header span.time span { + color: #888; + font-family: serif; + font-style: italic; +} + +main.article div.header a.link-home { + text-decoration: none; + float: right; + line-height: 2.4; +} + +main.article div.header h1, main.article div.header h2 { + margin-top: 0.85em; + margin-bottom: 0.25em; + font-size: 1.5em; +} + +main.article div.header h1 a, main.article div.header h2 a { + text-decoration: none; +} + +main.article div.header span.time { + display: block; +} + +#text h1:first-child { + display: none; +} + +#text { + text-align: justify; + hyphens: auto; +} + +#text li, #text table, #text blockquote { + text-align: left; +} + +#text img { max-width: 100%; - max-height: 10vh; + height: auto; +} + +.note { + padding: 1em; + background: #ff02; +} + +.note > p { + margin: 0.6rem 0; +} + +.important { + padding: 1em; + background: #eff5ff; +} + +.important > p { + margin: 0.6rem 0; +} + +/* Sidenotes */ + +#text .side > p:nth-child(2n+0) { + font-style: italic; + color: #555; +} + +#text .side > p:nth-child(2n+0) > i, #text .side > p:nth-child(2n+0) > em { + font-style: normal; } \ No newline at end of file diff --git a/sample_data/home/template.ejs b/sample_data/home/template.ejs index 6cd69bf..064c1a1 100644 --- a/sample_data/home/template.ejs +++ b/sample_data/home/template.ejs @@ -2,12 +2,20 @@ - GitBlog.md - Home - + GitBlog.md - <%= article.title %> + + -
- <%- article.content %> +
+
+ ↑ +

<%= article.title %>

+ Published on <%= article.year + '-' + article.month + '-' + article.day %> +
+
<%- article.content %>
+
+ Go to top - Back to home
\ No newline at end of file From 3fc01cadb89d6e0a33e925808fc10ed74c9591e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20GOUIN?= Date: Fri, 21 Jun 2019 11:44:39 +0200 Subject: [PATCH 039/120] Pages includes footer --- sample_data/home/error.ejs | 1 + sample_data/home/footer.ejs | 5 +++++ sample_data/home/index.ejs | 1 + sample_data/home/template.ejs | 3 ++- 4 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 sample_data/home/footer.ejs diff --git a/sample_data/home/error.ejs b/sample_data/home/error.ejs index 3d343ba..9593025 100644 --- a/sample_data/home/error.ejs +++ b/sample_data/home/error.ejs @@ -13,6 +13,7 @@ It means the resource you're trying to access is unavailable right now.
We're terribly sorry that you encountered this error.

Back to home + <%- include('footer'); %>
\ No newline at end of file diff --git a/sample_data/home/footer.ejs b/sample_data/home/footer.ejs new file mode 100644 index 0000000..42edf34 --- /dev/null +++ b/sample_data/home/footer.ejs @@ -0,0 +1,5 @@ +
+
+ @<%= new Date().getFullYear() %> - Made with GitBlog.md + +
\ No newline at end of file diff --git a/sample_data/home/index.ejs b/sample_data/home/index.ejs index 680f203..df1c851 100644 --- a/sample_data/home/index.ejs +++ b/sample_data/home/index.ejs @@ -21,6 +21,7 @@ <% } %> <% }); %> + <%- include('footer'); %> \ No newline at end of file diff --git a/sample_data/home/template.ejs b/sample_data/home/template.ejs index 064c1a1..6acc674 100644 --- a/sample_data/home/template.ejs +++ b/sample_data/home/template.ejs @@ -14,8 +14,9 @@ Published on <%= article.year + '-' + article.month + '-' + article.day %>
<%- article.content %>
-
+
Go to top - Back to home + <%- include('footer'); %> \ No newline at end of file From 5e7b700304f26015cadc53639760550d706b967e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20GOUIN?= Date: Fri, 21 Jun 2019 14:45:49 +0200 Subject: [PATCH 040/120] Style update --- sample_data/home/index.ejs | 6 ++---- sample_data/home/style.css | 17 +++++------------ 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/sample_data/home/index.ejs b/sample_data/home/index.ejs index df1c851..4c48df3 100644 --- a/sample_data/home/index.ejs +++ b/sample_data/home/index.ejs @@ -12,10 +12,8 @@

Articles in this blog :

<% articles.forEach((article) => { %>
-

<%- `${article.title}` %> ( - <%= `${article.day}/${article.month}/${article.year}` %> - ) -

+

<%- `${article.title}` %>

+ Published on <%= article.year + '-' + article.month + '-' + article.day %> <% if(article.thumbnail){ %> <%- `thumbnail` %> <% } %> diff --git a/sample_data/home/style.css b/sample_data/home/style.css index 396d21f..f9f689f 100644 --- a/sample_data/home/style.css +++ b/sample_data/home/style.css @@ -18,7 +18,7 @@ body { } main { - max-width: 70ch; + max-width: 75ch; padding: 2ch; margin: auto; @@ -45,7 +45,6 @@ hr { a { color: #3C3CA1; } - a:hover { color: #8484C6; } @@ -65,12 +64,11 @@ table td { vertical-align: baseline; padding-left: 8px; } - table td:first-of-type { padding-left: 0; } -div.header span.time span { +main.article div.header span.time span, div.article span.time span { color: #888; font-family: serif; font-style: italic; @@ -82,17 +80,17 @@ main.article div.header a.link-home { line-height: 2.4; } -main.article div.header h1, main.article div.header h2 { +main.article div.header h1, main.article div.header h2, div.article h3 { margin-top: 0.85em; margin-bottom: 0.25em; font-size: 1.5em; } -main.article div.header h1 a, main.article div.header h2 a { +main.article div.header h1 a, main.article div.header h2 a, div.article h3 a { text-decoration: none; } -main.article div.header span.time { +main.article div.header span.time, div.article span.time { display: block; } @@ -104,11 +102,9 @@ main.article div.header span.time { text-align: justify; hyphens: auto; } - #text li, #text table, #text blockquote { text-align: left; } - #text img { max-width: 100%; height: auto; @@ -118,7 +114,6 @@ main.article div.header span.time { padding: 1em; background: #ff02; } - .note > p { margin: 0.6rem 0; } @@ -127,7 +122,6 @@ main.article div.header span.time { padding: 1em; background: #eff5ff; } - .important > p { margin: 0.6rem 0; } @@ -138,7 +132,6 @@ main.article div.header span.time { font-style: italic; color: #555; } - #text .side > p:nth-child(2n+0) > i, #text .side > p:nth-child(2n+0) > em { font-style: normal; } \ No newline at end of file From fd7b24c08ea90135846051a2d9a4a638fe87ed57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20GOUIN?= Date: Fri, 21 Jun 2019 15:06:23 +0200 Subject: [PATCH 041/120] Style update --- sample_data/home/style.css | 71 +++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 40 deletions(-) diff --git a/sample_data/home/style.css b/sample_data/home/style.css index f9f689f..f01d8a3 100644 --- a/sample_data/home/style.css +++ b/sample_data/home/style.css @@ -11,9 +11,7 @@ body { font: 14px/1.45 -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif; color: #111; -webkit-text-size-adjust: none; - background-color: #F5F5F5; - height: 100vh; } @@ -21,18 +19,13 @@ main { max-width: 75ch; padding: 2ch; margin: auto; - background-color: #F0F0F0; - - min-height: 100%; + min-height: 100vh; } -q:before { - content: open-quote; -} - -q:after { - content: close-quote; +/* hide redundant text in article */ +#text h1:first-child { + display: none; } hr { @@ -45,6 +38,7 @@ hr { a { color: #3C3CA1; } + a:hover { color: #8484C6; } @@ -60,14 +54,31 @@ pre { padding: 10px 16px; } +blockquote { + border-left: 0.5em solid #ccc; + padding-left: 1em; + margin: 0.25em 0; + color: #333; +} + +blockquote > p { + margin: 0.6rem 0; +} + table td { vertical-align: baseline; padding-left: 8px; } + table td:first-of-type { padding-left: 0; } +#text table td, #text table th { + border: 1px solid #ccc; + padding: 0.25em 0.5em; +} + main.article div.header span.time span, div.article span.time span { color: #888; font-family: serif; @@ -83,7 +94,7 @@ main.article div.header a.link-home { main.article div.header h1, main.article div.header h2, div.article h3 { margin-top: 0.85em; margin-bottom: 0.25em; - font-size: 1.5em; + font-size: 2em; } main.article div.header h1 a, main.article div.header h2 a, div.article h3 a { @@ -94,44 +105,24 @@ main.article div.header span.time, div.article span.time { display: block; } -#text h1:first-child { - display: none; +div.article { + margin-left: 1em; +} + +div.article h3 { + font-size: 1.3em; } #text { text-align: justify; hyphens: auto; } + #text li, #text table, #text blockquote { text-align: left; } + #text img { max-width: 100%; height: auto; -} - -.note { - padding: 1em; - background: #ff02; -} -.note > p { - margin: 0.6rem 0; -} - -.important { - padding: 1em; - background: #eff5ff; -} -.important > p { - margin: 0.6rem 0; -} - -/* Sidenotes */ - -#text .side > p:nth-child(2n+0) { - font-style: italic; - color: #555; -} -#text .side > p:nth-child(2n+0) > i, #text .side > p:nth-child(2n+0) > em { - font-style: normal; } \ No newline at end of file From 24841d02f19fce888a511b6e66a1250bcb51229b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20GOUIN?= Date: Fri, 21 Jun 2019 15:18:33 +0200 Subject: [PATCH 042/120] Style update --- sample_data/article/index.md | 12 ++++++------ sample_data/home/style.css | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/sample_data/article/index.md b/sample_data/article/index.md index 04e93ea..971b3d2 100644 --- a/sample_data/article/index.md +++ b/sample_data/article/index.md @@ -35,16 +35,16 @@ Strikethrough uses two tildes. ~~Scratch this.~~ 1. First ordered list item 2. Another item -â‹…â‹…* Unordered sub-list. + * Unordered sub-list. 1. Actual numbers don't matter, just that it's a number -â‹…â‹…1. Ordered sub-list + 1. Ordered sub-list 4. And another item. -â‹…â‹…â‹…You can have properly indented paragraphs within list items. Notice the blank line above, and the leading spaces (at least one, but we'll use three here to also align the raw Markdown). + You can have properly indented paragraphs within list items. Notice the blank line above, and the leading spaces (at least one, but we'll use three here to also align the raw Markdown). -â‹…â‹…â‹…To have a line break without a paragraph, you will need to use two trailing spaces.â‹…â‹… -â‹…â‹…â‹…Note that this line is separate, but within the same paragraph.â‹…â‹… -â‹…â‹…â‹…(This is contrary to the typical GFM line break behaviour, where trailing spaces are not required.) + To have a line break without a paragraph, you will need to use two trailing spaces.â‹…â‹… + Note that this line is separate, but within the same paragraph.â‹…â‹… + (This is contrary to the typical GFM line break behaviour, where trailing spaces are not required.) * Unordered list can use asterisks - Or minuses diff --git a/sample_data/home/style.css b/sample_data/home/style.css index f01d8a3..fb4ca4e 100644 --- a/sample_data/home/style.css +++ b/sample_data/home/style.css @@ -79,6 +79,23 @@ table td:first-of-type { padding: 0.25em 0.5em; } +details { + background: #f8f8f8; + margin: 0.25em 0; + padding: 0; +} + +details > summary { + cursor: pointer; + padding: 0.5em 1em; +} + +details > p { + background: #f5f5f5; + padding: 0.5em 0.5em 0.5em 2em; + margin: 0; +} + main.article div.header span.time span, div.article span.time span { color: #888; font-family: serif; From 682a237323dd7baf26d256c10d984efa02e6d6c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20GOUIN?= Date: Fri, 21 Jun 2019 16:08:47 +0200 Subject: [PATCH 043/120] updated doc --- README.md | 181 ++++++++++++++++++++++++++++++++++++---- src/config.default.json | 2 - 2 files changed, 167 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 3d30a55..52af428 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,98 @@ -# GitBlog.md (WIP) -> This is a work in progress, some information written here might not be true yet. +# GitBlog.md [![Build Status](https://img.shields.io/travis/Klemek/GitBlog.md.svg?branch=master)](https://travis-ci.org/Klemek/GitBlog.md) [![Coverage Status](https://img.shields.io/coveralls/github/Klemek/GitBlog.md.svg?branch=master)](https://coveralls.io/github/Klemek/GitBlog.md?branch=master) A static blog using Markdown pulled from your git repository. -## Flow +* **[How it works](#how-it-works)** +* **[Installation](#installation)** +* **[Writing an article](#writing-an-article)** +* **[Configuration](#configuration)** + +## How it works +[back to top](#gitblog-md) + +There are 4 majors features of this project : + +#### 1. Home page + +
+diagram (click) +

![root](./uml/root.png) +

+
+ +When you access the root url of your blog, the app will fetch the template and inject the list of currently available articles. + + +#### 2. Article page + +
+diagram (click) +

![article](./uml/article.png) +

+
+ +As you access an article link, the server will fetch it's `index.md` Markdown file and render it in plain HTML using Showdown. + + +#### 3. Git webhook + +
+diagram (click) +

![webhook](./uml/webhook.png) +

+
+ +As you configured your data repository, when you push any data, it will trigger the webhook that will perform a `git pull` then refresh the data you just committed. + + +#### 4. RSS feed + +
+diagram (click) +

![rss](./uml/rss.png) +

+
+ +On the `/rss` endpoint, the servers gives you a RSS feed based on the list of articles which you can bookmark. + + ## Installation -**1. Download and install the latest version from the repo** +[back to top](#gitblog-md) + +#### 1. Download and install the latest version from the repo ```bash git clone https://github.com/klemek/gitblog.md.git npm install ``` -**2. Create your config file** +#### 2. Create your config file ```bash cd gitblog.md cp config.example.json config.json ``` then edit the config.json file with your custom values. +For example, you might want to change the app's port with : -**3. Start your server** +```json +{ + "node_port": 3030 +} +``` + +See [Configuration](#configuration) for more info. + +#### 3. Start your server ```bash npm run @@ -37,9 +100,18 @@ npm run node src/server.js ``` +You can check that it's up and running at [http://localhost:3000/](http://localhost:3000/) + You might want to use something like screen to separate the process from your current terminal session. -**4. Create and init your git source** +#### 4. Customize the blog's style + +At `npm install` a first article will be created for the current date. +You see it as an example of rendering of your blog. +Use it to edit your templates and styles located on the `data` folder. +At first, home page and articles are rendered using EJS engine but you can customize that into the configuration. + +#### 5. Create and init your git source You need to [create a new repository](https://github.com/new) on your favorite Git service. @@ -50,7 +122,9 @@ git remote add origin git push -u origin master ``` -**5. Refresh content with a webhook (optional)** +Now you just have to edit a local copy of your articles and, when you push them, to perform a simple `git pull` on that data folder. + +#### 6. Refresh content with a webhook (optional) Create a webhook on your git source (On GitHub, in the `Settings/Webhooks` part of the repository.) with the following parameters : @@ -58,23 +132,102 @@ Create a webhook on your git source (On GitHub, in the `Settings/Webhooks` part * Content type : `application/json` * Events : Just the push event -**6. Securize your webhook (optional)** +Now the server will perform the `git pull` task for you after a successful push on GitHub. + +#### 7. Securize your webhook (optional) Here are the steps for Github, if you use another platform adapt it your way (header format on the config) : * Create a password or random secret * Edit your configuration to add webhook info ```json -{ -... "webhook": { "endpoint": "/webhook", "secret": "sha1=", "signature_header": "X-Hub-Signature" }, -... -} ``` * Launch the server * Update your webhook on github to include the secret -* Check if Github successfully reached the endpoint \ No newline at end of file +* Check if Github successfully reached the endpoint + +## Writing an article +[back to top](#gitblog-md) + +TODO + +## Configuration +[back to top](#gitblog-md) + +* `node_port` (default: 3000) + + the port the server is listening to +* `data_dir` (default: data) + + the directory where will be located the git repo with templates and articles +* `view_engine` (default: ejs) + + the Express view engine used to render pages from templates +* `modules` + * `rss` (default: true) + + activate the RSS endpoint and its features + * `webhook` (default: true) + + activate the webhook endpoint and its features + * `prism` (default: true) + + activate Prism code highlighting +* `home` + * `index` (default: index.ejs) + + the name of the home page template on the data directory + + it will receive `articles`, a list of articles for rendering + * `error` (default: error.ejs) + + the name of the error page template on the data directory + + it will receive `error`, the error code + * `hidden` (default: `[.ejs]`) + + file extensions to be returned 404 when reached +* `article` + * `index` (default: index.md) + + the name of the Markdown page of the article on the `/year/month/day/` directory + * `template` (default: template.ejs) + + the name of the article page template on the data directory + * `thumbnail_tag`: (default: thumbnail) + + the alt text searched to get the thumbnail image on the article + + as in `![]()` + * `default_title`: (default: Untitled) + + the title of the article in case a level 1 title was not found + * `default_thumbnail`: (default: none) + + the path of the default thumbnail to get from the data directory +* `rss` + * `title`: (default: mygitblog RSS feed) + * `description`: (default: a generated RSS feed from my articles) + * `endpoint`: (default: /rss) + * `length`: (default: 10) + + how many last articles will be present in the feed +* `webhook` + * `endpoint`: (default: /webhook) + * `secret`: (default: none) + + see [above](#7-securize-your-webhook-optional-) + * `signature_header`: (default: none) + + see [above](#7-securize-your-webhook-optional-) + * `pull_command`: (default: git pull) + + the command used by the server on webhook trigger +* `showdown` + + Options to be applied to Showdown renderer (see [showdown options](https://github.com/showdownjs/showdown#valid-options) for more info) \ No newline at end of file diff --git a/src/config.default.json b/src/config.default.json index 6c9827d..0ab7f55 100644 --- a/src/config.default.json +++ b/src/config.default.json @@ -2,9 +2,7 @@ "node_port": 3000, "data_dir": "data", "view_engine": "ejs", - "language": "en-us", "modules": { - "plantuml": false, "rss": true, "webhook": true, "prism": true From 217c6069425ff801f8bee96137cfffa1fb15097a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20GOUIN?= Date: Fri, 21 Jun 2019 16:24:38 +0200 Subject: [PATCH 044/120] updated doc --- README.md | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 52af428..da633f3 100644 --- a/README.md +++ b/README.md @@ -107,10 +107,17 @@ You might want to use something like screen to separate the process from your cu #### 4. Customize the blog's style At `npm install` a first article will be created for the current date. -You see it as an example of rendering of your blog. +You can see it as an example of rendering of your blog. Use it to edit your templates and styles located on the `data` folder. + At first, home page and articles are rendered using EJS engine but you can customize that into the configuration. +Resources are located on the `data` folder and can be referenced as the root of your blog. + +``` +/styles/main.css => data/styles/main.css +``` + #### 5. Create and init your git source You need to [create a new repository](https://github.com/new) on your favorite Git service. @@ -154,7 +161,30 @@ Here are the steps for Github, if you use another platform adapt it your way (he ## Writing an article [back to top](#gitblog-md) -TODO +You need to write your article (and templates) on the git repository but **keep the data directory on the server untouched** to prevent any changes to harm the git pull normal behavior. + +To be referenced, an article need to be on a specific path containing its date and have a Markdown index file : + +``` +data/year/month/day/index.md +``` + +> note that month and day need to be 0 padded (`5th of june 2019 => 2019/06/05`) + +On your Markdown file you can write anything but some informations will be fetched automatically : + +* Title : first level 1 header (#) +* Thumbnail : first thumbnail tagged image (like `![thumbnail](url)`) + +On that same folder, you can place resources like images and reference them in relative paths : + +``` +![](./image.png) => data/year/month/day/image.png +``` + +> note that you cannot place resources on subfolders + +Any URL like `/year/month/day/anything/` will redirect to this article (and link to correct resources) ## Configuration [back to top](#gitblog-md) From cf5c6fcd6c865c199aeb821f91bd3d399a10ab25 Mon Sep 17 00:00:00 2001 From: Klemek Date: Fri, 21 Jun 2019 16:38:10 +0200 Subject: [PATCH 045/120] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a6a6dce..4229974 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ As you access an article link, the server will fetch it's `index.md` Markdown fi

-As you configured your data repository, when you push any data, it will trigger the webhook that will perform a `git pull` then refresh the data you just committed. +As you configured your data repository, when you push any data, it will trigger the webhook that will perform a `git pull` then refresh the article list. #### 4. RSS feed From d49771023d29006a6327d50350206e00ce8656ee Mon Sep 17 00:00:00 2001 From: Klemek Date: Fri, 21 Jun 2019 19:03:39 +0200 Subject: [PATCH 046/120] Fixed windows newline matching --- src/renderer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer.js b/src/renderer.js index f24a81e..34f66b2 100644 --- a/src/renderer.js +++ b/src/renderer.js @@ -11,7 +11,7 @@ module.exports = (config) => { return cb(err); if (config['modules']['prism']) { - const codeRegex = /```([\w-]+)\n((?:(?!```)[\s\S])*)\n```/m; + const codeRegex = /```([\w-]+)\r?\n((?:(?!```)[\s\S])*)\r?\n```/m; let match; while ((match = codeRegex.exec(data))) { const lang = match[1].trim(); From 4171f565f89a083f3cd5d3282efadc309f2bf667 Mon Sep 17 00:00:00 2001 From: Klemek Date: Fri, 21 Jun 2019 19:04:48 +0200 Subject: [PATCH 047/120] Changed version for the fix --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a09266c..dc3d0be 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "nodePort": 5000, "name": "gitblog.md", - "version": "1.0.0", + "version": "1.0.1", "description": "A static blog using Markdown pulled from your git repository.", "main": "src/server.js", "dependencies": { From ac8facdd6283eeca0dd00f6df2a5b915eef1833b Mon Sep 17 00:00:00 2001 From: Klemek Date: Fri, 21 Jun 2019 19:06:34 +0200 Subject: [PATCH 048/120] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 4229974..baff21d 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ A static blog using Markdown pulled from your git repository. +> You edit it on your local repo, you push it and it's already on your server's blog (plus your friend's RSS is updated) + * **[How it works](#how-it-works)** * **[Installation](#installation)** * **[Writing an article](#writing-an-article)** From 3d330b68d467e36e86cb4e1e60869766c1a93233 Mon Sep 17 00:00:00 2001 From: Klemek Date: Fri, 21 Jun 2019 19:06:53 +0200 Subject: [PATCH 049/120] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index baff21d..aa892ec 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ A static blog using Markdown pulled from your git repository. -> You edit it on your local repo, you push it and it's already on your server's blog (plus your friend's RSS is updated) +> You edit the markdown on your local repo, you push it and it's already on your server's blog (plus your friend's RSS is updated) * **[How it works](#how-it-works)** * **[Installation](#installation)** From ea9927db1b5784acd680119dfe3ec777b5768141 Mon Sep 17 00:00:00 2001 From: Klemek Date: Fri, 21 Jun 2019 19:17:46 +0200 Subject: [PATCH 050/120] Update README.md --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index aa892ec..2bec896 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,10 @@ A static blog using Markdown pulled from your git repository. -> You edit the markdown on your local repo, you push it and it's already on your server's blog (plus your friend's RSS is updated) +> Step 1 : ```$ vi 2019/06/21/index.md``` +> Step 2 : ```$ git add . && git commit -m "new article" && git push origin master``` +> Step 3 : That's it +> Step 4 : No seriously you're done, go check your updated blog * **[How it works](#how-it-works)** * **[Installation](#installation)** From 3cfa2bfc71810ce013cceb46b454ff228f2e64ec Mon Sep 17 00:00:00 2001 From: Klemek Date: Sat, 22 Jun 2019 00:07:54 +0200 Subject: [PATCH 051/120] Access log file option --- README.md | 106 ++++++++++++++++------------------------ src/app.js | 18 +++++++ src/config.default.json | 1 + test/app.test.js | 34 +++++++++++++ 4 files changed, 96 insertions(+), 63 deletions(-) diff --git a/README.md b/README.md index a6a6dce..73a3e88 100644 --- a/README.md +++ b/README.md @@ -190,75 +190,55 @@ Any URL like `/year/month/day/anything/` will redirect to this article (and link ## Configuration [back to top](#gitblog-md) -* `node_port` (default: 3000) - - the port the server is listening to -* `data_dir` (default: data) - - the directory where will be located the git repo with templates and articles -* `view_engine` (default: ejs) - - the Express view engine used to render pages from templates +* `node_port` (default: 3000) + the port the server is listening to +* `data_dir` (default: data) + the directory where will be located the git repo with templates and articles +* `view_engine` (default: ejs) + the Express view engine used to render pages from templates +* `access_log` (default: access.log) + log file where to save access requests (empty to disable) * `modules` - * `rss` (default: true) - - activate the RSS endpoint and its features - * `webhook` (default: true) - - activate the webhook endpoint and its features - * `prism` (default: true) - - activate Prism code highlighting + * `rss` (default: true) + activate the RSS endpoint and its features + * `webhook` (default: true) + activate the webhook endpoint and its features + * `prism` (default: true) + activate Prism code highlighting * `home` - * `index` (default: index.ejs) - - the name of the home page template on the data directory - - it will receive `articles`, a list of articles for rendering - * `error` (default: error.ejs) - - the name of the error page template on the data directory - - it will receive `error`, the error code - * `hidden` (default: `[.ejs]`) - - file extensions to be returned 404 when reached + * `index` (default: index.ejs) + the name of the home page template on the data directory + it will receive `articles`, a list of articles for rendering + * `error` (default: error.ejs) + the name of the error page template on the data directory + it will receive `error`, the error code + * `hidden` (default: `[.ejs]`) + file extensions to be returned 404 when reached * `article` - * `index` (default: index.md) - - the name of the Markdown page of the article on the `/year/month/day/` directory - * `template` (default: template.ejs) - - the name of the article page template on the data directory - * `thumbnail_tag`: (default: thumbnail) - - the alt text searched to get the thumbnail image on the article - - as in `![]()` - * `default_title`: (default: Untitled) - - the title of the article in case a level 1 title was not found - * `default_thumbnail`: (default: none) - - the path of the default thumbnail to get from the data directory + * `index` (default: index.md) + the name of the Markdown page of the article on the `/year/month/day/` directory + * `template` (default: template.ejs) + the name of the article page template on the data directory + * `thumbnail_tag`: (default: thumbnail) + the alt text searched to get the thumbnail image on the article + as in `![]()` + * `default_title`: (default: Untitled) + the title of the article in case a level 1 title was not found + * `default_thumbnail`: (default: none) + the path of the default thumbnail to get from the data directory * `rss` * `title`: (default: mygitblog RSS feed) * `description`: (default: a generated RSS feed from my articles) * `endpoint`: (default: /rss) - * `length`: (default: 10) - - how many last articles will be present in the feed + * `length`: (default: 10) + how many last articles will be present in the feed * `webhook` * `endpoint`: (default: /webhook) - * `secret`: (default: none) - - see [above](#7-securize-your-webhook-optional-) - * `signature_header`: (default: none) - - see [above](#7-securize-your-webhook-optional-) - * `pull_command`: (default: git pull) - - the command used by the server on webhook trigger -* `showdown` - - Options to be applied to Showdown renderer (see [showdown options](https://github.com/showdownjs/showdown#valid-options) for more info) + * `secret`: (default: none) + see [above](#7-securize-your-webhook-optional-) + * `signature_header`: (default: none) + see [above](#7-securize-your-webhook-optional-) + * `pull_command`: (default: git pull) + the command used by the server on webhook trigger +* `showdown` + Options to be applied to Showdown renderer (see [showdown options](https://github.com/showdownjs/showdown#valid-options) for more info) diff --git a/src/app.js b/src/app.js index f6802d2..1e9d38a 100644 --- a/src/app.js +++ b/src/app.js @@ -3,6 +3,8 @@ const app = express(); const fs = require('fs'); const path = require('path'); +app.enable('trust proxy'); + //rss const Rss = require('rss'); @@ -94,6 +96,22 @@ module.exports = (config) => { }); }; + //log request at result end + app.use((req, res, next) => { + if (config['access_log']) { + const end = res.end; + res.end = (chunk, encoding) => { + fs.appendFile(config['access_log'], + res.statusCode + ' ' + req.method + ' ' + req.url + ' ' + new Date().toUTCString() + ' ' + (req.ips.join(' ') || req.ip) + '\n', + {encoding: 'UTF-8'}, () => { + res.end = end; + res.end(chunk, encoding); + }); + }; + } + next(); + }); + // home endpoint : send the correct index page or error if not existing app.get('/', (req, res) => { const homePath = path.join(config['data_dir'], config['home']['index']); diff --git a/src/config.default.json b/src/config.default.json index 0ab7f55..49777fc 100644 --- a/src/config.default.json +++ b/src/config.default.json @@ -2,6 +2,7 @@ "node_port": 3000, "data_dir": "data", "view_engine": "ejs", + "access_log": "access.log", "modules": { "rss": true, "webhook": true, diff --git a/test/app.test.js b/test/app.test.js index c694be9..7b5d6d1 100644 --- a/test/app.test.js +++ b/test/app.test.js @@ -20,6 +20,7 @@ config['home']['hidden'].push('.test'); config['rss']['endpoint'] = '/rsstest'; config['rss']['length'] = 2; config['webhook']['endpoint'] = '/webhooktest'; +config['access_log'] = path.join(dataDir, 'access.log'); const app = require('../src/app')(config); @@ -35,6 +36,39 @@ afterAll(() => { } }); +describe('Test logging', () => { + test('test get 200', (done) => { + request(app).get('/rsstest').then(() => { + fs.readFile(path.join(dataDir, 'access.log'), {encoding: 'UTF-8'}, (err, data) => { + expect(err).toBeNull(); + expect(data).toBe('200 GET /rsstest ' + new Date().toUTCString() + ' ::ffff:127.0.0.1\n'); + done(); + }); + }); + }); + test('test post 400', (done) => { + request(app).post('/rsstest').then(() => { + fs.readFile(path.join(dataDir, 'access.log'), {encoding: 'UTF-8'}, (err, data) => { + expect(err).toBeNull(); + expect(data).toBe('400 POST /rsstest ' + new Date().toUTCString() + ' ::ffff:127.0.0.1\n'); + done(); + }); + }); + }); + test('test 2 requests', (done) => { + request(app).get('/rss').then(() => { + request(app).post('/rsstest').then(() => { + fs.readFile(path.join(dataDir, 'access.log'), {encoding: 'UTF-8'}, (err, data) => { + expect(err).toBeNull(); + expect(data).toBe('404 GET /rss ' + new Date().toUTCString() + ' ::ffff:127.0.0.1\n' + + '400 POST /rsstest ' + new Date().toUTCString() + ' ::ffff:127.0.0.1\n'); + done(); + }); + }); + }); + }); +}); + describe('Test root path', () => { test('404 no index no error', (done) => { request(app).get('/').then((response) => { From 8cc3d40b96323b31772ba0596395c73d5a6e3b1d Mon Sep 17 00:00:00 2001 From: Klemek Date: Sat, 22 Jun 2019 09:30:34 +0200 Subject: [PATCH 052/120] Fixed default config file --- src/config.default.json | 2 +- test/config.test.js | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/config.default.json b/src/config.default.json index 49777fc..7c9c6b4 100644 --- a/src/config.default.json +++ b/src/config.default.json @@ -20,7 +20,7 @@ "template": "template.ejs", "thumbnail_tag": "thumbnail", "default_title": "Untitled", - "default_thumbnail": null + "default_thumbnail": "" }, "rss": { "title": "mygitblog RSS feed", diff --git a/test/config.test.js b/test/config.test.js index fa72475..74bf82b 100644 --- a/test/config.test.js +++ b/test/config.test.js @@ -1,5 +1,6 @@ /* jshint -W117 */ const fs = require('fs'); +const path = require('path'); const configFile = 'config.json'; const tmpConfigFile = 'config.temp.json'; @@ -29,6 +30,18 @@ test('no config', () => { expect(config['data_dir']).toBe('data'); }); +test('example config', () => { + if (fs.existsSync(configFile)) + fs.unlinkSync(configFile); + fs.copyFileSync(path.join('src', 'config.default.json'), configFile); + const data = fs.readFileSync(configFile, {encoding: 'UTF-8'}); + fs.writeFileSync(configFile, data.replace('3000', '3333'), {encoding: 'UTF-8'}); + const config = require('../src/config')(); + expect(config).toBeDefined(); + expect(config['node_port']).toBe(3333); + expect(config['data_dir']).toBe('data'); +}); + test('invalid config ignored', () => { fs.writeFileSync(configFile, 'invalid JSON'); const config = require('../src/config')(); From c90d573f1185f094e61103495e49c3cc3189dc48 Mon Sep 17 00:00:00 2001 From: Klemek Date: Sat, 22 Jun 2019 09:35:32 +0200 Subject: [PATCH 053/120] Reformated some file walker tests --- test/file_walker.test.js | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/test/file_walker.test.js b/test/file_walker.test.js index 4eb6c2c..bf30e49 100644 --- a/test/file_walker.test.js +++ b/test/file_walker.test.js @@ -117,9 +117,10 @@ describe('Test index article reading', () => { `); fw.readIndexFile(file, 'custom_thumbnail', (err, info) => { expect(err).toBeNull(); - expect(info).toBeDefined(); - expect(info.title).toBe('This is an awesome title !?¤'); - expect(info.thumbnail).toBe('./thumbnail.jpg'); + expect(info).toEqual({ + title: 'This is an awesome title !?¤', + thumbnail: './thumbnail.jpg' + }); done(); }); }); @@ -132,9 +133,10 @@ describe('Test index article reading', () => { `); fw.readIndexFile(file, 'custom_thumbnail', (err, info) => { expect(err).toBeNull(); - expect(info).toBeDefined(); - expect(info.title).not.toBeDefined(); - expect(info.thumbnail).toBe('./thumbnail.jpg'); + expect(info).toEqual({ + title: undefined, + thumbnail: './thumbnail.jpg' + }); done(); }); }); @@ -143,9 +145,10 @@ describe('Test index article reading', () => { fs.writeFileSync(file, '#title'); fw.readIndexFile(file, 'custom_thumbnail', (err, info) => { expect(err).toBeNull(); - expect(info).toBeDefined(); - expect(info.title).toBe('title'); - expect(info.thumbnail).not.toBeDefined(); + expect(info).toEqual({ + title: 'title', + thumbnail: undefined + }); done(); }); }); @@ -158,9 +161,10 @@ describe('Test index article reading', () => { `); fw.readIndexFile(file, 'thumbnail', (err, info) => { expect(err).toBeNull(); - expect(info).toBeDefined(); - expect(info.title).toBe('This is an awesome title !?¤'); - expect(info.thumbnail).not.toBeDefined(); + expect(info).toEqual({ + title: 'This is an awesome title !?¤', + thumbnail: undefined + }); done(); }); }); @@ -174,9 +178,10 @@ describe('Test index article reading', () => { `); fw.readIndexFile(file, 'custom_thumbnail', (err, info) => { expect(err).toBeNull(); - expect(info).toBeDefined(); - expect(info.title).toBe('This is an awesome title !?¤'); - expect(info.thumbnail).toBe('./thumbnail.jpg'); + expect(info).toEqual({ + title: 'This is an awesome title !?¤', + thumbnail: './thumbnail.jpg' + }); done(); }); }); From 705dafa4e482e718909f7ffee63e3666bcc5cf51 Mon Sep 17 00:00:00 2001 From: Klemek Date: Sat, 22 Jun 2019 10:02:25 +0200 Subject: [PATCH 054/120] Error log option --- .gitignore | 3 ++- README.md | 2 ++ src/app.js | 14 ++++++++++---- src/config.default.json | 1 + test/app.test.js | 41 ++++++++++++++++++++++++++++++++++++++++- test/test_utils.js | 13 +++++++++++-- 6 files changed, 66 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 606eb9f..54ecd89 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ /config.example.json /data /test_data -/git_secret \ No newline at end of file +/access.log +/error.log \ No newline at end of file diff --git a/README.md b/README.md index 73a3e88..f32b751 100644 --- a/README.md +++ b/README.md @@ -198,6 +198,8 @@ Any URL like `/year/month/day/anything/` will redirect to this article (and link the Express view engine used to render pages from templates * `access_log` (default: access.log) log file where to save access requests (empty to disable) +* `error_log` (default: error.log) + log file where to save all server errors (empty to disable) * `modules` * `rss` (default: true) activate the RSS endpoint and its features diff --git a/src/app.js b/src/app.js index 1e9d38a..08b72a0 100644 --- a/src/app.js +++ b/src/app.js @@ -102,7 +102,7 @@ module.exports = (config) => { const end = res.end; res.end = (chunk, encoding) => { fs.appendFile(config['access_log'], - res.statusCode + ' ' + req.method + ' ' + req.url + ' ' + new Date().toUTCString() + ' ' + (req.ips.join(' ') || req.ip) + '\n', + `${res.statusCode} ${req.method} ${req.url} ${new Date().toUTCString()} ${req.ips.join(' ') || req.ip}\n`, {encoding: 'UTF-8'}, () => { res.end = end; res.end(chunk, encoding); @@ -235,10 +235,16 @@ module.exports = (config) => { res.status(400).send('bad request'); }); + //log all server errors app.use((err, req, res, next) => { - console.log(cons.error, `error when handling ${req.path} request : ${err}`); - console.error(err.stack); - next(err); + console.log(cons.error, `error when handling ${req.method} ${req.path} request : ${err}`); + if (!config['error_log']) + next(err); + fs.appendFile(config['error_log'], + `500 ${req.method} ${req.url} ${new Date().toUTCString()} ${req.ips.join(' ') || req.ip}\n${err.stack}\n`, + {encoding: 'UTF-8'}, () => { + next(err); + }); }); // must be use in a server.js to start the server diff --git a/src/config.default.json b/src/config.default.json index 7c9c6b4..5f39d6e 100644 --- a/src/config.default.json +++ b/src/config.default.json @@ -3,6 +3,7 @@ "data_dir": "data", "view_engine": "ejs", "access_log": "access.log", + "error_log": "error.log", "modules": { "rss": true, "webhook": true, diff --git a/test/app.test.js b/test/app.test.js index 7b5d6d1..bea34ec 100644 --- a/test/app.test.js +++ b/test/app.test.js @@ -21,6 +21,7 @@ config['rss']['endpoint'] = '/rsstest'; config['rss']['length'] = 2; config['webhook']['endpoint'] = '/webhooktest'; config['access_log'] = path.join(dataDir, 'access.log'); +config['error_log'] = path.join(dataDir, 'error.log'); const app = require('../src/app')(config); @@ -36,7 +37,16 @@ afterAll(() => { } }); -describe('Test logging', () => { +describe('Test request logging', () => { + test('test no log', (done) => { + const tmp = config['access_log']; + config['access_log'] = ''; + request(app).get('/rsstest').then(() => { + config['access_log'] = tmp; + expect(fs.existsSync(path.join(dataDir, 'access.log'))).toBe(false); + done(); + }); + }); test('test get 200', (done) => { request(app).get('/rsstest').then(() => { fs.readFile(path.join(dataDir, 'access.log'), {encoding: 'UTF-8'}, (err, data) => { @@ -69,6 +79,35 @@ describe('Test logging', () => { }); }); +describe('Test error logging', () => { + test('test no log', (done) => { + const tmp = config['home']['hidden']; + config['home']['hidden'] = null; + const tmp2 = config['errpr_log']; + config['error_log'] = ''; + request(app).get('/somefile.txt').then(() => { + config['home']['hidden'] = tmp; + config['error_log'] = tmp2; + expect(fs.existsSync(path.join(dataDir, 'error.log'))).toBe(false); + done(); + }); + }); + test('test null error ', (done) => { + const tmp = config['home']['hidden']; + config['home']['hidden'] = null; + request(app).get('/somefile.txt').then(() => { + config['home']['hidden'] = tmp; + fs.readFile(path.join(dataDir, 'error.log'), {encoding: 'UTF-8'}, (err, data) => { + expect(err).toBeNull(); + const start = data.split('\n').slice(0, 2).join('\n'); + const expected = '500 GET /somefile.txt ' + new Date().toUTCString() + ' ::ffff:127.0.0.1\nTypeError: Cannot read property \'includes\' of null'; + expect(start).toBe(expected); + done(); + }); + }); + }); +}); + describe('Test root path', () => { test('404 no index no error', (done) => { request(app).get('/').then((response) => { diff --git a/test/test_utils.js b/test/test_utils.js index 6b509c5..8c250f5 100644 --- a/test/test_utils.js +++ b/test/test_utils.js @@ -4,12 +4,21 @@ const path = require('path'); const deleteFolderSync = (dir) => { if (!fs.existsSync(dir)) return; - fs.readdirSync(dir, {withFileTypes: true}).forEach((item) => { + let items; + const deleteItem = (item) => { if (item.isDirectory()) deleteFolderSync(path.join(dir, item.name)); else fs.unlinkSync(path.join(dir, item.name)); - }); + }; + do { + items = fs.readdirSync(dir, {withFileTypes: true}); + try { + items.forEach(deleteItem); + } catch (e) { + console.error(e); + } + } while (items.length > 0); fs.rmdirSync(dir); }; From f760c4e0d331351ca0d149ac61a2fdd0e2ba3173 Mon Sep 17 00:00:00 2001 From: Klemek Date: Sat, 22 Jun 2019 10:03:22 +0200 Subject: [PATCH 055/120] Changed version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index dc3d0be..75cba16 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "nodePort": 5000, "name": "gitblog.md", - "version": "1.0.1", + "version": "1.0.2", "description": "A static blog using Markdown pulled from your git repository.", "main": "src/server.js", "dependencies": { From d3431797642ba191b0a8de4e3f440991cf2e5866 Mon Sep 17 00:00:00 2001 From: Klemek Date: Sat, 22 Jun 2019 10:17:04 +0200 Subject: [PATCH 056/120] Updated code coverage files --- package.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 75cba16..e48d98d 100644 --- a/package.json +++ b/package.json @@ -37,5 +37,13 @@ "bugs": { "url": "https://github.com/klemek/gitblog.md/issues" }, - "homepage": "https://github.com/klemek/gitblog.md#readme" + "homepage": "https://github.com/klemek/gitblog.md#readme", + "jest": { + "collectCoverageFrom": [ + "src/**/*.js", + "!/node_modules/", + "!src/server.js", + "!src/postinstall.js" + ] + } } From def326676cce1a351f3011163979b3a5a748b567 Mon Sep 17 00:00:00 2001 From: Klemek Date: Sat, 22 Jun 2019 11:00:02 +0200 Subject: [PATCH 057/120] More tests --- src/app.js | 5 +-- test/app.test.js | 78 ++++++++++++++++++++++++++-------------- test/file_walker.test.js | 2 +- test/renderer.test.js | 2 +- 4 files changed, 55 insertions(+), 32 deletions(-) diff --git a/src/app.js b/src/app.js index 08b72a0..7ee2dc1 100644 --- a/src/app.js +++ b/src/app.js @@ -154,10 +154,7 @@ module.exports = (config) => { app.post(config['webhook']['endpoint'], (req, res) => { if (config['modules']['webhook']) { if (config['webhook']['signature_header'] && config['webhook']['secret']) { - const payload = JSON.stringify(req.body); - if (!payload) { - return res.sendStatus(403); - } + const payload = JSON.stringify(req.body) || ''; const hmac = crypto.createHmac('sha1', config['webhook']['secret']); const digest = 'sha1=' + hmac.update(payload).digest('hex'); const checksum = req.headers[config['webhook']['signature_header']]; diff --git a/test/app.test.js b/test/app.test.js index bea34ec..da9f2c7 100644 --- a/test/app.test.js +++ b/test/app.test.js @@ -13,19 +13,24 @@ const config = require('../src/config')(); config['test'] = true; config['data_dir'] = dataDir; +config['webhook']['endpoint'] = '/webhooktest'; +config['rss']['endpoint'] = '/rsstest'; +config['rss']['length'] = 2; config['home']['index'] = testIndex; config['home']['error'] = testError; config['article']['template'] = testTemplate; -config['home']['hidden'].push('.test'); -config['rss']['endpoint'] = '/rsstest'; -config['rss']['length'] = 2; -config['webhook']['endpoint'] = '/webhooktest'; -config['access_log'] = path.join(dataDir, 'access.log'); -config['error_log'] = path.join(dataDir, 'error.log'); const app = require('../src/app')(config); beforeEach((done, fail) => { + config['data_dir'] = dataDir; + config['article']['index'] = 'index.md'; + config['home']['hidden'] = ['.ejs', '.test']; + config['access_log'] = ''; + config['error_log'] = ''; + config['modules']['rss'] = true; + config['modules']['webhook'] = true; + utils.deleteFolderSync(dataDir); fs.mkdirSync(dataDir); app.reload(done, fail); @@ -37,17 +42,22 @@ afterAll(() => { } }); +describe('Test reload', () => { + test('reload fail', (done, fail) => { + config['data_dir'] = ''; + app.reload(fail, done); + }); +}); + describe('Test request logging', () => { test('test no log', (done) => { - const tmp = config['access_log']; - config['access_log'] = ''; request(app).get('/rsstest').then(() => { - config['access_log'] = tmp; expect(fs.existsSync(path.join(dataDir, 'access.log'))).toBe(false); done(); }); }); test('test get 200', (done) => { + config['access_log'] = path.join(dataDir, 'access.log'); request(app).get('/rsstest').then(() => { fs.readFile(path.join(dataDir, 'access.log'), {encoding: 'UTF-8'}, (err, data) => { expect(err).toBeNull(); @@ -57,6 +67,7 @@ describe('Test request logging', () => { }); }); test('test post 400', (done) => { + config['access_log'] = path.join(dataDir, 'access.log'); request(app).post('/rsstest').then(() => { fs.readFile(path.join(dataDir, 'access.log'), {encoding: 'UTF-8'}, (err, data) => { expect(err).toBeNull(); @@ -66,6 +77,7 @@ describe('Test request logging', () => { }); }); test('test 2 requests', (done) => { + config['access_log'] = path.join(dataDir, 'access.log'); request(app).get('/rss').then(() => { request(app).post('/rsstest').then(() => { fs.readFile(path.join(dataDir, 'access.log'), {encoding: 'UTF-8'}, (err, data) => { @@ -81,22 +93,16 @@ describe('Test request logging', () => { describe('Test error logging', () => { test('test no log', (done) => { - const tmp = config['home']['hidden']; config['home']['hidden'] = null; - const tmp2 = config['errpr_log']; - config['error_log'] = ''; request(app).get('/somefile.txt').then(() => { - config['home']['hidden'] = tmp; - config['error_log'] = tmp2; expect(fs.existsSync(path.join(dataDir, 'error.log'))).toBe(false); done(); }); }); test('test null error ', (done) => { - const tmp = config['home']['hidden']; config['home']['hidden'] = null; + config['error_log'] = path.join(dataDir, 'error.log'); request(app).get('/somefile.txt').then(() => { - config['home']['hidden'] = tmp; fs.readFile(path.join(dataDir, 'error.log'), {encoding: 'UTF-8'}, (err, data) => { expect(err).toBeNull(); const start = data.split('\n').slice(0, 2).join('\n'); @@ -123,6 +129,13 @@ describe('Test root path', () => { done(); }); }); + test('500 render error', (done) => { + fs.writeFileSync(path.join(dataDir, testIndex), 'articles <%= null.length %>'); + request(app).get('/').then((response) => { + expect(response.statusCode).toBe(500); + done(); + }); + }); test('200 no articles', (done) => { fs.writeFileSync(path.join(dataDir, testIndex), 'articles <%= articles.length %>'); request(app).get('/').then((response) => { @@ -156,7 +169,6 @@ describe('Test RSS feed', () => { config['modules']['rss'] = false; request(app).get('/rsstest').then((response) => { expect(response.statusCode).toBe(404); - config['modules']['rss'] = true; done(); }); }); @@ -168,6 +180,16 @@ describe('Test RSS feed', () => { done(); }); }); + test('200 rss cache', (done) => { + request(app).get('/rsstest').then(() => { + request(app).get('/rsstest').then((response) => { + expect(response.statusCode).toBe(200); + expect(response.text.length).toBeGreaterThan(0); + expect(response.text.split('').length).toBe(1); + done(); + }); + }); + }); test('200 2 rss items', (done, fail) => { utils.createEmptyDirs([ path.join(dataDir, '2019', '05', '05'), @@ -213,7 +235,6 @@ describe('Test webhook', () => { config['modules']['webhook'] = false; request(app).post('/webhooktest').then((response) => { expect(response.statusCode).toBe(400); - config['modules']['webhook'] = true; done(); }); }); @@ -244,14 +265,6 @@ describe('Test webhook', () => { done(); }); }); - test('403 no payload', (done) => { - config['webhook']['signature_header'] = 'testheader'; - config['webhook']['secret'] = 'testvalue'; - request(app).post('/webhooktest').then((response) => { - expect(response.statusCode).toBe(403); - done(); - }); - }); test('403 wrong secret', (done) => { config['webhook']['signature_header'] = 'testheader'; config['webhook']['secret'] = 'testvalue'; @@ -282,6 +295,19 @@ describe('Test articles rendering', () => { }); }); + test('500 no index', (done, fail) => { + utils.createEmptyDirs([path.join(dataDir, '2019', '05', '05'),]); + fs.writeFileSync(path.join(dataDir, '2019', '05', '05', 'index.md'), '# Hello'); + fs.writeFileSync(path.join(dataDir, testTemplate), '<%- article.content %><%- `reload` %>'); + app.reload(() => { + config['article']['index'] = 'invalid.md'; + request(app).get('/2019/05/05/hello/').then((response) => { + expect(response.statusCode).toBe(500); + done(); + }); + }, fail); + }); + test('500 no template', (done, fail) => { utils.createEmptyDirs([path.join(dataDir, '2019', '05', '05'),]); fs.writeFileSync(path.join(dataDir, '2019', '05', '05', 'index.md'), '# Hello'); diff --git a/test/file_walker.test.js b/test/file_walker.test.js index bf30e49..55ff7ce 100644 --- a/test/file_walker.test.js +++ b/test/file_walker.test.js @@ -22,6 +22,7 @@ const config = { const fw = require('../src/file_walker')(config); beforeEach(() => { + config['data_dir'] = dataDir; utils.deleteFolderSync(dataDir); fs.mkdirSync(dataDir); }); @@ -193,7 +194,6 @@ describe('Test article fetching', () => { fw.fetchArticles((err, list) => { expect(err).not.toBeNull(); expect(list).not.toBeDefined(); - config['data_dir'] = dataDir; done(); }); }); diff --git a/test/renderer.test.js b/test/renderer.test.js index c2912eb..fff5723 100644 --- a/test/renderer.test.js +++ b/test/renderer.test.js @@ -19,6 +19,7 @@ const config = { const renderer = require('../src/renderer')(config); beforeEach(() => { + config['modules']['prism'] = true; utils.deleteFolderSync(dataDir); fs.mkdirSync(dataDir); }); @@ -61,7 +62,6 @@ test('no prism', (done) => { renderer.render(file, (err, html) => { expect(err).toBeNull(); expect(html).toBe('
print("hello")\n
\n
print("hello")\n
'); - config['modules']['prism'] = true; done(); }); }); From 9d3c1d08478c46b2a5c3fe074f178788192e16e9 Mon Sep 17 00:00:00 2001 From: Klemek Date: Sat, 22 Jun 2019 13:48:08 +0200 Subject: [PATCH 058/120] MathJax support --- .gitignore | 3 +- README.md | 7 ++ package-lock.json | 216 +++++++++++++--------------------------- package.json | 1 + src/config.default.json | 7 +- src/renderer.js | 106 ++++++++++++++++---- test/renderer.test.js | 172 ++++++++++++++++++++++---------- 7 files changed, 292 insertions(+), 220 deletions(-) diff --git a/.gitignore b/.gitignore index 54ecd89..9295227 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ /data /test_data /access.log -/error.log \ No newline at end of file +/error.log +/coverage \ No newline at end of file diff --git a/README.md b/README.md index f32b751..9b2e970 100644 --- a/README.md +++ b/README.md @@ -207,6 +207,8 @@ Any URL like `/year/month/day/anything/` will redirect to this article (and link activate the webhook endpoint and its features * `prism` (default: true) activate Prism code highlighting + * `mathjax` (default: true) + activate MathJax equations formatting * `home` * `index` (default: index.ejs) the name of the home page template on the data directory @@ -244,3 +246,8 @@ Any URL like `/year/month/day/anything/` will redirect to this article (and link the command used by the server on webhook trigger * `showdown` Options to be applied to Showdown renderer (see [showdown options](https://github.com/showdownjs/showdown#valid-options) for more info) +* `mathjax` + * `output_format`: (default: svg) + specify the output format between svg, html or MathMl (mml) + * `speak_text`: (default: true) + activate the alternate text in equations diff --git a/package-lock.json b/package-lock.json index 9fb4096..d56c5eb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gitblog.md", - "version": "1.0.0", + "version": "1.0.2", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -1257,8 +1257,7 @@ "abab": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.0.tgz", - "integrity": "sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w==", - "dev": true + "integrity": "sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w==" }, "accepts": { "version": "1.3.7", @@ -1272,14 +1271,12 @@ "acorn": { "version": "5.7.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", - "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", - "dev": true + "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==" }, "acorn-globals": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.2.tgz", "integrity": "sha512-BbzvZhVtZP+Bs1J1HcwrQe8ycfO0wStkSGxuul3He3GkHOIZ6eTqOkPuw9IP1X3+IkOo4wiJmwkobzXYz4wewQ==", - "dev": true, "requires": { "acorn": "^6.0.1", "acorn-walk": "^6.0.1" @@ -1288,22 +1285,19 @@ "acorn": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", - "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==", - "dev": true + "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==" } } }, "acorn-walk": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.1.tgz", - "integrity": "sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==", - "dev": true + "integrity": "sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==" }, "ajv": { "version": "6.10.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", - "dev": true, "requires": { "fast-deep-equal": "^2.0.1", "fast-json-stable-stringify": "^2.0.0", @@ -1373,8 +1367,7 @@ "array-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", - "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", - "dev": true + "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=" }, "array-flatten": { "version": "1.1.1", @@ -1392,7 +1385,6 @@ "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "dev": true, "requires": { "safer-buffer": "~2.1.0" } @@ -1400,8 +1392,7 @@ "assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" }, "assign-symbols": { "version": "1.0.0", @@ -1425,14 +1416,12 @@ "async-limiter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", - "dev": true + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, "atob": { "version": "2.1.2", @@ -1443,14 +1432,12 @@ "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" }, "aws4": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", - "dev": true + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" }, "babel-cli": { "version": "6.26.0", @@ -2311,7 +2298,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "dev": true, "requires": { "tweetnacl": "^0.14.3" } @@ -2353,8 +2339,7 @@ "browser-process-hrtime": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz", - "integrity": "sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw==", - "dev": true + "integrity": "sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw==" }, "browser-resolve": { "version": "1.11.3", @@ -2457,8 +2442,7 @@ "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, "chalk": { "version": "1.1.3", @@ -2604,7 +2588,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, "requires": { "delayed-stream": "~1.0.0" } @@ -2680,8 +2663,7 @@ "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "coveralls": { "version": "3.0.4", @@ -2723,14 +2705,12 @@ "cssom": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.6.tgz", - "integrity": "sha512-DtUeseGk9/GBW0hl0vVPpU22iHL6YB5BUX7ml1hB+GMpo0NX5G4voX3kdWiMSEguFtcW3Vh3djqNF4aIe6ne0A==", - "dev": true + "integrity": "sha512-DtUeseGk9/GBW0hl0vVPpU22iHL6YB5BUX7ml1hB+GMpo0NX5G4voX3kdWiMSEguFtcW3Vh3djqNF4aIe6ne0A==" }, "cssstyle": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.2.2.tgz", "integrity": "sha512-43wY3kl1CVQSvL7wUY1qXkxVGkStjpkDmVjiIKX8R97uhajy8Bybay78uOtqvh7Q5GK75dNPfW0geWjE6qQQow==", - "dev": true, "requires": { "cssom": "0.3.x" } @@ -2739,7 +2719,6 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, "requires": { "assert-plus": "^1.0.0" } @@ -2748,7 +2727,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz", "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==", - "dev": true, "requires": { "abab": "^2.0.0", "whatwg-mimetype": "^2.2.0", @@ -2759,7 +2737,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz", "integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==", - "dev": true, "requires": { "lodash.sortby": "^4.7.0", "tr46": "^1.0.1", @@ -2790,8 +2767,7 @@ "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" }, "define-properties": { "version": "1.1.3", @@ -2858,8 +2834,7 @@ "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, "delegate": { "version": "3.2.0", @@ -2902,7 +2877,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", - "dev": true, "requires": { "webidl-conversions": "^4.0.2" } @@ -2911,7 +2885,6 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dev": true, "requires": { "jsbn": "~0.1.0", "safer-buffer": "^2.1.0" @@ -2996,7 +2969,6 @@ "version": "1.11.1", "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.1.tgz", "integrity": "sha512-JwiqFD9KdGVVpeuRa68yU3zZnBEOcPs0nKW7wZzXky8Z7tffdYUHbe11bPCV5jYlK6DVdKLWLm0f5I/QlL0Kmw==", - "dev": true, "requires": { "esprima": "^3.1.3", "estraverse": "^4.2.0", @@ -3009,7 +2981,6 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, "optional": true } } @@ -3017,20 +2988,17 @@ "esprima": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", - "dev": true + "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=" }, "estraverse": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", - "dev": true + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=" }, "esutils": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", - "dev": true + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=" }, "etag": { "version": "1.8.1", @@ -3184,8 +3152,7 @@ "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, "extend-shallow": { "version": "3.0.2", @@ -3221,26 +3188,22 @@ "extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" }, "fast-deep-equal": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", - "dev": true + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" }, "fast-json-stable-stringify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", - "dev": true + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" }, "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" }, "fast-safe-stringify": { "version": "2.0.6", @@ -3305,14 +3268,12 @@ "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" }, "form-data": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.6", @@ -3930,7 +3891,6 @@ "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, "requires": { "assert-plus": "^1.0.0" } @@ -4026,14 +3986,12 @@ "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" }, "har-validator": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", - "dev": true, "requires": { "ajv": "^6.5.5", "har-schema": "^2.0.0" @@ -4157,7 +4115,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", - "dev": true, "requires": { "whatwg-encoding": "^1.0.1" } @@ -4178,7 +4135,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, "requires": { "assert-plus": "^1.0.0", "jsprim": "^1.2.2", @@ -4440,8 +4396,7 @@ "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" }, "is-windows": { "version": "1.0.2", @@ -4469,8 +4424,7 @@ "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, "istanbul-lib-coverage": { "version": "2.0.5", @@ -6680,14 +6634,12 @@ "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" }, "jsdom": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz", "integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==", - "dev": true, "requires": { "abab": "^2.0.0", "acorn": "^5.5.3", @@ -6732,20 +6684,17 @@ "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" }, "json5": { "version": "0.5.1", @@ -6757,7 +6706,6 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, "requires": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", @@ -6797,8 +6745,7 @@ "left-pad": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", - "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==", - "dev": true + "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==" }, "leven": { "version": "2.1.0", @@ -6810,7 +6757,6 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, "requires": { "prelude-ls": "~1.1.2", "type-check": "~0.3.2" @@ -6840,14 +6786,12 @@ "lodash": { "version": "4.17.11", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", - "dev": true + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" }, "lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", - "dev": true + "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=" }, "log-driver": { "version": "1.2.7", @@ -6931,6 +6875,21 @@ "dev": true, "optional": true }, + "mathjax": { + "version": "2.7.5", + "resolved": "https://registry.npmjs.org/mathjax/-/mathjax-2.7.5.tgz", + "integrity": "sha512-OzsJNitEHAJB3y4IIlPCAvS0yoXwYjlo2Y4kmm9KQzyIBZt2d8yKRalby3uTRNN4fZQiGL2iMXjpdP1u2Rq2DQ==" + }, + "mathjax-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/mathjax-node/-/mathjax-node-2.1.1.tgz", + "integrity": "sha1-JcgPSU91QEGP/Pqcx1bf0hUCAb0=", + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "jsdom": "^11.0.0", + "mathjax": "^2.7.2" + } + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -7237,14 +7196,12 @@ "nwsapi": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.1.4.tgz", - "integrity": "sha512-iGfd9Y6SFdTNldEy2L0GUhcarIutFmk+MPWIn9dmj8NMIup03G08uUF2KGbbmv/Ux4RT0VZJoP/sVbWA6d/VIw==", - "dev": true + "integrity": "sha512-iGfd9Y6SFdTNldEy2L0GUhcarIutFmk+MPWIn9dmj8NMIup03G08uUF2KGbbmv/Ux4RT0VZJoP/sVbWA6d/VIw==" }, "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" }, "object-assign": { "version": "4.1.1", @@ -7366,7 +7323,6 @@ "version": "0.8.2", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", - "dev": true, "requires": { "deep-is": "~0.1.3", "fast-levenshtein": "~2.0.4", @@ -7379,8 +7335,7 @@ "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" } } }, @@ -7496,8 +7451,7 @@ "parse5": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", - "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", - "dev": true + "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==" }, "parseurl": { "version": "1.3.3", @@ -7549,8 +7503,7 @@ "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, "pify": { "version": "3.0.0", @@ -7624,8 +7577,7 @@ "pn": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", - "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", - "dev": true + "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==" }, "posix-character-classes": { "version": "0.1.1", @@ -7636,8 +7588,7 @@ "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" }, "preserve": { "version": "0.2.0", @@ -7722,8 +7673,7 @@ "psl": { "version": "1.1.32", "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.32.tgz", - "integrity": "sha512-MHACAkHpihU/REGGPLj4sEfc/XKW2bheigvHO1dUqjaKigMp1C8+WLQYRGgeKFMsw5PMfegZcaN8IDXK/cD0+g==", - "dev": true + "integrity": "sha512-MHACAkHpihU/REGGPLj4sEfc/XKW2bheigvHO1dUqjaKigMp1C8+WLQYRGgeKFMsw5PMfegZcaN8IDXK/cD0+g==" }, "pump": { "version": "3.0.0", @@ -7738,8 +7688,7 @@ "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, "qs": { "version": "6.7.0", @@ -8308,7 +8257,6 @@ "version": "2.88.0", "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", - "dev": true, "requires": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", @@ -8335,20 +8283,17 @@ "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" }, "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" }, "tough-cookie": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", - "dev": true, "requires": { "psl": "^1.1.24", "punycode": "^1.4.1" @@ -8360,7 +8305,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.2.tgz", "integrity": "sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag==", - "dev": true, "requires": { "lodash": "^4.17.11" } @@ -8369,7 +8313,6 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.7.tgz", "integrity": "sha512-rIMnbBdgNViL37nZ1b3L/VfPOpSi0TqVDQPAvO6U14lMzOLrt5nilxCQqtDKhZeDiW0/hkCXGoQjhgJd/tCh6w==", - "dev": true, "requires": { "request-promise-core": "1.1.2", "stealthy-require": "^1.1.1", @@ -8829,8 +8772,7 @@ "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, "select": { "version": "1.1.2", @@ -9152,7 +9094,6 @@ "version": "1.16.1", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "dev": true, "requires": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", @@ -9200,8 +9141,7 @@ "stealthy-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", - "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", - "dev": true + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=" }, "string-length": { "version": "2.0.0", @@ -9363,8 +9303,7 @@ "symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "dev": true + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" }, "test-exclude": { "version": "5.2.3", @@ -9461,7 +9400,6 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, "requires": { "psl": "^1.1.28", "punycode": "^2.1.1" @@ -9471,7 +9409,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", - "dev": true, "requires": { "punycode": "^2.1.0" } @@ -9486,7 +9423,6 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, "requires": { "safe-buffer": "^5.0.1" } @@ -9494,14 +9430,12 @@ "tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, "requires": { "prelude-ls": "~1.1.2" } @@ -9625,7 +9559,6 @@ "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "dev": true, "requires": { "punycode": "^2.1.0" } @@ -9672,8 +9605,7 @@ "uuid": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", - "dev": true + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" }, "v8flags": { "version": "2.1.1", @@ -9703,7 +9635,6 @@ "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, "requires": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", @@ -9714,7 +9645,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz", "integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=", - "dev": true, "requires": { "browser-process-hrtime": "^0.1.2" } @@ -9731,14 +9661,12 @@ "webidl-conversions": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "dev": true + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==" }, "whatwg-encoding": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", - "dev": true, "requires": { "iconv-lite": "0.4.24" } @@ -9746,14 +9674,12 @@ "whatwg-mimetype": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", - "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", - "dev": true + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==" }, "whatwg-url": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz", "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==", - "dev": true, "requires": { "lodash.sortby": "^4.7.0", "tr46": "^1.0.1", @@ -9842,7 +9768,6 @@ "version": "5.2.2", "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", - "dev": true, "requires": { "async-limiter": "~1.0.0" } @@ -9855,8 +9780,7 @@ "xml-name-validator": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", - "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", - "dev": true + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==" }, "y18n": { "version": "3.2.1", diff --git a/package.json b/package.json index e48d98d..1a46d7b 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "crypto": "^1.0.1", "ejs": "^2.6.2", "express": "^4.17.1", + "mathjax-node": "^2.1.1", "ncp": "^2.0.0", "node-prismjs": "^0.1.2", "prismjs": "^1.16.0", diff --git a/src/config.default.json b/src/config.default.json index 5f39d6e..203e07c 100644 --- a/src/config.default.json +++ b/src/config.default.json @@ -7,7 +7,8 @@ "modules": { "rss": true, "webhook": true, - "prism": true + "prism": true, + "mathjax": true }, "home": { "index": "index.ejs", @@ -42,5 +43,9 @@ "tasklists": true, "openLinksInNewWindow": true, "emoji": true + }, + "mathjax": { + "output_format": "svg", + "speak_text": true } } \ No newline at end of file diff --git a/src/renderer.js b/src/renderer.js index 34f66b2..119a5ce 100644 --- a/src/renderer.js +++ b/src/renderer.js @@ -1,35 +1,99 @@ const fs = require('fs'); const showdown = require('showdown'); -const Prism = require('node-prismjs'); module.exports = (config) => { const converter = new showdown.Converter(config['showdown']); + + const renderShowDown = (data, cb) => { + const html = converter.makeHtml(data); + cb(html); + }; + + let Prism; + if (config['modules']['prism']) + Prism = require('node-prismjs'); + + const renderPrism = (data, cb) => { + if (!config['modules']['prism']) + return cb(data); + const codeRegex = /```([\w-]+)\r?\n((?:(?!```)[\s\S])*)\r?\n```/m; + let match; + while ((match = codeRegex.exec(data))) { + const lang = match[1].trim(); + const code = match[2].trim(); + try { + const block = Prism.highlight(code, Prism.languages[lang] || Prism.languages.autoit, lang); + data = data.slice(0, match.index) + `
` + block + '
' + data.slice(match.index + match[0].length); + } catch (err) { + console.error(err); + } + } + cb(data); + }; + + let mjAPI; + if (config['modules']['mathjax']) { + mjAPI = require('mathjax-node'); + mjAPI.config({ + MathJax: { + tex2jax: { + inlineMath: [['$', '$']], + displayMath: [['$$', '$$']] + } + } + }); + } + + const renderMathJax = (data, cb) => { + if (!config['modules']['mathjax']) + return cb(data); + + const doMJ = (match, format) => { + const eq = match[1].trim(); + const output = config['mathjax']['output_format']; + const mjConf = { + math: eq, + format: format, + speakText: config['mathjax']['speak_text'] + }; + mjConf[output] = true; + mjAPI.typeset(mjConf, (res) => { + data = data.slice(0, match.index) + res[output] + data.slice(match.index + match[0].length); + renderMathJax(data, (data2) => { + cb(data2); + }); + }); + }; + + const eqRegex = /\$\$((?:(?!\$\$)[\s\S])*)\$\$/m; + const inlineEqRegex = /\$([^$]*)\$/; + + let match; + if ((match = eqRegex.exec(data))) { + doMJ(match, 'TeX'); + } else if ((match = inlineEqRegex.exec(data))) { + doMJ(match, 'inline-TeX'); + } else { + cb(data); + } + }; + return { + renderShowDown: config['test'] ? renderShowDown : undefined, + renderPrism: config['test'] ? renderPrism : undefined, + renderMathJax: config['test'] ? renderMathJax : undefined, render: (file, cb) => { fs.readFile(file, {encoding: 'UTF-8'}, (err, data) => { if (err) return cb(err); - if (config['modules']['prism']) { - const codeRegex = /```([\w-]+)\r?\n((?:(?!```)[\s\S])*)\r?\n```/m; - let match; - while ((match = codeRegex.exec(data))) { - const lang = match[1].trim(); - const code = match[2].trim(); - try { - const block = Prism.highlight(code, Prism.languages[lang] || Prism.languages.autoit, lang); - data = data.slice(0, match.index) + `
` + block + '
' + data.slice(match.index + match[0].length); - } catch (err) { - console.error(err); - } - } - } - - - const html = converter.makeHtml(data); - - - cb(null, html); + renderPrism(data, (data2) => { + renderMathJax(data2, (data3) => { + renderShowDown(data3, (html) => { + cb(null, html); + }); + }); + }); }); } }; diff --git a/test/renderer.test.js b/test/renderer.test.js index fff5723..0d05864 100644 --- a/test/renderer.test.js +++ b/test/renderer.test.js @@ -7,12 +7,18 @@ const dataDir = 'test_data'; const file = path.join(dataDir, 'test.md'); const config = { + 'test': true, 'modules': { 'prism': true, + 'mathjax': true }, 'showdown': { 'simplifiedAutoLink': true, 'smartIndentationFix': true + }, + 'mathjax': { + 'output_format': 'html', + 'speak_text': false } }; @@ -20,6 +26,7 @@ const renderer = require('../src/renderer')(config); beforeEach(() => { config['modules']['prism'] = true; + config['modules']['mathjax'] = true; utils.deleteFolderSync(dataDir); fs.mkdirSync(dataDir); }); @@ -30,67 +37,130 @@ afterAll(() => { } }); -test('invalid file', (done) => { - renderer.render('invalid file', (err, html) => { - expect(err).not.toBeNull(); - expect(html).not.toBeDefined(); - done(); +describe('Test Showdown', () => { + test('normal', (done) => { + renderer.renderShowDown('# Hello', (html) => { + expect(html).toBe('

Hello

'); + done(); + }); + }); + test('custom rules', (done) => { + renderer.renderShowDown('www.google.com', (html) => { + expect(html).toBe('

www.google.com

'); + done(); + }); + }); + test('code format', (done) => { + renderer.renderShowDown('```python\nprint("hello")\n```\n\n```python\nprint("hello")\n```', (html) => { + expect(html).toBe('
print("hello")\n
\n
print("hello")\n
'); + done(); + }); }); }); -test('normal file', (done) => { - fs.writeFileSync(file, `# Hello`); - renderer.render(file, (err, html) => { - expect(err).toBeNull(); - expect(html).toBe('

Hello

'); - done(); +describe('Test Prism', () => { + test('no prism', (done) => { + config['modules']['prism'] = false; + renderer.renderPrism('```python\nprint("hello")\n```\n\n```python\nprint("hello")\n```', (data) => { + expect(data).toBe('```python\nprint("hello")\n```\n\n```python\nprint("hello")\n```'); + done(); + }); + }); + + test('prism correct', (done) => { + renderer.renderPrism('```python\nprint("hello")\n```', (data) => { + expect(data).not.toBe('
print("hello")\n
'); + expect(data.indexOf('
')).toBe(0);
+      done();
+    });
+  });
+
+  test('prism invalid lang', (done) => {
+    renderer.renderPrism('```pythdon\nprint("hello")\n```', (data) => {
+      expect(data).not.toBe('
print("hello")\n
'); + expect(data.indexOf('
')).toBe(0);
+      done();
+    });
+  });
+
+  test('prism mutliple code blocks', (done) => {
+    renderer.renderPrism('```python\n\n```\n```python\n\n```', (data) => {
+      expect(data).toBe('
\n
'); + done(); + }); }); }); -test('custom rules', (done) => { - fs.writeFileSync(file, `www.google.com`); - renderer.render(file, (err, html) => { - expect(err).toBeNull(); - expect(html).toBe('

www.google.com

'); - done(); +describe('Test MathJax', () => { + test('no mathjax', (done) => { + config['modules']['mathjax'] = false; + renderer.renderMathJax('$$\nhello\n$$\ntest$test$', (data) => { + expect(data).toBe('$$\nhello\n$$\ntest$test$'); + done(); + }); + }); + test('full eq', (done) => { + renderer.renderMathJax('$$\n\nA\n\n$$', (data) => { + expect(data).toBe('' + + '' + + '' + + 'A' + + ''); + done(); + }); + }); + test('inline eq', (done) => { + renderer.renderMathJax('start $a$ end', (data) => { + expect(data).toBe('start ' + + '' + + '' + + '' + + 'a' + + '' + + ' end'); + done(); + }); + }); + test('multiple eq', (done) => { + renderer.renderMathJax('$$\n\nA\n\n$$\nstart $a$ end\n$$\n\nA\n\n$$', (data) => { + expect(data).toBe('' + + '' + + '' + + '' + + 'A' + + '\n' + + 'start ' + + '' + + '' + + '' + + 'a' + + '' + + ' end\n' + + '' + + '' + + '' + + 'A' + + ''); + done(); + }); }); }); -test('no prism', (done) => { - config['modules']['prism'] = false; - fs.writeFileSync(file, '```python\nprint("hello")\n```\n\n```python\nprint("hello")\n```'); - renderer.render(file, (err, html) => { - expect(err).toBeNull(); - expect(html).toBe('
print("hello")\n
\n
print("hello")\n
'); - done(); +describe('Test render', () => { + test('invalid file', (done) => { + renderer.render('invalid file', (err, html) => { + expect(err).not.toBeNull(); + expect(html).not.toBeDefined(); + done(); + }); }); -}); -test('prism correct', (done) => { - fs.writeFileSync(file, '```python\nprint("hello")\n```'); - renderer.render(file, (err, html) => { - expect(err).toBeNull(); - expect(html).not.toBe('
print("hello")\n
'); - expect(html.indexOf('
')).toBe(0);
-    done();
-  });
-});
-
-test('prism invalid lang', (done) => {
-  fs.writeFileSync(file, '```pythdon\nprint("hello")\n```');
-  renderer.render(file, (err, html) => {
-    expect(err).toBeNull();
-    expect(html).not.toBe('
print("hello")\n
'); - expect(html.indexOf('
')).toBe(0);
-    done();
-  });
-});
-
-test('prism mutliple code blocks', (done) => {
-  fs.writeFileSync(file, '```python\n\n```\n\n```python\n\n```');
-  renderer.render(file, (err, html) => {
-    expect(err).toBeNull();
-    expect(html).toBe('
\n
'); - done(); + test('normal file', (done) => { + fs.writeFileSync(file, `# Hello`); + renderer.render(file, (err, html) => { + expect(err).toBeNull(); + expect(html).toBe('

Hello

'); + done(); + }); }); }); \ No newline at end of file From 6bdcd6872e47fbc64c48e7d8654b309a8b3b37fc Mon Sep 17 00:00:00 2001 From: Klemek Date: Sat, 22 Jun 2019 14:13:20 +0200 Subject: [PATCH 059/120] MathJax small fix --- src/renderer.js | 2 +- test/renderer.test.js | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/renderer.js b/src/renderer.js index 119a5ce..8944f9f 100644 --- a/src/renderer.js +++ b/src/renderer.js @@ -66,7 +66,7 @@ module.exports = (config) => { }; const eqRegex = /\$\$((?:(?!\$\$)[\s\S])*)\$\$/m; - const inlineEqRegex = /\$([^$]*)\$/; + const inlineEqRegex = /\$([^$\n]*)\$/; let match; if ((match = eqRegex.exec(data))) { diff --git a/test/renderer.test.js b/test/renderer.test.js index 0d05864..f9e8279 100644 --- a/test/renderer.test.js +++ b/test/renderer.test.js @@ -121,6 +121,12 @@ describe('Test MathJax', () => { done(); }); }); + test('fake inline eq', (done) => { + renderer.renderMathJax('i have $6\nyou have $5', (data) => { + expect(data).toBe('i have $6\nyou have $5'); + done(); + }); + }); test('multiple eq', (done) => { renderer.renderMathJax('$$\n\nA\n\n$$\nstart $a$ end\n$$\n\nA\n\n$$', (data) => { expect(data).toBe('' + From 333bbf6eb86740f72d9ee8b67d04d1cc4ff602b8 Mon Sep 17 00:00:00 2001 From: Klemek Date: Sat, 22 Jun 2019 14:25:41 +0200 Subject: [PATCH 060/120] updated article index.md --- sample_data/article/index.md | 62 ++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/sample_data/article/index.md b/sample_data/article/index.md index 971b3d2..370dc41 100644 --- a/sample_data/article/index.md +++ b/sample_data/article/index.md @@ -4,7 +4,24 @@ If you see this page, that means it's working ## Guide to Markdown formatting +* [Headers](#headers) +* [Emphasis](#emphasis) +* [Lists](#lists) +* [Links](#links) +* [Images](#images) +* [Code and Syntax Highlighting](#codeandsyntaxhighlighting) +* [Tables](#tables) +* [Blockquotes](#blockquotes) +* [Inline HTML](#inlinehtml) +* [Horizontal Rule](#horizontalrule) +* [Line Breaks](#linebreaks) +* [Check Boxes](#checkboxes) +* [Spoilers](#spoilers) +* [Math Equations](#mathequations) +* [Youtube Videos](#youtubevideos) + ### Headers +[Back to top](#top) # H1 ## H2 @@ -22,6 +39,7 @@ Alt-H2 ------ ### Emphasis +[Back to top](#top) Emphasis, aka italics, with *asterisks* or _underscores_. @@ -32,6 +50,7 @@ Combined emphasis with **asterisks and _underscores_**. Strikethrough uses two tildes. ~~Scratch this.~~ ### Lists +[Back to top](#top) 1. First ordered list item 2. Another item @@ -51,6 +70,7 @@ Strikethrough uses two tildes. ~~Scratch this.~~ + Or pluses ### Links +[Back to top](#top) [I'm an inline-style link](https://www.google.com) @@ -75,6 +95,7 @@ Some text to show that the reference links can follow later. [link text itself]: http://www.reddit.com ### Images +[Back to top](#top) Here's our logo (hover to see the title text): @@ -88,6 +109,7 @@ Reference-style: ### Code and Syntax Highlighting +[Back to top](#top) Inline `code` has `back-ticks around` it. @@ -108,6 +130,7 @@ But let's throw in a tag. ### Tables +[Back to top](#top) Colons can be used to align columns. @@ -127,6 +150,7 @@ Markdown | Less | Pretty 1 | 2 | 3 ### Blockquotes +[Back to top](#top) > Blockquotes are very handy in email to emulate reply text. > This line is part of the same quote. @@ -136,6 +160,7 @@ Quote break. > This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can *put* **Markdown** into a blockquote. ### Inline HTML +[Back to top](#top)
Definition list
@@ -146,6 +171,7 @@ Quote break.
### Horizontal Rule +[Back to top](#top) Three or more... @@ -162,6 +188,7 @@ ___ Underscores ### Line Breaks +[Back to top](#top) Here's a line for us to start with. @@ -169,3 +196,38 @@ This line is separated from the one above by two newlines, so it will be a *sepa This line is also a separate paragraph, but... This line is only separated by a single newline, so it's a separate line in the *same paragraph*. + +### Check Boxes +[Back to top](#top) + +* [x] Task completed +* [] Task to do + +### Spoilers +[Back to top](#top) + +
Title of the spoiler (click)

+Content of the spoiler + +On several lines +

+ +### Math Equations +[Back to top](#top) + +You can use LaTeX equations with MathJax for full equations and inline ones (based on the number of $) : + +$$ +\large{\beta=\sum_{i}^{\alpha }\frac{x^{i}}{\alpha}} +$$ + + +Where $\alpha$ is cool + + +### Youtube Videos +[Back to top](#top) + +Just use the "embedded" export on Youtube with dimensions of 535x300 for best results + + \ No newline at end of file From 0480536a2076fadc8ccb53d9d95e3d1979fad064 Mon Sep 17 00:00:00 2001 From: Klemek Date: Sat, 22 Jun 2019 14:29:54 +0200 Subject: [PATCH 061/120] Updated footer for more info --- package.json | 3 +-- sample_data/home/footer.ejs | 3 ++- src/app.js | 4 ++++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 1a46d7b..4b499d1 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,6 @@ { - "nodePort": 5000, "name": "gitblog.md", - "version": "1.0.2", + "version": "1.0.3", "description": "A static blog using Markdown pulled from your git repository.", "main": "src/server.js", "dependencies": { diff --git a/sample_data/home/footer.ejs b/sample_data/home/footer.ejs index 42edf34..1008e4e 100644 --- a/sample_data/home/footer.ejs +++ b/sample_data/home/footer.ejs @@ -1,5 +1,6 @@
- @<%= new Date().getFullYear() %> - Made with GitBlog.md + RSS feed - @<%= new Date().getFullYear() %> - Made with GitBlog.md (v<%= info.version %>)
\ No newline at end of file diff --git a/src/app.js b/src/app.js index 7ee2dc1..a59a44d 100644 --- a/src/app.js +++ b/src/app.js @@ -2,6 +2,7 @@ const express = require('express'); const app = express(); const fs = require('fs'); const path = require('path'); +const pjson = require('../package.json'); app.enable('trust proxy'); @@ -71,6 +72,9 @@ module.exports = (config) => { * @param code - code to send along the page */ const render = (res, vPath, data, code = 200) => { + data.info = { + version: pjson.version + }; res.render(vPath, data, (err, html) => { if (err) { res.sendStatus(500); From 0d173cfcef5f9caa6ef1c5fde859792fef3fd523 Mon Sep 17 00:00:00 2001 From: Klemek Date: Sat, 22 Jun 2019 14:37:52 +0200 Subject: [PATCH 062/120] [skip CI] updated README.md --- README.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9b2e970..e6c82fc 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ A static blog using Markdown pulled from your git repository. * **[How it works](#how-it-works)** * **[Installation](#installation)** * **[Writing an article](#writing-an-article)** +* **[Modules](#modules)** * **[Configuration](#configuration)** ## How it works @@ -187,6 +188,18 @@ On that same folder, you can place resources like images and reference them in r Any URL like `/year/month/day/anything/` will redirect to this article (and link to correct resources) +## Modules +[back to top](#gitblog-md) + +* **RSS** + It allows your users to use the feed to be updated as soon as a new article is out +* **Webhook** + It update your blog from your online repo when it's updated +* **Prism** + It highlight code blocks to be more readable (more info [here](https://prismjs.com/), you will need the corresponding CSS file on your templates) +* **MathJax** + It allows you to add math equations to your articles by simply writing LaTeX between $$ for full size (and between $ for inline) (more info [here](https://www.mathjax.org/)) + ## Configuration [back to top](#gitblog-md) @@ -245,7 +258,7 @@ Any URL like `/year/month/day/anything/` will redirect to this article (and link * `pull_command`: (default: git pull) the command used by the server on webhook trigger * `showdown` - Options to be applied to Showdown renderer (see [showdown options](https://github.com/showdownjs/showdown#valid-options) for more info) + Options to be applied to Showdown renderer (see [showdown options](https://github.com/showdownjs/showdown/wiki/Showdown-Options) for more info) * `mathjax` * `output_format`: (default: svg) specify the output format between svg, html or MathMl (mml) From 35a7747d6d67213569504ef7d35b2c133aa9c99f Mon Sep 17 00:00:00 2001 From: Klemek Date: Sat, 22 Jun 2019 14:38:59 +0200 Subject: [PATCH 063/120] better warning when no config.json --- src/config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.js b/src/config.js index a51b3c7..dca7338 100644 --- a/src/config.js +++ b/src/config.js @@ -25,7 +25,7 @@ module.exports = () => { let config = JSON.parse(configData); return merge(refConfig, config); } catch (error) { - console.error('Failed to load config.json : ' + error); + console.log('\x1b[33mâš \x1b[0m %s', 'Failed to load config.json : ' + error); return refConfig; } }; \ No newline at end of file From 6dbc7f359b58536ece2bb62429f343524b03518e Mon Sep 17 00:00:00 2001 From: Klemek Date: Sun, 23 Jun 2019 13:48:28 +0200 Subject: [PATCH 064/120] PlantUML integration --- .jshintignore | 2 + .travis.yml | 5 - README.md | 8 +- package-lock.json | 2 +- package.json | 3 +- sample_data/article/index.md | 28 + src/config.default.json | 6 +- src/lib/plantuml_synchro.js | 1715 ++++++++++++++++++++++++++++++++++ src/renderer.js | 31 +- src/script_loader.js | 10 + test/renderer.test.js | 32 +- test/script_loader.test.js | 51 + 12 files changed, 1878 insertions(+), 15 deletions(-) create mode 100644 .jshintignore create mode 100644 src/lib/plantuml_synchro.js create mode 100644 src/script_loader.js create mode 100644 test/script_loader.test.js diff --git a/.jshintignore b/.jshintignore new file mode 100644 index 0000000..4453f2b --- /dev/null +++ b/.jshintignore @@ -0,0 +1,2 @@ +/node_modules +/src/lib \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 3f71628..a968088 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,13 +6,8 @@ cache: npm: true directories: - node_modules -addons: - apt: - packages: - - graphviz install: - npm install - - npm install node-plantuml before_script: - npm install -g jshint script: diff --git a/README.md b/README.md index e6c82fc..2c256c1 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ On the `/rss` endpoint, the servers gives you a RSS feed based on the list of ar #### 1. Download and install the latest version from the repo ```bash git clone https://github.com/klemek/gitblog.md.git -npm install +npm install --production ``` #### 2. Create your config file ```bash @@ -198,7 +198,9 @@ Any URL like `/year/month/day/anything/` will redirect to this article (and link * **Prism** It highlight code blocks to be more readable (more info [here](https://prismjs.com/), you will need the corresponding CSS file on your templates) * **MathJax** - It allows you to add math equations to your articles by simply writing LaTeX between $$ for full size (and between $ for inline) (more info [here](https://www.mathjax.org/)) + It allows you to add math equations to your articles by simply writing LaTeX between `$$` for full size (and between $ for inline) (more info [here](https://www.mathjax.org/)) +* **PlantUML** + It allows you to add UML diagrams with PlantUML Syntax between `@startuml` and `@enduml` (more info [here](http://www.plantuml.com)) ## Configuration [back to top](#gitblog-md) @@ -222,6 +224,8 @@ Any URL like `/year/month/day/anything/` will redirect to this article (and link activate Prism code highlighting * `mathjax` (default: true) activate MathJax equations formatting + * `plantuml` (default: true) + activate PlantUML diagram rendering * `home` * `index` (default: index.ejs) the name of the home page template on the data directory diff --git a/package-lock.json b/package-lock.json index d56c5eb..cd14f17 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gitblog.md", - "version": "1.0.2", + "version": "1.0.3", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 4b499d1..7680f75 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,8 @@ "src/**/*.js", "!/node_modules/", "!src/server.js", - "!src/postinstall.js" + "!src/postinstall.js", + "!src/lib/*.js" ] } } diff --git a/sample_data/article/index.md b/sample_data/article/index.md index 370dc41..2154e57 100644 --- a/sample_data/article/index.md +++ b/sample_data/article/index.md @@ -18,6 +18,7 @@ If you see this page, that means it's working * [Check Boxes](#checkboxes) * [Spoilers](#spoilers) * [Math Equations](#mathequations) +* [UML](#uml) * [Youtube Videos](#youtubevideos) ### Headers @@ -224,6 +225,33 @@ $$ Where $\alpha$ is cool +### UML +[Back to top](#top) + +You can use PlantUML diagrams with `@startuml` and `@enduml` tags : + +@startuml +title Article +cloud web +node nodejs { + TCP -right- [express] + [showdown] +} +package data { + package "2019/06/18" { + component index [ + index.md + image.png + ... + ] + } +} +web -down-> TCP : 1. /2019/06/18/title +express -down-> index : 2. fetch +index -up-> showdown : 3. markdown +showdown -left-> express : 4. html +express -up-> web : 5. html +@enduml ### Youtube Videos [Back to top](#top) diff --git a/src/config.default.json b/src/config.default.json index 203e07c..d5e5518 100644 --- a/src/config.default.json +++ b/src/config.default.json @@ -8,7 +8,8 @@ "rss": true, "webhook": true, "prism": true, - "mathjax": true + "mathjax": true, + "plantuml": true }, "home": { "index": "index.ejs", @@ -47,5 +48,8 @@ "mathjax": { "output_format": "svg", "speak_text": true + }, + "plantuml": { + "output_format": "svg" } } \ No newline at end of file diff --git a/src/lib/plantuml_synchro.js b/src/lib/plantuml_synchro.js new file mode 100644 index 0000000..285b197 --- /dev/null +++ b/src/lib/plantuml_synchro.js @@ -0,0 +1,1715 @@ +/* Copyright (C) 1999 Masanao Izumo + * Version: 1.0.1 + * LastModified: Dec 25 1999 + */ + +/* Interface: + * data = zip_deflate(src); + */ + +/* constant parameters */ +var zip_WSIZE = 32768; // Sliding Window size +var zip_STORED_BLOCK = 0; +var zip_STATIC_TREES = 1; +var zip_DYN_TREES = 2; + +/* for deflate */ +var zip_DEFAULT_LEVEL = 6; +var zip_FULL_SEARCH = true; +var zip_INBUFSIZ = 32768; // Input buffer size +var zip_INBUF_EXTRA = 64; // Extra buffer +var zip_OUTBUFSIZ = 1024 * 8; +var zip_window_size = 2 * zip_WSIZE; +var zip_MIN_MATCH = 3; +var zip_MAX_MATCH = 258; +var zip_BITS = 16; +// for SMALL_MEM +var zip_LIT_BUFSIZE = 0x2000; +var zip_HASH_BITS = 13; +// for MEDIUM_MEM +// var zip_LIT_BUFSIZE = 0x4000; +// var zip_HASH_BITS = 14; +// for BIG_MEM +// var zip_LIT_BUFSIZE = 0x8000; +// var zip_HASH_BITS = 15; +if (zip_LIT_BUFSIZE > zip_INBUFSIZ) + alert('error: zip_INBUFSIZ is too small'); +if ((zip_WSIZE << 1) > (1 << zip_BITS)) + alert('error: zip_WSIZE is too large'); +if (zip_HASH_BITS > zip_BITS - 1) + alert('error: zip_HASH_BITS is too large'); +if (zip_HASH_BITS < 8 || zip_MAX_MATCH != 258) + alert('error: Code too clever'); +var zip_DIST_BUFSIZE = zip_LIT_BUFSIZE; +var zip_HASH_SIZE = 1 << zip_HASH_BITS; +var zip_HASH_MASK = zip_HASH_SIZE - 1; +var zip_WMASK = zip_WSIZE - 1; +var zip_NIL = 0; // Tail of hash chains +var zip_TOO_FAR = 4096; +var zip_MIN_LOOKAHEAD = zip_MAX_MATCH + zip_MIN_MATCH + 1; +var zip_MAX_DIST = zip_WSIZE - zip_MIN_LOOKAHEAD; +var zip_SMALLEST = 1; +var zip_MAX_BITS = 15; +var zip_MAX_BL_BITS = 7; +var zip_LENGTH_CODES = 29; +var zip_LITERALS = 256; +var zip_END_BLOCK = 256; +var zip_L_CODES = zip_LITERALS + 1 + zip_LENGTH_CODES; +var zip_D_CODES = 30; +var zip_BL_CODES = 19; +var zip_REP_3_6 = 16; +var zip_REPZ_3_10 = 17; +var zip_REPZ_11_138 = 18; +var zip_HEAP_SIZE = 2 * zip_L_CODES + 1; +var zip_H_SHIFT = parseInt((zip_HASH_BITS + zip_MIN_MATCH - 1) / + zip_MIN_MATCH); + +/* variables */ +var zip_free_queue; +var zip_qhead, zip_qtail; +var zip_initflag; +var zip_outbuf = null; +var zip_outcnt, zip_outoff; +var zip_complete; +var zip_window; +var zip_d_buf; +var zip_l_buf; +var zip_prev; +var zip_bi_buf; +var zip_bi_valid; +var zip_block_start; +var zip_ins_h; +var zip_hash_head; +var zip_prev_match; +var zip_match_available; +var zip_match_length; +var zip_prev_length; +var zip_strstart; +var zip_match_start; +var zip_eofile; +var zip_lookahead; +var zip_max_chain_length; +var zip_max_lazy_match; +var zip_compr_level; +var zip_good_match; +var zip_nice_match; +var zip_dyn_ltree; +var zip_dyn_dtree; +var zip_static_ltree; +var zip_static_dtree; +var zip_bl_tree; +var zip_l_desc; +var zip_d_desc; +var zip_bl_desc; +var zip_bl_count; +var zip_heap; +var zip_heap_len; +var zip_heap_max; +var zip_depth; +var zip_length_code; +var zip_dist_code; +var zip_base_length; +var zip_base_dist; +var zip_flag_buf; +var zip_last_lit; +var zip_last_dist; +var zip_last_flags; +var zip_flags; +var zip_flag_bit; +var zip_opt_len; +var zip_static_len; +var zip_deflate_data; +var zip_deflate_pos; + +/* constant tables */ +var zip_extra_lbits = new Array( + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0); +var zip_extra_dbits = new Array( + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13); +var zip_extra_blbits = new Array( + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 7); +var zip_bl_order = new Array( + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15); +var zip_configuration_table = new Array( + new zip_DeflateConfiguration(0, 0, 0, 0), + new zip_DeflateConfiguration(4, 4, 8, 4), + new zip_DeflateConfiguration(4, 5, 16, 8), + new zip_DeflateConfiguration(4, 6, 32, 32), + new zip_DeflateConfiguration(4, 4, 16, 16), + new zip_DeflateConfiguration(8, 16, 32, 32), + new zip_DeflateConfiguration(8, 16, 128, 128), + new zip_DeflateConfiguration(8, 32, 128, 256), + new zip_DeflateConfiguration(32, 128, 258, 1024), + new zip_DeflateConfiguration(32, 258, 258, 4096)); + +/* objects (deflate) */ + +function zip_DeflateCT() { + this.fc = 0; // frequency count or bit string + this.dl = 0; // father node in Huffman tree or length of bit string +} + +function zip_DeflateTreeDesc() { + this.dyn_tree = null; // the dynamic tree + this.static_tree = null; // corresponding static tree or NULL + this.extra_bits = null; // extra bits for each code or NULL + this.extra_base = 0; // base index for extra_bits + this.elems = 0; // max number of elements in the tree + this.max_length = 0; // max bit length for the codes + this.max_code = 0; // largest code with non zero frequency +} + +/* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ +function zip_DeflateConfiguration(a, b, c, d) { + this.good_length = a; // reduce lazy search above this match length + this.max_lazy = b; // do not perform lazy search above this match length + this.nice_length = c; // quit search above this match length + this.max_chain = d; +} + +function zip_DeflateBuffer() { + this.next = null; + this.len = 0; + this.ptr = new Array(zip_OUTBUFSIZ); + this.off = 0; +} + +/* routines (deflate) */ + +function zip_deflate_start(level) { + var i; + + if (!level) + level = zip_DEFAULT_LEVEL; + else if (level < 1) + level = 1; + else if (level > 9) + level = 9; + + zip_compr_level = level; + zip_initflag = false; + zip_eofile = false; + if (zip_outbuf != null) + return; + + zip_free_queue = zip_qhead = zip_qtail = null; + zip_outbuf = new Array(zip_OUTBUFSIZ); + zip_window = new Array(zip_window_size); + zip_d_buf = new Array(zip_DIST_BUFSIZE); + zip_l_buf = new Array(zip_INBUFSIZ + zip_INBUF_EXTRA); + zip_prev = new Array(1 << zip_BITS); + zip_dyn_ltree = new Array(zip_HEAP_SIZE); + for (i = 0; i < zip_HEAP_SIZE; i++) + zip_dyn_ltree[i] = new zip_DeflateCT(); + zip_dyn_dtree = new Array(2 * zip_D_CODES + 1); + for (i = 0; i < 2 * zip_D_CODES + 1; i++) + zip_dyn_dtree[i] = new zip_DeflateCT(); + zip_static_ltree = new Array(zip_L_CODES + 2); + for (i = 0; i < zip_L_CODES + 2; i++) + zip_static_ltree[i] = new zip_DeflateCT(); + zip_static_dtree = new Array(zip_D_CODES); + for (i = 0; i < zip_D_CODES; i++) + zip_static_dtree[i] = new zip_DeflateCT(); + zip_bl_tree = new Array(2 * zip_BL_CODES + 1); + for (i = 0; i < 2 * zip_BL_CODES + 1; i++) + zip_bl_tree[i] = new zip_DeflateCT(); + zip_l_desc = new zip_DeflateTreeDesc(); + zip_d_desc = new zip_DeflateTreeDesc(); + zip_bl_desc = new zip_DeflateTreeDesc(); + zip_bl_count = new Array(zip_MAX_BITS + 1); + zip_heap = new Array(2 * zip_L_CODES + 1); + zip_depth = new Array(2 * zip_L_CODES + 1); + zip_length_code = new Array(zip_MAX_MATCH - zip_MIN_MATCH + 1); + zip_dist_code = new Array(512); + zip_base_length = new Array(zip_LENGTH_CODES); + zip_base_dist = new Array(zip_D_CODES); + zip_flag_buf = new Array(parseInt(zip_LIT_BUFSIZE / 8)); +} + +function zip_deflate_end() { + zip_free_queue = zip_qhead = zip_qtail = null; + zip_outbuf = null; + zip_window = null; + zip_d_buf = null; + zip_l_buf = null; + zip_prev = null; + zip_dyn_ltree = null; + zip_dyn_dtree = null; + zip_static_ltree = null; + zip_static_dtree = null; + zip_bl_tree = null; + zip_l_desc = null; + zip_d_desc = null; + zip_bl_desc = null; + zip_bl_count = null; + zip_heap = null; + zip_depth = null; + zip_length_code = null; + zip_dist_code = null; + zip_base_length = null; + zip_base_dist = null; + zip_flag_buf = null; +} + +function zip_reuse_queue(p) { + p.next = zip_free_queue; + zip_free_queue = p; +} + +function zip_new_queue() { + var p; + + if (zip_free_queue != null) { + p = zip_free_queue; + zip_free_queue = zip_free_queue.next; + } else + p = new zip_DeflateBuffer(); + p.next = null; + p.len = p.off = 0; + + return p; +} + +function zip_head1(i) { + return zip_prev[zip_WSIZE + i]; +} + +function zip_head2(i, val) { + return zip_prev[zip_WSIZE + i] = val; +} + +/* put_byte is used for the compressed output, put_ubyte for the + * uncompressed output. However unlzw() uses window for its + * suffix table instead of its output buffer, so it does not use put_ubyte + * (to be cleaned up). + */ +function zip_put_byte(c) { + zip_outbuf[zip_outoff + zip_outcnt++] = c; + if (zip_outoff + zip_outcnt == zip_OUTBUFSIZ) + zip_qoutbuf(); +} + +/* Output a 16 bit value, lsb first */ +function zip_put_short(w) { + w &= 0xffff; + if (zip_outoff + zip_outcnt < zip_OUTBUFSIZ - 2) { + zip_outbuf[zip_outoff + zip_outcnt++] = (w & 0xff); + zip_outbuf[zip_outoff + zip_outcnt++] = (w >>> 8); + } else { + zip_put_byte(w & 0xff); + zip_put_byte(w >>> 8); + } +} + +/* ========================================================================== + * Insert string s in the dictionary and set match_head to the previous head + * of the hash chain (the most recent string with same hash key). Return + * the previous length of the hash chain. + * IN assertion: all calls to to INSERT_STRING are made with consecutive + * input characters and the first MIN_MATCH bytes of s are valid + * (except for the last MIN_MATCH-1 bytes of the input file). + */ +function zip_INSERT_STRING() { + zip_ins_h = ((zip_ins_h << zip_H_SHIFT) + ^ (zip_window[zip_strstart + zip_MIN_MATCH - 1] & 0xff)) + & zip_HASH_MASK; + zip_hash_head = zip_head1(zip_ins_h); + zip_prev[zip_strstart & zip_WMASK] = zip_hash_head; + zip_head2(zip_ins_h, zip_strstart); +} + +/* Send a code of the given tree. c and tree must not have side effects */ +function zip_SEND_CODE(c, tree) { + zip_send_bits(tree[c].fc, tree[c].dl); +} + +/* Mapping from a distance to a distance code. dist is the distance - 1 and + * must not have side effects. dist_code[256] and dist_code[257] are never + * used. + */ +function zip_D_CODE(dist) { + return (dist < 256 ? zip_dist_code[dist] + : zip_dist_code[256 + (dist >> 7)]) & 0xff; +} + +/* ========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +function zip_SMALLER(tree, n, m) { + return tree[n].fc < tree[m].fc || + (tree[n].fc == tree[m].fc && zip_depth[n] <= zip_depth[m]); +} + +/* ========================================================================== + * read string data + */ +function zip_read_buff(buff, offset, n) { + var i; + for (i = 0; i < n && zip_deflate_pos < zip_deflate_data.length; i++) + buff[offset + i] = + zip_deflate_data.charCodeAt(zip_deflate_pos++) & 0xff; + return i; +} + +/* ========================================================================== + * Initialize the "longest match" routines for a new file + */ +function zip_lm_init() { + var j; + + /* Initialize the hash table. */ + for (j = 0; j < zip_HASH_SIZE; j++) +// zip_head2(j, zip_NIL); + zip_prev[zip_WSIZE + j] = 0; + /* prev will be initialized on the fly */ + + /* Set the default configuration parameters: + */ + zip_max_lazy_match = zip_configuration_table[zip_compr_level].max_lazy; + zip_good_match = zip_configuration_table[zip_compr_level].good_length; + if (!zip_FULL_SEARCH) + zip_nice_match = zip_configuration_table[zip_compr_level].nice_length; + zip_max_chain_length = zip_configuration_table[zip_compr_level].max_chain; + + zip_strstart = 0; + zip_block_start = 0; + + zip_lookahead = zip_read_buff(zip_window, 0, 2 * zip_WSIZE); + if (zip_lookahead <= 0) { + zip_eofile = true; + zip_lookahead = 0; + return; + } + zip_eofile = false; + /* Make sure that we always have enough lookahead. This is important + * if input comes from a device such as a tty. + */ + while (zip_lookahead < zip_MIN_LOOKAHEAD && !zip_eofile) + zip_fill_window(); + + /* If lookahead < MIN_MATCH, ins_h is garbage, but this is + * not important since only literal bytes will be emitted. + */ + zip_ins_h = 0; + for (j = 0; j < zip_MIN_MATCH - 1; j++) { +// UPDATE_HASH(ins_h, window[j]); + zip_ins_h = ((zip_ins_h << zip_H_SHIFT) ^ (zip_window[j] & 0xff)) & zip_HASH_MASK; + } +} + +/* ========================================================================== + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + */ +function zip_longest_match(cur_match) { + var chain_length = zip_max_chain_length; // max hash chain length + var scanp = zip_strstart; // current string + var matchp; // matched string + var len; // length of current match + var best_len = zip_prev_length; // best match length so far + + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + var limit = (zip_strstart > zip_MAX_DIST ? zip_strstart - zip_MAX_DIST : zip_NIL); + + var strendp = zip_strstart + zip_MAX_MATCH; + var scan_end1 = zip_window[scanp + best_len - 1]; + var scan_end = zip_window[scanp + best_len]; + + /* Do not waste too much time if we already have a good match: */ + if (zip_prev_length >= zip_good_match) + chain_length >>= 2; + +// Assert(encoder->strstart <= window_size-MIN_LOOKAHEAD, "insufficient lookahead"); + + do { +// Assert(cur_match < encoder->strstart, "no future"); + matchp = cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2: + */ + if (zip_window[matchp + best_len] != scan_end || + zip_window[matchp + best_len - 1] != scan_end1 || + zip_window[matchp] != zip_window[scanp] || + zip_window[++matchp] != zip_window[scanp + 1]) { + continue; + } + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scanp += 2; + matchp++; + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (zip_window[++scanp] == zip_window[++matchp] && + zip_window[++scanp] == zip_window[++matchp] && + zip_window[++scanp] == zip_window[++matchp] && + zip_window[++scanp] == zip_window[++matchp] && + zip_window[++scanp] == zip_window[++matchp] && + zip_window[++scanp] == zip_window[++matchp] && + zip_window[++scanp] == zip_window[++matchp] && + zip_window[++scanp] == zip_window[++matchp] && + scanp < strendp); + + len = zip_MAX_MATCH - (strendp - scanp); + scanp = strendp - zip_MAX_MATCH; + + if (len > best_len) { + zip_match_start = cur_match; + best_len = len; + if (zip_FULL_SEARCH) { + if (len >= zip_MAX_MATCH) break; + } else { + if (len >= zip_nice_match) break; + } + + scan_end1 = zip_window[scanp + best_len - 1]; + scan_end = zip_window[scanp + best_len]; + } + } while ((cur_match = zip_prev[cur_match & zip_WMASK]) > limit + && --chain_length != 0); + + return best_len; +} + +/* ========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead, and sets eofile if end of input file. + * IN assertion: lookahead < MIN_LOOKAHEAD && strstart + lookahead > 0 + * OUT assertions: at least one byte has been read, or eofile is set; + * file reads are performed for at least two bytes (required for the + * translate_eol option). + */ +function zip_fill_window() { + var n, m; + + // Amount of free space at the end of the window. + var more = zip_window_size - zip_lookahead - zip_strstart; + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (more == -1) { + /* Very unlikely, but possible on 16 bit machine if strstart == 0 + * and lookahead == 1 (input done one byte at time) + */ + more--; + } else if (zip_strstart >= zip_WSIZE + zip_MAX_DIST) { + /* By the IN assertion, the window is not empty so we can't confuse + * more == 0 with more == 64K on a 16 bit machine. + */ +// Assert(window_size == (ulg)2*WSIZE, "no sliding with BIG_MEM"); + +// System.arraycopy(window, WSIZE, window, 0, WSIZE); + for (n = 0; n < zip_WSIZE; n++) + zip_window[n] = zip_window[n + zip_WSIZE]; + + zip_match_start -= zip_WSIZE; + zip_strstart -= zip_WSIZE; /* we now have strstart >= MAX_DIST: */ + zip_block_start -= zip_WSIZE; + + for (n = 0; n < zip_HASH_SIZE; n++) { + m = zip_head1(n); + zip_head2(n, m >= zip_WSIZE ? m - zip_WSIZE : zip_NIL); + } + for (n = 0; n < zip_WSIZE; n++) { + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + m = zip_prev[n]; + zip_prev[n] = (m >= zip_WSIZE ? m - zip_WSIZE : zip_NIL); + } + more += zip_WSIZE; + } + // At this point, more >= 2 + if (!zip_eofile) { + n = zip_read_buff(zip_window, zip_strstart + zip_lookahead, more); + if (n <= 0) + zip_eofile = true; + else + zip_lookahead += n; + } +} + +/* ========================================================================== + * Processes a new input file and return its compressed length. This + * function does not perform lazy evaluationof matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ +function zip_deflate_fast() { + while (zip_lookahead != 0 && zip_qhead == null) { + var flush; // set if current block must be flushed + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + zip_INSERT_STRING(); + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if (zip_hash_head != zip_NIL && + zip_strstart - zip_hash_head <= zip_MAX_DIST) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + zip_match_length = zip_longest_match(zip_hash_head); + /* longest_match() sets match_start */ + if (zip_match_length > zip_lookahead) + zip_match_length = zip_lookahead; + } + if (zip_match_length >= zip_MIN_MATCH) { +// check_match(strstart, match_start, match_length); + + flush = zip_ct_tally(zip_strstart - zip_match_start, + zip_match_length - zip_MIN_MATCH); + zip_lookahead -= zip_match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ + if (zip_match_length <= zip_max_lazy_match) { + zip_match_length--; // string at strstart already in hash table + do { + zip_strstart++; + zip_INSERT_STRING(); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. If lookahead < MIN_MATCH + * these bytes are garbage, but it does not matter since + * the next lookahead bytes will be emitted as literals. + */ + } while (--zip_match_length != 0); + zip_strstart++; + } else { + zip_strstart += zip_match_length; + zip_match_length = 0; + zip_ins_h = zip_window[zip_strstart] & 0xff; +// UPDATE_HASH(ins_h, window[strstart + 1]); + zip_ins_h = ((zip_ins_h << zip_H_SHIFT) ^ (zip_window[zip_strstart + 1] & 0xff)) & zip_HASH_MASK; + +//#if MIN_MATCH != 3 +// Call UPDATE_HASH() MIN_MATCH-3 more times +//#endif + + } + } else { + /* No match, output a literal byte */ + flush = zip_ct_tally(0, zip_window[zip_strstart] & 0xff); + zip_lookahead--; + zip_strstart++; + } + if (flush) { + zip_flush_block(0); + zip_block_start = zip_strstart; + } + + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + while (zip_lookahead < zip_MIN_LOOKAHEAD && !zip_eofile) + zip_fill_window(); + } +} + +function zip_deflate_better() { + /* Process the input block. */ + while (zip_lookahead != 0 && zip_qhead == null) { + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + zip_INSERT_STRING(); + + /* Find the longest match, discarding those <= prev_length. + */ + zip_prev_length = zip_match_length; + zip_prev_match = zip_match_start; + zip_match_length = zip_MIN_MATCH - 1; + + if (zip_hash_head != zip_NIL && + zip_prev_length < zip_max_lazy_match && + zip_strstart - zip_hash_head <= zip_MAX_DIST) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + zip_match_length = zip_longest_match(zip_hash_head); + /* longest_match() sets match_start */ + if (zip_match_length > zip_lookahead) + zip_match_length = zip_lookahead; + + /* Ignore a length 3 match if it is too distant: */ + if (zip_match_length == zip_MIN_MATCH && + zip_strstart - zip_match_start > zip_TOO_FAR) { + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + zip_match_length--; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (zip_prev_length >= zip_MIN_MATCH && + zip_match_length <= zip_prev_length) { + var flush; // set if current block must be flushed + +// check_match(strstart - 1, prev_match, prev_length); + flush = zip_ct_tally(zip_strstart - 1 - zip_prev_match, + zip_prev_length - zip_MIN_MATCH); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. + */ + zip_lookahead -= zip_prev_length - 1; + zip_prev_length -= 2; + do { + zip_strstart++; + zip_INSERT_STRING(); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. If lookahead < MIN_MATCH + * these bytes are garbage, but it does not matter since the + * next lookahead bytes will always be emitted as literals. + */ + } while (--zip_prev_length != 0); + zip_match_available = 0; + zip_match_length = zip_MIN_MATCH - 1; + zip_strstart++; + if (flush) { + zip_flush_block(0); + zip_block_start = zip_strstart; + } + } else if (zip_match_available != 0) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + if (zip_ct_tally(0, zip_window[zip_strstart - 1] & 0xff)) { + zip_flush_block(0); + zip_block_start = zip_strstart; + } + zip_strstart++; + zip_lookahead--; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + zip_match_available = 1; + zip_strstart++; + zip_lookahead--; + } + + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + while (zip_lookahead < zip_MIN_LOOKAHEAD && !zip_eofile) + zip_fill_window(); + } +} + +function zip_init_deflate() { + if (zip_eofile) + return; + zip_bi_buf = 0; + zip_bi_valid = 0; + zip_ct_init(); + zip_lm_init(); + + zip_qhead = null; + zip_outcnt = 0; + zip_outoff = 0; + + if (zip_compr_level <= 3) { + zip_prev_length = zip_MIN_MATCH - 1; + zip_match_length = 0; + } else { + zip_match_length = zip_MIN_MATCH - 1; + zip_match_available = 0; + } + + zip_complete = false; +} + +/* ========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +function zip_deflate_internal(buff, off, buff_size) { + var n; + + if (!zip_initflag) { + zip_init_deflate(); + zip_initflag = true; + if (zip_lookahead == 0) { // empty + zip_complete = true; + return 0; + } + } + + if ((n = zip_qcopy(buff, off, buff_size)) == buff_size) + return buff_size; + + if (zip_complete) + return n; + + if (zip_compr_level <= 3) // optimized for speed + zip_deflate_fast(); + else + zip_deflate_better(); + if (zip_lookahead == 0) { + if (zip_match_available != 0) + zip_ct_tally(0, zip_window[zip_strstart - 1] & 0xff); + zip_flush_block(1); + zip_complete = true; + } + return n + zip_qcopy(buff, n + off, buff_size - n); +} + +function zip_qcopy(buff, off, buff_size) { + var n, i, j; + + n = 0; + while (zip_qhead != null && n < buff_size) { + i = buff_size - n; + if (i > zip_qhead.len) + i = zip_qhead.len; +// System.arraycopy(qhead.ptr, qhead.off, buff, off + n, i); + for (j = 0; j < i; j++) + buff[off + n + j] = zip_qhead.ptr[zip_qhead.off + j]; + + zip_qhead.off += i; + zip_qhead.len -= i; + n += i; + if (zip_qhead.len == 0) { + var p; + p = zip_qhead; + zip_qhead = zip_qhead.next; + zip_reuse_queue(p); + } + } + + if (n == buff_size) + return n; + + if (zip_outoff < zip_outcnt) { + i = buff_size - n; + if (i > zip_outcnt - zip_outoff) + i = zip_outcnt - zip_outoff; + // System.arraycopy(outbuf, outoff, buff, off + n, i); + for (j = 0; j < i; j++) + buff[off + n + j] = zip_outbuf[zip_outoff + j]; + zip_outoff += i; + n += i; + if (zip_outcnt == zip_outoff) + zip_outcnt = zip_outoff = 0; + } + return n; +} + +/* ========================================================================== + * Allocate the match buffer, initialize the various tables and save the + * location of the internal file attribute (ascii/binary) and method + * (DEFLATE/STORE). + */ +function zip_ct_init() { + var n; // iterates over tree elements + var bits; // bit counter + var length; // length value + var code; // code value + var dist; // distance index + + if (zip_static_dtree[0].dl != 0) return; // ct_init already called + + zip_l_desc.dyn_tree = zip_dyn_ltree; + zip_l_desc.static_tree = zip_static_ltree; + zip_l_desc.extra_bits = zip_extra_lbits; + zip_l_desc.extra_base = zip_LITERALS + 1; + zip_l_desc.elems = zip_L_CODES; + zip_l_desc.max_length = zip_MAX_BITS; + zip_l_desc.max_code = 0; + + zip_d_desc.dyn_tree = zip_dyn_dtree; + zip_d_desc.static_tree = zip_static_dtree; + zip_d_desc.extra_bits = zip_extra_dbits; + zip_d_desc.extra_base = 0; + zip_d_desc.elems = zip_D_CODES; + zip_d_desc.max_length = zip_MAX_BITS; + zip_d_desc.max_code = 0; + + zip_bl_desc.dyn_tree = zip_bl_tree; + zip_bl_desc.static_tree = null; + zip_bl_desc.extra_bits = zip_extra_blbits; + zip_bl_desc.extra_base = 0; + zip_bl_desc.elems = zip_BL_CODES; + zip_bl_desc.max_length = zip_MAX_BL_BITS; + zip_bl_desc.max_code = 0; + + // Initialize the mapping length (0..255) -> length code (0..28) + length = 0; + for (code = 0; code < zip_LENGTH_CODES - 1; code++) { + zip_base_length[code] = length; + for (n = 0; n < (1 << zip_extra_lbits[code]); n++) + zip_length_code[length++] = code; + } + // Assert (length == 256, "ct_init: length != 256"); + + /* Note that the length 255 (match length 258) can be represented + * in two different ways: code 284 + 5 bits or code 285, so we + * overwrite length_code[255] to use the best encoding: + */ + zip_length_code[length - 1] = code; + + /* Initialize the mapping dist (0..32K) -> dist code (0..29) */ + dist = 0; + for (code = 0; code < 16; code++) { + zip_base_dist[code] = dist; + for (n = 0; n < (1 << zip_extra_dbits[code]); n++) { + zip_dist_code[dist++] = code; + } + } + // Assert (dist == 256, "ct_init: dist != 256"); + dist >>= 7; // from now on, all distances are divided by 128 + for (; code < zip_D_CODES; code++) { + zip_base_dist[code] = dist << 7; + for (n = 0; n < (1 << (zip_extra_dbits[code] - 7)); n++) + zip_dist_code[256 + dist++] = code; + } + // Assert (dist == 256, "ct_init: 256+dist != 512"); + + // Construct the codes of the static literal tree + for (bits = 0; bits <= zip_MAX_BITS; bits++) + zip_bl_count[bits] = 0; + n = 0; + while (n <= 143) { + zip_static_ltree[n++].dl = 8; + zip_bl_count[8]++; + } + while (n <= 255) { + zip_static_ltree[n++].dl = 9; + zip_bl_count[9]++; + } + while (n <= 279) { + zip_static_ltree[n++].dl = 7; + zip_bl_count[7]++; + } + while (n <= 287) { + zip_static_ltree[n++].dl = 8; + zip_bl_count[8]++; + } + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + zip_gen_codes(zip_static_ltree, zip_L_CODES + 1); + + /* The static distance tree is trivial: */ + for (n = 0; n < zip_D_CODES; n++) { + zip_static_dtree[n].dl = 5; + zip_static_dtree[n].fc = zip_bi_reverse(n, 5); + } + + // Initialize the first block of the first file: + zip_init_block(); +} + +/* ========================================================================== + * Initialize a new block. + */ +function zip_init_block() { + var n; // iterates over tree elements + + // Initialize the trees. + for (n = 0; n < zip_L_CODES; n++) zip_dyn_ltree[n].fc = 0; + for (n = 0; n < zip_D_CODES; n++) zip_dyn_dtree[n].fc = 0; + for (n = 0; n < zip_BL_CODES; n++) zip_bl_tree[n].fc = 0; + + zip_dyn_ltree[zip_END_BLOCK].fc = 1; + zip_opt_len = zip_static_len = 0; + zip_last_lit = zip_last_dist = zip_last_flags = 0; + zip_flags = 0; + zip_flag_bit = 1; +} + +/* ========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +function zip_pqdownheap( + tree, // the tree to restore + k) { // node to move down + var v = zip_heap[k]; + var j = k << 1; // left son of k + + while (j <= zip_heap_len) { + // Set j to the smallest of the two sons: + if (j < zip_heap_len && + zip_SMALLER(tree, zip_heap[j + 1], zip_heap[j])) + j++; + + // Exit if v is smaller than both sons + if (zip_SMALLER(tree, v, zip_heap[j])) + break; + + // Exchange v with the smallest son + zip_heap[k] = zip_heap[j]; + k = j; + + // And continue down the tree, setting j to the left son of k + j <<= 1; + } + zip_heap[k] = v; +} + +/* ========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +function zip_gen_bitlen(desc) { // the tree descriptor + var tree = desc.dyn_tree; + var extra = desc.extra_bits; + var base = desc.extra_base; + var max_code = desc.max_code; + var max_length = desc.max_length; + var stree = desc.static_tree; + var h; // heap index + var n, m; // iterate over the tree elements + var bits; // bit length + var xbits; // extra bits + var f; // frequency + var overflow = 0; // number of elements with bit length too large + + for (bits = 0; bits <= zip_MAX_BITS; bits++) + zip_bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[zip_heap[zip_heap_max]].dl = 0; // root of the heap + + for (h = zip_heap_max + 1; h < zip_HEAP_SIZE; h++) { + n = zip_heap[h]; + bits = tree[tree[n].dl].dl + 1; + if (bits > max_length) { + bits = max_length; + overflow++; + } + tree[n].dl = bits; + // We overwrite tree[n].dl which is no longer needed + + if (n > max_code) + continue; // not a leaf node + + zip_bl_count[bits]++; + xbits = 0; + if (n >= base) + xbits = extra[n - base]; + f = tree[n].fc; + zip_opt_len += f * (bits + xbits); + if (stree != null) + zip_static_len += f * (stree[n].dl + xbits); + } + if (overflow == 0) + return; + + // This happens for example on obj2 and pic of the Calgary corpus + + // Find the first bit length which could increase: + do { + bits = max_length - 1; + while (zip_bl_count[bits] == 0) + bits--; + zip_bl_count[bits]--; // move one leaf down the tree + zip_bl_count[bits + 1] += 2; // move one overflow item as its brother + zip_bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = zip_bl_count[bits]; + while (n != 0) { + m = zip_heap[--h]; + if (m > max_code) + continue; + if (tree[m].dl != bits) { + zip_opt_len += (bits - tree[m].dl) * tree[m].fc; + tree[m].fc = bits; + } + n--; + } + } +} + +/* ========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +function zip_gen_codes(tree, // the tree to decorate + max_code) { // largest code with non zero frequency + var next_code = new Array(zip_MAX_BITS + 1); // next code value for each bit length + var code = 0; // running code value + var bits; // bit index + var n; // code index + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= zip_MAX_BITS; bits++) { + code = ((code + zip_bl_count[bits - 1]) << 1); + next_code[bits] = code; + } + + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ +// Assert (code + encoder->bl_count[MAX_BITS]-1 == (1<> 1; n >= 1; n--) + zip_pqdownheap(tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + do { + n = zip_heap[zip_SMALLEST]; + zip_heap[zip_SMALLEST] = zip_heap[zip_heap_len--]; + zip_pqdownheap(tree, zip_SMALLEST); + + m = zip_heap[zip_SMALLEST]; // m = node of next least frequency + + // keep the nodes sorted by frequency + zip_heap[--zip_heap_max] = n; + zip_heap[--zip_heap_max] = m; + + // Create a new node father of n and m + tree[node].fc = tree[n].fc + tree[m].fc; +// depth[node] = (char)(MAX(depth[n], depth[m]) + 1); + if (zip_depth[n] > zip_depth[m] + 1) + zip_depth[node] = zip_depth[n]; + else + zip_depth[node] = zip_depth[m] + 1; + tree[n].dl = tree[m].dl = node; + + // and insert the new node in the heap + zip_heap[zip_SMALLEST] = node++; + zip_pqdownheap(tree, zip_SMALLEST); + + } while (zip_heap_len >= 2); + + zip_heap[--zip_heap_max] = zip_heap[zip_SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + zip_gen_bitlen(desc); + + // The field len is now set, we can generate the bit codes + zip_gen_codes(tree, max_code); +} + +/* ========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. Updates opt_len to take into account the repeat + * counts. (The contribution of the bit length codes will be added later + * during the construction of bl_tree.) + */ +function zip_scan_tree(tree,// the tree to be scanned + max_code) { // and its largest code of non zero frequency + var n; // iterates over all tree elements + var prevlen = -1; // last emitted length + var curlen; // length of current code + var nextlen = tree[0].dl; // length of next code + var count = 0; // repeat count of the current code + var max_count = 7; // max repeat count + var min_count = 4; // min repeat count + + if (nextlen == 0) { + max_count = 138; + min_count = 3; + } + tree[max_code + 1].dl = 0xffff; // guard + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; + nextlen = tree[n + 1].dl; + if (++count < max_count && curlen == nextlen) + continue; + else if (count < min_count) + zip_bl_tree[curlen].fc += count; + else if (curlen != 0) { + if (curlen != prevlen) + zip_bl_tree[curlen].fc++; + zip_bl_tree[zip_REP_3_6].fc++; + } else if (count <= 10) + zip_bl_tree[zip_REPZ_3_10].fc++; + else + zip_bl_tree[zip_REPZ_11_138].fc++; + count = 0; + prevlen = curlen; + if (nextlen == 0) { + max_count = 138; + min_count = 3; + } else if (curlen == nextlen) { + max_count = 6; + min_count = 3; + } else { + max_count = 7; + min_count = 4; + } + } +} + +/* ========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +function zip_send_tree(tree, // the tree to be scanned + max_code) { // and its largest code of non zero frequency + var n; // iterates over all tree elements + var prevlen = -1; // last emitted length + var curlen; // length of current code + var nextlen = tree[0].dl; // length of next code + var count = 0; // repeat count of the current code + var max_count = 7; // max repeat count + var min_count = 4; // min repeat count + + /* tree[max_code+1].dl = -1; */ /* guard already set */ + if (nextlen == 0) { + max_count = 138; + min_count = 3; + } + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; + nextlen = tree[n + 1].dl; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { + zip_SEND_CODE(curlen, zip_bl_tree); + } while (--count != 0); + } else if (curlen != 0) { + if (curlen != prevlen) { + zip_SEND_CODE(curlen, zip_bl_tree); + count--; + } + // Assert(count >= 3 && count <= 6, " 3_6?"); + zip_SEND_CODE(zip_REP_3_6, zip_bl_tree); + zip_send_bits(count - 3, 2); + } else if (count <= 10) { + zip_SEND_CODE(zip_REPZ_3_10, zip_bl_tree); + zip_send_bits(count - 3, 3); + } else { + zip_SEND_CODE(zip_REPZ_11_138, zip_bl_tree); + zip_send_bits(count - 11, 7); + } + count = 0; + prevlen = curlen; + if (nextlen == 0) { + max_count = 138; + min_count = 3; + } else if (curlen == nextlen) { + max_count = 6; + min_count = 3; + } else { + max_count = 7; + min_count = 4; + } + } +} + +/* ========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +function zip_build_bl_tree() { + var max_blindex; // index of last bit length code of non zero freq + + // Determine the bit length frequencies for literal and distance trees + zip_scan_tree(zip_dyn_ltree, zip_l_desc.max_code); + zip_scan_tree(zip_dyn_dtree, zip_d_desc.max_code); + + // Build the bit length tree: + zip_build_tree(zip_bl_desc); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = zip_BL_CODES - 1; max_blindex >= 3; max_blindex--) { + if (zip_bl_tree[zip_bl_order[max_blindex]].dl != 0) break; + } + /* Update opt_len to include the bit length tree and counts */ + zip_opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4; +// Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", +// encoder->opt_len, encoder->static_len)); + + return max_blindex; +} + +/* ========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +function zip_send_all_trees(lcodes, dcodes, blcodes) { // number of codes for each tree + var rank; // index in bl_order + +// Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); +// Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, +// "too many codes"); +// Tracev((stderr, "\nbl counts: ")); + zip_send_bits(lcodes - 257, 5); // not +255 as stated in appnote.txt + zip_send_bits(dcodes - 1, 5); + zip_send_bits(blcodes - 4, 4); // not -3 as stated in appnote.txt + for (rank = 0; rank < blcodes; rank++) { +// Tracev((stderr, "\nbl code %2d ", bl_order[rank])); + zip_send_bits(zip_bl_tree[zip_bl_order[rank]].dl, 3); + } + + // send the literal tree + zip_send_tree(zip_dyn_ltree, lcodes - 1); + + // send the distance tree + zip_send_tree(zip_dyn_dtree, dcodes - 1); +} + +/* ========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. + */ +function zip_flush_block(eof) { // true if this is the last block for a file + var opt_lenb, static_lenb; // opt_len and static_len in bytes + var max_blindex; // index of last bit length code of non zero freq + var stored_len; // length of input block + + stored_len = zip_strstart - zip_block_start; + zip_flag_buf[zip_last_flags] = zip_flags; // Save the flags for the last 8 items + + // Construct the literal and distance trees + zip_build_tree(zip_l_desc); +// Tracev((stderr, "\nlit data: dyn %ld, stat %ld", +// encoder->opt_len, encoder->static_len)); + + zip_build_tree(zip_d_desc); +// Tracev((stderr, "\ndist data: dyn %ld, stat %ld", +// encoder->opt_len, encoder->static_len)); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = zip_build_bl_tree(); + + // Determine the best encoding. Compute first the block length in bytes + opt_lenb = (zip_opt_len + 3 + 7) >> 3; + static_lenb = (zip_static_len + 3 + 7) >> 3; + +// Trace((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u dist %u ", +// opt_lenb, encoder->opt_len, +// static_lenb, encoder->static_len, stored_len, +// encoder->last_lit, encoder->last_dist)); + + if (static_lenb <= opt_lenb) + opt_lenb = static_lenb; + if (stored_len + 4 <= opt_lenb // 4: two words for the lengths + && zip_block_start >= 0) { + var i; + + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + zip_send_bits((zip_STORED_BLOCK << 1) + eof, 3); /* send block type */ + zip_bi_windup(); /* align on byte boundary */ + zip_put_short(stored_len); + zip_put_short(~stored_len); + + // copy block + /* + p = &window[block_start]; + for(i = 0; i < stored_len; i++) + put_byte(p[i]); + */ + for (i = 0; i < stored_len; i++) + zip_put_byte(zip_window[zip_block_start + i]); + + } else if (static_lenb == opt_lenb) { + zip_send_bits((zip_STATIC_TREES << 1) + eof, 3); + zip_compress_block(zip_static_ltree, zip_static_dtree); + } else { + zip_send_bits((zip_DYN_TREES << 1) + eof, 3); + zip_send_all_trees(zip_l_desc.max_code + 1, + zip_d_desc.max_code + 1, + max_blindex + 1); + zip_compress_block(zip_dyn_ltree, zip_dyn_dtree); + } + + zip_init_block(); + + if (eof != 0) + zip_bi_windup(); +} + +/* ========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +function zip_ct_tally( + dist, // distance of matched string + lc) { // match length-MIN_MATCH or unmatched char (if dist==0) + zip_l_buf[zip_last_lit++] = lc; + if (dist == 0) { + // lc is the unmatched char + zip_dyn_ltree[lc].fc++; + } else { + // Here, lc is the match length - MIN_MATCH + dist--; // dist = match distance - 1 +// Assert((ush)dist < (ush)MAX_DIST && +// (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && +// (ush)D_CODE(dist) < (ush)D_CODES, "ct_tally: bad match"); + + zip_dyn_ltree[zip_length_code[lc] + zip_LITERALS + 1].fc++; + zip_dyn_dtree[zip_D_CODE(dist)].fc++; + + zip_d_buf[zip_last_dist++] = dist; + zip_flags |= zip_flag_bit; + } + zip_flag_bit <<= 1; + + // Output the flags if they fill a byte + if ((zip_last_lit & 7) == 0) { + zip_flag_buf[zip_last_flags++] = zip_flags; + zip_flags = 0; + zip_flag_bit = 1; + } + // Try to guess if it is profitable to stop the current block here + if (zip_compr_level > 2 && (zip_last_lit & 0xfff) == 0) { + // Compute an upper bound for the compressed length + var out_length = zip_last_lit * 8; + var in_length = zip_strstart - zip_block_start; + var dcode; + + for (dcode = 0; dcode < zip_D_CODES; dcode++) { + out_length += zip_dyn_dtree[dcode].fc * (5 + zip_extra_dbits[dcode]); + } + out_length >>= 3; +// Trace((stderr,"\nlast_lit %u, last_dist %u, in %ld, out ~%ld(%ld%%) ", +// encoder->last_lit, encoder->last_dist, in_length, out_length, +// 100L - out_length*100L/in_length)); + if (zip_last_dist < parseInt(zip_last_lit / 2) && + out_length < parseInt(in_length / 2)) + return true; + } + return (zip_last_lit == zip_LIT_BUFSIZE - 1 || + zip_last_dist == zip_DIST_BUFSIZE); + /* We avoid equality with LIT_BUFSIZE because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +/* ========================================================================== + * Send the block data compressed using the given Huffman trees + */ +function zip_compress_block( + ltree, // literal tree + dtree) { // distance tree + var dist; // distance of matched string + var lc; // match length or unmatched char (if dist == 0) + var lx = 0; // running index in l_buf + var dx = 0; // running index in d_buf + var fx = 0; // running index in flag_buf + var flag = 0; // current flags + var code; // the code to send + var extra; // number of extra bits to send + + if (zip_last_lit != 0) do { + if ((lx & 7) == 0) + flag = zip_flag_buf[fx++]; + lc = zip_l_buf[lx++] & 0xff; + if ((flag & 1) == 0) { + zip_SEND_CODE(lc, ltree); /* send a literal byte */ +// Tracecv(isgraph(lc), (stderr," '%c' ", lc)); + } else { + // Here, lc is the match length - MIN_MATCH + code = zip_length_code[lc]; + zip_SEND_CODE(code + zip_LITERALS + 1, ltree); // send the length code + extra = zip_extra_lbits[code]; + if (extra != 0) { + lc -= zip_base_length[code]; + zip_send_bits(lc, extra); // send the extra length bits + } + dist = zip_d_buf[dx++]; + // Here, dist is the match distance - 1 + code = zip_D_CODE(dist); +// Assert (code < D_CODES, "bad d_code"); + + zip_SEND_CODE(code, dtree); // send the distance code + extra = zip_extra_dbits[code]; + if (extra != 0) { + dist -= zip_base_dist[code]; + zip_send_bits(dist, extra); // send the extra distance bits + } + } // literal or match pair ? + flag >>= 1; + } while (lx < zip_last_lit); + + zip_SEND_CODE(zip_END_BLOCK, ltree); +} + +/* ========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +var zip_Buf_size = 16; // bit size of bi_buf +function zip_send_bits( + value, // value to send + length) { // number of bits + /* If not enough room in bi_buf, use (valid) bits from bi_buf and + * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) + * unused bits in value. + */ + if (zip_bi_valid > zip_Buf_size - length) { + zip_bi_buf |= (value << zip_bi_valid); + zip_put_short(zip_bi_buf); + zip_bi_buf = (value >> (zip_Buf_size - zip_bi_valid)); + zip_bi_valid += length - zip_Buf_size; + } else { + zip_bi_buf |= value << zip_bi_valid; + zip_bi_valid += length; + } +} + +/* ========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +function zip_bi_reverse( + code, // the value to invert + len) { // its bit length + var res = 0; + do { + res |= code & 1; + code >>= 1; + res <<= 1; + } while (--len > 0); + return res >> 1; +} + +/* ========================================================================== + * Write out any remaining bits in an incomplete byte. + */ +function zip_bi_windup() { + if (zip_bi_valid > 8) { + zip_put_short(zip_bi_buf); + } else if (zip_bi_valid > 0) { + zip_put_byte(zip_bi_buf); + } + zip_bi_buf = 0; + zip_bi_valid = 0; +} + +function zip_qoutbuf() { + if (zip_outcnt != 0) { + var q, i; + q = zip_new_queue(); + if (zip_qhead == null) + zip_qhead = zip_qtail = q; + else + zip_qtail = zip_qtail.next = q; + q.len = zip_outcnt - zip_outoff; +// System.arraycopy(zip_outbuf, zip_outoff, q.ptr, 0, q.len); + for (i = 0; i < q.len; i++) + q.ptr[i] = zip_outbuf[zip_outoff + i]; + zip_outcnt = zip_outoff = 0; + } +} + +function zip_deflate(str, level) { + var out, buff; + var i, j; + + zip_deflate_data = str; + zip_deflate_pos = 0; + if (typeof level == 'undefined') + level = zip_DEFAULT_LEVEL; + zip_deflate_start(level); + + buff = new Array(1024); + out = ''; + while ((i = zip_deflate_internal(buff, 0, buff.length)) > 0) { + for (j = 0; j < i; j++) + out += String.fromCharCode(buff[j]); + } + zip_deflate_data = null; // G.C. + return out; +} + +GID = function (id) { + return document.getElementById(id) +}; + +function encode64(data) { + r = ''; + for (i = 0; i < data.length; i += 3) { + if (i + 2 == data.length) { + r += append3bytes(data.charCodeAt(i), data.charCodeAt(i + 1), 0); + } else if (i + 1 == data.length) { + r += append3bytes(data.charCodeAt(i), 0, 0); + } else { + r += append3bytes(data.charCodeAt(i), data.charCodeAt(i + 1), data.charCodeAt(i + 2)); + } + } + return r; +} + +function append3bytes(b1, b2, b3) { + c1 = b1 >> 2; + c2 = ((b1 & 0x3) << 4) | (b2 >> 4); + c3 = ((b2 & 0xF) << 2) | (b3 >> 6); + c4 = b3 & 0x3F; + r = ''; + r += encode6bit(c1 & 0x3F); + r += encode6bit(c2 & 0x3F); + r += encode6bit(c3 & 0x3F); + r += encode6bit(c4 & 0x3F); + return r; +} + +function encode6bit(b) { + if (b < 10) { + return String.fromCharCode(48 + b); + } + b -= 10; + if (b < 26) { + return String.fromCharCode(65 + b); + } + b -= 26; + if (b < 26) { + return String.fromCharCode(97 + b); + } + b -= 26; + if (b == 0) { + return '-'; + } + if (b == 1) { + return '_'; + } + return '?'; +} \ No newline at end of file diff --git a/src/renderer.js b/src/renderer.js index 8944f9f..644da6b 100644 --- a/src/renderer.js +++ b/src/renderer.js @@ -1,4 +1,5 @@ const fs = require('fs'); +const path = require('path'); const showdown = require('showdown'); module.exports = (config) => { @@ -31,6 +32,25 @@ module.exports = (config) => { cb(data); }; + if (config['modules']['plantuml']) { + require('./script_loader')(path.join(__dirname, 'lib', 'plantuml_synchro.js')); + } + + const renderPlantUML = (data, cb) => { + if (!config['modules']['plantuml']) + return cb(data); + const umlRegex = /@startuml\r?\n((?:(?!@enduml)[\s\S])*)\r?\n@enduml/m; + let match; + while ((match = umlRegex.exec(data))) { + const code = match[1].trim(); + const s = unescape(encodeURIComponent(code)); // jshint ignore:line + const compressed = global['zip_deflate'](s); + const url = `http://www.plantuml.com/plantuml/${config['plantuml']['output_format']}/${encode64(compressed)}`;// jshint ignore:line + data = data.slice(0, match.index) + `generated PlantUML diagram` + data.slice(match.index + match[0].length); + } + cb(data); + }; + let mjAPI; if (config['modules']['mathjax']) { mjAPI = require('mathjax-node'); @@ -81,16 +101,19 @@ module.exports = (config) => { return { renderShowDown: config['test'] ? renderShowDown : undefined, renderPrism: config['test'] ? renderPrism : undefined, + renderPlantUML: config['test'] ? renderPlantUML : undefined, renderMathJax: config['test'] ? renderMathJax : undefined, render: (file, cb) => { fs.readFile(file, {encoding: 'UTF-8'}, (err, data) => { if (err) return cb(err); - renderPrism(data, (data2) => { - renderMathJax(data2, (data3) => { - renderShowDown(data3, (html) => { - cb(null, html); + renderPrism(data, (data) => { + renderPlantUML(data, (data) => { + renderMathJax(data, (data) => { + renderShowDown(data, (html) => { + cb(null, html); + }); }); }); }); diff --git a/src/script_loader.js b/src/script_loader.js new file mode 100644 index 0000000..8668ff2 --- /dev/null +++ b/src/script_loader.js @@ -0,0 +1,10 @@ +const fs = require('fs'); + +/** + * Import client-side script into the "global" var + * @param scriptPath + */ +module.exports = (scriptPath) => { + eval.call(global, fs.readFileSync(scriptPath, {encoding: 'UTF-8'})); +}; + diff --git a/test/renderer.test.js b/test/renderer.test.js index f9e8279..d2cc29d 100644 --- a/test/renderer.test.js +++ b/test/renderer.test.js @@ -10,7 +10,8 @@ const config = { 'test': true, 'modules': { 'prism': true, - 'mathjax': true + 'mathjax': true, + 'plantuml': true }, 'showdown': { 'simplifiedAutoLink': true, @@ -19,6 +20,9 @@ const config = { 'mathjax': { 'output_format': 'html', 'speak_text': false + }, + 'plantuml': { + 'output_format': 'svg' } }; @@ -27,6 +31,7 @@ const renderer = require('../src/renderer')(config); beforeEach(() => { config['modules']['prism'] = true; config['modules']['mathjax'] = true; + config['modules']['plantuml'] = true; utils.deleteFolderSync(dataDir); fs.mkdirSync(dataDir); }); @@ -91,6 +96,31 @@ describe('Test Prism', () => { }); }); +describe('Test PlantUML', () => { + test('no plantuml', (done) => { + config['modules']['plantuml'] = false; + renderer.renderPlantUML('@startuml\nBob -> Alice : hello\n@enduml', (data) => { + expect(data).toBe('@startuml\nBob -> Alice : hello\n@enduml'); + done(); + }); + }); + + test('plantuml correct', (done) => { + renderer.renderPlantUML('@startuml\nBob -> Alice : hello\n@enduml', (data) => { + expect(data).toBe('generated PlantUML diagram'); + done(); + }); + }); + + test('plantuml multiple uml', (done) => { + renderer.renderPlantUML('@startuml\nBob -> Alice : hello\n@enduml\n@startuml\nBob -> Alice : hello\n@enduml', (data) => { + expect(data).toBe('generated PlantUML diagram\ngenerated PlantUML diagram'); + done(); + }); + }); +}); + + describe('Test MathJax', () => { test('no mathjax', (done) => { config['modules']['mathjax'] = false; diff --git a/test/script_loader.test.js b/test/script_loader.test.js new file mode 100644 index 0000000..bd4ef10 --- /dev/null +++ b/test/script_loader.test.js @@ -0,0 +1,51 @@ +/* jshint -W117 */ +const fs = require('fs'); +const path = require('path'); +const utils = require('./test_utils'); + +const dataDir = 'test_data'; + +beforeEach(() => { + utils.deleteFolderSync(dataDir); + fs.mkdirSync(dataDir); +}); + +afterAll(() => { + if (fs.existsSync(dataDir)) { + utils.deleteFolderSync(dataDir); + } +}); + +test('load 1 script', () => { + const file = path.join(dataDir, 'test.js'); + fs.writeFileSync(file, ` + var a = 5; + function b(){ + return a; + }`); + require('../src/script_loader')(file); + expect(global['b']).toBeDefined(); + expect(global['b']()).toBe(5); +}); + +test('load 2 script', () => { + const file1 = path.join(dataDir, 'test.js'); + fs.writeFileSync(file1, ` + var a = 5; + function b(){ + return a; + }`); + const file2 = path.join(dataDir, 'test2.js'); + fs.writeFileSync(file2, ` + var a = 9; + function b(){ + return a; + }`); + require('../src/script_loader')(file1); + expect(global['b']).toBeDefined(); + expect(global['b']()).toBe(5); + + require('../src/script_loader.js')(file2); + expect(global['b']).toBeDefined(); + expect(global['b']()).toBe(9); +}); \ No newline at end of file From bd8385ea6078c51d670943a6b6a856f51800fbda Mon Sep 17 00:00:00 2001 From: Klemek Date: Sun, 23 Jun 2019 13:51:07 +0200 Subject: [PATCH 065/120] [skip CI] updated version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7680f75..a741eba 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitblog.md", - "version": "1.0.3", + "version": "1.1", "description": "A static blog using Markdown pulled from your git repository.", "main": "src/server.js", "dependencies": { From 14cd1436c3baf44391663f50e5b6c05956f01d92 Mon Sep 17 00:00:00 2001 From: Klemek Date: Sun, 23 Jun 2019 13:56:31 +0200 Subject: [PATCH 066/120] Better without infinite loop --- package.json | 2 +- src/renderer.js | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index a741eba..427cf14 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitblog.md", - "version": "1.1", + "version": "1.1.1", "description": "A static blog using Markdown pulled from your git repository.", "main": "src/server.js", "dependencies": { diff --git a/src/renderer.js b/src/renderer.js index 644da6b..6094f08 100644 --- a/src/renderer.js +++ b/src/renderer.js @@ -22,12 +22,8 @@ module.exports = (config) => { while ((match = codeRegex.exec(data))) { const lang = match[1].trim(); const code = match[2].trim(); - try { - const block = Prism.highlight(code, Prism.languages[lang] || Prism.languages.autoit, lang); - data = data.slice(0, match.index) + `
` + block + '
' + data.slice(match.index + match[0].length); - } catch (err) { - console.error(err); - } + const block = Prism.highlight(code, Prism.languages[lang] || Prism.languages.autoit, lang); + data = data.slice(0, match.index) + `
` + block + '
' + data.slice(match.index + match[0].length); } cb(data); }; From 55e258e0933e723de65b0da893840e41d15cd080 Mon Sep 17 00:00:00 2001 From: Klemek Date: Sun, 23 Jun 2019 14:14:35 +0200 Subject: [PATCH 067/120] Fixed Express.static mime type --- package.json | 2 +- src/app.js | 2 +- test/app.test.js | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 427cf14..9a2eb96 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitblog.md", - "version": "1.1.1", + "version": "1.1.2", "description": "A static blog using Markdown pulled from your git repository.", "main": "src/server.js", "dependencies": { diff --git a/src/app.js b/src/app.js index a59a44d..2dd6093 100644 --- a/src/app.js +++ b/src/app.js @@ -225,7 +225,7 @@ module.exports = (config) => { }); // serve all static files via get - app.get('*', express.static(config['data_dir'])); + app.get('*', express.static(path.join(__dirname, '..', config['data_dir']))); // catch express.static errors (mostly not found) by displaying 404 app.get('*', (req, res) => { showError(req.path, 404, res); diff --git a/test/app.test.js b/test/app.test.js index da9f2c7..9db6f98 100644 --- a/test/app.test.js +++ b/test/app.test.js @@ -385,9 +385,10 @@ describe('Test static files', () => { }); }); test('200 valid file', (done) => { - fs.writeFileSync(path.join(dataDir, 'somefile.txt'), 'filecontent'); - request(app).get('/somefile.txt').then((response) => { + fs.writeFileSync(path.join(dataDir, 'somefile.css'), 'filecontent'); + request(app).get('/somefile.css').then((response) => { expect(response.statusCode).toBe(200); + expect(response.type).toBe('text/css'); expect(response.text).toBe('filecontent'); done(); }); From f2bd0ec10e7e6edb11bd97fadbd9d986a1ed02fb Mon Sep 17 00:00:00 2001 From: Klemek Date: Sun, 23 Jun 2019 14:44:32 +0200 Subject: [PATCH 068/120] Fixed config merge --- package.json | 2 +- src/config.js | 4 ++++ test/config.test.js | 14 ++++++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 9a2eb96..2dac4ab 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitblog.md", - "version": "1.1.2", + "version": "1.1.3", "description": "A static blog using Markdown pulled from your git repository.", "main": "src/server.js", "dependencies": { diff --git a/src/config.js b/src/config.js index dca7338..23b532b 100644 --- a/src/config.js +++ b/src/config.js @@ -10,6 +10,10 @@ const fs = require('fs'); const merge = (ref, src) => { if (typeof ref !== typeof src) { return ref; + } else if (ref.length && !src.length) { + return ref; + } else if (ref.length && src.length) { + return src; } else if (typeof ref === 'object') { const out = {}; Object.keys(ref).forEach((key) => out[key] = merge(ref[key], src[key])); diff --git a/test/config.test.js b/test/config.test.js index 74bf82b..04b1bcf 100644 --- a/test/config.test.js +++ b/test/config.test.js @@ -64,4 +64,18 @@ test('wrong config fixed', () => { expect(config).toBeDefined(); expect(config['node_port']).toBe(3000); expect(config['data_dir']).toBe('data2'); +}); + +test('array parsing', () => { + fs.writeFileSync(configFile, '{"home":{"hidden":["item1","item2"]}}'); + const config = require('../src/config')(); + expect(config).toBeDefined(); + expect(config['home']['hidden']).toEqual(['item1', 'item2']); +}); + +test('array fix', () => { + fs.writeFileSync(configFile, '{"home":{"hidden":{}}}'); + const config = require('../src/config')(); + expect(config).toBeDefined(); + expect(config['home']['hidden']).toEqual(['.ejs']); }); \ No newline at end of file From 989bcdf1304868edf33099fecb3d3873f3d32622 Mon Sep 17 00:00:00 2001 From: Klemek Date: Sun, 23 Jun 2019 15:02:33 +0200 Subject: [PATCH 069/120] Pages metadata by default --- package.json | 2 +- sample_data/home/index.ejs | 14 +++++++++++--- sample_data/home/template.ejs | 14 +++++++++++++- src/app.js | 12 ++++++++++++ src/config.default.json | 2 ++ 5 files changed, 39 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 2dac4ab..81423c9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitblog.md", - "version": "1.1.3", + "version": "1.1.4", "description": "A static blog using Markdown pulled from your git repository.", "main": "src/server.js", "dependencies": { diff --git a/sample_data/home/index.ejs b/sample_data/home/index.ejs index 4c48df3..3813e90 100644 --- a/sample_data/home/index.ejs +++ b/sample_data/home/index.ejs @@ -2,13 +2,21 @@ - GitBlog.md - Home + <%= info.title %> - Home + + + <%= `` %> + <%= `` %> + <%= `` %> + <%= `` %> + <%= `` %> +
-

GitBlog.md

- A static blog using Markdown pulled from your git repository +

<%= info.title %>

+ <%= info.description %>

Articles in this blog :

<% articles.forEach((article) => { %>
diff --git a/sample_data/home/template.ejs b/sample_data/home/template.ejs index 6acc674..872322c 100644 --- a/sample_data/home/template.ejs +++ b/sample_data/home/template.ejs @@ -2,7 +2,19 @@ - GitBlog.md - <%= article.title %> + <%= info.title %> - <%= article.title %> + + + <%= `` %> + <%= `` %> + <%= `` %> + <%= `` %> + <%= `` %> + <% if (article.thumbnail) { %> + <%= `` %> + <%= `` %> + <% } %> + diff --git a/src/app.js b/src/app.js index 2dd6093..e0bb3cb 100644 --- a/src/app.js +++ b/src/app.js @@ -36,6 +36,7 @@ module.exports = (config) => { const articles = {}; let lastRSS = ''; + let host; /** * Fetch articles from the data folder and send success as a response @@ -73,6 +74,9 @@ module.exports = (config) => { */ const render = (res, vPath, data, code = 200) => { data.info = { + title: config['home']['title'], + description: config['home']['description'], + host: host, version: pjson.version }; res.render(vPath, data, (err, html) => { @@ -100,6 +104,14 @@ module.exports = (config) => { }); }; + app.use((req, res, next) => { + if (!host) { + host = 'http://' + req.headers.host; + console.log(cons.ok, 'Currently hosted on ' + host); + } + next(); + }); + //log request at result end app.use((req, res, next) => { if (config['access_log']) { diff --git a/src/config.default.json b/src/config.default.json index d5e5518..687ef07 100644 --- a/src/config.default.json +++ b/src/config.default.json @@ -12,6 +12,8 @@ "plantuml": true }, "home": { + "title": "GitBlog.md", + "description": "A static blog using Markdown pulled from your git repository", "index": "index.ejs", "error": "error.ejs", "hidden": [ From fa6d91db20a7bc1df8f802ae07005757a6b9eebe Mon Sep 17 00:00:00 2001 From: Klemek Date: Sun, 23 Jun 2019 15:04:15 +0200 Subject: [PATCH 070/120] updated README.md --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 2c256c1..8f70641 100644 --- a/README.md +++ b/README.md @@ -227,6 +227,12 @@ Any URL like `/year/month/day/anything/` will redirect to this article (and link * `plantuml` (default: true) activate PlantUML diagram rendering * `home` + * `title` (default: GitBlog.md) + the title of your blog, **strongly advised to be changed** + given to the template to complete page title and metadata + * `description` (default: A static blog using Markdown pulled from your git repository) + the description of your blog, **strongly advised to be changed** + given to the template to complete page title and metadata * `index` (default: index.ejs) the name of the home page template on the data directory it will receive `articles`, a list of articles for rendering From 576948acee3251e4554c20176379c85831eee133 Mon Sep 17 00:00:00 2001 From: Klemek Date: Sun, 23 Jun 2019 15:05:06 +0200 Subject: [PATCH 071/120] ViewPort property --- sample_data/home/error.ejs | 3 +++ sample_data/home/index.ejs | 2 ++ sample_data/home/template.ejs | 2 ++ 3 files changed, 7 insertions(+) diff --git a/sample_data/home/error.ejs b/sample_data/home/error.ejs index 9593025..c348a4b 100644 --- a/sample_data/home/error.ejs +++ b/sample_data/home/error.ejs @@ -3,6 +3,9 @@ Error <%= error %> + + + diff --git a/sample_data/home/index.ejs b/sample_data/home/index.ejs index 3813e90..c429cda 100644 --- a/sample_data/home/index.ejs +++ b/sample_data/home/index.ejs @@ -11,6 +11,8 @@ <%= `` %> <%= `` %> + + diff --git a/sample_data/home/template.ejs b/sample_data/home/template.ejs index 872322c..d1a3be3 100644 --- a/sample_data/home/template.ejs +++ b/sample_data/home/template.ejs @@ -15,6 +15,8 @@ <%= `` %> <% } %> + + From 847d228c0a3e62db320936338838ba907f6ac8c9 Mon Sep 17 00:00:00 2001 From: Klemek Date: Sun, 23 Jun 2019 15:06:15 +0200 Subject: [PATCH 072/120] Updated templates --- sample_data/home/error.ejs | 9 ++++++++- sample_data/home/index.ejs | 4 ++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/sample_data/home/error.ejs b/sample_data/home/error.ejs index c348a4b..b8ccbdc 100644 --- a/sample_data/home/error.ejs +++ b/sample_data/home/error.ejs @@ -2,7 +2,14 @@ - Error <%= error %> + <%= info.title %> - Error <%= error %> + + + <%= `` %> + <%= `` %> + <%= `` %> + <%= `` %> + <%= `` %> diff --git a/sample_data/home/index.ejs b/sample_data/home/index.ejs index c429cda..67c0afb 100644 --- a/sample_data/home/index.ejs +++ b/sample_data/home/index.ejs @@ -5,8 +5,8 @@ <%= info.title %> - Home - <%= `` %> - <%= `` %> + <%= `` %> + <%= `` %> <%= `` %> <%= `` %> <%= `` %> From 1806d60ca7df065cc08ddd52e6ff77c42a317d03 Mon Sep 17 00:00:00 2001 From: Klemek Date: Sun, 23 Jun 2019 15:14:47 +0200 Subject: [PATCH 073/120] Fixed meta tags being wrong --- package.json | 2 +- sample_data/home/error.ejs | 10 +++++----- sample_data/home/index.ejs | 10 +++++----- sample_data/home/template.ejs | 14 +++++++------- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/package.json b/package.json index 81423c9..8e65711 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitblog.md", - "version": "1.1.4", + "version": "1.1.5", "description": "A static blog using Markdown pulled from your git repository.", "main": "src/server.js", "dependencies": { diff --git a/sample_data/home/error.ejs b/sample_data/home/error.ejs index b8ccbdc..c6c5597 100644 --- a/sample_data/home/error.ejs +++ b/sample_data/home/error.ejs @@ -5,11 +5,11 @@ <%= info.title %> - Error <%= error %> - <%= `` %> - <%= `` %> - <%= `` %> - <%= `` %> - <%= `` %> + <%- `` %> + <%- `` %> + <%- `` %> + <%- `` %> + <%- `` %> diff --git a/sample_data/home/index.ejs b/sample_data/home/index.ejs index 67c0afb..e91b227 100644 --- a/sample_data/home/index.ejs +++ b/sample_data/home/index.ejs @@ -5,11 +5,11 @@ <%= info.title %> - Home - <%= `` %> - <%= `` %> - <%= `` %> - <%= `` %> - <%= `` %> + <%- `` %> + <%- `` %> + <%- `` %> + <%- `` %> + <%- `` %> diff --git a/sample_data/home/template.ejs b/sample_data/home/template.ejs index d1a3be3..5855270 100644 --- a/sample_data/home/template.ejs +++ b/sample_data/home/template.ejs @@ -5,14 +5,14 @@ <%= info.title %> - <%= article.title %> - <%= `` %> - <%= `` %> - <%= `` %> - <%= `` %> - <%= `` %> + <%- `` %> + <%- `` %> + <%- `` %> + <%- `` %> + <%- `` %> <% if (article.thumbnail) { %> - <%= `` %> - <%= `` %> + <%- `` %> + <%- `` %> <% } %> From a3a23be1c205222833224d0210ab3db9ec15b1b3 Mon Sep 17 00:00:00 2001 From: Klemek Date: Sun, 23 Jun 2019 15:34:34 +0200 Subject: [PATCH 074/120] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 4808b7b..9395732 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,9 @@ You need to [create a new repository](https://github.com/new) on your favorite G ```bash #gitblog.md/ cd data +git init git remote add origin +git commit -m "initial commit" git push -u origin master ``` From 6aceacad183a14646279b4be61a3ef013b08f727 Mon Sep 17 00:00:00 2001 From: Klemek Date: Sun, 23 Jun 2019 15:34:46 +0200 Subject: [PATCH 075/120] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 9395732..ef08967 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,7 @@ You need to [create a new repository](https://github.com/new) on your favorite G cd data git init git remote add origin +git add . git commit -m "initial commit" git push -u origin master ``` From 6cf7be3afb98a85e1004eaf1ec4b858aacc08909 Mon Sep 17 00:00:00 2001 From: Klemek Date: Sun, 23 Jun 2019 15:37:23 +0200 Subject: [PATCH 076/120] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ef08967..b8feb28 100644 --- a/README.md +++ b/README.md @@ -160,7 +160,7 @@ Here are the steps for Github, if you use another platform adapt it your way (he ```json "webhook": { "endpoint": "/webhook", - "secret": "sha1=", + "secret": "", "signature_header": "X-Hub-Signature" }, ``` From 5e05f250f4682df1b253652d211db3e7e748ba57 Mon Sep 17 00:00:00 2001 From: Klemek Date: Wed, 26 Jun 2019 10:06:12 +0200 Subject: [PATCH 077/120] Update style.css --- sample_data/home/style.css | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/sample_data/home/style.css b/sample_data/home/style.css index fb4ca4e..cbd8361 100644 --- a/sample_data/home/style.css +++ b/sample_data/home/style.css @@ -58,7 +58,7 @@ blockquote { border-left: 0.5em solid #ccc; padding-left: 1em; margin: 0.25em 0; - color: #333; + color: #555; } blockquote > p { @@ -108,7 +108,7 @@ main.article div.header a.link-home { line-height: 2.4; } -main.article div.header h1, main.article div.header h2, div.article h3 { +main.article div.header h1, main.article div.header h2 { margin-top: 0.85em; margin-bottom: 0.25em; font-size: 2em; @@ -123,11 +123,19 @@ main.article div.header span.time, div.article span.time { } div.article { - margin-left: 1em; + margin: 0 1em 1em 1em; } div.article h3 { font-size: 1.3em; + margin:0; +} + +div.article img{ + max-width: 100%; + height: auto; + margin-right:1em; + margin-top:0.25em; } #text { @@ -142,4 +150,4 @@ div.article h3 { #text img { max-width: 100%; height: auto; -} \ No newline at end of file +} From 1341aa5a56b90816166b025043c877ec11346679 Mon Sep 17 00:00:00 2001 From: Klemek Date: Wed, 26 Jun 2019 11:46:06 +0200 Subject: [PATCH 078/120] Update config.default.json --- src/config.default.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/config.default.json b/src/config.default.json index 687ef07..c13b358 100644 --- a/src/config.default.json +++ b/src/config.default.json @@ -37,7 +37,7 @@ "endpoint": "/webhook", "secret": "", "signature_header": "", - "pull_command": "git pull" + "pull_command": "git pull origin master" }, "showdown": { "parseImgDimensions": true, @@ -54,4 +54,4 @@ "plantuml": { "output_format": "svg" } -} \ No newline at end of file +} From 7a4a4f9006b555b67954d4f61e0609d553cda1b8 Mon Sep 17 00:00:00 2001 From: Klemek Date: Wed, 26 Jun 2019 15:07:32 +0200 Subject: [PATCH 079/120] Update footer.ejs --- sample_data/home/footer.ejs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sample_data/home/footer.ejs b/sample_data/home/footer.ejs index 1008e4e..4695f3c 100644 --- a/sample_data/home/footer.ejs +++ b/sample_data/home/footer.ejs @@ -1,6 +1,6 @@
- RSS feed - @<%= new Date().getFullYear() %> - Made with RSS feed - <%= new Date().getFullYear() %> - Made with GitBlog.md (v<%= info.version %>) -
\ No newline at end of file + From aebc3da5bce40134f1036228257790088395b866 Mon Sep 17 00:00:00 2001 From: Klemek Date: Wed, 26 Jun 2019 16:43:03 +0200 Subject: [PATCH 080/120] Update template.ejs --- sample_data/home/template.ejs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sample_data/home/template.ejs b/sample_data/home/template.ejs index 5855270..35602f1 100644 --- a/sample_data/home/template.ejs +++ b/sample_data/home/template.ejs @@ -11,7 +11,7 @@ <%- `` %> <%- `` %> <% if (article.thumbnail) { %> - <%- `` %> + <%- `` %> <%- `` %> <% } %> @@ -33,4 +33,4 @@ <%- include('footer'); %>
- \ No newline at end of file + From b6ac0a73b406fd5783015d98f2ca03926d51c4cb Mon Sep 17 00:00:00 2001 From: Klemek Date: Wed, 26 Jun 2019 18:21:26 +0200 Subject: [PATCH 081/120] Update style.css --- sample_data/home/style.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sample_data/home/style.css b/sample_data/home/style.css index cbd8361..e0b0dcc 100644 --- a/sample_data/home/style.css +++ b/sample_data/home/style.css @@ -8,7 +8,7 @@ body, html { } body { - font: 14px/1.45 -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif; + font: 15px sans-serif; color: #111; -webkit-text-size-adjust: none; background-color: #F5F5F5; @@ -16,8 +16,8 @@ body { } main { - max-width: 75ch; - padding: 2ch; + max-width: 42rem; + padding: 2rem; margin: auto; background-color: #F0F0F0; min-height: 100vh; From bd42883330781dc1e8e8f097e6bb9ceda29ec27c Mon Sep 17 00:00:00 2001 From: Klemek Date: Wed, 26 Jun 2019 18:21:44 +0200 Subject: [PATCH 082/120] Update template.ejs --- sample_data/home/template.ejs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sample_data/home/template.ejs b/sample_data/home/template.ejs index 35602f1..adac905 100644 --- a/sample_data/home/template.ejs +++ b/sample_data/home/template.ejs @@ -11,8 +11,8 @@ <%- `` %> <%- `` %> <% if (article.thumbnail) { %> - <%- `` %> - <%- `` %> + <%- `` %> + <%- `` %> <% } %> From 528e4be1fefc1515d84fb5f7ad1e882c19c9c785 Mon Sep 17 00:00:00 2001 From: Klemek Date: Wed, 26 Jun 2019 18:34:40 +0200 Subject: [PATCH 083/120] Updated templates --- .gitignore | 1 + sample_data/home/error.ejs | 12 +----------- sample_data/home/head.ejs | 25 +++++++++++++++++++++++++ sample_data/home/index.ejs | 12 +----------- sample_data/home/template.ejs | 17 +---------------- 5 files changed, 29 insertions(+), 38 deletions(-) create mode 100644 sample_data/home/head.ejs diff --git a/.gitignore b/.gitignore index 9295227..32b7f43 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ /config.json /config.example.json /data +/data/* /test_data /access.log /error.log diff --git a/sample_data/home/error.ejs b/sample_data/home/error.ejs index c6c5597..2fdfeea 100644 --- a/sample_data/home/error.ejs +++ b/sample_data/home/error.ejs @@ -3,17 +3,7 @@ <%= info.title %> - Error <%= error %> - - - <%- `` %> - <%- `` %> - <%- `` %> - <%- `` %> - <%- `` %> - - - - + <%- include('head'); %>
diff --git a/sample_data/home/head.ejs b/sample_data/home/head.ejs new file mode 100644 index 0000000..0829bed --- /dev/null +++ b/sample_data/home/head.ejs @@ -0,0 +1,25 @@ + +<% if(locals.article){ %> + <%- `` %> + <%- `` %> + <%- `` %> + <%- `` %> + <%- `` %> + <% if (article.thumbnail) { %> + <%- `` %> + <%- `` %> + <% } %> + +<% } else { %> + <%- `` %> + <%- `` %> + <%- `` %> + <%- `` %> + <%- `` %> +<% } %> + + + + + + \ No newline at end of file diff --git a/sample_data/home/index.ejs b/sample_data/home/index.ejs index e91b227..7f975b6 100644 --- a/sample_data/home/index.ejs +++ b/sample_data/home/index.ejs @@ -3,17 +3,7 @@ <%= info.title %> - Home - - - <%- `` %> - <%- `` %> - <%- `` %> - <%- `` %> - <%- `` %> - - - - + <%- include('head'); %>
diff --git a/sample_data/home/template.ejs b/sample_data/home/template.ejs index adac905..6e54334 100644 --- a/sample_data/home/template.ejs +++ b/sample_data/home/template.ejs @@ -3,22 +3,7 @@ <%= info.title %> - <%= article.title %> - - - <%- `` %> - <%- `` %> - <%- `` %> - <%- `` %> - <%- `` %> - <% if (article.thumbnail) { %> - <%- `` %> - <%- `` %> - <% } %> - - - - - + <%- include('head'); %>
From ae4e2eb8d5e48eded072aa4faa0be6306b9475d7 Mon Sep 17 00:00:00 2001 From: Klemek Date: Wed, 26 Jun 2019 18:43:41 +0200 Subject: [PATCH 084/120] Fixing Firefox RSS handling --- src/app.js | 2 +- test/app.test.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app.js b/src/app.js index e0bb3cb..36cfd71 100644 --- a/src/app.js +++ b/src/app.js @@ -160,7 +160,7 @@ module.exports = (config) => { }); lastRSS = feed.xml(); } - res.type('rss').send(lastRSS); + res.type(req.headers['user-agent'].match(/Mozilla/) ? 'xml' : 'rss').send(lastRSS); } else { showError(req.path, 404, res); } diff --git a/test/app.test.js b/test/app.test.js index 9db6f98..2cb744d 100644 --- a/test/app.test.js +++ b/test/app.test.js @@ -175,6 +175,7 @@ describe('Test RSS feed', () => { test('200 empty rss', (done) => { request(app).get('/rsstest').then((response) => { expect(response.statusCode).toBe(200); + expect(response.type).toBe('application/rss+xml'); expect(response.text.length).toBeGreaterThan(0); expect(response.text.split('').length).toBe(1); done(); From 8fc7ff1ca76728a28d8d673fbcd7515aaf787212 Mon Sep 17 00:00:00 2001 From: Klemek Date: Wed, 26 Jun 2019 18:44:31 +0200 Subject: [PATCH 085/120] Updated version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8e65711..b4cf32e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitblog.md", - "version": "1.1.5", + "version": "1.2.0", "description": "A static blog using Markdown pulled from your git repository.", "main": "src/server.js", "dependencies": { From 0fde4288064da6b0939dd7610de339ae017ee6dd Mon Sep 17 00:00:00 2001 From: Klemek Date: Wed, 26 Jun 2019 18:56:01 +0200 Subject: [PATCH 086/120] Updated coverage --- src/app.js | 2 +- test/app.test.js | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/app.js b/src/app.js index 36cfd71..b47aa2b 100644 --- a/src/app.js +++ b/src/app.js @@ -160,7 +160,7 @@ module.exports = (config) => { }); lastRSS = feed.xml(); } - res.type(req.headers['user-agent'].match(/Mozilla/) ? 'xml' : 'rss').send(lastRSS); + res.type(req.headers['user-agent'].match(/Mozilla/) ? 'text/xml' : 'rss').send(lastRSS); } else { showError(req.path, 404, res); } diff --git a/test/app.test.js b/test/app.test.js index 2cb744d..1f3e453 100644 --- a/test/app.test.js +++ b/test/app.test.js @@ -181,6 +181,13 @@ describe('Test RSS feed', () => { done(); }); }); + test('200 Mozilla fix', (done) => { + request(app).get('/rsstest').set('user-agent', 'Mozilla Firefox 64.0').then((response) => { + expect(response.statusCode).toBe(200); + expect(response.type).toBe('text/xml'); + done(); + }); + }); test('200 rss cache', (done) => { request(app).get('/rsstest').then(() => { request(app).get('/rsstest').then((response) => { From a7fedb149f7bb206714fafea1da662525b1870d1 Mon Sep 17 00:00:00 2001 From: Klemek Date: Wed, 26 Jun 2019 19:28:00 +0200 Subject: [PATCH 087/120] Host from config if specified --- src/app.js | 18 +++++++++++------- src/config.default.json | 1 + 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/app.js b/src/app.js index b47aa2b..3d7a118 100644 --- a/src/app.js +++ b/src/app.js @@ -36,7 +36,7 @@ module.exports = (config) => { const articles = {}; let lastRSS = ''; - let host; + let host = config['host']; /** * Fetch articles from the data folder and send success as a response @@ -67,17 +67,20 @@ module.exports = (config) => { /** * Render the page with the view engine and catch errors + * @param req * @param res * @param vPath - path of the view * @param data - data to pass to the view * @param code - code to send along the page */ - const render = (res, vPath, data, code = 200) => { + const render = (req, res, vPath, data, code = 200) => { data.info = { title: config['home']['title'], description: config['home']['description'], host: host, - version: pjson.version + version: pjson.version, + request: req, + config: config }; res.render(vPath, data, (err, html) => { if (err) { @@ -90,17 +93,18 @@ module.exports = (config) => { /** * Show an error with the correct page + * @param req * @param resPath - the page of the original error * @param code - error code * @param res */ - const showError = (resPath, code, res) => { + const showError = (req, resPath, code, res) => { const errorPath = path.join(config['data_dir'], config['home']['error']); fs.access(errorPath, fs.constants.R_OK, (err) => { if (err) res.sendStatus(code); else - render(res, errorPath, {error: code, path: resPath}, code); + render(req, res, errorPath, {error: code, path: resPath}, code); }); }; @@ -135,7 +139,7 @@ module.exports = (config) => { if (err) showError(req.path, 404, res); else - render(res, homePath, {articles: Object.values(articles).sort((a, b) => ('' + b.path).localeCompare(a.path))}); + render(req, res, homePath, {articles: Object.values(articles).sort((a, b) => ('' + b.path).localeCompare(a.path))}); }); }); @@ -219,7 +223,7 @@ module.exports = (config) => { console.log(cons.error, `no template found at ${templatePath}`); showError(req.path, 500, res); } else - render(res, templatePath, {article: article}); + render(req, res, templatePath, {article: article}); }); }); } diff --git a/src/config.default.json b/src/config.default.json index c13b358..4918ed6 100644 --- a/src/config.default.json +++ b/src/config.default.json @@ -1,5 +1,6 @@ { "node_port": 3000, + "host": "", "data_dir": "data", "view_engine": "ejs", "access_log": "access.log", From 4b472764846e04c6783c741cb1aefff7cf8b51f5 Mon Sep 17 00:00:00 2001 From: Klemek Date: Wed, 26 Jun 2019 19:28:18 +0200 Subject: [PATCH 088/120] Updated readme --- README.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/README.md b/README.md index 8f70641..0466fc2 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,30 @@ Resources are located on the `data` folder and can be referenced as the root of /styles/main.css => data/styles/main.css ``` +In your template, the following data is sent : + +* `info` (every pages) + * `title` : the blog's title as in the config + * `description` the blog's description as in the config + * `host` : the specified or guessed host with the protocol + * `version` : the GitBlog.md current running version + * `request` : the Express request object + * `config` : the content of the config +* `article` (article pages only) + * `title` : the full title + * `thumbnail` the URL path of the thumbnail + * `url` : the URL path for this article (with the title) + * `date` : a JS date + * `year` + * `month` + * `day` + * `path` : the URL path for the folder of the article (without the title) + * `realPath` : the system's path for the folder + * `escapedTitle` : the code with alphanumeric and underscore characters only +* `error` (error pages only) + * `error` : the error code + * `path` : the resource that caused the error + #### 5. Create and init your git source You need to [create a new repository](https://github.com/new) on your favorite Git service. @@ -207,6 +231,9 @@ Any URL like `/year/month/day/anything/` will redirect to this article (and link * `node_port` (default: 3000) the port the server is listening to +* `host` (default: none) + if set (like `https://mywebsite.com`, it will be used as reference for creating links + by default, host is guessed based on first request * `data_dir` (default: data) the directory where will be located the git repo with templates and articles * `view_engine` (default: ejs) From ddf964eb27492ae6968b032a58873d8a12fc1f06 Mon Sep 17 00:00:00 2001 From: Klemek Date: Wed, 26 Jun 2019 19:28:29 +0200 Subject: [PATCH 089/120] Updated version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b4cf32e..1a1d5e8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitblog.md", - "version": "1.2.0", + "version": "1.2.1", "description": "A static blog using Markdown pulled from your git repository.", "main": "src/server.js", "dependencies": { From fc7bc63c46ec7fa0ddcd03f47ec60c30dc6a6933 Mon Sep 17 00:00:00 2001 From: Klemek Date: Wed, 26 Jun 2019 19:44:52 +0200 Subject: [PATCH 090/120] Nodemon config --- README.md | 8 ++++++++ package.json | 13 ++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0466fc2..ad9d597 100644 --- a/README.md +++ b/README.md @@ -184,6 +184,14 @@ Here are the steps for Github, if you use another platform adapt it your way (he * Update your webhook on github to include the secret * Check if Github successfully reached the endpoint +#### 8. Keep your server always up and running (optionnal) + +This project `package.json` comes with a [nodemon](https://github.com/remy/nodemon) config. + +After installing (`npm i -g nodemon`) you can then run the app with juste the `nodemon` command in the working directory. + +With this method, you can do a simple `git pull` to update your server. + ## Writing an article [back to top](#gitblog-md) diff --git a/package.json b/package.json index 1a1d5e8..13fdb88 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitblog.md", - "version": "1.2.1", + "version": "1.2.2", "description": "A static blog using Markdown pulled from your git repository.", "main": "src/server.js", "dependencies": { @@ -46,5 +46,16 @@ "!src/postinstall.js", "!src/lib/*.js" ] + }, + "nodemonConfig": { + "ignore": [ + "test/*", + "sample_data/*", + "data/*", + "uml/*", + "*.log", + "README.md" + ], + "delay": "2500" } } From 52d37d56cdd9ae93d16a957d5f7b7e12fecf66ba Mon Sep 17 00:00:00 2001 From: Klemek Date: Wed, 26 Jun 2019 19:48:51 +0200 Subject: [PATCH 091/120] Bug fix --- src/app.js | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/app.js b/src/app.js index 3d7a118..122ad10 100644 --- a/src/app.js +++ b/src/app.js @@ -94,17 +94,16 @@ module.exports = (config) => { /** * Show an error with the correct page * @param req - * @param resPath - the page of the original error - * @param code - error code * @param res + * @param code - error code */ - const showError = (req, resPath, code, res) => { + const showError = (req, res, code) => { const errorPath = path.join(config['data_dir'], config['home']['error']); fs.access(errorPath, fs.constants.R_OK, (err) => { if (err) res.sendStatus(code); else - render(req, res, errorPath, {error: code, path: resPath}, code); + render(req, res, errorPath, {error: code, path: req.path}, code); }); }; @@ -137,7 +136,7 @@ module.exports = (config) => { const homePath = path.join(config['data_dir'], config['home']['index']); fs.access(homePath, fs.constants.R_OK, (err) => { if (err) - showError(req.path, 404, res); + showError(req, res, 404); else render(req, res, homePath, {articles: Object.values(articles).sort((a, b) => ('' + b.path).localeCompare(a.path))}); }); @@ -150,15 +149,15 @@ module.exports = (config) => { const feed = new Rss({ 'title': config['rss']['title'], 'description': config['rss']['description'], - 'feed_url': 'http://' + req.headers.host + req.url, - 'site_url': 'http://' + req.headers.host + 'feed_url': host + req.url, + 'site_url': host }); Object.values(articles) .slice(0, config['rss']['length']) .forEach((article) => { feed.item({ title: article.title, - url: 'http://' + req.headers.host + article.url, + url: host + article.url, date: article.date }); }); @@ -166,7 +165,7 @@ module.exports = (config) => { } res.type(req.headers['user-agent'].match(/Mozilla/) ? 'text/xml' : 'rss').send(lastRSS); } else { - showError(req.path, 404, res); + showError(req, res, 404); } }); @@ -209,19 +208,19 @@ module.exports = (config) => { const articlePath = req.path.substr(1, 10); const article = articles[articlePath]; if (!article) - showError(req.path, 404, res); + showError(req, res, 404); else { renderer.render(path.join(article.realPath, config['article']['index']), (err, html) => { if (err) { console.log(cons.error, `failed to render article ${req.path} : ${err}`); - return showError(req.path, 500, res); + return showError(req, res, 500); } article.content = html; const templatePath = path.join(config['data_dir'], config['article']['template']); fs.access(templatePath, fs.constants.R_OK, (err) => { if (err) { console.log(cons.error, `no template found at ${templatePath}`); - showError(req.path, 500, res); + showError(req, res, 500); } else render(req, res, templatePath, {article: article}); }); @@ -235,7 +234,7 @@ module.exports = (config) => { // catch all hidden file type and return 404 app.get('*', (req, res, next) => { if (config['home']['hidden'].includes(path.extname(req.path))) - showError(req.path, 404, res); + showError(req, res, 404); else next(); }); @@ -244,7 +243,7 @@ module.exports = (config) => { app.get('*', express.static(path.join(__dirname, '..', config['data_dir']))); // catch express.static errors (mostly not found) by displaying 404 app.get('*', (req, res) => { - showError(req.path, 404, res); + showError(req, res, 404); }); // catch all other methods and return 400 From 1b91002c03d8e4f4f8400ec400cee16dc491b89c Mon Sep 17 00:00:00 2001 From: Klemek Date: Wed, 26 Jun 2019 20:09:08 +0200 Subject: [PATCH 092/120] Updated templates meta tags --- sample_data/home/error.ejs | 3 +-- sample_data/home/head.ejs | 19 ++++++++++++------- sample_data/home/index.ejs | 3 +-- sample_data/home/template.ejs | 3 +-- 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/sample_data/home/error.ejs b/sample_data/home/error.ejs index 2fdfeea..3bbcdd4 100644 --- a/sample_data/home/error.ejs +++ b/sample_data/home/error.ejs @@ -1,9 +1,8 @@ - - <%= info.title %> - Error <%= error %> <%- include('head'); %> + <%= info.title %> - Error <%= error %>
diff --git a/sample_data/home/head.ejs b/sample_data/home/head.ejs index 0829bed..be46678 100644 --- a/sample_data/home/head.ejs +++ b/sample_data/home/head.ejs @@ -1,21 +1,26 @@ + + + + +<%- `` %> +<%- `` %> <% if(locals.article){ %> + <%- `` %> <%- `` %> <%- `` %> - <%- `` %> - <%- `` %> - <%- `` %> <% if (article.thumbnail) { %> <%- `` %> <%- `` %> <% } %> - + + <% } else { %> + <%- `` %> <%- `` %> <%- `` %> - <%- `` %> - <%- `` %> - <%- `` %> + + <%- `` %> <% } %> diff --git a/sample_data/home/index.ejs b/sample_data/home/index.ejs index 7f975b6..0517fdc 100644 --- a/sample_data/home/index.ejs +++ b/sample_data/home/index.ejs @@ -1,9 +1,8 @@ - - <%= info.title %> - Home <%- include('head'); %> + <%= info.title %> - Home
diff --git a/sample_data/home/template.ejs b/sample_data/home/template.ejs index 6e54334..e2590c1 100644 --- a/sample_data/home/template.ejs +++ b/sample_data/home/template.ejs @@ -1,9 +1,8 @@ - - <%= info.title %> - <%= article.title %> <%- include('head'); %> + <%= info.title %> - <%= article.title %>
From 6af40125223546e4ab0e2ce058289635a328f277 Mon Sep 17 00:00:00 2001 From: Klemek Date: Wed, 26 Jun 2019 20:59:42 +0200 Subject: [PATCH 093/120] Hidden files path matching --- README.md | 4 +-- package.json | 2 +- src/app.js | 63 ++++++++++++++++++++++------------------- src/config.default.json | 3 +- test/app.test.js | 43 ++++++++++++++++++++++------ test/config.test.js | 2 +- 6 files changed, 73 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index ad9d597..16a7cc9 100644 --- a/README.md +++ b/README.md @@ -140,9 +140,7 @@ In your template, the following data is sent : * `path` : the URL path for the folder of the article (without the title) * `realPath` : the system's path for the folder * `escapedTitle` : the code with alphanumeric and underscore characters only -* `error` (error pages only) - * `error` : the error code - * `path` : the resource that caused the error +* `error` (error pages only) : the error code #### 5. Create and init your git source diff --git a/package.json b/package.json index 13fdb88..8f09d1a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitblog.md", - "version": "1.2.2", + "version": "1.2.3", "description": "A static blog using Markdown pulled from your git repository.", "main": "src/server.js", "dependencies": { diff --git a/src/app.js b/src/app.js index 122ad10..405953c 100644 --- a/src/app.js +++ b/src/app.js @@ -26,6 +26,28 @@ const cons = { }; module.exports = (config) => { + /** + * Fetch articles from the data folder and send success as a response + * @param success + * @param error + */ + let reload; + /** + * Render the page with the view engine and catch errors + * @param req + * @param res + * @param vPath - path of the view + * @param data - data to pass to the view + * @param code - code to send along the page + */ + let render; + /** + * Show an error with the correct page + * @param req + * @param res + * @param code - error code + */ + let showError; const fw = require('./file_walker')(config); const renderer = require('./renderer')(config); @@ -38,12 +60,7 @@ module.exports = (config) => { let lastRSS = ''; let host = config['host']; - /** - * Fetch articles from the data folder and send success as a response - * @param success - * @param error - */ - const reload = (success, error) => { + reload = (success, error) => { fw.fetchArticles((err, dict) => { if (err) { console.error(cons.error, 'error loading articles : ' + err); @@ -65,15 +82,7 @@ module.exports = (config) => { if (config['test']) app.reload = reload; - /** - * Render the page with the view engine and catch errors - * @param req - * @param res - * @param vPath - path of the view - * @param data - data to pass to the view - * @param code - code to send along the page - */ - const render = (req, res, vPath, data, code = 200) => { + render = (req, res, vPath, data, code = 200) => { data.info = { title: config['home']['title'], description: config['home']['description'], @@ -83,27 +92,24 @@ module.exports = (config) => { config: config }; res.render(vPath, data, (err, html) => { - if (err) { + if (err && vPath !== path.join(config['data_dir'], config['home']['error'])) { + console.log(cons.error, `failed to render page ${vPath} : ${err}`); + showError(req, res, 500); + } else if (err) { res.sendStatus(500); - console.log(cons.error, `failed to render ${vPath} : ${err}`); + console.log(cons.error, `failed to render error page : ${err}`); } else res.status(code).send(html); }); }; - /** - * Show an error with the correct page - * @param req - * @param res - * @param code - error code - */ - const showError = (req, res, code) => { + showError = (req, res, code) => { const errorPath = path.join(config['data_dir'], config['home']['error']); fs.access(errorPath, fs.constants.R_OK, (err) => { if (err) res.sendStatus(code); else - render(req, res, errorPath, {error: code, path: req.path}, code); + render(req, res, errorPath, {error: code}, code); }); }; @@ -232,11 +238,10 @@ module.exports = (config) => { }); // catch all hidden file type and return 404 - app.get('*', (req, res, next) => { - if (config['home']['hidden'].includes(path.extname(req.path))) + config['home']['hidden'].forEach(pathMatcher => { + app.get(pathMatcher, (req, res) => { showError(req, res, 404); - else - next(); + }); }); // serve all static files via get diff --git a/src/config.default.json b/src/config.default.json index 4918ed6..0f62c17 100644 --- a/src/config.default.json +++ b/src/config.default.json @@ -18,7 +18,8 @@ "index": "index.ejs", "error": "error.ejs", "hidden": [ - ".ejs" + "*.ejs", + "/.git*" ] }, "article": { diff --git a/test/app.test.js b/test/app.test.js index 1f3e453..b64914b 100644 --- a/test/app.test.js +++ b/test/app.test.js @@ -16,16 +16,15 @@ config['data_dir'] = dataDir; config['webhook']['endpoint'] = '/webhooktest'; config['rss']['endpoint'] = '/rsstest'; config['rss']['length'] = 2; -config['home']['index'] = testIndex; config['home']['error'] = testError; config['article']['template'] = testTemplate; const app = require('../src/app')(config); beforeEach((done, fail) => { + config['home']['index'] = testIndex; config['data_dir'] = dataDir; config['article']['index'] = 'index.md'; - config['home']['hidden'] = ['.ejs', '.test']; config['access_log'] = ''; config['error_log'] = ''; config['modules']['rss'] = true; @@ -93,20 +92,20 @@ describe('Test request logging', () => { describe('Test error logging', () => { test('test no log', (done) => { - config['home']['hidden'] = null; - request(app).get('/somefile.txt').then(() => { + config['home']['index'] = null; + request(app).get('/').then(() => { expect(fs.existsSync(path.join(dataDir, 'error.log'))).toBe(false); done(); }); }); test('test null error ', (done) => { - config['home']['hidden'] = null; + config['home']['index'] = null; config['error_log'] = path.join(dataDir, 'error.log'); - request(app).get('/somefile.txt').then(() => { + request(app).get('/').then(() => { fs.readFile(path.join(dataDir, 'error.log'), {encoding: 'UTF-8'}, (err, data) => { expect(err).toBeNull(); const start = data.split('\n').slice(0, 2).join('\n'); - const expected = '500 GET /somefile.txt ' + new Date().toUTCString() + ' ::ffff:127.0.0.1\nTypeError: Cannot read property \'includes\' of null'; + const expected = '500 GET / ' + new Date().toUTCString() + ' ::ffff:127.0.0.1\nTypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received type object'; expect(start).toBe(expected); done(); }); @@ -136,6 +135,23 @@ describe('Test root path', () => { done(); }); }); + test('500 render error with page', (done) => { + fs.writeFileSync(path.join(dataDir, testIndex), 'articles <%= null.length %>'); + fs.writeFileSync(path.join(dataDir, testError), 'error <%= error %>'); + request(app).get('/').then((response) => { + expect(response.statusCode).toBe(500); + expect(response.text).toBe('error 500'); + done(); + }); + }); + test('500 render error with failing page', (done) => { + fs.writeFileSync(path.join(dataDir, testIndex), 'articles <%= null.length %>'); + fs.writeFileSync(path.join(dataDir, testError), 'error <%= null.error %>'); + request(app).get('/').then((response) => { + expect(response.statusCode).toBe(500); + done(); + }); + }); test('200 no articles', (done) => { fs.writeFileSync(path.join(dataDir, testIndex), 'articles <%= articles.length %>'); request(app).get('/').then((response) => { @@ -386,8 +402,17 @@ describe('Test static files', () => { }); }); test('404 hidden file', (done) => { - fs.writeFileSync(path.join(dataDir, 'somefile.test'), ''); - request(app).get('/somefile.test').then((response) => { + utils.createEmptyDirs([path.join(dataDir, 'tmp')]); + fs.writeFileSync(path.join(dataDir, 'tmp', 'somefile.ejs'), ''); + request(app).get('/tmp/somefile.ejs').then((response) => { + expect(response.statusCode).toBe(404); + done(); + }); + }); + test('404 hidden folder', (done) => { + utils.createEmptyDirs([path.join(dataDir, '.git')]); + fs.writeFileSync(path.join(dataDir, '.git', 'file.txt'), ''); + request(app).get('/.git/file.txt').then((response) => { expect(response.statusCode).toBe(404); done(); }); diff --git a/test/config.test.js b/test/config.test.js index 04b1bcf..fbe86c0 100644 --- a/test/config.test.js +++ b/test/config.test.js @@ -77,5 +77,5 @@ test('array fix', () => { fs.writeFileSync(configFile, '{"home":{"hidden":{}}}'); const config = require('../src/config')(); expect(config).toBeDefined(); - expect(config['home']['hidden']).toEqual(['.ejs']); + expect(config['home']['hidden']).toEqual(['*.ejs', '/.git*']); }); \ No newline at end of file From dfb93b6764395e0151256efbddc7ed1a0f78e0c4 Mon Sep 17 00:00:00 2001 From: Klemek Date: Wed, 26 Jun 2019 21:03:39 +0200 Subject: [PATCH 094/120] [skip CI]Updated nodemon config --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 8f09d1a..3b561dd 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ ] }, "nodemonConfig": { + "verbose": true, "ignore": [ "test/*", "sample_data/*", @@ -55,7 +56,6 @@ "uml/*", "*.log", "README.md" - ], - "delay": "2500" + ] } } From b6afcd4992e7b36ea196ce45aeb4762471356e10 Mon Sep 17 00:00:00 2001 From: Klemek Date: Wed, 26 Jun 2019 21:20:45 +0200 Subject: [PATCH 095/120] Update README.md --- README.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4d0aba0..026beb5 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,10 @@ Resources are located on the `data` folder and can be referenced as the root of In your template, the following data is sent : +
+details (click) +

+ * `info` (every pages) * `title` : the blog's title as in the config * `description` the blog's description as in the config @@ -146,6 +150,8 @@ In your template, the following data is sent : * `realPath` : the system's path for the folder * `escapedTitle` : the code with alphanumeric and underscore characters only * `error` (error pages only) : the error code +

+
#### 5. Create and init your git source @@ -280,8 +286,8 @@ Any URL like `/year/month/day/anything/` will redirect to this article (and link * `error` (default: error.ejs) the name of the error page template on the data directory it will receive `error`, the error code - * `hidden` (default: `[.ejs]`) - file extensions to be returned 404 when reached + * `hidden` (default: `[*.ejs,/.git*]`) + path matches to be returned 404 when reached * `article` * `index` (default: index.md) the name of the Markdown page of the article on the `/year/month/day/` directory From 3b07b6b9c5ba0bd9a0532685e6c0cc499f3b7ff1 Mon Sep 17 00:00:00 2001 From: Klemek Date: Mon, 1 Jul 2019 22:18:40 +0200 Subject: [PATCH 096/120] Drafted articles --- README.md | 2 ++ package.json | 2 +- src/app.js | 6 +++- src/config.default.json | 1 + src/file_walker.js | 12 ++++---- test/app.test.js | 19 +++++++----- test/file_walker.test.js | 64 +++++++++++++++++++++++++++++++++++++++- 7 files changed, 90 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 16a7cc9..baa206a 100644 --- a/README.md +++ b/README.md @@ -277,6 +277,8 @@ Any URL like `/year/month/day/anything/` will redirect to this article (and link * `article` * `index` (default: index.md) the name of the Markdown page of the article on the `/year/month/day/` directory + * `draft` (default: draft.md) + the name of the Markdown page of an article not shown on the list * `template` (default: template.ejs) the name of the article page template on the data directory * `thumbnail_tag`: (default: thumbnail) diff --git a/package.json b/package.json index 3b561dd..ee82655 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitblog.md", - "version": "1.2.3", + "version": "1.2.4", "description": "A static blog using Markdown pulled from your git repository.", "main": "src/server.js", "dependencies": { diff --git a/src/app.js b/src/app.js index 405953c..e9f0b5b 100644 --- a/src/app.js +++ b/src/app.js @@ -144,7 +144,11 @@ module.exports = (config) => { if (err) showError(req, res, 404); else - render(req, res, homePath, {articles: Object.values(articles).sort((a, b) => ('' + b.path).localeCompare(a.path))}); + render(req, res, homePath, + { + articles: Object.values(articles) + .filter(d => !d.draft).sort((a, b) => ('' + b.path).localeCompare(a.path)) + }); }); }); diff --git a/src/config.default.json b/src/config.default.json index 0f62c17..63e3f92 100644 --- a/src/config.default.json +++ b/src/config.default.json @@ -24,6 +24,7 @@ }, "article": { "index": "index.md", + "draft": "draft.md", "template": "template.ejs", "thumbnail_tag": "thumbnail", "default_title": "Untitled", diff --git a/src/file_walker.js b/src/file_walker.js index b0bdcae..83ab260 100644 --- a/src/file_walker.js +++ b/src/file_walker.js @@ -1,7 +1,7 @@ const fs = require('fs'); const path = require('path'); -const joinUrl = (...paths) => path.join(...paths).replace(/\\/g,'/'); +const joinUrl = (...paths) => path.join(...paths).replace(/\\/g, '/'); /** * Get all files path inside a given folder path @@ -71,8 +71,8 @@ module.exports = (config) => { if (err) return cb(err); const paths = fileList - .map((p) => p.substr(config['data_dir'].length+1).split(path.sep)) - .filter((p) => p.length === 4 && p[3] === config['article']['index'] && + .map((p) => p.substr(config['data_dir'].length + 1).split(path.sep)) + .filter((p) => p.length === 4 && (p[3] === config['article']['index'] || p[3] === config['article']['draft']) && /^\d{4}$/.test(p[0]) && /^\d{2}$/.test(p[1]) && /^\d{2}$/.test(p[2])); if (paths.length === 0) cb(null, {}); @@ -81,6 +81,7 @@ module.exports = (config) => { paths.forEach((p) => { const article = { path: joinUrl(p[0], p[1], p[2]), + draft: p[3] === config['article']['draft'], realPath: path.join(config['data_dir'], p[0], p[1], p[2]), year: parseInt(p[0]), month: parseInt(p[1]), @@ -89,14 +90,15 @@ module.exports = (config) => { article.date = new Date(article.year, article.month, article.day); article.date.setUTCHours(0); remaining++; - readIndexFile(path.join(article.realPath, config['article']['index']), config['article']['thumbnail_tag'], (err, info) => { + readIndexFile(path.join(article.realPath, p[3]), config['article']['thumbnail_tag'], (err, info) => { if (err) return cb(err); article.title = info.title || config['article']['default_title']; article.thumbnail = info.thumbnail ? joinUrl(article.path, info.thumbnail) : config['article']['default_thumbnail']; article.escapedTitle = article.title.toLowerCase().replace(/[^\w]/gm, ' ').trim().replace(/ /gm, '_'); article.url = '/' + joinUrl(article.path, article.escapedTitle) + '/'; - articles[article.path] = article; + if (!articles[article.path] || !article.draft) + articles[article.path] = article; remaining--; if (remaining === 0) cb(null, articles); diff --git a/test/app.test.js b/test/app.test.js index b64914b..1d8c6e2 100644 --- a/test/app.test.js +++ b/test/app.test.js @@ -121,10 +121,10 @@ describe('Test root path', () => { }); }); test('404 no index but error page', (done) => { - fs.writeFileSync(path.join(dataDir, testError), 'error <%= error %> at <%= path %>'); + fs.writeFileSync(path.join(dataDir, testError), 'error <%= error %>'); request(app).get('/').then((response) => { expect(response.statusCode).toBe(404); - expect(response.text).toBe('error 404 at /'); + expect(response.text).toBe('error 404'); done(); }); }); @@ -160,14 +160,17 @@ describe('Test root path', () => { done(); }); }); - test('200 2 articles', (done, fail) => { + test('200 2 articles 1 drafted', (done, fail) => { utils.createEmptyDirs([ path.join(dataDir, '2019', '05', '05'), - path.join(dataDir, '2018', '05', '05') + path.join(dataDir, '2018', '05', '05'), + path.join(dataDir, '2017', '05', '05') ]); utils.createEmptyFiles([ - path.join(dataDir, '2019', '05', '05', 'index.md'), - path.join(dataDir, '2018', '05', '05', 'index.md') + path.join(dataDir, '2019', '05', '05', 'draft.md'), + path.join(dataDir, '2018', '05', '05', 'index.md'), + path.join(dataDir, '2018', '05', '05', 'draft.md'), + path.join(dataDir, '2017', '05', '05', 'index.md'), ]); fs.writeFileSync(path.join(dataDir, testIndex), 'articles <%= articles.length %>'); app.reload(() => { @@ -394,10 +397,10 @@ describe('Test static files', () => { }); }); test('404 invalid file but error page', (done) => { - fs.writeFileSync(path.join(dataDir, testError), 'error <%= error %> at <%= path %>'); + fs.writeFileSync(path.join(dataDir, testError), 'error <%= error %>'); request(app).get('/somefile.txt').then((response) => { expect(response.statusCode).toBe(404); - expect(response.text).toBe('error 404 at /somefile.txt'); + expect(response.text).toBe('error 404'); done(); }); }); diff --git a/test/file_walker.test.js b/test/file_walker.test.js index 55ff7ce..519ad9e 100644 --- a/test/file_walker.test.js +++ b/test/file_walker.test.js @@ -6,13 +6,14 @@ const utils = require('./test_utils'); const dataDir = 'test_data'; const testIndex = 'testindex.md'; -const joinUrl = (...paths) => path.join(...paths).replace(/\\/g,'/'); +const joinUrl = (...paths) => path.join(...paths).replace(/\\/g, '/'); const config = { 'test': true, 'data_dir': dataDir, 'article': { 'index': testIndex, + 'draft': 'draft.md', 'default_title': 'Untitled', 'default_thumbnail': 'default.png', 'thumbnail_tag': 'thumbnail' @@ -238,6 +239,7 @@ describe('Test article fetching', () => { realPath: dir, year: 2019, month: 5, + draft: false, day: 5, date: date, title: 'Untitled', @@ -269,6 +271,7 @@ describe('Test article fetching', () => { year: 2019, month: 5, day: 5, + draft: false, date: date, title: 'Title with : info !', thumbnail: joinUrl('2019', '05', '05', './thumbnail.jpg'), @@ -278,5 +281,64 @@ describe('Test article fetching', () => { done(); }); }); + test('correct draft file', (done) => { + const dir = path.join(dataDir, '2019', '05', '05'); + const file = path.join(dir, 'draft.md'); + utils.createEmptyDirs([dir]); + fs.writeFileSync(file, ` + # Title with : info ! + ![thumbnail](./thumbnail.jpg) + this is some text + `); + const date = new Date(2019, 5, 5); + date.setUTCHours(0); + fw.fetchArticles((err, dict) => { + expect(err).toBeNull(); + expect(dict).toBeDefined(); + expect(Object.keys(dict).length).toBe(1); + expect(dict[joinUrl('2019', '05', '05')]).toEqual({ + path: joinUrl('2019', '05', '05'), + realPath: dir, + year: 2019, + month: 5, + day: 5, + draft: true, + date: date, + title: 'Title with : info !', + thumbnail: joinUrl('2019', '05', '05', './thumbnail.jpg'), + escapedTitle: 'title_with___info', + url: '/' + joinUrl('2019', '05', '05', 'title_with___info') + '/', + }); + done(); + }); + }); + test('index file override draft', (done) => { + const dir = path.join(dataDir, '2019', '05', '05'); + const file = path.join(dir, testIndex); + const file2 = path.join(dir, 'draft.md'); + utils.createEmptyDirs([dir]); + utils.createEmptyFiles([file, file2]); + const date = new Date(2019, 5, 5); + date.setUTCHours(0); + fw.fetchArticles((err, dict) => { + expect(err).toBeNull(); + expect(dict).toBeDefined(); + expect(Object.keys(dict).length).toBe(1); + expect(dict[joinUrl('2019', '05', '05')]).toEqual({ + path: joinUrl('2019', '05', '05'), + realPath: dir, + year: 2019, + month: 5, + draft: false, + day: 5, + date: date, + title: 'Untitled', + thumbnail: 'default.png', + escapedTitle: 'untitled', + url: '/' + joinUrl('2019', '05', '05', 'untitled') + '/', + }); + done(); + }); + }); }); From 8bb455b576b2dbae4419226461cf92c7501104d3 Mon Sep 17 00:00:00 2001 From: Klemek Date: Mon, 1 Jul 2019 23:15:13 +0200 Subject: [PATCH 097/120] Fixed draft rendering bug --- package.json | 2 +- src/app.js | 5 +++-- src/file_walker.js | 4 ++-- test/app.test.js | 18 +++++++++++++++--- test/file_walker.test.js | 8 ++++---- 5 files changed, 25 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index ee82655..b05cb18 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitblog.md", - "version": "1.2.4", + "version": "1.2.5", "description": "A static blog using Markdown pulled from your git repository.", "main": "src/server.js", "dependencies": { diff --git a/src/app.js b/src/app.js index e9f0b5b..5496fe2 100644 --- a/src/app.js +++ b/src/app.js @@ -69,8 +69,9 @@ module.exports = (config) => { Object.keys(articles).forEach((key) => delete articles[key]); Object.keys(dict).forEach((key) => articles[key] = dict[key]); const nb = Object.keys(articles).length; + const dnb = Object.values(articles).filter(a => a.draft).length; if (nb > 0) - console.log(cons.ok, `loaded ${nb} article${nb > 1 ? 's' : ''}`); + console.log(cons.ok, `loaded ${nb} article${nb > 1 ? 's' : ''} (${dnb} drafted)`); else console.log(cons.warn, `no articles loaded, check your configuration`); @@ -220,7 +221,7 @@ module.exports = (config) => { if (!article) showError(req, res, 404); else { - renderer.render(path.join(article.realPath, config['article']['index']), (err, html) => { + renderer.render(article.realPath, (err, html) => { if (err) { console.log(cons.error, `failed to render article ${req.path} : ${err}`); return showError(req, res, 500); diff --git a/src/file_walker.js b/src/file_walker.js index 83ab260..4cb0d73 100644 --- a/src/file_walker.js +++ b/src/file_walker.js @@ -82,7 +82,7 @@ module.exports = (config) => { const article = { path: joinUrl(p[0], p[1], p[2]), draft: p[3] === config['article']['draft'], - realPath: path.join(config['data_dir'], p[0], p[1], p[2]), + realPath: path.join(config['data_dir'], p[0], p[1], p[2], p[3]), year: parseInt(p[0]), month: parseInt(p[1]), day: parseInt(p[2]) @@ -90,7 +90,7 @@ module.exports = (config) => { article.date = new Date(article.year, article.month, article.day); article.date.setUTCHours(0); remaining++; - readIndexFile(path.join(article.realPath, p[3]), config['article']['thumbnail_tag'], (err, info) => { + readIndexFile(article.realPath, config['article']['thumbnail_tag'], (err, info) => { if (err) return cb(err); article.title = info.title || config['article']['default_title']; diff --git a/test/app.test.js b/test/app.test.js index 1d8c6e2..38e6002 100644 --- a/test/app.test.js +++ b/test/app.test.js @@ -322,12 +322,11 @@ describe('Test articles rendering', () => { }); }); - test('500 no index', (done, fail) => { + test('500 fail to render', (done, fail) => { utils.createEmptyDirs([path.join(dataDir, '2019', '05', '05'),]); fs.writeFileSync(path.join(dataDir, '2019', '05', '05', 'index.md'), '# Hello'); - fs.writeFileSync(path.join(dataDir, testTemplate), '<%- article.content %><%- `reload` %>'); + fs.writeFileSync(path.join(dataDir, testTemplate), '<%- articl.content %><%- `reload` %>'); app.reload(() => { - config['article']['index'] = 'invalid.md'; request(app).get('/2019/05/05/hello/').then((response) => { expect(response.statusCode).toBe(500); done(); @@ -359,6 +358,19 @@ describe('Test articles rendering', () => { }, fail); }); + test('200 rendered draft', (done, fail) => { + utils.createEmptyDirs([path.join(dataDir, '2019', '05', '05'),]); + fs.writeFileSync(path.join(dataDir, '2019', '05', '05', 'draft.md'), '# Hello'); + fs.writeFileSync(path.join(dataDir, testTemplate), '<%- article.content %><%- `reload` %>'); + app.reload(() => { + request(app).get('/2019/05/05/hello/').then((response) => { + expect(response.statusCode).toBe(200); + expect(response.text).toBe('

Hello

reload'); + done(); + }); + }, fail); + }); + test('200 other url', (done, fail) => { utils.createEmptyDirs([path.join(dataDir, '2019', '05', '05'),]); utils.createEmptyFiles([ diff --git a/test/file_walker.test.js b/test/file_walker.test.js index 519ad9e..6df5689 100644 --- a/test/file_walker.test.js +++ b/test/file_walker.test.js @@ -236,7 +236,7 @@ describe('Test article fetching', () => { expect(Object.keys(dict).length).toBe(1); expect(dict[joinUrl('2019', '05', '05')]).toEqual({ path: joinUrl('2019', '05', '05'), - realPath: dir, + realPath: file, year: 2019, month: 5, draft: false, @@ -267,7 +267,7 @@ describe('Test article fetching', () => { expect(Object.keys(dict).length).toBe(1); expect(dict[joinUrl('2019', '05', '05')]).toEqual({ path: joinUrl('2019', '05', '05'), - realPath: dir, + realPath: file, year: 2019, month: 5, day: 5, @@ -298,7 +298,7 @@ describe('Test article fetching', () => { expect(Object.keys(dict).length).toBe(1); expect(dict[joinUrl('2019', '05', '05')]).toEqual({ path: joinUrl('2019', '05', '05'), - realPath: dir, + realPath: file, year: 2019, month: 5, day: 5, @@ -326,7 +326,7 @@ describe('Test article fetching', () => { expect(Object.keys(dict).length).toBe(1); expect(dict[joinUrl('2019', '05', '05')]).toEqual({ path: joinUrl('2019', '05', '05'), - realPath: dir, + realPath: file, year: 2019, month: 5, draft: false, From 889258c874269a7e47fed0bb549abe43a3fa5829 Mon Sep 17 00:00:00 2001 From: Klemek Date: Fri, 12 Jul 2019 11:53:02 +0200 Subject: [PATCH 098/120] Update .travis.yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a968088..05465ab 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,5 +11,5 @@ install: before_script: - npm install -g jshint script: - - jest --silent --coverage --coverageReporters=text-lcov | coveralls + - jest --silent --coverage --coverageReporters=text-lcov && cat ./coverage/lcov.info | coveralls - jshint ./src From 4a9b70ac68c16590765a484c48b7df872ce289e9 Mon Sep 17 00:00:00 2001 From: Klemek Date: Fri, 12 Jul 2019 13:53:55 +0200 Subject: [PATCH 099/120] Update .travis.yml --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 05465ab..2a9dc04 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,5 +11,6 @@ install: before_script: - npm install -g jshint script: - - jest --silent --coverage --coverageReporters=text-lcov && cat ./coverage/lcov.info | coveralls - - jshint ./src + - jest --coverage + - jshint . + - cat ./coverage/lcov.info | coveralls From 34e8d4cb6f57b4a1026e83f4e6e39da39605fe34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20GOUIN?= Date: Fri, 12 Jul 2019 13:57:27 +0200 Subject: [PATCH 100/120] updated CI --- .travis.yml | 3 ++- package-lock.json | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index a968088..c6968f7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,5 +11,6 @@ install: before_script: - npm install -g jshint script: - - jest --silent --coverage --coverageReporters=text-lcov | coveralls + - jest --coverage --silent - jshint ./src + - cat ./coverage/lcov.info | coveralls diff --git a/package-lock.json b/package-lock.json index cd14f17..e7cf978 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gitblog.md", - "version": "1.0.3", + "version": "1.1.5", "lockfileVersion": 1, "requires": true, "dependencies": { From 896f302bcf736e903a9ba9b09b0e1aed5381b209 Mon Sep 17 00:00:00 2001 From: Klemek Date: Thu, 18 Jul 2019 14:07:24 +0200 Subject: [PATCH 101/120] updated default template --- sample_data/home/index.ejs | 8 +++++--- sample_data/home/style.css | 27 +++++++++++++++++++++++++-- sample_data/home/template.ejs | 2 +- 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/sample_data/home/index.ejs b/sample_data/home/index.ejs index 0517fdc..aeb4bc4 100644 --- a/sample_data/home/index.ejs +++ b/sample_data/home/index.ejs @@ -6,16 +6,18 @@
-

<%= info.title %>

+

<%= info.title %>

<%= info.description %>

Articles in this blog :

<% articles.forEach((article) => { %>
-

<%- `${article.title}` %>

- Published on <%= article.year + '-' + article.month + '-' + article.day %> + <%- `` %> +

<%- `${article.title}` %>

+ Published on  <%= article.year + '-' + ('0' + article.month).slice(-2) + '-' + ('0' + article.day).slice(-2) %> <% if(article.thumbnail){ %> <%- `thumbnail` %> <% } %> + <%- `
` %>
<% }); %> <%- include('footer'); %> diff --git a/sample_data/home/style.css b/sample_data/home/style.css index e0b0dcc..62a05de 100644 --- a/sample_data/home/style.css +++ b/sample_data/home/style.css @@ -16,7 +16,7 @@ body { } main { - max-width: 42rem; + max-width: 45rem; padding: 2rem; margin: auto; background-color: #F0F0F0; @@ -54,6 +54,13 @@ pre { padding: 10px 16px; } +:not(pre) > code { + padding: 0.25em 0.5em; + border-radius: 0.25em; + background: #DDD; + font-size: 90%; +} + blockquote { border-left: 0.5em solid #ccc; padding-left: 1em; @@ -108,10 +115,11 @@ main.article div.header a.link-home { line-height: 2.4; } -main.article div.header h1, main.article div.header h2 { +main.article div.header h1, main.article div.header h2, .title { margin-top: 0.85em; margin-bottom: 0.25em; font-size: 2em; + font-weight: bold; } main.article div.header h1 a, main.article div.header h2 a, div.article h3 a { @@ -129,6 +137,12 @@ div.article { div.article h3 { font-size: 1.3em; margin:0; + color: #3C3CA1; +} + +div.article a { + text-decoration: none; + color: inherit; } div.article img{ @@ -138,6 +152,15 @@ div.article img{ margin-top:0.25em; } +div.article:hover { + opacity: 0.9; +} + +div.article:active { + opacity: 0.8; +} + + #text { text-align: justify; hyphens: auto; diff --git a/sample_data/home/template.ejs b/sample_data/home/template.ejs index e2590c1..f1f4c82 100644 --- a/sample_data/home/template.ejs +++ b/sample_data/home/template.ejs @@ -9,7 +9,7 @@
↑

<%= article.title %>

- Published on <%= article.year + '-' + article.month + '-' + article.day %> + <%= article.draft ? 'Drafted on' : 'Published on' %>  <%= article.year + '-' + ('0' + article.month).slice(-2) + '-' + ('0' + article.day).slice(-2) %>
<%- article.content %>

From 2e8ff1be92e53c41e08c46ca85aaf117a52140a6 Mon Sep 17 00:00:00 2001 From: Klemek Date: Thu, 18 Jul 2019 16:41:25 +0200 Subject: [PATCH 102/120] fixed mathjax in code --- src/renderer.js | 29 ++++++++++++++++------------- test/renderer.test.js | 6 ++++++ 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/renderer.js b/src/renderer.js index 6094f08..289e7b9 100644 --- a/src/renderer.js +++ b/src/renderer.js @@ -64,7 +64,9 @@ module.exports = (config) => { if (!config['modules']['mathjax']) return cb(data); - const doMJ = (match, format) => { + const spl = data.split('```'); + + const doMJ = (match, format, i) => { const eq = match[1].trim(); const output = config['mathjax']['output_format']; const mjConf = { @@ -74,8 +76,8 @@ module.exports = (config) => { }; mjConf[output] = true; mjAPI.typeset(mjConf, (res) => { - data = data.slice(0, match.index) + res[output] + data.slice(match.index + match[0].length); - renderMathJax(data, (data2) => { + spl[i] = spl[i].slice(0, match.index) + res[output] + spl[i].slice(match.index + match[0].length); + renderMathJax(spl.join('```'), (data2) => { cb(data2); }); }); @@ -84,14 +86,15 @@ module.exports = (config) => { const eqRegex = /\$\$((?:(?!\$\$)[\s\S])*)\$\$/m; const inlineEqRegex = /\$([^$\n]*)\$/; - let match; - if ((match = eqRegex.exec(data))) { - doMJ(match, 'TeX'); - } else if ((match = inlineEqRegex.exec(data))) { - doMJ(match, 'inline-TeX'); - } else { - cb(data); + for (let i = 0; i < spl.length; i += 2) { + let match; + if ((match = eqRegex.exec(spl[i]))) { + return doMJ(match, 'TeX', i); + } else if ((match = inlineEqRegex.exec(spl[i]))) { + return doMJ(match, 'inline-TeX', i); + } } + cb(data); }; return { @@ -104,9 +107,9 @@ module.exports = (config) => { if (err) return cb(err); - renderPrism(data, (data) => { - renderPlantUML(data, (data) => { - renderMathJax(data, (data) => { + renderPlantUML(data, (data) => { + renderMathJax(data, (data) => { + renderPrism(data, (data) => { renderShowDown(data, (html) => { cb(null, html); }); diff --git a/test/renderer.test.js b/test/renderer.test.js index d2cc29d..aa09068 100644 --- a/test/renderer.test.js +++ b/test/renderer.test.js @@ -157,6 +157,12 @@ describe('Test MathJax', () => { done(); }); }); + test('no eq in code', (done) => { + renderer.renderMathJax('this code is ```start $a$ end $$hello$$``` beautiful', (data) => { + expect(data).toBe('this code is ```start $a$ end $$hello$$``` beautiful'); + done(); + }); + }); test('multiple eq', (done) => { renderer.renderMathJax('$$\n\nA\n\n$$\nstart $a$ end\n$$\n\nA\n\n$$', (data) => { expect(data).toBe('' + From 53e1fe720156e2a945cf2fe93f9129d1470b47f5 Mon Sep 17 00:00:00 2001 From: Klemek Date: Fri, 19 Jul 2019 10:01:01 +0200 Subject: [PATCH 103/120] mathjax/plantuml rendering only outside of code/scripts --- src/renderer.js | 80 ++++++++++++++++++++++++++++++++++++------- test/renderer.test.js | 24 +++++++++++++ 2 files changed, 91 insertions(+), 13 deletions(-) diff --git a/src/renderer.js b/src/renderer.js index 289e7b9..2fd6a91 100644 --- a/src/renderer.js +++ b/src/renderer.js @@ -5,6 +5,55 @@ const showdown = require('showdown'); module.exports = (config) => { const converter = new showdown.Converter(config['showdown']); + /** + * get parts outside of codes/scripts + * @param {string} data + * @returns {{index:number, end:number, text:string}[]} parts + */ + const getParts = (data) => { + let parts = []; + let match; + let i = 0; + while ((match = /```/m.exec(data.slice(i)))) { + parts.push({ + index: i, + end: i + match.index, + text: data.slice(i, i + match.index), + }); + i += match.index + match[0].length; + } + if (i < data.length) + parts.push({ + index: i, + end: data.length, + text: data.slice(i, data.length), + }); + + parts = parts.filter((p, i) => i % 2 === 0); //filter out code parts + + // detect scripts outside of code + parts.forEach((p, pi) => { + let i = 0; + const subParts = []; + while ((match = /(\n\n``````kenobi'; + const parts = renderer.getParts(data); + expect(parts.map(p => p.text)).toEqual([ + 'Hello\nthere\n', '\ngeneral', '\n', '\n', 'kenobi' + ]); + }); +}); + describe('Test Showdown', () => { test('normal', (done) => { renderer.renderShowDown('# Hello', (html) => { @@ -112,6 +129,13 @@ describe('Test PlantUML', () => { }); }); + test('plantuml ignored in code', (done) => { + renderer.renderPlantUML('code:\n```@startuml\nBob -> Alice : hello\n@enduml```', (data) => { + expect(data).toBe('code:\n```@startuml\nBob -> Alice : hello\n@enduml```'); + done(); + }); + }); + test('plantuml multiple uml', (done) => { renderer.renderPlantUML('@startuml\nBob -> Alice : hello\n@enduml\n@startuml\nBob -> Alice : hello\n@enduml', (data) => { expect(data).toBe('generated PlantUML diagram\ngenerated PlantUML diagram'); From 4a32995ca11fc74a79b30595173d41299bcf39ee Mon Sep 17 00:00:00 2001 From: Klemek Date: Fri, 19 Jul 2019 10:19:12 +0200 Subject: [PATCH 104/120] fa-diagrams support --- package-lock.json | 26 +++++++++++++++------ package.json | 2 ++ src/config.default.json | 3 ++- src/renderer.js | 50 +++++++++++++++++++++++++++++++++++++---- test/renderer.test.js | 32 +++++++++++++++++++++++++- 5 files changed, 100 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index e7cf978..356f460 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gitblog.md", - "version": "1.1.5", + "version": "1.2.5", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -1337,7 +1337,6 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, "requires": { "sprintf-js": "~1.0.2" } @@ -3190,6 +3189,14 @@ "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" }, + "fa-diagrams": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fa-diagrams/-/fa-diagrams-1.0.3.tgz", + "integrity": "sha512-z9pFYYnc6s1sUs4lq+DY2m5ddLCKN3jdo5O6hv450WwjiQ7otk1RoGQsvpMcwSU1kMEzPJUI6AeTVvzyIElhwQ==", + "requires": { + "xml-js": "^1.6.11" + } + }, "fast-deep-equal": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", @@ -6617,7 +6624,6 @@ "version": "3.13.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, "requires": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -6626,8 +6632,7 @@ "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" } } }, @@ -9087,8 +9092,7 @@ "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, "sshpk": { "version": "1.16.1", @@ -9777,6 +9781,14 @@ "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", "integrity": "sha1-eLpyAgApxbyHuKgaPPzXS0ovweU=" }, + "xml-js": { + "version": "1.6.11", + "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", + "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==", + "requires": { + "sax": "^1.2.4" + } + }, "xml-name-validator": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", diff --git a/package.json b/package.json index b05cb18..5c5b8e2 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,8 @@ "crypto": "^1.0.1", "ejs": "^2.6.2", "express": "^4.17.1", + "fa-diagrams": "^1.0.3", + "js-yaml": "^3.13.1", "mathjax-node": "^2.1.1", "ncp": "^2.0.0", "node-prismjs": "^0.1.2", diff --git a/src/config.default.json b/src/config.default.json index 63e3f92..79fe3f3 100644 --- a/src/config.default.json +++ b/src/config.default.json @@ -10,7 +10,8 @@ "webhook": true, "prism": true, "mathjax": true, - "plantuml": true + "plantuml": true, + "fa-diagrams": true }, "home": { "title": "GitBlog.md", diff --git a/src/renderer.js b/src/renderer.js index 2fd6a91..266feb1 100644 --- a/src/renderer.js +++ b/src/renderer.js @@ -150,22 +150,64 @@ module.exports = (config) => { cb(data); }; + let faDiagrams; + let yaml; + if (config['modules']['fa-diagrams']) { + faDiagrams = require('fa-diagrams'); + yaml = require('js-yaml'); + } + + const renderFaDiagrams = (data, cb) => { + if (!config['modules']['fa-diagrams']) + return cb(data); + const parts = getParts(data); + const diagramsRegex = /@startfad\r?\n((?:(?!@endfad)[\s\S])*)\r?\n@endfad/m; + let match; + parts.forEach(part => { + while ((match = diagramsRegex.exec(part.text))) { + const code = match[1].trim(); + let output; + try { + const diagData = yaml.safeLoad(code); + const findLineBreaks = (data) => { + Object.keys(data).forEach(key => { + if (typeof data[key] === 'object') + findLineBreaks(data[key]); + else if (typeof data[key] === 'string') + data[key] = data[key].replace(/\\n/gm, '\n'); + }); + }; + findLineBreaks(diagData); + output = faDiagrams.compute(diagData); + } catch (err) { + output = `${err.toString()}`; + } + part.text = part.text.slice(0, match.index) + output + part.text.slice(match.index + match[0].length); + } + data = data.slice(0, part.index) + part.text + data.slice(part.end); + }); + cb(data); + }; + return { getParts: config['test'] ? getParts : undefined, renderShowDown: config['test'] ? renderShowDown : undefined, renderPrism: config['test'] ? renderPrism : undefined, renderPlantUML: config['test'] ? renderPlantUML : undefined, renderMathJax: config['test'] ? renderMathJax : undefined, + renderFaDiagrams: config['test'] ? renderFaDiagrams : undefined, render: (file, cb) => { fs.readFile(file, {encoding: 'UTF-8'}, (err, data) => { if (err) return cb(err); renderPlantUML(data, (data) => { - renderMathJax(data, (data) => { - renderPrism(data, (data) => { - renderShowDown(data, (html) => { - cb(null, html); + renderFaDiagrams(data, (data) => { + renderMathJax(data, (data) => { + renderPrism(data, (data) => { + renderShowDown(data, (html) => { + cb(null, html); + }); }); }); }); diff --git a/test/renderer.test.js b/test/renderer.test.js index 5d05f7b..b4324a4 100644 --- a/test/renderer.test.js +++ b/test/renderer.test.js @@ -11,7 +11,8 @@ const config = { 'modules': { 'prism': true, 'mathjax': true, - 'plantuml': true + 'plantuml': true, + 'fa-diagrams': true, }, 'showdown': { 'simplifiedAutoLink': true, @@ -32,6 +33,7 @@ beforeEach(() => { config['modules']['prism'] = true; config['modules']['mathjax'] = true; config['modules']['plantuml'] = true; + config['modules']['fa-diagrams'] = true; utils.deleteFolderSync(dataDir); fs.mkdirSync(dataDir); }); @@ -212,6 +214,34 @@ describe('Test MathJax', () => { }); }); +describe('Test fa-diagrams', () => { + test('no fa-diagrams', (done) => { + config['modules']['fa-diagrams'] = false; + renderer.renderFaDiagrams('@startfad\noptions:\n\trendering:\t\tcolor:red\n\n@endfad', (data) => { + expect(data).toBe('@startfad\noptions:\n\trendering:\t\tcolor:red\n\n@endfad'); + done(); + }); + }); + test('no fa-diagrams in code', (done) => { + renderer.renderFaDiagrams('code:\n```\n@startfad\noptions:\n\trendering:\t\tcolor:red\n\n@endfad\n```', (data) => { + expect(data).toBe('code:\n```\n@startfad\noptions:\n\trendering:\t\tcolor:red\n\n@endfad\n```'); + done(); + }); + }); + test('valid fa-diagrams', (done) => { + renderer.renderFaDiagrams('before\n@startfad\noptions:\n rendering:\n color: red\n@endfad\nafter', (data) => { + expect(data).toBe('before\n\nafter'); + done(); + }); + }); + test('invalid yaml', (done) => { + renderer.renderFaDiagrams('before\n@startfad\noptions:\n@endfad\nafter', (data) => { + expect(data).toBe('before\nTypeError: Cannot convert undefined or null to object\nafter'); + done(); + }); + }); +}); + describe('Test render', () => { test('invalid file', (done) => { renderer.render('invalid file', (err, html) => { From a05d380fcfd9d86798d86ceadc3cf91c6136fc85 Mon Sep 17 00:00:00 2001 From: Klemek Date: Fri, 19 Jul 2019 10:41:01 +0200 Subject: [PATCH 105/120] fixed parts detection --- src/renderer.js | 9 ++++----- test/renderer.test.js | 38 +++++++++++++++++++++++++++++++------- 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/src/renderer.js b/src/renderer.js index 266feb1..af8c3f5 100644 --- a/src/renderer.js +++ b/src/renderer.js @@ -17,7 +17,6 @@ module.exports = (config) => { while ((match = /```/m.exec(data.slice(i)))) { parts.push({ index: i, - end: i + match.index, text: data.slice(i, i + match.index), }); i += match.index + match[0].length; @@ -25,7 +24,6 @@ module.exports = (config) => { if (i < data.length) parts.push({ index: i, - end: data.length, text: data.slice(i, data.length), }); @@ -38,7 +36,6 @@ module.exports = (config) => { while ((match = /(\n\n``````kenobi'; const parts = renderer.getParts(data); - expect(parts.map(p => p.text)).toEqual([ - 'Hello\nthere\n', '\ngeneral', '\n', '\n', 'kenobi' + expect(parts).toEqual([ + { + index: 0, + end: 12, + text: 'Hello\nthere\n' + }, + { + index: 22, + end: 30, + text: '\ngeneral' + }, + { + index: 53, + end: 54, + text: '\n' + }, + { + index: 78, + end: 79, + text: '\n' + }, + { + index: 109, + end: 115, + text: 'kenobi' + }, ]); }); }); @@ -132,8 +156,8 @@ describe('Test PlantUML', () => { }); test('plantuml ignored in code', (done) => { - renderer.renderPlantUML('code:\n```@startuml\nBob -> Alice : hello\n@enduml```', (data) => { - expect(data).toBe('code:\n```@startuml\nBob -> Alice : hello\n@enduml```'); + renderer.renderPlantUML('code:\n```@startuml\nBob -> Alice : hello\n@enduml```\n ```@startuml``` @enduml', (data) => { + expect(data).toBe('code:\n```@startuml\nBob -> Alice : hello\n@enduml```\n ```@startuml``` @enduml'); done(); }); }); @@ -183,9 +207,9 @@ describe('Test MathJax', () => { done(); }); }); - test('no eq in code', (done) => { - renderer.renderMathJax('this code is ```start $a$ end $$hello$$``` beautiful', (data) => { - expect(data).toBe('this code is ```start $a$ end $$hello$$``` beautiful'); + test('no eq in code / script', (done) => { + renderer.renderMathJax('this code is ```start $a$ end $$hello$$``` beautiful \n```$no eq$```', (data) => { + expect(data).toBe('this code is ```start $a$ end $$hello$$``` beautiful \n```$no eq$```'); done(); }); }); From bfa1521f85ce16ea1dd6c76e4159202228e2753e Mon Sep 17 00:00:00 2001 From: Klemek Date: Fri, 19 Jul 2019 10:41:21 +0200 Subject: [PATCH 106/120] updated sample to show fa-diagrams --- sample_data/article/index.md | 25 +++++++++++++++++++++++++ sample_data/home/style.css | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/sample_data/article/index.md b/sample_data/article/index.md index 2154e57..481db60 100644 --- a/sample_data/article/index.md +++ b/sample_data/article/index.md @@ -19,6 +19,7 @@ If you see this page, that means it's working * [Spoilers](#spoilers) * [Math Equations](#mathequations) * [UML](#uml) +* [Diagrams](#diagrams) * [Youtube Videos](#youtubevideos) ### Headers @@ -253,6 +254,30 @@ showdown -left-> express : 4. html express -up-> web : 5. html @enduml +### Diagrams +[Back to top](#top) + +You can use [fa-diagrams](https://github.com/Klemek/fa-diagrams) with `@startfad` and `@endfad` tags and using YAML inside + +@startuml +nodes: + - name: node1 + icon: laptop-code + color: '#4E342E' + bottom: my app + - name: node2 + icon: globe + color: '#455A64' + bottom: world +links: + - from: node1 + to: node2 + color: '#333333' + top: + icon: envelope + bottom: '"hello"' +@enduml + ### Youtube Videos [Back to top](#top) diff --git a/sample_data/home/style.css b/sample_data/home/style.css index 62a05de..01bbc72 100644 --- a/sample_data/home/style.css +++ b/sample_data/home/style.css @@ -170,7 +170,7 @@ div.article:active { text-align: left; } -#text img { +#text img, #text svg { max-width: 100%; height: auto; } From bbc4d7c270eb40d003a986f5bda8aecd9cca6474 Mon Sep 17 00:00:00 2001 From: Klemek Date: Fri, 19 Jul 2019 10:42:25 +0200 Subject: [PATCH 107/120] [skip CI] updated README.md --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3f41137..82a523d 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,8 @@ -# GitBlog.md - - [![Build Status](https://img.shields.io/travis/Klemek/GitBlog.md.svg?branch=master)](https://travis-ci.org/Klemek/GitBlog.md) [![Coverage Status](https://img.shields.io/coveralls/github/Klemek/GitBlog.md.svg?branch=master)](https://coveralls.io/github/Klemek/GitBlog.md?branch=master) +# GitBlog.md + A static blog using Markdown pulled from your git repository. > Step 1 : ```$ vi 2019/06/21/index.md``` From 02a768a6afc36791140950ac6fc5b9e517775f8d Mon Sep 17 00:00:00 2001 From: Klemek Date: Fri, 19 Jul 2019 10:44:26 +0200 Subject: [PATCH 108/120] [skip CI] updated README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 82a523d..43adc9a 100644 --- a/README.md +++ b/README.md @@ -244,6 +244,9 @@ Any URL like `/year/month/day/anything/` will redirect to this article (and link It allows you to add math equations to your articles by simply writing LaTeX between `$$` for full size (and between $ for inline) (more info [here](https://www.mathjax.org/)) * **PlantUML** It allows you to add UML diagrams with PlantUML Syntax between `@startuml` and `@enduml` (more info [here](http://www.plantuml.com)) +* **fa-diagrams** + It allows you to define SVG diagrams with Font-Awesome icons in YAML between `@startfad` and `@endfad` (more info [here](https://github.com/Klemek/fa-diagrams)) + ## Configuration [back to top](#gitblog-md) @@ -272,6 +275,8 @@ Any URL like `/year/month/day/anything/` will redirect to this article (and link activate MathJax equations formatting * `plantuml` (default: true) activate PlantUML diagram rendering + * `fa-diagrams` (default: true) + activate fa-diagrams rendering * `home` * `title` (default: GitBlog.md) the title of your blog, **strongly advised to be changed** From ca49a29dd917a2960a12c97fd26006d14ff08142 Mon Sep 17 00:00:00 2001 From: Klemek Date: Fri, 19 Jul 2019 10:44:58 +0200 Subject: [PATCH 109/120] [skip CI] updated version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5c5b8e2..ebb236b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitblog.md", - "version": "1.2.5", + "version": "1.2.6", "description": "A static blog using Markdown pulled from your git repository.", "main": "src/server.js", "dependencies": { From d426b4136825ef2add6515fae319b023d6c94dcf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 19 Jul 2019 08:48:39 +0000 Subject: [PATCH 110/120] Bump lodash from 4.17.11 to 4.17.15 Bumps [lodash](https://github.com/lodash/lodash) from 4.17.11 to 4.17.15. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/compare/4.17.11...4.17.15) Signed-off-by: dependabot[bot] --- package-lock.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 356f460..15c9437 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gitblog.md", - "version": "1.2.5", + "version": "1.2.6", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -6789,9 +6789,9 @@ } }, "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" }, "lodash.sortby": { "version": "4.7.0", From babc533efcf9c03165f551db048e88da2d7c3ef8 Mon Sep 17 00:00:00 2001 From: Klemek Date: Fri, 19 Jul 2019 10:56:26 +0200 Subject: [PATCH 111/120] updated package-lock --- package-lock.json | 159 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 112 insertions(+), 47 deletions(-) diff --git a/package-lock.json b/package-lock.json index 356f460..beee0cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gitblog.md", - "version": "1.2.5", + "version": "1.2.6", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -2687,11 +2687,13 @@ } }, "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", "requires": { - "lru-cache": "^4.0.1", + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", "shebang-command": "^1.2.0", "which": "^1.2.9" } @@ -2914,7 +2916,6 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", - "dev": true, "requires": { "once": "^1.4.0" } @@ -3011,12 +3012,12 @@ "dev": true }, "execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", "is-stream": "^1.1.0", "npm-run-path": "^2.0.0", "p-finally": "^1.0.0", @@ -3884,9 +3885,12 @@ "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" }, "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } }, "get-value": { "version": "2.0.6", @@ -4197,9 +4201,9 @@ } }, "invert-kv": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-3.0.0.tgz", + "integrity": "sha512-JzF8q2BeZA1ZkE3XROwRpoMQ9ObMgTtp0JH8EXewlbkikuOj2GPLIpUipdO+VL8QsTr2teAJD02EFGGL5cO7uw==" }, "ipaddr.js": { "version": "1.9.0", @@ -6734,11 +6738,11 @@ "dev": true }, "lcid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-3.0.0.tgz", + "integrity": "sha512-cT68NXBqcByQPcYCoMArX8SI5SuzOTjF7nibqHlf24K01zYbFifLHaacZi8Ye3dvmBP9XaatbRO8uXM6YPHlSg==", "requires": { - "invert-kv": "^1.0.0" + "invert-kv": "^3.0.0" } }, "lcov-parse": { @@ -6853,7 +6857,6 @@ "version": "0.1.3", "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", - "dev": true, "requires": { "p-defer": "^1.0.0" } @@ -6901,11 +6904,13 @@ "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, "mem": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", - "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/mem/-/mem-5.1.1.tgz", + "integrity": "sha512-qvwipnozMohxLXG1pOqoLiZKNkC4r4qqRucSoDwXowsNGDSULiqFTRUF05vcZWnwJSG22qTsynQhxbaMtnX9gw==", "requires": { - "mimic-fn": "^1.0.0" + "map-age-cleaner": "^0.1.3", + "mimic-fn": "^2.1.0", + "p-is-promise": "^2.1.0" } }, "merge-descriptors": { @@ -6982,9 +6987,9 @@ } }, "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" }, "minimatch": { "version": "3.0.4", @@ -7107,8 +7112,7 @@ "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" }, "node-int64": { "version": "0.4.0", @@ -7309,7 +7313,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1" } @@ -7351,13 +7354,13 @@ "dev": true }, "os-locale": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", - "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-4.0.0.tgz", + "integrity": "sha512-HsSR1+2l6as4Wp2SGZxqLnuFHxVvh1Ir9pvZxyujsC13egZVe7P0YeBLN0ijQzM/twrO5To3ia3jzBXAvpMTEA==", "requires": { - "execa": "^0.7.0", - "lcid": "^1.0.0", - "mem": "^1.1.0" + "execa": "^1.0.0", + "lcid": "^3.0.0", + "mem": "^5.0.0" } }, "os-tmpdir": { @@ -7380,8 +7383,7 @@ "p-defer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", - "dev": true + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=" }, "p-each-series": { "version": "1.0.0", @@ -7400,8 +7402,7 @@ "p-is-promise": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", - "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", - "dev": true + "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==" }, "p-limit": { "version": "1.3.0", @@ -7684,7 +7685,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, "requires": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -8788,8 +8788,7 @@ "semver": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", - "dev": true + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" }, "send": { "version": "0.17.1", @@ -9754,8 +9753,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "write-file-atomic": { "version": "2.4.1", @@ -9821,6 +9819,73 @@ "which-module": "^2.0.0", "y18n": "^3.2.1", "yargs-parser": "^8.1.0" + }, + "dependencies": { + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "requires": { + "invert-kv": "^1.0.0" + } + }, + "mem": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", + "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" + }, + "os-locale": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", + "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "requires": { + "execa": "^0.7.0", + "lcid": "^1.0.0", + "mem": "^1.1.0" + } + } } }, "yargs-parser": { From c9f57233a4e66c04918148a848a103797ad9e19c Mon Sep 17 00:00:00 2001 From: Klemek Date: Mon, 19 Aug 2019 14:13:54 +0200 Subject: [PATCH 112/120] faDiagram switch to TOML lang --- README.md | 2 +- package-lock.json | 96 +++++++----------------------------- package.json | 1 + sample_data/article/index.md | 42 +++++++++------- src/renderer.js | 6 +-- test/renderer.test.js | 17 ++++--- 6 files changed, 57 insertions(+), 107 deletions(-) diff --git a/README.md b/README.md index 43adc9a..45bc3d4 100644 --- a/README.md +++ b/README.md @@ -245,7 +245,7 @@ Any URL like `/year/month/day/anything/` will redirect to this article (and link * **PlantUML** It allows you to add UML diagrams with PlantUML Syntax between `@startuml` and `@enduml` (more info [here](http://www.plantuml.com)) * **fa-diagrams** - It allows you to define SVG diagrams with Font-Awesome icons in YAML between `@startfad` and `@endfad` (more info [here](https://github.com/Klemek/fa-diagrams)) + It allows you to define SVG diagrams with Font-Awesome icons in [TOML](https://github.com/toml-lang/toml) between `@startfad` and `@endfad` (more info [here](https://github.com/Klemek/fa-diagrams)) ## Configuration diff --git a/package-lock.json b/package-lock.json index beee0cd..158a806 100644 --- a/package-lock.json +++ b/package-lock.json @@ -285,6 +285,11 @@ } } }, + "@iarna/toml": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.3.tgz", + "integrity": "sha512-FmuxfCuolpLl0AnQ2NHSzoUKWEJDFl63qXjzdoWBVyFCXzMGm1spBzk7LeHNoVCiWCF7mRVms9e6jEV9+MoPbg==" + }, "@jest/console": { "version": "24.7.1", "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.7.1.tgz", @@ -2686,18 +2691,6 @@ } } }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, "crypto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", @@ -2916,6 +2909,7 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "dev": true, "requires": { "once": "^1.4.0" } @@ -3011,20 +3005,6 @@ "integrity": "sha512-9sLAvzhI5nc8TpuQUh4ahMdCrWT00wPWz7j47/emR5+2qEfoZP5zzUXvx+vdx+H6ohhnsYC31iX04QLYJK8zTg==", "dev": true }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, "exit": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", @@ -3884,14 +3864,6 @@ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "requires": { - "pump": "^3.0.0" - } - }, "get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", @@ -4200,11 +4172,6 @@ "loose-envify": "^1.0.0" } }, - "invert-kv": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-3.0.0.tgz", - "integrity": "sha512-JzF8q2BeZA1ZkE3XROwRpoMQ9ObMgTtp0JH8EXewlbkikuOj2GPLIpUipdO+VL8QsTr2teAJD02EFGGL5cO7uw==" - }, "ipaddr.js": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", @@ -6737,14 +6704,6 @@ "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "dev": true }, - "lcid": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-3.0.0.tgz", - "integrity": "sha512-cT68NXBqcByQPcYCoMArX8SI5SuzOTjF7nibqHlf24K01zYbFifLHaacZi8Ye3dvmBP9XaatbRO8uXM6YPHlSg==", - "requires": { - "invert-kv": "^3.0.0" - } - }, "lcov-parse": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/lcov-parse/-/lcov-parse-0.0.10.tgz", @@ -6857,6 +6816,7 @@ "version": "0.1.3", "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "dev": true, "requires": { "p-defer": "^1.0.0" } @@ -6903,16 +6863,6 @@ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, - "mem": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/mem/-/mem-5.1.1.tgz", - "integrity": "sha512-qvwipnozMohxLXG1pOqoLiZKNkC4r4qqRucSoDwXowsNGDSULiqFTRUF05vcZWnwJSG22qTsynQhxbaMtnX9gw==", - "requires": { - "map-age-cleaner": "^0.1.3", - "mimic-fn": "^2.1.0", - "p-is-promise": "^2.1.0" - } - }, "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -6986,11 +6936,6 @@ "mime-db": "1.40.0" } }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" - }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -7112,7 +7057,8 @@ "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true }, "node-int64": { "version": "0.4.0", @@ -7313,6 +7259,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, "requires": { "wrappy": "1" } @@ -7353,16 +7300,6 @@ "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true }, - "os-locale": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-4.0.0.tgz", - "integrity": "sha512-HsSR1+2l6as4Wp2SGZxqLnuFHxVvh1Ir9pvZxyujsC13egZVe7P0YeBLN0ijQzM/twrO5To3ia3jzBXAvpMTEA==", - "requires": { - "execa": "^1.0.0", - "lcid": "^3.0.0", - "mem": "^5.0.0" - } - }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", @@ -7383,7 +7320,8 @@ "p-defer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=" + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", + "dev": true }, "p-each-series": { "version": "1.0.0", @@ -7402,7 +7340,8 @@ "p-is-promise": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", - "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==" + "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", + "dev": true }, "p-limit": { "version": "1.3.0", @@ -7685,6 +7624,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, "requires": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -8788,7 +8728,8 @@ "semver": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "dev": true }, "send": { "version": "0.17.1", @@ -9753,7 +9694,8 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true }, "write-file-atomic": { "version": "2.4.1", diff --git a/package.json b/package.json index ebb236b..55ffad8 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "description": "A static blog using Markdown pulled from your git repository.", "main": "src/server.js", "dependencies": { + "@iarna/toml": "^2.2.3", "body-parser": "^1.19.0", "crypto": "^1.0.1", "ejs": "^2.6.2", diff --git a/sample_data/article/index.md b/sample_data/article/index.md index 481db60..c4911a8 100644 --- a/sample_data/article/index.md +++ b/sample_data/article/index.md @@ -257,26 +257,30 @@ express -up-> web : 5. html ### Diagrams [Back to top](#top) -You can use [fa-diagrams](https://github.com/Klemek/fa-diagrams) with `@startfad` and `@endfad` tags and using YAML inside +You can use [fa-diagrams](https://github.com/Klemek/fa-diagrams) with `@startfad` and `@endfad` tags and using [TOML](https://github.com/toml-lang/toml) inside -@startuml -nodes: - - name: node1 - icon: laptop-code - color: '#4E342E' - bottom: my app - - name: node2 - icon: globe - color: '#455A64' - bottom: world -links: - - from: node1 - to: node2 - color: '#333333' - top: - icon: envelope - bottom: '"hello"' -@enduml +@startfad +[[nodes]] +name = "node1" +icon = "laptop-code" +color = "#4E342E" +bottom = "my app" + +[[nodes]] +name = "node2" +icon = "globe" +color = "#455A64" +bottom = "world" + +[[links]] +from = "node1" +to = "node2" +color = "#333333" +bottom = '"hello"' + + [links.top] + icon = "envelope" +@endfad ### Youtube Videos [Back to top](#top) diff --git a/src/renderer.js b/src/renderer.js index af8c3f5..f283840 100644 --- a/src/renderer.js +++ b/src/renderer.js @@ -150,10 +150,10 @@ module.exports = (config) => { }; let faDiagrams; - let yaml; + let toml; if (config['modules']['fa-diagrams']) { faDiagrams = require('fa-diagrams'); - yaml = require('js-yaml'); + toml = require('@iarna/toml'); } const renderFaDiagrams = (data, cb) => { @@ -167,7 +167,7 @@ module.exports = (config) => { const code = match[1].trim(); let output; try { - const diagData = yaml.safeLoad(code); + const diagData = toml.parse(code); const findLineBreaks = (data) => { Object.keys(data).forEach(key => { if (typeof data[key] === 'object') diff --git a/test/renderer.test.js b/test/renderer.test.js index 16addbe..953cff0 100644 --- a/test/renderer.test.js +++ b/test/renderer.test.js @@ -241,26 +241,29 @@ describe('Test MathJax', () => { describe('Test fa-diagrams', () => { test('no fa-diagrams', (done) => { config['modules']['fa-diagrams'] = false; - renderer.renderFaDiagrams('@startfad\noptions:\n\trendering:\t\tcolor:red\n\n@endfad', (data) => { - expect(data).toBe('@startfad\noptions:\n\trendering:\t\tcolor:red\n\n@endfad'); + renderer.renderFaDiagrams('@startfad\noptions.rendering.color=\'red\'\n@endfad', (data) => { + expect(data).toBe('@startfad\noptions.rendering.color=\'red\'\n@endfad'); done(); }); }); test('no fa-diagrams in code', (done) => { - renderer.renderFaDiagrams('code:\n```\n@startfad\noptions:\n\trendering:\t\tcolor:red\n\n@endfad\n```', (data) => { - expect(data).toBe('code:\n```\n@startfad\noptions:\n\trendering:\t\tcolor:red\n\n@endfad\n```'); + renderer.renderFaDiagrams('code:\n```\n@startfad\noptions.rendering.color=\'red\'\n@endfad\n```', (data) => { + expect(data).toBe('code:\n```\n@startfad\noptions.rendering.color=\'red\'\n@endfad\n```'); done(); }); }); test('valid fa-diagrams', (done) => { - renderer.renderFaDiagrams('before\n@startfad\noptions:\n rendering:\n color: red\n@endfad\nafter', (data) => { + renderer.renderFaDiagrams('before\n@startfad\noptions.rendering.color=\'red\'\n@endfad\nafter', (data) => { expect(data).toBe('before\n\nafter'); done(); }); }); test('invalid yaml', (done) => { - renderer.renderFaDiagrams('before\n@startfad\noptions:\n@endfad\nafter', (data) => { - expect(data).toBe('before\nTypeError: Cannot convert undefined or null to object\nafter'); + renderer.renderFaDiagrams('before\n@startfad\noptions.rendering.color=red\n@endfad\nafter', (data) => { + expect(data).toBe('before\nTomlError: Unexpected character, expecting string, number, datetime, boolean, inline array or inline table at row 1, col 26, pos 25:\n' + + '1> options.rendering.color=red\n' + + ' ^\n' + + '\n\nafter'); done(); }); }); From a27a53e238af139df8213abac00ffba6e1b75f98 Mon Sep 17 00:00:00 2001 From: Klemek Date: Mon, 19 Aug 2019 14:14:52 +0200 Subject: [PATCH 113/120] faDiagram switch to TOML lang --- package-lock.json | 8 ++++++-- package.json | 1 - test/renderer.test.js | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7ea53dc..893237a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1342,6 +1342,7 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, "requires": { "sprintf-js": "~1.0.2" } @@ -6595,6 +6596,7 @@ "version": "3.13.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, "requires": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -6603,7 +6605,8 @@ "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true } } }, @@ -9032,7 +9035,8 @@ "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true }, "sshpk": { "version": "1.16.1", diff --git a/package.json b/package.json index 55ffad8..f5aabd8 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,6 @@ "ejs": "^2.6.2", "express": "^4.17.1", "fa-diagrams": "^1.0.3", - "js-yaml": "^3.13.1", "mathjax-node": "^2.1.1", "ncp": "^2.0.0", "node-prismjs": "^0.1.2", diff --git a/test/renderer.test.js b/test/renderer.test.js index 953cff0..3af15aa 100644 --- a/test/renderer.test.js +++ b/test/renderer.test.js @@ -258,7 +258,7 @@ describe('Test fa-diagrams', () => { done(); }); }); - test('invalid yaml', (done) => { + test('invalid toml', (done) => { renderer.renderFaDiagrams('before\n@startfad\noptions.rendering.color=red\n@endfad\nafter', (data) => { expect(data).toBe('before\nTomlError: Unexpected character, expecting string, number, datetime, boolean, inline array or inline table at row 1, col 26, pos 25:\n' + '1> options.rendering.color=red\n' + From add01b28fedb5fecd8800b1d817b03e8af42dad7 Mon Sep 17 00:00:00 2001 From: Klemek Date: Mon, 19 Aug 2019 14:25:01 +0200 Subject: [PATCH 114/120] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f5aabd8..67cee62 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitblog.md", - "version": "1.2.6", + "version": "1.2.7", "description": "A static blog using Markdown pulled from your git repository.", "main": "src/server.js", "dependencies": { From dd5af2b8655aee92498b4066cc4fc613771397c3 Mon Sep 17 00:00:00 2001 From: Klemek Date: Thu, 19 Sep 2019 09:26:17 +0200 Subject: [PATCH 115/120] [skip CI] update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 45bc3d4..74d8a24 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ [![Build Status](https://img.shields.io/travis/Klemek/GitBlog.md.svg?branch=master)](https://travis-ci.org/Klemek/GitBlog.md) [![Coverage Status](https://img.shields.io/coveralls/github/Klemek/GitBlog.md.svg?branch=master)](https://coveralls.io/github/Klemek/GitBlog.md?branch=master) +[![Language grade: JavaScript](https://img.shields.io/lgtm/grade/javascript/g/Klemek/GitBlog.md.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/Klemek/GitBlog.md/context:javascript) +[![Total alerts](https://img.shields.io/lgtm/alerts/g/Klemek/GitBlog.md.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/Klemek/GitBlog.md/alerts/) # GitBlog.md From 99e4bb5c4da6824e4029b5d7b06e686e8404cb96 Mon Sep 17 00:00:00 2001 From: Klemek Date: Thu, 19 Sep 2019 19:03:49 +0200 Subject: [PATCH 116/120] [skip CI] lgtm config --- lgtm.yml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 lgtm.yml diff --git a/lgtm.yml b/lgtm.yml new file mode 100644 index 0000000..7643c5b --- /dev/null +++ b/lgtm.yml @@ -0,0 +1,7 @@ +path_classifiers: + test: + - test + docs: + - uml + library: + - src/lib \ No newline at end of file From c9ef93088b22e4464d187bfb1ff25fa30c0c9368 Mon Sep 17 00:00:00 2001 From: Klemek Date: Thu, 19 Sep 2019 19:13:41 +0200 Subject: [PATCH 117/120] express rate limit --- package-lock.json | 7 ++++++- package.json | 1 + src/app.js | 8 ++++++++ src/config.default.json | 1 + 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 893237a..49b9f40 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gitblog.md", - "version": "1.2.6", + "version": "1.2.7", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -3130,6 +3130,11 @@ "vary": "~1.1.2" } }, + "express-rate-limit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-5.0.0.tgz", + "integrity": "sha512-dhT57wqxfqmkOi4HM7NuT4Gd7gbUgSK2ocG27Y6lwm8lbOAw9XQfeANawGq8wLDtlGPO1ZgDj0HmKsykTxfFAg==" + }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", diff --git a/package.json b/package.json index 67cee62..ae9e65d 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "crypto": "^1.0.1", "ejs": "^2.6.2", "express": "^4.17.1", + "express-rate-limit": "^5.0.0", "fa-diagrams": "^1.0.3", "mathjax-node": "^2.1.1", "ncp": "^2.0.0", diff --git a/src/app.js b/src/app.js index 5496fe2..7dfaf50 100644 --- a/src/app.js +++ b/src/app.js @@ -3,6 +3,7 @@ const app = express(); const fs = require('fs'); const path = require('path'); const pjson = require('../package.json'); +const rateLimit = require('express-rate-limit'); app.enable('trust proxy'); @@ -122,6 +123,13 @@ module.exports = (config) => { next(); }); + //rate limit for safer server + const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: config['rate_limit'] + }); + app.use(limiter); + //log request at result end app.use((req, res, next) => { if (config['access_log']) { diff --git a/src/config.default.json b/src/config.default.json index 79fe3f3..f0d0678 100644 --- a/src/config.default.json +++ b/src/config.default.json @@ -3,6 +3,7 @@ "host": "", "data_dir": "data", "view_engine": "ejs", + "rate_limit": 100, "access_log": "access.log", "error_log": "error.log", "modules": { From 7e9e1e19faf088dbbf21ffbcfad7e4ddac01ebad Mon Sep 17 00:00:00 2001 From: Klemek Date: Thu, 19 Sep 2019 19:14:16 +0200 Subject: [PATCH 118/120] changed version number --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ae9e65d..43266f0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitblog.md", - "version": "1.2.7", + "version": "1.2.8", "description": "A static blog using Markdown pulled from your git repository.", "main": "src/server.js", "dependencies": { From ae2eb52cf836d7bbdbf5b50546c65473de9d981e Mon Sep 17 00:00:00 2001 From: Klemek Date: Thu, 19 Sep 2019 19:16:31 +0200 Subject: [PATCH 119/120] [skip CI]updated README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 45bc3d4..f24e25b 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ [![Build Status](https://img.shields.io/travis/Klemek/GitBlog.md.svg?branch=master)](https://travis-ci.org/Klemek/GitBlog.md) [![Coverage Status](https://img.shields.io/coveralls/github/Klemek/GitBlog.md.svg?branch=master)](https://coveralls.io/github/Klemek/GitBlog.md?branch=master) +[![Language grade: JavaScript](https://img.shields.io/lgtm/grade/javascript/g/Klemek/GitBlog.md.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/Klemek/GitBlog.md/context:javascript) +[![Total alerts](https://img.shields.io/lgtm/alerts/g/Klemek/GitBlog.md.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/Klemek/GitBlog.md/alerts/) # GitBlog.md @@ -260,6 +262,8 @@ Any URL like `/year/month/day/anything/` will redirect to this article (and link the directory where will be located the git repo with templates and articles * `view_engine` (default: ejs) the Express view engine used to render pages from templates +* `rate_limit` (default: 100) + number of requests allowed in a time-frame of 15 minutes * `access_log` (default: access.log) log file where to save access requests (empty to disable) * `error_log` (default: error.log) From 7245876b07821edd51532fd89b96d971b7b2f2f5 Mon Sep 17 00:00:00 2001 From: Klemek Date: Thu, 19 Sep 2019 19:46:09 +0200 Subject: [PATCH 120/120] dependencies fix --- package-lock.json | 6086 +++++++++------------------------------------ package.json | 3 - 2 files changed, 1129 insertions(+), 4960 deletions(-) diff --git a/package-lock.json b/package-lock.json index 49b9f40..ccfe23a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,35 +1,35 @@ { "name": "gitblog.md", - "version": "1.2.7", + "version": "1.2.8", "lockfileVersion": 1, "requires": true, "dependencies": { "@babel/code-frame": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", - "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", + "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", "dev": true, "requires": { "@babel/highlight": "^7.0.0" } }, "@babel/core": { - "version": "7.4.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.4.5.tgz", - "integrity": "sha512-OvjIh6aqXtlsA8ujtGKfC7LYWksYSX8yQcM8Ay3LuvVeQ63lcOKgoZWVqcpFwkd29aYU9rVx7jxhfhiEDV9MZA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.6.0.tgz", + "integrity": "sha512-FuRhDRtsd6IptKpHXAa+4WPZYY2ZzgowkbLBecEDDSje1X/apG7jQM33or3NdOmjXBKWGOg4JmSiRfUfuTtHXw==", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.4.4", - "@babel/helpers": "^7.4.4", - "@babel/parser": "^7.4.5", - "@babel/template": "^7.4.4", - "@babel/traverse": "^7.4.5", - "@babel/types": "^7.4.4", + "@babel/code-frame": "^7.5.5", + "@babel/generator": "^7.6.0", + "@babel/helpers": "^7.6.0", + "@babel/parser": "^7.6.0", + "@babel/template": "^7.6.0", + "@babel/traverse": "^7.6.0", + "@babel/types": "^7.6.0", "convert-source-map": "^1.1.0", "debug": "^4.1.0", "json5": "^2.1.0", - "lodash": "^4.17.11", + "lodash": "^4.17.13", "resolve": "^1.3.2", "semver": "^5.4.1", "source-map": "^0.5.0" @@ -44,46 +44,37 @@ "ms": "^2.1.1" } }, - "json5": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.0.tgz", - "integrity": "sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ==", - "dev": true, - "requires": { - "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 - }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true } } }, "@babel/generator": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.4.tgz", - "integrity": "sha512-53UOLK6TVNqKxf7RUh8NE851EHRxOOeVXKbK2bivdb+iziMyk03Sr4eaE9OELCbyZAAafAKPDwF2TPUES5QbxQ==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.0.tgz", + "integrity": "sha512-Ms8Mo7YBdMMn1BYuNtKuP/z0TgEIhbcyB8HVR6PPNYp4P61lMsABiS4A3VG1qznjXVCf3r+fVHhm4efTYVsySA==", "dev": true, "requires": { - "@babel/types": "^7.4.4", + "@babel/types": "^7.6.0", "jsesc": "^2.5.1", - "lodash": "^4.17.11", + "lodash": "^4.17.13", "source-map": "^0.5.0", "trim-right": "^1.0.1" }, "dependencies": { - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", "dev": true } } @@ -124,68 +115,31 @@ } }, "@babel/helpers": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.4.4.tgz", - "integrity": "sha512-igczbR/0SeuPR8RFfC7tGrbdTbFL3QTvH6D+Z6zNxnTe//GyqmtHmDkzrqDmyZ3eSwPqB/LhyKoU5DXsp+Vp2A==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.6.0.tgz", + "integrity": "sha512-W9kao7OBleOjfXtFGgArGRX6eCP0UEcA2ZWEWNkJdRZnHhW4eEbeswbG3EwaRsnQUAEGWYgMq1HsIXuNNNy2eQ==", "dev": true, "requires": { - "@babel/template": "^7.4.4", - "@babel/traverse": "^7.4.4", - "@babel/types": "^7.4.4" + "@babel/template": "^7.6.0", + "@babel/traverse": "^7.6.0", + "@babel/types": "^7.6.0" } }, "@babel/highlight": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", - "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", + "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", "dev": true, "requires": { "chalk": "^2.0.0", "esutils": "^2.0.2", "js-tokens": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } } }, "@babel/parser": { - "version": "7.4.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.5.tgz", - "integrity": "sha512-9mUqkL1FF5T7f0WDFfAoDdiMVPWsdD1gZYzSnaXsxUCUqzuch/8of9G3VUSNiZmMBoRxT3neyVsqeiL/ZPcjew==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.0.tgz", + "integrity": "sha512-+o2q111WEx4srBs7L9eJmcwi655eD8sXniLqMB93TBK9GrNzGrxDWSjiqz2hLU0Ha8MTXFIP0yd9fNdP+m43ZQ==", "dev": true }, "@babel/plugin-syntax-object-rest-spread": { @@ -198,31 +152,31 @@ } }, "@babel/template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.4.tgz", - "integrity": "sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.6.0.tgz", + "integrity": "sha512-5AEH2EXD8euCk446b7edmgFdub/qfH1SN6Nii3+fyXP807QRx9Q73A2N5hNwRRslC2H9sNzaFhsPubkS4L8oNQ==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.4.4", - "@babel/types": "^7.4.4" + "@babel/parser": "^7.6.0", + "@babel/types": "^7.6.0" } }, "@babel/traverse": { - "version": "7.4.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.5.tgz", - "integrity": "sha512-Vc+qjynwkjRmIFGxy0KYoPj4FdVDxLej89kMHFsWScq999uX+pwcX4v9mWRjW0KcAYTPAuVQl2LKP1wEVLsp+A==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.6.0.tgz", + "integrity": "sha512-93t52SaOBgml/xY74lsmt7xOR4ufYvhb5c5qiM6lu4J/dWGMAfAh6eKw4PjLes6DI6nQgearoxnFJk60YchpvQ==", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.4.4", + "@babel/code-frame": "^7.5.5", + "@babel/generator": "^7.6.0", "@babel/helper-function-name": "^7.1.0", "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.4.5", - "@babel/types": "^7.4.4", + "@babel/parser": "^7.6.0", + "@babel/types": "^7.6.0", "debug": "^4.1.0", "globals": "^11.1.0", - "lodash": "^4.17.11" + "lodash": "^4.17.13" }, "dependencies": { "debug": { @@ -234,12 +188,6 @@ "ms": "^2.1.1" } }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -249,22 +197,14 @@ } }, "@babel/types": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", - "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", + "version": "7.6.1", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.6.1.tgz", + "integrity": "sha512-X7gdiuaCmA0uRjCmRtYJNAVCc/q+5xSgsfKJHqMN4iNLILX39677fJE1O40arPMh0TTtS9ItH67yre6c7k6t0g==", "dev": true, "requires": { "esutils": "^2.0.2", - "lodash": "^4.17.11", + "lodash": "^4.17.13", "to-fast-properties": "^2.0.0" - }, - "dependencies": { - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true - } } }, "@cnakazawa/watch": { @@ -275,14 +215,6 @@ "requires": { "exec-sh": "^0.3.2", "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 - } } }, "@iarna/toml": { @@ -291,85 +223,49 @@ "integrity": "sha512-FmuxfCuolpLl0AnQ2NHSzoUKWEJDFl63qXjzdoWBVyFCXzMGm1spBzk7LeHNoVCiWCF7mRVms9e6jEV9+MoPbg==" }, "@jest/console": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.7.1.tgz", - "integrity": "sha512-iNhtIy2M8bXlAOULWVTUxmnelTLFneTNEkHCgPmgd+zNwy9zVddJ6oS5rZ9iwoscNdT5mMwUd0C51v/fSlzItg==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", + "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", "dev": true, "requires": { - "@jest/source-map": "^24.3.0", + "@jest/source-map": "^24.9.0", "chalk": "^2.0.1", "slash": "^2.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } } }, "@jest/core": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-24.8.0.tgz", - "integrity": "sha512-R9rhAJwCBQzaRnrRgAdVfnglUuATXdwTRsYqs6NMdVcAl5euG8LtWDe+fVkN27YfKVBW61IojVsXKaOmSnqd/A==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-24.9.0.tgz", + "integrity": "sha512-Fogg3s4wlAr1VX7q+rhV9RVnUv5tD7VuWfYy1+whMiWUrvl7U3QJSJyWcDio9Lq2prqYsZaeTv2Rz24pWGkJ2A==", "dev": true, "requires": { "@jest/console": "^24.7.1", - "@jest/reporters": "^24.8.0", - "@jest/test-result": "^24.8.0", - "@jest/transform": "^24.8.0", - "@jest/types": "^24.8.0", + "@jest/reporters": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", "ansi-escapes": "^3.0.0", "chalk": "^2.0.1", "exit": "^0.1.2", "graceful-fs": "^4.1.15", - "jest-changed-files": "^24.8.0", - "jest-config": "^24.8.0", - "jest-haste-map": "^24.8.0", - "jest-message-util": "^24.8.0", + "jest-changed-files": "^24.9.0", + "jest-config": "^24.9.0", + "jest-haste-map": "^24.9.0", + "jest-message-util": "^24.9.0", "jest-regex-util": "^24.3.0", - "jest-resolve-dependencies": "^24.8.0", - "jest-runner": "^24.8.0", - "jest-runtime": "^24.8.0", - "jest-snapshot": "^24.8.0", - "jest-util": "^24.8.0", - "jest-validate": "^24.8.0", - "jest-watcher": "^24.8.0", + "jest-resolve": "^24.9.0", + "jest-resolve-dependencies": "^24.9.0", + "jest-runner": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-snapshot": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "jest-watcher": "^24.9.0", "micromatch": "^3.1.10", "p-each-series": "^1.0.0", - "pirates": "^4.0.1", "realpath-native": "^1.1.0", "rimraf": "^2.5.4", + "slash": "^2.0.0", "strip-ansi": "^5.0.0" }, "dependencies": { @@ -379,300 +275,6 @@ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, "strip-ansi": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", @@ -681,51 +283,42 @@ "requires": { "ansi-regex": "^4.1.0" } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } } } }, "@jest/environment": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-24.8.0.tgz", - "integrity": "sha512-vlGt2HLg7qM+vtBrSkjDxk9K0YtRBi7HfRFaDxoRtyi+DyVChzhF20duvpdAnKVBV6W5tym8jm0U9EfXbDk1tw==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-24.9.0.tgz", + "integrity": "sha512-5A1QluTPhvdIPFYnO3sZC3smkNeXPVELz7ikPbhUj0bQjB07EoE9qtLrem14ZUYWdVayYbsjVwIiL4WBIMV4aQ==", "dev": true, "requires": { - "@jest/fake-timers": "^24.8.0", - "@jest/transform": "^24.8.0", - "@jest/types": "^24.8.0", - "jest-mock": "^24.8.0" + "@jest/fake-timers": "^24.9.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0" } }, "@jest/fake-timers": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.8.0.tgz", - "integrity": "sha512-2M4d5MufVXwi6VzZhJ9f5S/wU4ud2ck0kxPof1Iz3zWx6Y+V2eJrES9jEktB6O3o/oEyk+il/uNu9PvASjWXQw==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.9.0.tgz", + "integrity": "sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A==", "dev": true, "requires": { - "@jest/types": "^24.8.0", - "jest-message-util": "^24.8.0", - "jest-mock": "^24.8.0" + "@jest/types": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-mock": "^24.9.0" } }, "@jest/reporters": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-24.8.0.tgz", - "integrity": "sha512-eZ9TyUYpyIIXfYCrw0UHUWUvE35vx5I92HGMgS93Pv7du+GHIzl+/vh8Qj9MCWFK/4TqyttVBPakWMOfZRIfxw==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-24.9.0.tgz", + "integrity": "sha512-mu4X0yjaHrffOsWmVLzitKmmmWSQ3GGuefgNscUSWNiUNcEOSEQk9k3pERKEQVBb0Cnn88+UESIsZEMH3o88Gw==", "dev": true, "requires": { - "@jest/environment": "^24.8.0", - "@jest/test-result": "^24.8.0", - "@jest/transform": "^24.8.0", - "@jest/types": "^24.8.0", + "@jest/environment": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", "chalk": "^2.0.1", "exit": "^0.1.2", "glob": "^7.1.2", @@ -733,458 +326,91 @@ "istanbul-lib-instrument": "^3.0.1", "istanbul-lib-report": "^2.0.4", "istanbul-lib-source-maps": "^3.0.1", - "istanbul-reports": "^2.1.1", - "jest-haste-map": "^24.8.0", - "jest-resolve": "^24.8.0", - "jest-runtime": "^24.8.0", - "jest-util": "^24.8.0", + "istanbul-reports": "^2.2.6", + "jest-haste-map": "^24.9.0", + "jest-resolve": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-util": "^24.9.0", "jest-worker": "^24.6.0", - "node-notifier": "^5.2.1", + "node-notifier": "^5.4.2", "slash": "^2.0.0", "source-map": "^0.6.0", "string-length": "^2.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } } }, "@jest/source-map": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.3.0.tgz", - "integrity": "sha512-zALZt1t2ou8le/crCeeiRYzvdnTzaIlpOWaet45lNSqNJUnXbppUUFR4ZUAlzgDmKee4Q5P/tKXypI1RiHwgag==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", + "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", "dev": true, "requires": { "callsites": "^3.0.0", "graceful-fs": "^4.1.15", "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } } }, "@jest/test-result": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.8.0.tgz", - "integrity": "sha512-+YdLlxwizlfqkFDh7Mc7ONPQAhA4YylU1s529vVM1rsf67vGZH/2GGm5uO8QzPeVyaVMobCQ7FTxl38QrKRlng==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", + "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", "dev": true, "requires": { - "@jest/console": "^24.7.1", - "@jest/types": "^24.8.0", + "@jest/console": "^24.9.0", + "@jest/types": "^24.9.0", "@types/istanbul-lib-coverage": "^2.0.0" } }, "@jest/test-sequencer": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-24.8.0.tgz", - "integrity": "sha512-OzL/2yHyPdCHXEzhoBuq37CE99nkme15eHkAzXRVqthreWZamEMA0WoetwstsQBCXABhczpK03JNbc4L01vvLg==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-24.9.0.tgz", + "integrity": "sha512-6qqsU4o0kW1dvA95qfNog8v8gkRN9ph6Lz7r96IvZpHdNipP2cBcb07J1Z45mz/VIS01OHJ3pY8T5fUY38tg4A==", "dev": true, "requires": { - "@jest/test-result": "^24.8.0", - "jest-haste-map": "^24.8.0", - "jest-runner": "^24.8.0", - "jest-runtime": "^24.8.0" + "@jest/test-result": "^24.9.0", + "jest-haste-map": "^24.9.0", + "jest-runner": "^24.9.0", + "jest-runtime": "^24.9.0" } }, "@jest/transform": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.8.0.tgz", - "integrity": "sha512-xBMfFUP7TortCs0O+Xtez2W7Zu1PLH9bvJgtraN1CDST6LBM/eTOZ9SfwS/lvV8yOfcDpFmwf9bq5cYbXvqsvA==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.9.0.tgz", + "integrity": "sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ==", "dev": true, "requires": { "@babel/core": "^7.1.0", - "@jest/types": "^24.8.0", + "@jest/types": "^24.9.0", "babel-plugin-istanbul": "^5.1.0", "chalk": "^2.0.1", "convert-source-map": "^1.4.0", "fast-json-stable-stringify": "^2.0.0", "graceful-fs": "^4.1.15", - "jest-haste-map": "^24.8.0", - "jest-regex-util": "^24.3.0", - "jest-util": "^24.8.0", + "jest-haste-map": "^24.9.0", + "jest-regex-util": "^24.9.0", + "jest-util": "^24.9.0", "micromatch": "^3.1.10", + "pirates": "^4.0.1", "realpath-native": "^1.1.0", "slash": "^2.0.0", "source-map": "^0.6.1", "write-file-atomic": "2.4.1" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } } }, "@jest/types": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.8.0.tgz", - "integrity": "sha512-g17UxVr2YfBtaMUxn9u/4+siG1ptg9IGYAYwvpwn61nBg779RXnjE/m7CxYcIzEt0AbHZZAHSEZNhkE2WxURVg==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^12.0.9" + "@types/yargs": "^13.0.0" } }, "@types/babel__core": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.2.tgz", - "integrity": "sha512-cfCCrFmiGY/yq0NuKNxIQvZFy9kY/1immpSpTngOnyIbD4+eJOG5mxphhHDv3CHL9GltO4GcKr54kGBg3RNdbg==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.3.tgz", + "integrity": "sha512-8fBo0UR2CcwWxeX7WIIgJ7lXjasFxoYgRnFHUj+hRvKkpiBJbxhdAPTCY6/ZKM0uxANFVzt4yObSLuTiTnazDA==", "dev": true, "requires": { "@babel/parser": "^7.1.0", @@ -1254,15 +480,24 @@ "dev": true }, "@types/yargs": { - "version": "12.0.12", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-12.0.12.tgz", - "integrity": "sha512-SOhuU4wNBxhhTHxYaiG5NY4HBhDIDnJF60GU+2LqHAdKKer86//e4yg69aENCtQ04n0ovz+tq2YPME5t5yp4pw==", + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.2.tgz", + "integrity": "sha512-lwwgizwk/bIIU+3ELORkyuOgDjCh7zuWDFqRtPPhhVgq9N1F7CvLNKg1TX4f2duwtKQ0p044Au9r1PLIXHrIzQ==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-13.1.0.tgz", + "integrity": "sha512-gCubfBUZ6KxzoibJ+SCUc/57Ms1jz5NjHe4+dI2krNmU5zCPAphyLJYyTOg06ueIyfj+SaCUqmzun7ImlxDcKg==", "dev": true }, "abab": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.0.tgz", - "integrity": "sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.1.tgz", + "integrity": "sha512-1zSbbCuoIjafKZ3mblY5ikvAb0ODUbqBnFuUb7f6uLeQhhGJ0vEV4ntmtxKLT2WgXCO94E07BjunsIw1jOMPZw==" }, "accepts": { "version": "1.3.7", @@ -1279,30 +514,30 @@ "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==" }, "acorn-globals": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.2.tgz", - "integrity": "sha512-BbzvZhVtZP+Bs1J1HcwrQe8ycfO0wStkSGxuul3He3GkHOIZ6eTqOkPuw9IP1X3+IkOo4wiJmwkobzXYz4wewQ==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.4.tgz", + "integrity": "sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==", "requires": { "acorn": "^6.0.1", "acorn-walk": "^6.0.1" }, "dependencies": { "acorn": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", - "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==" + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.3.0.tgz", + "integrity": "sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==" } } }, "acorn-walk": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.1.tgz", - "integrity": "sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==" + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz", + "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==" }, "ajv": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", - "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", "requires": { "fast-deep-equal": "^2.0.1", "fast-json-stable-stringify": "^2.0.0", @@ -1322,20 +557,22 @@ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" }, "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } }, "anymatch": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", - "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", "dev": true, - "optional": true, "requires": { - "micromatch": "^2.1.5", - "normalize-path": "^2.0.0" + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" } }, "argparse": { @@ -1348,14 +585,10 @@ } }, "arr-diff": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", - "dev": true, - "optional": true, - "requires": { - "arr-flatten": "^1.0.1" - } + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true }, "arr-flatten": { "version": "1.1.0", @@ -1380,11 +613,10 @@ "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, "array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", - "dev": true, - "optional": true + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true }, "asn1": { "version": "0.2.4", @@ -1411,17 +643,10 @@ "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", "dev": true }, - "async-each": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", - "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", - "dev": true, - "optional": true - }, "async-limiter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" }, "asynckit": { "version": "0.4.0", @@ -1444,296 +669,28 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" }, - "babel-cli": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-cli/-/babel-cli-6.26.0.tgz", - "integrity": "sha1-UCq1SHTX24itALiHoGODzgPQAvE=", - "dev": true, - "requires": { - "babel-core": "^6.26.0", - "babel-polyfill": "^6.26.0", - "babel-register": "^6.26.0", - "babel-runtime": "^6.26.0", - "chokidar": "^1.6.1", - "commander": "^2.11.0", - "convert-source-map": "^1.5.0", - "fs-readdir-recursive": "^1.0.0", - "glob": "^7.1.2", - "lodash": "^4.17.4", - "output-file-sync": "^1.1.2", - "path-is-absolute": "^1.0.1", - "slash": "^1.0.0", - "source-map": "^0.5.6", - "v8flags": "^2.1.1" - } - }, - "babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" - } - }, - "babel-core": { - "version": "6.26.3", - "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", - "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", - "dev": true, - "requires": { - "babel-code-frame": "^6.26.0", - "babel-generator": "^6.26.0", - "babel-helpers": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-register": "^6.26.0", - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "convert-source-map": "^1.5.1", - "debug": "^2.6.9", - "json5": "^0.5.1", - "lodash": "^4.17.4", - "minimatch": "^3.0.4", - "path-is-absolute": "^1.0.1", - "private": "^0.1.8", - "slash": "^1.0.0", - "source-map": "^0.5.7" - } - }, - "babel-generator": { - "version": "6.26.1", - "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", - "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", - "dev": true, - "requires": { - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "detect-indent": "^4.0.0", - "jsesc": "^1.3.0", - "lodash": "^4.17.4", - "source-map": "^0.5.7", - "trim-right": "^1.0.1" - } - }, - "babel-helper-builder-binary-assignment-operator-visitor": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz", - "integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=", - "dev": true, - "requires": { - "babel-helper-explode-assignable-expression": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-helper-call-delegate": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", - "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", - "dev": true, - "requires": { - "babel-helper-hoist-variables": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-helper-define-map": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz", - "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=", - "dev": true, - "requires": { - "babel-helper-function-name": "^6.24.1", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "lodash": "^4.17.4" - } - }, - "babel-helper-explode-assignable-expression": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz", - "integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-helper-function-name": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", - "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", - "dev": true, - "requires": { - "babel-helper-get-function-arity": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-helper-get-function-arity": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", - "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-helper-hoist-variables": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", - "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-helper-optimise-call-expression": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", - "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-helper-regex": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz", - "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "lodash": "^4.17.4" - } - }, - "babel-helper-remap-async-to-generator": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz", - "integrity": "sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=", - "dev": true, - "requires": { - "babel-helper-function-name": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-helper-replace-supers": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", - "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", - "dev": true, - "requires": { - "babel-helper-optimise-call-expression": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-helpers": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", - "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" - } - }, "babel-jest": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.8.0.tgz", - "integrity": "sha512-+5/kaZt4I9efoXzPlZASyK/lN9qdRKmmUav9smVc0ruPQD7IsfucQ87gpOE8mn2jbDuS6M/YOW6n3v9ZoIfgnw==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.9.0.tgz", + "integrity": "sha512-ntuddfyiN+EhMw58PTNL1ph4C9rECiQXjI4nMMBKBaNjXvqLdkXpPRcMSr4iyBrJg/+wz9brFUD6RhOAT6r4Iw==", "dev": true, "requires": { - "@jest/transform": "^24.8.0", - "@jest/types": "^24.8.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", "@types/babel__core": "^7.1.0", "babel-plugin-istanbul": "^5.1.0", - "babel-preset-jest": "^24.6.0", + "babel-preset-jest": "^24.9.0", "chalk": "^2.4.2", "slash": "^2.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "babel-messages": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", - "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-check-es2015-constants": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", - "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" } }, "babel-plugin-istanbul": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.1.4.tgz", - "integrity": "sha512-dySz4VJMH+dpndj0wjJ8JPs/7i1TdSPb1nRrn56/92pKOF9VKC1FMFJmMXjzlGGusnCAqujP6PBCiKq0sVA+YQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz", + "integrity": "sha512-5LphC0USA8t4i1zCtjbbNb6jJj/9+X6P37Qfirc/70EQ34xKlMW+a1RHGwxGI+SwWpNwZ27HqvzAobeqaXwiZw==", "dev": true, "requires": { + "@babel/helper-plugin-utils": "^7.0.0", "find-up": "^3.0.0", "istanbul-lib-instrument": "^3.3.0", "test-exclude": "^5.2.3" @@ -1759,9 +716,9 @@ } }, "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -1785,447 +742,24 @@ } }, "babel-plugin-jest-hoist": { - "version": "24.6.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.6.0.tgz", - "integrity": "sha512-3pKNH6hMt9SbOv0F3WVmy5CWQ4uogS3k0GY5XLyQHJ9EGpAT9XWkFd2ZiXXtkwFHdAHa5j7w7kfxSP5lAIwu7w==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.9.0.tgz", + "integrity": "sha512-2EMA2P8Vp7lG0RAzr4HXqtYwacfMErOuv1U3wrvxHX6rD1sV6xS3WXG3r8TRQ2r6w8OhvSdWt+z41hQNwNm3Xw==", "dev": true, "requires": { "@types/babel__traverse": "^7.0.6" } }, - "babel-plugin-syntax-async-functions": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", - "integrity": "sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU=", - "dev": true - }, - "babel-plugin-syntax-exponentiation-operator": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz", - "integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=", - "dev": true - }, - "babel-plugin-syntax-trailing-function-commas": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz", - "integrity": "sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM=", - "dev": true - }, - "babel-plugin-transform-async-to-generator": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz", - "integrity": "sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=", - "dev": true, - "requires": { - "babel-helper-remap-async-to-generator": "^6.24.1", - "babel-plugin-syntax-async-functions": "^6.8.0", - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-arrow-functions": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", - "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-block-scoped-functions": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", - "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-block-scoping": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz", - "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "lodash": "^4.17.4" - } - }, - "babel-plugin-transform-es2015-classes": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", - "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", - "dev": true, - "requires": { - "babel-helper-define-map": "^6.24.1", - "babel-helper-function-name": "^6.24.1", - "babel-helper-optimise-call-expression": "^6.24.1", - "babel-helper-replace-supers": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-computed-properties": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", - "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-destructuring": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", - "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-duplicate-keys": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz", - "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-for-of": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", - "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-function-name": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", - "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", - "dev": true, - "requires": { - "babel-helper-function-name": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-literals": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", - "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-modules-amd": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", - "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", - "dev": true, - "requires": { - "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-modules-commonjs": { - "version": "6.26.2", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz", - "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==", - "dev": true, - "requires": { - "babel-plugin-transform-strict-mode": "^6.24.1", - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-types": "^6.26.0" - } - }, - "babel-plugin-transform-es2015-modules-systemjs": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", - "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", - "dev": true, - "requires": { - "babel-helper-hoist-variables": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-modules-umd": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz", - "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=", - "dev": true, - "requires": { - "babel-plugin-transform-es2015-modules-amd": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-object-super": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz", - "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", - "dev": true, - "requires": { - "babel-helper-replace-supers": "^6.24.1", - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-parameters": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", - "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", - "dev": true, - "requires": { - "babel-helper-call-delegate": "^6.24.1", - "babel-helper-get-function-arity": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-shorthand-properties": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", - "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-spread": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", - "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-sticky-regex": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", - "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", - "dev": true, - "requires": { - "babel-helper-regex": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-template-literals": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", - "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-typeof-symbol": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", - "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-unicode-regex": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", - "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", - "dev": true, - "requires": { - "babel-helper-regex": "^6.24.1", - "babel-runtime": "^6.22.0", - "regexpu-core": "^2.0.0" - } - }, - "babel-plugin-transform-exponentiation-operator": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz", - "integrity": "sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=", - "dev": true, - "requires": { - "babel-helper-builder-binary-assignment-operator-visitor": "^6.24.1", - "babel-plugin-syntax-exponentiation-operator": "^6.8.0", - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-regenerator": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", - "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", - "dev": true, - "requires": { - "regenerator-transform": "^0.10.0" - } - }, - "babel-plugin-transform-strict-mode": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", - "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-polyfill": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz", - "integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "core-js": "^2.5.0", - "regenerator-runtime": "^0.10.5" - }, - "dependencies": { - "regenerator-runtime": { - "version": "0.10.5", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", - "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=", - "dev": true - } - } - }, - "babel-preset-env": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.7.0.tgz", - "integrity": "sha512-9OR2afuKDneX2/q2EurSftUYM0xGu4O2D9adAhVfADDhrYDaxXV0rBbevVYoY9n6nyX1PmQW/0jtpJvUNr9CHg==", - "dev": true, - "requires": { - "babel-plugin-check-es2015-constants": "^6.22.0", - "babel-plugin-syntax-trailing-function-commas": "^6.22.0", - "babel-plugin-transform-async-to-generator": "^6.22.0", - "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", - "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", - "babel-plugin-transform-es2015-block-scoping": "^6.23.0", - "babel-plugin-transform-es2015-classes": "^6.23.0", - "babel-plugin-transform-es2015-computed-properties": "^6.22.0", - "babel-plugin-transform-es2015-destructuring": "^6.23.0", - "babel-plugin-transform-es2015-duplicate-keys": "^6.22.0", - "babel-plugin-transform-es2015-for-of": "^6.23.0", - "babel-plugin-transform-es2015-function-name": "^6.22.0", - "babel-plugin-transform-es2015-literals": "^6.22.0", - "babel-plugin-transform-es2015-modules-amd": "^6.22.0", - "babel-plugin-transform-es2015-modules-commonjs": "^6.23.0", - "babel-plugin-transform-es2015-modules-systemjs": "^6.23.0", - "babel-plugin-transform-es2015-modules-umd": "^6.23.0", - "babel-plugin-transform-es2015-object-super": "^6.22.0", - "babel-plugin-transform-es2015-parameters": "^6.23.0", - "babel-plugin-transform-es2015-shorthand-properties": "^6.22.0", - "babel-plugin-transform-es2015-spread": "^6.22.0", - "babel-plugin-transform-es2015-sticky-regex": "^6.22.0", - "babel-plugin-transform-es2015-template-literals": "^6.22.0", - "babel-plugin-transform-es2015-typeof-symbol": "^6.23.0", - "babel-plugin-transform-es2015-unicode-regex": "^6.22.0", - "babel-plugin-transform-exponentiation-operator": "^6.22.0", - "babel-plugin-transform-regenerator": "^6.22.0", - "browserslist": "^3.2.6", - "invariant": "^2.2.2", - "semver": "^5.3.0" - } - }, "babel-preset-jest": { - "version": "24.6.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-24.6.0.tgz", - "integrity": "sha512-pdZqLEdmy1ZK5kyRUfvBb2IfTPb2BUvIJczlPspS8fWmBQslNNDBqVfh7BW5leOVJMDZKzjD8XEyABTk6gQ5yw==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-24.9.0.tgz", + "integrity": "sha512-izTUuhE4TMfTRPF92fFwD2QfdXaZW08qvWTFCI51V8rW5x00UuPgc3ajRoWofXOuxjfcOM5zzSYsQS3H8KGCAg==", "dev": true, "requires": { "@babel/plugin-syntax-object-rest-spread": "^7.0.0", - "babel-plugin-jest-hoist": "^24.6.0" + "babel-plugin-jest-hoist": "^24.9.0" } }, - "babel-register": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", - "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", - "dev": true, - "requires": { - "babel-core": "^6.26.0", - "babel-runtime": "^6.26.0", - "core-js": "^2.5.0", - "home-or-tmp": "^2.0.0", - "lodash": "^4.17.4", - "mkdirp": "^0.5.1", - "source-map-support": "^0.4.15" - } - }, - "babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", - "dev": true, - "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" - } - }, - "babel-template": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", - "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "lodash": "^4.17.4" - } - }, - "babel-traverse": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", - "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", - "dev": true, - "requires": { - "babel-code-frame": "^6.26.0", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "debug": "^2.6.8", - "globals": "^9.18.0", - "invariant": "^2.2.2", - "lodash": "^4.17.4" - } - }, - "babel-types": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", - "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "esutils": "^2.0.2", - "lodash": "^4.17.4", - "to-fast-properties": "^1.0.3" - } - }, - "babylon": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", - "dev": true - }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -2284,18 +818,6 @@ "is-data-descriptor": "^1.0.0", "kind-of": "^6.0.2" } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true } } }, @@ -2307,13 +829,6 @@ "tweetnacl": "^0.14.3" } }, - "binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", - "dev": true, - "optional": true - }, "body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", @@ -2341,6 +856,35 @@ "concat-map": "0.0.1" } }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, "browser-process-hrtime": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz", @@ -2363,20 +907,10 @@ } } }, - "browserslist": { - "version": "3.2.8", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-3.2.8.tgz", - "integrity": "sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30000844", - "electron-to-chromium": "^1.3.47" - } - }, "bser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.0.0.tgz", - "integrity": "sha1-mseNPtXZFYBP2HrLFYvHlxR6Fxk=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.0.tgz", + "integrity": "sha512-8zsjWrQkkBoLK6uxASk1nJ2SKv97ltiGDo6A3wA0/yRPz+CwmEyDo0hUrhIuukG2JHpAl3bvFIixw2/3Hi0DOg==", "dev": true, "requires": { "node-int64": "^0.4.0" @@ -2408,14 +942,6 @@ "to-object-path": "^0.3.0", "union-value": "^1.0.0", "unset-value": "^1.0.0" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } } }, "callsites": { @@ -2429,12 +955,6 @@ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" }, - "caniuse-lite": { - "version": "1.0.30000975", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000975.tgz", - "integrity": "sha512-ZsXA9YWQX6ATu5MNg+Vx/cMQ+hM6vBBSqDeJs8ruk9z0ky4yIHML15MoxcFt088ST2uyjgqyUGRJButkptWf0w==", - "dev": true - }, "capture-exit": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", @@ -2450,51 +970,14 @@ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - } - } - }, - "chokidar": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", - "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", - "dev": true, - "optional": true, - "requires": { - "anymatch": "^1.3.0", - "async-each": "^1.0.0", - "fsevents": "^1.0.0", - "glob-parent": "^2.0.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^2.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } }, "ci-info": { @@ -2523,19 +1006,13 @@ "requires": { "is-descriptor": "^0.1.0" } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true } } }, "clipboard": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.4.tgz", - "integrity": "sha512-Vw26VSLRpJfBofiVaFb/I8PVfdI1OxKcYShe6fm0sP/DtmiWQNCjhM/okTvdCo0G+lMMm1rMYbk4IK4x1X+kgQ==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-1.7.1.tgz", + "integrity": "sha1-Ng1taUbpmnof7zleQrqStem1oWs=", "optional": true, "requires": { "good-listener": "^1.2.2", @@ -2601,7 +1078,8 @@ "version": "2.20.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", - "dev": true + "dev": true, + "optional": true }, "component-emitter": { "version": "1.3.0", @@ -2659,53 +1137,44 @@ "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", "dev": true }, - "core-js": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", - "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==", - "dev": true - }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "coveralls": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/coveralls/-/coveralls-3.0.4.tgz", - "integrity": "sha512-eyqUWA/7RT0JagiL0tThVhjbIjoiEUyWCjtUJoOPcWoeofP5WK/jb2OJYoBFrR6DvplR+AxOyuBqk4JHkk5ykA==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/coveralls/-/coveralls-3.0.6.tgz", + "integrity": "sha512-Pgh4v3gCI4T/9VijVrm8Ym5v0OgjvGLKj3zTUwkvsCiwqae/p6VLzpsFNjQS2i6ewV7ef+DjFJ5TSKxYt/mCrA==", "dev": true, "requires": { "growl": "~> 1.10.0", - "js-yaml": "^3.11.0", + "js-yaml": "^3.13.1", "lcov-parse": "^0.0.10", "log-driver": "^1.2.7", "minimist": "^1.2.0", "request": "^2.86.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } } }, - "crypto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", - "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==" + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } }, "cssom": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.6.tgz", - "integrity": "sha512-DtUeseGk9/GBW0hl0vVPpU22iHL6YB5BUX7ml1hB+GMpo0NX5G4voX3kdWiMSEguFtcW3Vh3djqNF4aIe6ne0A==" + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" }, "cssstyle": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.2.2.tgz", - "integrity": "sha512-43wY3kl1CVQSvL7wUY1qXkxVGkStjpkDmVjiIKX8R97uhajy8Bybay78uOtqvh7Q5GK75dNPfW0geWjE6qQQow==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.4.0.tgz", + "integrity": "sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==", "requires": { "cssom": "0.3.x" } @@ -2811,18 +1280,6 @@ "is-data-descriptor": "^1.0.0", "kind-of": "^6.0.2" } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true } } }, @@ -2847,15 +1304,6 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, - "detect-indent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", - "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", - "dev": true, - "requires": { - "repeating": "^2.0.0" - } - }, "detect-newline": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", @@ -2863,9 +1311,9 @@ "dev": true }, "diff-sequences": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.3.0.tgz", - "integrity": "sha512-xLqpez+Zj9GKSnPWS0WZw1igGocZ+uua8+y+5dDNTT934N3QuY1sp2LkHzwiaYQGz60hMq0pjAshdeXm5VUOEw==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.9.0.tgz", + "integrity": "sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew==", "dev": true }, "domexception": { @@ -2891,14 +1339,14 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "ejs": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.6.2.tgz", - "integrity": "sha512-PcW2a0tyTuPHz3tWyYqtK6r1fZ3gp+3Sop8Ph+ZYN81Ob5rwmbHEzaqs10N3BEsaGTkh/ooniXK+WwszGlc2+Q==" + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.7.1.tgz", + "integrity": "sha512-kS/gEPzZs3Y1rRsbGX4UOSjtP/CeJP0CxSNZHYxGfVM/VgLcv0ZqM7C45YyTj2DI2g7+P9Dd24C+IMIg6D0nYQ==" }, - "electron-to-chromium": { - "version": "1.3.165", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.165.tgz", - "integrity": "sha512-iIS8axR524EAnvUtWUNnREnYjQrS0zUvutIKYgTVuN3MzcjrV31EuJYKw7DGOtFO9DQw+JiXeaVDPQWMskG1wQ==", + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", "dev": true }, "encodeurl": { @@ -2925,17 +1373,21 @@ } }, "es-abstract": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", - "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.14.2.tgz", + "integrity": "sha512-DgoQmbpFNOofkjJtKwr87Ma5EW4Dc8fWhD0R+ndq7Oc456ivUfGOOP6oAZTTKl5/CcNMP+EN+e3/iUzgE0veZg==", "dev": true, "requires": { "es-to-primitive": "^1.2.0", "function-bind": "^1.1.1", "has": "^1.0.3", + "has-symbols": "^1.0.0", "is-callable": "^1.1.4", "is-regex": "^1.0.4", - "object-keys": "^1.0.12" + "object-inspect": "^1.6.0", + "object-keys": "^1.1.1", + "string.prototype.trimleft": "^2.0.0", + "string.prototype.trimright": "^2.0.0" } }, "es-to-primitive": { @@ -2961,23 +1413,15 @@ "dev": true }, "escodegen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.1.tgz", - "integrity": "sha512-JwiqFD9KdGVVpeuRa68yU3zZnBEOcPs0nKW7wZzXky8Z7tffdYUHbe11bPCV5jYlK6DVdKLWLm0f5I/QlL0Kmw==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.12.0.tgz", + "integrity": "sha512-TuA+EhsanGcme5T3R0L80u4t8CpbXQjegRmf7+FPTJrtCTErXFeelblRgHQa1FofEzqYYJmJ/OqjTwREp9qgmg==", "requires": { "esprima": "^3.1.3", "estraverse": "^4.2.0", "esutils": "^2.0.2", "optionator": "^0.8.1", "source-map": "~0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "optional": true - } } }, "esprima": { @@ -2986,14 +1430,14 @@ "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=" }, "estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=" + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" }, "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=" + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" }, "etag": { "version": "1.8.1", @@ -3006,6 +1450,20 @@ "integrity": "sha512-9sLAvzhI5nc8TpuQUh4ahMdCrWT00wPWz7j47/emR5+2qEfoZP5zzUXvx+vdx+H6ohhnsYC31iX04QLYJK8zTg==", "dev": true }, + "execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, "exit": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", @@ -3013,84 +1471,52 @@ "dev": true }, "expand-brackets": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "dev": true, - "optional": true, "requires": { - "is-posix-bracket": "^0.1.0" - } - }, - "expand-range": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", - "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", - "dev": true, - "optional": true, - "requires": { - "fill-range": "^2.1.0" + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" }, "dependencies": { - "fill-range": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", - "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, - "optional": true, "requires": { - "is-number": "^2.1.0", - "isobject": "^2.0.0", - "randomatic": "^3.0.0", - "repeat-element": "^1.1.2", - "repeat-string": "^1.5.2" + "is-descriptor": "^0.1.0" } }, - "is-number": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, - "optional": true, "requires": { - "kind-of": "^3.0.2" - } - }, - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "optional": true, - "requires": { - "isarray": "1.0.0" + "is-extendable": "^0.1.0" } } } }, "expect": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-24.8.0.tgz", - "integrity": "sha512-/zYvP8iMDrzaaxHVa724eJBCKqSHmO0FA7EDkBiRHxg6OipmMn1fN+C8T9L9K8yr7UONkOifu6+LLH+z76CnaA==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-24.9.0.tgz", + "integrity": "sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q==", "dev": true, "requires": { - "@jest/types": "^24.8.0", + "@jest/types": "^24.9.0", "ansi-styles": "^3.2.0", - "jest-get-type": "^24.8.0", - "jest-matcher-utils": "^24.8.0", - "jest-message-util": "^24.8.0", - "jest-regex-util": "^24.3.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - } + "jest-get-type": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-regex-util": "^24.9.0" } }, "express": { @@ -3162,13 +1588,68 @@ } }, "extglob": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", "dev": true, - "optional": true, "requires": { - "is-extglob": "^1.0.0" + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } } }, "extsprintf": { @@ -3200,9 +1681,9 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" }, "fast-safe-stringify": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.6.tgz", - "integrity": "sha512-q8BZ89jjc+mz08rSxROs8VsrBBcn1SIw1kq9NjolL509tkABRk9io01RAjSaEv1Xb2uFLt8VtRiZbGp5H8iDtg==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", + "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==", "dev": true }, "fb-watchman": { @@ -3214,12 +1695,28 @@ "bser": "^2.0.0" } }, - "filename-regex": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", - "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, - "optional": true + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } }, "finalhandler": { "version": "1.1.2", @@ -3249,16 +1746,6 @@ "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", "dev": true }, - "for-own": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", - "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", - "dev": true, - "optional": true, - "requires": { - "for-in": "^1.0.1" - } - }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -3299,12 +1786,6 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, - "fs-readdir-recursive": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", - "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", - "dev": true - }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -3870,6 +2351,11 @@ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" + }, "get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", @@ -3898,31 +2384,10 @@ "path-is-absolute": "^1.0.0" } }, - "glob-base": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", - "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", - "dev": true, - "optional": true, - "requires": { - "glob-parent": "^2.0.0", - "is-glob": "^2.0.0" - } - }, - "glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", - "dev": true, - "optional": true, - "requires": { - "is-glob": "^2.0.0" - } - }, "globals": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true }, "good-listener": { @@ -3935,9 +2400,9 @@ } }, "graceful-fs": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", "dev": true }, "growl": { @@ -3953,23 +2418,15 @@ "dev": true }, "handlebars": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz", - "integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.2.0.tgz", + "integrity": "sha512-Kb4xn5Qh1cxAKvQnzNWZ512DhABzyFNmsaJf3OAkWNa4NkaqWcNI8Tao8Tasi0/F4JD9oyG0YxuFyvyR57d+Gw==", "dev": true, "requires": { "neo-async": "^2.6.0", "optimist": "^0.6.1", "source-map": "^0.6.1", "uglify-js": "^3.1.4" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } } }, "har-schema": { @@ -3995,23 +2452,6 @@ "function-bind": "^1.1.1" } }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - } - } - }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -4033,14 +2473,6 @@ "get-value": "^2.0.6", "has-values": "^1.0.0", "isobject": "^3.0.0" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } } }, "has-values": { @@ -4053,26 +2485,6 @@ "kind-of": "^4.0.0" }, "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, "kind-of": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", @@ -4084,20 +2496,10 @@ } } }, - "home-or-tmp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", - "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", - "dev": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.1" - } - }, "hosted-git-info": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", - "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.4.tgz", + "integrity": "sha512-pzXIvANXEFrc5oFFXRMkbLPQ2rXRoDERwDLyrcUxGhaZhgP54BBSl9Oheh7Vv0T090cszWBxPjkQQ5Sq1PbBRQ==", "dev": true }, "html-encoding-sniffer": { @@ -4178,6 +2580,11 @@ "loose-envify": "^1.0.0" } }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" + }, "ipaddr.js": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", @@ -4190,6 +2597,17 @@ "dev": true, "requires": { "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } } }, "is-arrayish": { @@ -4198,16 +2616,6 @@ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", "dev": true }, - "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", - "dev": true, - "optional": true, - "requires": { - "binary-extensions": "^1.0.0" - } - }, "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", @@ -4236,6 +2644,17 @@ "dev": true, "requires": { "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } } }, "is-date-object": { @@ -4263,45 +2682,12 @@ } } }, - "is-dotfile": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", - "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", - "dev": true, - "optional": true - }, - "is-equal-shallow": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", - "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", - "dev": true, - "optional": true, - "requires": { - "is-primitive": "^2.0.0" - } - }, "is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", "dev": true }, - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true, - "optional": true - }, - "is-finite": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", - "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", @@ -4313,14 +2699,24 @@ "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", "dev": true }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, - "optional": true, "requires": { - "is-extglob": "^1.0.0" + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } } }, "is-plain-object": { @@ -4330,30 +2726,8 @@ "dev": true, "requires": { "isobject": "^3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } } }, - "is-posix-bracket": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", - "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", - "dev": true, - "optional": true - }, - "is-primitive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", - "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", - "dev": true, - "optional": true - }, "is-regex": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", @@ -4405,6 +2779,12 @@ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -4432,9 +2812,9 @@ }, "dependencies": { "semver": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.1.1.tgz", - "integrity": "sha512-rWYq2e5iYW+fFe/oPPtYJxYgjBm8sC4rmoGdUOgBB7VnwKt6HrL793l2voH1UlsyYZpJ4g0wfjnTEO1s1NP2eQ==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true } } @@ -4488,12 +2868,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true } } }, @@ -4507,23 +2881,20 @@ } }, "jest": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-24.8.0.tgz", - "integrity": "sha512-o0HM90RKFRNWmAWvlyV8i5jGZ97pFwkeVoGvPW1EtLTgJc2+jcuqcbbqcSZLE/3f2S5pt0y2ZBETuhpWNl1Reg==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-24.9.0.tgz", + "integrity": "sha512-YvkBL1Zm7d2B1+h5fHEOdyjCG+sGMz4f8D86/0HiqJ6MB4MnDc8FgP5vdWsGnemOQro7lnYo8UakZ3+5A0jxGw==", "dev": true, "requires": { "import-local": "^2.0.0", - "jest-cli": "^24.8.0" + "jest-cli": "^24.9.0" }, "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true }, "camelcase": { "version": "5.3.1", @@ -4531,43 +2902,15 @@ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" } }, "find-up": { @@ -4579,49 +2922,31 @@ "locate-path": "^3.0.0" } }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, "jest-cli": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-24.8.0.tgz", - "integrity": "sha512-+p6J00jSMPQ116ZLlHJJvdf8wbjNbZdeSX9ptfHX06/MSNaXmKihQzx5vQcw0q2G6JsdVkUIdWbOWtSnaYs3yA==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-24.9.0.tgz", + "integrity": "sha512-+VLRKyitT3BWoMeSUIHRxV/2g8y9gw91Jh5z2UmXZzkZKpbC08CSehVxgHUwTpy+HwGcns/tqafQDJW7imYvGg==", "dev": true, "requires": { - "@jest/core": "^24.8.0", - "@jest/test-result": "^24.8.0", - "@jest/types": "^24.8.0", + "@jest/core": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", "chalk": "^2.0.1", "exit": "^0.1.2", "import-local": "^2.0.0", "is-ci": "^2.0.0", - "jest-config": "^24.8.0", - "jest-util": "^24.8.0", - "jest-validate": "^24.8.0", + "jest-config": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", "prompts": "^2.0.1", "realpath-native": "^1.1.0", - "yargs": "^12.0.2" - } - }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "dev": true, - "requires": { - "invert-kv": "^2.0.0" + "yargs": "^13.3.0" } }, "locate-path": { @@ -4634,38 +2959,10 @@ "path-exists": "^3.0.0" } }, - "mem": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", - "dev": true, - "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "dev": true, - "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - } - }, "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -4686,39 +2983,71 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" } }, - "yargs": { - "version": "12.0.5", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", - "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "dev": true, "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.2.0", + "ansi-regex": "^4.1.0" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yargs": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", + "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", + "dev": true, + "requires": { + "cliui": "^5.0.0", "find-up": "^3.0.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.0.0", + "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", + "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", - "string-width": "^2.0.0", + "string-width": "^3.0.0", "which-module": "^2.0.0", - "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^11.1.1" + "y18n": "^4.0.0", + "yargs-parser": "^13.1.1" } }, "yargs-parser": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", - "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", + "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", "dev": true, "requires": { "camelcase": "^5.0.0", @@ -4728,12 +3057,12 @@ } }, "jest-changed-files": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-24.8.0.tgz", - "integrity": "sha512-qgANC1Yrivsq+UrLXsvJefBKVoCsKB0Hv+mBb6NMjjZ90wwxCDmU3hsCXBya30cH+LnPYjwgcU65i6yJ5Nfuug==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-24.9.0.tgz", + "integrity": "sha512-6aTWpe2mHF0DhL28WjdkO8LyGjs3zItPET4bMSeXU6T3ub4FPMw+mcOcbdGXQOAfmLcxofD23/5Bl9Z4AkFwqg==", "dev": true, "requires": { - "@jest/types": "^24.8.0", + "@jest/types": "^24.9.0", "execa": "^1.0.0", "throat": "^4.0.0" }, @@ -4778,1211 +3107,186 @@ } }, "jest-config": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.8.0.tgz", - "integrity": "sha512-Czl3Nn2uEzVGsOeaewGWoDPD8GStxCpAe0zOYs2x2l0fZAgPbCr3uwUkgNKV3LwE13VXythM946cd5rdGkkBZw==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.9.0.tgz", + "integrity": "sha512-RATtQJtVYQrp7fvWg6f5y3pEFj9I+H8sWw4aKxnDZ96mob5i5SD6ZEGWgMLXQ4LE8UurrjbdlLWdUeo+28QpfQ==", "dev": true, "requires": { "@babel/core": "^7.1.0", - "@jest/test-sequencer": "^24.8.0", - "@jest/types": "^24.8.0", - "babel-jest": "^24.8.0", + "@jest/test-sequencer": "^24.9.0", + "@jest/types": "^24.9.0", + "babel-jest": "^24.9.0", "chalk": "^2.0.1", "glob": "^7.1.1", - "jest-environment-jsdom": "^24.8.0", - "jest-environment-node": "^24.8.0", - "jest-get-type": "^24.8.0", - "jest-jasmine2": "^24.8.0", + "jest-environment-jsdom": "^24.9.0", + "jest-environment-node": "^24.9.0", + "jest-get-type": "^24.9.0", + "jest-jasmine2": "^24.9.0", "jest-regex-util": "^24.3.0", - "jest-resolve": "^24.8.0", - "jest-util": "^24.8.0", - "jest-validate": "^24.8.0", + "jest-resolve": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", "micromatch": "^3.1.10", - "pretty-format": "^24.8.0", + "pretty-format": "^24.9.0", "realpath-native": "^1.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } } }, "jest-diff": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.8.0.tgz", - "integrity": "sha512-wxetCEl49zUpJ/bvUmIFjd/o52J+yWcoc5ZyPq4/W1LUKGEhRYDIbP1KcF6t+PvqNrGAFk4/JhtxDq/Nnzs66g==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.9.0.tgz", + "integrity": "sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ==", "dev": true, "requires": { "chalk": "^2.0.1", - "diff-sequences": "^24.3.0", - "jest-get-type": "^24.8.0", - "pretty-format": "^24.8.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } + "diff-sequences": "^24.9.0", + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" } }, "jest-docblock": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-24.3.0.tgz", - "integrity": "sha512-nlANmF9Yq1dufhFlKG9rasfQlrY7wINJbo3q01tu56Jv5eBU5jirylhF2O5ZBnLxzOVBGRDz/9NAwNyBtG4Nyg==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-24.9.0.tgz", + "integrity": "sha512-F1DjdpDMJMA1cN6He0FNYNZlo3yYmOtRUnktrT9Q37njYzC5WEaDdmbynIgy0L/IvXvvgsG8OsqhLPXTpfmZAA==", "dev": true, "requires": { "detect-newline": "^2.1.0" } }, "jest-each": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.8.0.tgz", - "integrity": "sha512-NrwK9gaL5+XgrgoCsd9svsoWdVkK4gnvyhcpzd6m487tXHqIdYeykgq3MKI1u4I+5Zf0tofr70at9dWJDeb+BA==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.9.0.tgz", + "integrity": "sha512-ONi0R4BvW45cw8s2Lrx8YgbeXL1oCQ/wIDwmsM3CqM/nlblNCPmnC3IPQlMbRFZu3wKdQ2U8BqM6lh3LJ5Bsog==", "dev": true, "requires": { - "@jest/types": "^24.8.0", + "@jest/types": "^24.9.0", "chalk": "^2.0.1", - "jest-get-type": "^24.8.0", - "jest-util": "^24.8.0", - "pretty-format": "^24.8.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } + "jest-get-type": "^24.9.0", + "jest-util": "^24.9.0", + "pretty-format": "^24.9.0" } }, "jest-environment-jsdom": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.8.0.tgz", - "integrity": "sha512-qbvgLmR7PpwjoFjM/sbuqHJt/NCkviuq9vus9NBn/76hhSidO+Z6Bn9tU8friecegbJL8gzZQEMZBQlFWDCwAQ==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.9.0.tgz", + "integrity": "sha512-Zv9FV9NBRzLuALXjvRijO2351DRQeLYXtpD4xNvfoVFw21IOKNhZAEUKcbiEtjTkm2GsJ3boMVgkaR7rN8qetA==", "dev": true, "requires": { - "@jest/environment": "^24.8.0", - "@jest/fake-timers": "^24.8.0", - "@jest/types": "^24.8.0", - "jest-mock": "^24.8.0", - "jest-util": "^24.8.0", + "@jest/environment": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-util": "^24.9.0", "jsdom": "^11.5.1" } }, "jest-environment-node": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-24.8.0.tgz", - "integrity": "sha512-vIGUEScd1cdDgR6sqn2M08sJTRLQp6Dk/eIkCeO4PFHxZMOgy+uYLPMC4ix3PEfM5Au/x3uQ/5Tl0DpXXZsJ/Q==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-24.9.0.tgz", + "integrity": "sha512-6d4V2f4nxzIzwendo27Tr0aFm+IXWa0XEUnaH6nU0FMaozxovt+sfRvh4J47wL1OvF83I3SSTu0XK+i4Bqe7uA==", "dev": true, "requires": { - "@jest/environment": "^24.8.0", - "@jest/fake-timers": "^24.8.0", - "@jest/types": "^24.8.0", - "jest-mock": "^24.8.0", - "jest-util": "^24.8.0" + "@jest/environment": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-util": "^24.9.0" } }, "jest-get-type": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.8.0.tgz", - "integrity": "sha512-RR4fo8jEmMD9zSz2nLbs2j0zvPpk/KCEz3a62jJWbd2ayNo0cb+KFRxPHVhE4ZmgGJEQp0fosmNz84IfqM8cMQ==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", + "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", "dev": true }, "jest-haste-map": { - "version": "24.8.1", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.8.1.tgz", - "integrity": "sha512-SwaxMGVdAZk3ernAx2Uv2sorA7jm3Kx+lR0grp6rMmnY06Kn/urtKx1LPN2mGTea4fCT38impYT28FfcLUhX0g==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.9.0.tgz", + "integrity": "sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ==", "dev": true, "requires": { - "@jest/types": "^24.8.0", + "@jest/types": "^24.9.0", "anymatch": "^2.0.0", "fb-watchman": "^2.0.0", "fsevents": "^1.2.7", "graceful-fs": "^4.1.15", "invariant": "^2.2.4", - "jest-serializer": "^24.4.0", - "jest-util": "^24.8.0", - "jest-worker": "^24.6.0", + "jest-serializer": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.9.0", "micromatch": "^3.1.10", "sane": "^4.0.3", "walker": "^1.0.7" - }, - "dependencies": { - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - } } }, "jest-jasmine2": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-24.8.0.tgz", - "integrity": "sha512-cEky88npEE5LKd5jPpTdDCLvKkdyklnaRycBXL6GNmpxe41F0WN44+i7lpQKa/hcbXaQ+rc9RMaM4dsebrYong==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-24.9.0.tgz", + "integrity": "sha512-Cq7vkAgaYKp+PsX+2/JbTarrk0DmNhsEtqBXNwUHkdlbrTBLtMJINADf2mf5FkowNsq8evbPc07/qFO0AdKTzw==", "dev": true, "requires": { "@babel/traverse": "^7.1.0", - "@jest/environment": "^24.8.0", - "@jest/test-result": "^24.8.0", - "@jest/types": "^24.8.0", + "@jest/environment": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", "chalk": "^2.0.1", "co": "^4.6.0", - "expect": "^24.8.0", + "expect": "^24.9.0", "is-generator-fn": "^2.0.0", - "jest-each": "^24.8.0", - "jest-matcher-utils": "^24.8.0", - "jest-message-util": "^24.8.0", - "jest-runtime": "^24.8.0", - "jest-snapshot": "^24.8.0", - "jest-util": "^24.8.0", - "pretty-format": "^24.8.0", + "jest-each": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-snapshot": "^24.9.0", + "jest-util": "^24.9.0", + "pretty-format": "^24.9.0", "throat": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } } }, "jest-leak-detector": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-24.8.0.tgz", - "integrity": "sha512-cG0yRSK8A831LN8lIHxI3AblB40uhv0z+SsQdW3GoMMVcK+sJwrIIyax5tu3eHHNJ8Fu6IMDpnLda2jhn2pD/g==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-24.9.0.tgz", + "integrity": "sha512-tYkFIDsiKTGwb2FG1w8hX9V0aUb2ot8zY/2nFg087dUageonw1zrLMP4W6zsRO59dPkTSKie+D4rhMuP9nRmrA==", "dev": true, "requires": { - "pretty-format": "^24.8.0" + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" } }, "jest-matcher-utils": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.8.0.tgz", - "integrity": "sha512-lex1yASY51FvUuHgm0GOVj7DCYEouWSlIYmCW7APSqB9v8mXmKSn5+sWVF0MhuASG0bnYY106/49JU1FZNl5hw==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz", + "integrity": "sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA==", "dev": true, "requires": { "chalk": "^2.0.1", - "jest-diff": "^24.8.0", - "jest-get-type": "^24.8.0", - "pretty-format": "^24.8.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } + "jest-diff": "^24.9.0", + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" } }, "jest-message-util": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.8.0.tgz", - "integrity": "sha512-p2k71rf/b6ns8btdB0uVdljWo9h0ovpnEe05ZKWceQGfXYr4KkzgKo3PBi8wdnd9OtNh46VpNIJynUn/3MKm1g==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", + "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^24.8.0", - "@jest/types": "^24.8.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", "@types/stack-utils": "^1.0.1", "chalk": "^2.0.1", "micromatch": "^3.1.10", "slash": "^2.0.0", "stack-utils": "^1.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } } }, "jest-mock": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.8.0.tgz", - "integrity": "sha512-6kWugwjGjJw+ZkK4mDa0Df3sDlUTsV47MSrT0nGQ0RBWJbpODDQ8MHDVtGtUYBne3IwZUhtB7elxHspU79WH3A==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", + "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", "dev": true, "requires": { - "@jest/types": "^24.8.0" + "@jest/types": "^24.9.0" } }, "jest-pnp-resolver": { @@ -5992,179 +3296,98 @@ "dev": true }, "jest-regex-util": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.3.0.tgz", - "integrity": "sha512-tXQR1NEOyGlfylyEjg1ImtScwMq8Oh3iJbGTjN7p0J23EuVX1MA8rwU69K4sLbCmwzgCUbVkm0FkSF9TdzOhtg==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", + "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", "dev": true }, "jest-resolve": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.8.0.tgz", - "integrity": "sha512-+hjSzi1PoRvnuOICoYd5V/KpIQmkAsfjFO71458hQ2Whi/yf1GDeBOFj8Gxw4LrApHsVJvn5fmjcPdmoUHaVKw==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.9.0.tgz", + "integrity": "sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ==", "dev": true, "requires": { - "@jest/types": "^24.8.0", + "@jest/types": "^24.9.0", "browser-resolve": "^1.11.3", "chalk": "^2.0.1", "jest-pnp-resolver": "^1.2.1", "realpath-native": "^1.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } } }, "jest-resolve-dependencies": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-24.8.0.tgz", - "integrity": "sha512-hyK1qfIf/krV+fSNyhyJeq3elVMhK9Eijlwy+j5jqmZ9QsxwKBiP6qukQxaHtK8k6zql/KYWwCTQ+fDGTIJauw==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-24.9.0.tgz", + "integrity": "sha512-Fm7b6AlWnYhT0BXy4hXpactHIqER7erNgIsIozDXWl5dVm+k8XdGVe1oTg1JyaFnOxarMEbax3wyRJqGP2Pq+g==", "dev": true, "requires": { - "@jest/types": "^24.8.0", + "@jest/types": "^24.9.0", "jest-regex-util": "^24.3.0", - "jest-snapshot": "^24.8.0" + "jest-snapshot": "^24.9.0" } }, "jest-runner": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-24.8.0.tgz", - "integrity": "sha512-utFqC5BaA3JmznbissSs95X1ZF+d+4WuOWwpM9+Ak356YtMhHE/GXUondZdcyAAOTBEsRGAgH/0TwLzfI9h7ow==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-24.9.0.tgz", + "integrity": "sha512-KksJQyI3/0mhcfspnxxEOBueGrd5E4vV7ADQLT9ESaCzz02WnbdbKWIf5Mkaucoaj7obQckYPVX6JJhgUcoWWg==", "dev": true, "requires": { "@jest/console": "^24.7.1", - "@jest/environment": "^24.8.0", - "@jest/test-result": "^24.8.0", - "@jest/types": "^24.8.0", + "@jest/environment": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", "chalk": "^2.4.2", "exit": "^0.1.2", "graceful-fs": "^4.1.15", - "jest-config": "^24.8.0", + "jest-config": "^24.9.0", "jest-docblock": "^24.3.0", - "jest-haste-map": "^24.8.0", - "jest-jasmine2": "^24.8.0", - "jest-leak-detector": "^24.8.0", - "jest-message-util": "^24.8.0", - "jest-resolve": "^24.8.0", - "jest-runtime": "^24.8.0", - "jest-util": "^24.8.0", + "jest-haste-map": "^24.9.0", + "jest-jasmine2": "^24.9.0", + "jest-leak-detector": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-resolve": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-util": "^24.9.0", "jest-worker": "^24.6.0", "source-map-support": "^0.5.6", "throat": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "source-map-support": { - "version": "0.5.12", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.12.tgz", - "integrity": "sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } } }, "jest-runtime": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-24.8.0.tgz", - "integrity": "sha512-Mq0aIXhvO/3bX44ccT+czU1/57IgOMyy80oM0XR/nyD5zgBcesF84BPabZi39pJVA6UXw+fY2Q1N+4BiVUBWOA==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-24.9.0.tgz", + "integrity": "sha512-8oNqgnmF3v2J6PVRM2Jfuj8oX3syKmaynlDMMKQ4iyzbQzIG6th5ub/lM2bCMTmoTKM3ykcUYI2Pw9xwNtjMnw==", "dev": true, "requires": { "@jest/console": "^24.7.1", - "@jest/environment": "^24.8.0", + "@jest/environment": "^24.9.0", "@jest/source-map": "^24.3.0", - "@jest/transform": "^24.8.0", - "@jest/types": "^24.8.0", - "@types/yargs": "^12.0.2", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/yargs": "^13.0.0", "chalk": "^2.0.1", "exit": "^0.1.2", "glob": "^7.1.3", "graceful-fs": "^4.1.15", - "jest-config": "^24.8.0", - "jest-haste-map": "^24.8.0", - "jest-message-util": "^24.8.0", - "jest-mock": "^24.8.0", + "jest-config": "^24.9.0", + "jest-haste-map": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-mock": "^24.9.0", "jest-regex-util": "^24.3.0", - "jest-resolve": "^24.8.0", - "jest-snapshot": "^24.8.0", - "jest-util": "^24.8.0", - "jest-validate": "^24.8.0", + "jest-resolve": "^24.9.0", + "jest-snapshot": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", "realpath-native": "^1.1.0", "slash": "^2.0.0", "strip-bom": "^3.0.0", - "yargs": "^12.0.2" + "yargs": "^13.3.0" }, "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true }, "camelcase": { "version": "5.3.1", @@ -6172,43 +3395,15 @@ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" } }, "find-up": { @@ -6220,30 +3415,12 @@ "locate-path": "^3.0.0" } }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "dev": true, - "requires": { - "invert-kv": "^2.0.0" - } - }, "locate-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", @@ -6254,38 +3431,10 @@ "path-exists": "^3.0.0" } }, - "mem": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", - "dev": true, - "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "dev": true, - "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - } - }, "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -6306,45 +3455,71 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, - "slash": { + "require-main-filename": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" } }, - "yargs": { - "version": "12.0.5", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", - "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "dev": true, "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.2.0", + "ansi-regex": "^4.1.0" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yargs": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", + "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", + "dev": true, + "requires": { + "cliui": "^5.0.0", "find-up": "^3.0.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.0.0", + "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", + "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", - "string-width": "^2.0.0", + "string-width": "^3.0.0", "which-module": "^2.0.0", - "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^11.1.1" + "y18n": "^4.0.0", + "yargs-parser": "^13.1.1" } }, "yargs-parser": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", - "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", + "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", "dev": true, "requires": { "camelcase": "^5.0.0", @@ -6354,73 +3529,51 @@ } }, "jest-serializer": { - "version": "24.4.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.4.0.tgz", - "integrity": "sha512-k//0DtglVstc1fv+GY/VHDIjrtNjdYvYjMlbLUed4kxrE92sIUewOi5Hj3vrpB8CXfkJntRPDRjCrCvUhBdL8Q==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.9.0.tgz", + "integrity": "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==", "dev": true }, "jest-snapshot": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.8.0.tgz", - "integrity": "sha512-5ehtWoc8oU9/cAPe6fez6QofVJLBKyqkY2+TlKTOf0VllBB/mqUNdARdcjlZrs9F1Cv+/HKoCS/BknT0+tmfPg==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.9.0.tgz", + "integrity": "sha512-uI/rszGSs73xCM0l+up7O7a40o90cnrk429LOiK3aeTvfC0HHmldbd81/B7Ix81KSFe1lwkbl7GnBGG4UfuDew==", "dev": true, "requires": { "@babel/types": "^7.0.0", - "@jest/types": "^24.8.0", + "@jest/types": "^24.9.0", "chalk": "^2.0.1", - "expect": "^24.8.0", - "jest-diff": "^24.8.0", - "jest-matcher-utils": "^24.8.0", - "jest-message-util": "^24.8.0", - "jest-resolve": "^24.8.0", + "expect": "^24.9.0", + "jest-diff": "^24.9.0", + "jest-get-type": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-resolve": "^24.9.0", "mkdirp": "^0.5.1", "natural-compare": "^1.4.0", - "pretty-format": "^24.8.0", - "semver": "^5.5.0" + "pretty-format": "^24.9.0", + "semver": "^6.2.0" }, "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true } } }, "jest-util": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.8.0.tgz", - "integrity": "sha512-DYZeE+XyAnbNt0BG1OQqKy/4GVLPtzwGx5tsnDrFcax36rVE3lTA5fbvgmbVPUZf9w77AJ8otqR4VBbfFJkUZA==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", + "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", "dev": true, "requires": { - "@jest/console": "^24.7.1", - "@jest/fake-timers": "^24.8.0", - "@jest/source-map": "^24.3.0", - "@jest/test-result": "^24.8.0", - "@jest/types": "^24.8.0", + "@jest/console": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/source-map": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", "callsites": "^3.0.0", "chalk": "^2.0.1", "graceful-fs": "^4.1.15", @@ -6428,155 +3581,52 @@ "mkdirp": "^0.5.1", "slash": "^2.0.0", "source-map": "^0.6.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } } }, "jest-validate": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.8.0.tgz", - "integrity": "sha512-+/N7VOEMW1Vzsrk3UWBDYTExTPwf68tavEPKDnJzrC6UlHtUDU/fuEdXqFoHzv9XnQ+zW6X3qMZhJ3YexfeLDA==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.9.0.tgz", + "integrity": "sha512-HPIt6C5ACwiqSiwi+OfSSHbK8sG7akG8eATl+IPKaeIjtPOeBUd/g3J7DghugzxrGjI93qS/+RPKe1H6PqvhRQ==", "dev": true, "requires": { - "@jest/types": "^24.8.0", - "camelcase": "^5.0.0", + "@jest/types": "^24.9.0", + "camelcase": "^5.3.1", "chalk": "^2.0.1", - "jest-get-type": "^24.8.0", - "leven": "^2.1.0", - "pretty-format": "^24.8.0" + "jest-get-type": "^24.9.0", + "leven": "^3.1.0", + "pretty-format": "^24.9.0" }, "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, "camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } } } }, "jest-watcher": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-24.8.0.tgz", - "integrity": "sha512-SBjwHt5NedQoVu54M5GEx7cl7IGEFFznvd/HNT8ier7cCAx/Qgu9ZMlaTQkvK22G1YOpcWBLQPFSImmxdn3DAw==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-24.9.0.tgz", + "integrity": "sha512-+/fLOfKPXXYJDYlks62/4R4GoT+GU1tYZed99JSCOsmzkkF7727RqKrjNAxtfO4YpGv11wybgRvCjR73lK2GZw==", "dev": true, "requires": { - "@jest/test-result": "^24.8.0", - "@jest/types": "^24.8.0", - "@types/yargs": "^12.0.9", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/yargs": "^13.0.0", "ansi-escapes": "^3.0.0", "chalk": "^2.0.1", - "jest-util": "^24.8.0", + "jest-util": "^24.9.0", "string-length": "^2.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } } }, "jest-worker": { - "version": "24.6.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.6.0.tgz", - "integrity": "sha512-jDwgW5W9qGNvpI1tNnvajh0a5IE/PuGLFmHk6aR/BZFz8tSgGw17GsDPXAJ6p91IvYDjOw8GpFbvvZGAK+DPQQ==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", + "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", "dev": true, "requires": { - "merge-stream": "^1.0.1", + "merge-stream": "^2.0.0", "supports-color": "^6.1.0" }, "dependencies": { @@ -6592,9 +3642,9 @@ } }, "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, "js-yaml": { @@ -6654,9 +3704,9 @@ } }, "jsesc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", - "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true }, "json-parse-better-errors": { @@ -6681,10 +3731,13 @@ "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" }, "json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", - "dev": true + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.0.tgz", + "integrity": "sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } }, "jsprim": { "version": "1.4.1", @@ -6698,13 +3751,10 @@ } }, "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true }, "kleur": { "version": "3.0.3", @@ -6712,6 +3762,14 @@ "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "dev": true }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "requires": { + "invert-kv": "^1.0.0" + } + }, "lcov-parse": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/lcov-parse/-/lcov-parse-0.0.10.tgz", @@ -6724,9 +3782,9 @@ "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==" }, "leven": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", - "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", "dev": true }, "levn": { @@ -6820,15 +3878,6 @@ "tmpl": "1.0.x" } }, - "map-age-cleaner": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", - "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", - "dev": true, - "requires": { - "p-defer": "^1.0.0" - } - }, "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", @@ -6844,17 +3893,10 @@ "object-visit": "^1.0.0" } }, - "math-random": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz", - "integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==", - "dev": true, - "optional": true - }, "mathjax": { - "version": "2.7.5", - "resolved": "https://registry.npmjs.org/mathjax/-/mathjax-2.7.5.tgz", - "integrity": "sha512-OzsJNitEHAJB3y4IIlPCAvS0yoXwYjlo2Y4kmm9KQzyIBZt2d8yKRalby3uTRNN4fZQiGL2iMXjpdP1u2Rq2DQ==" + "version": "2.7.6", + "resolved": "https://registry.npmjs.org/mathjax/-/mathjax-2.7.6.tgz", + "integrity": "sha512-RKFn28kVFSL9xyy6aCoI5fj/Vb9yCr4sI5VdzrKy95FuN7zngJfNZNqWezSVjNYFjZ8Dfrap5GoYcPqqhKWSkA==" }, "mathjax-node": { "version": "2.1.1", @@ -6871,19 +3913,24 @@ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, + "mem": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", + "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", + "requires": { + "mimic-fn": "^1.0.0" + } + }, "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" }, "merge-stream": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz", - "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=", - "dev": true, - "requires": { - "readable-stream": "^2.0.1" - } + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true }, "methods": { "version": "1.1.2", @@ -6891,39 +3938,24 @@ "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" }, "micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", "dev": true, - "optional": true, "requires": { - "arr-diff": "^2.0.0", - "array-unique": "^0.2.1", - "braces": "^1.8.2", - "expand-brackets": "^0.1.4", - "extglob": "^0.3.1", - "filename-regex": "^2.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.1", - "kind-of": "^3.0.2", - "normalize-path": "^2.0.1", - "object.omit": "^2.0.0", - "parse-glob": "^3.0.4", - "regex-cache": "^0.4.2" - }, - "dependencies": { - "braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", - "dev": true, - "optional": true, - "requires": { - "expand-range": "^1.8.1", - "preserve": "^0.2.0", - "repeat-element": "^1.1.2" - } - } + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" } }, "mime": { @@ -6944,6 +3976,11 @@ "mime-db": "1.40.0" } }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -6954,15 +3991,15 @@ } }, "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true }, "mixin-deep": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", - "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", "dev": true, "requires": { "for-in": "^1.0.2", @@ -6987,6 +4024,14 @@ "dev": true, "requires": { "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + } } }, "ms": { @@ -7018,26 +4063,6 @@ "regex-not": "^1.0.0", "snapdragon": "^0.8.1", "to-regex": "^3.0.1" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - } } }, "natural-compare": { @@ -7081,9 +4106,9 @@ "dev": true }, "node-notifier": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.0.tgz", - "integrity": "sha512-SUDEb+o71XR5lXSTyivXd9J7fCloE3SyP4lSgt3lU2oSANiox+SxlNRGPjDKrwU1YN3ix2KN/VGGCg0t01rttQ==", + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.3.tgz", + "integrity": "sha512-M4UBGcs4jeOK9CjTsYwkvH6/MzuUmGCyTW+kCY7uO+1ZVr0+FHGdPdIf5CCLqAaxnRrWidyoQlNkMIIVwbKB8Q==", "dev": true, "requires": { "growly": "^1.3.0", @@ -7101,17 +4126,6 @@ "prismjs": "~1.6.0" }, "dependencies": { - "clipboard": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-1.7.1.tgz", - "integrity": "sha1-Ng1taUbpmnof7zleQrqStem1oWs=", - "optional": true, - "requires": { - "good-listener": "^1.2.2", - "select": "^1.1.2", - "tiny-emitter": "^2.0.0" - } - }, "prismjs": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.6.0.tgz", @@ -7166,12 +4180,6 @@ "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, "object-copy": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", @@ -7191,9 +4199,24 @@ "requires": { "is-descriptor": "^0.1.0" } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } } } }, + "object-inspect": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz", + "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==", + "dev": true + }, "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", @@ -7207,14 +4230,6 @@ "dev": true, "requires": { "isobject": "^3.0.0" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } } }, "object.getownpropertydescriptors": { @@ -7227,17 +4242,6 @@ "es-abstract": "^1.5.1" } }, - "object.omit": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", - "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", - "dev": true, - "optional": true, - "requires": { - "for-own": "^0.1.4", - "is-extendable": "^0.1.1" - } - }, "object.pick": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", @@ -7245,14 +4249,6 @@ "dev": true, "requires": { "isobject": "^3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } } }, "on-finished": { @@ -7280,6 +4276,20 @@ "requires": { "minimist": "~0.0.1", "wordwrap": "~0.0.2" + }, + "dependencies": { + "minimist": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", + "dev": true + }, + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "dev": true + } } }, "optionator": { @@ -7293,44 +4303,18 @@ "prelude-ls": "~1.1.2", "type-check": "~0.3.2", "wordwrap": "~1.0.0" - }, - "dependencies": { - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" - } } }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "dev": true - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, - "output-file-sync": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/output-file-sync/-/output-file-sync-1.1.2.tgz", - "integrity": "sha1-0KM+7+YaIF+suQCS6CZZjVJFznY=", - "dev": true, + "os-locale": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", + "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", "requires": { - "graceful-fs": "^4.1.4", - "mkdirp": "^0.5.1", - "object-assign": "^4.1.0" + "execa": "^0.7.0", + "lcid": "^1.0.0", + "mem": "^1.1.0" } }, - "p-defer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", - "dev": true - }, "p-each-series": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-1.0.0.tgz", @@ -7345,12 +4329,6 @@ "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" }, - "p-is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", - "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", - "dev": true - }, "p-limit": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", @@ -7378,19 +4356,6 @@ "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" }, - "parse-glob": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", - "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", - "dev": true, - "optional": true, - "requires": { - "glob-base": "^0.3.0", - "is-dotfile": "^1.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.0" - } - }, "parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", @@ -7502,9 +4467,9 @@ } }, "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -7543,20 +4508,13 @@ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" }, - "preserve": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", - "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", - "dev": true, - "optional": true - }, "pretty-format": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.8.0.tgz", - "integrity": "sha512-P952T7dkrDEplsR+TuY7q3VXDae5Sr7zmQb12JU/NDQa/3CH7/QW0yvqLcGN6jL+zQFKaoJcPc+yJxMTGmosqw==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", + "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", "dev": true, "requires": { - "@jest/types": "^24.8.0", + "@jest/types": "^24.9.0", "ansi-regex": "^4.0.0", "ansi-styles": "^3.2.0", "react-is": "^16.8.4" @@ -7567,46 +4525,44 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } } } }, "prismjs": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.16.0.tgz", - "integrity": "sha512-OA4MKxjFZHSvZcisLGe14THYsug/nF6O1f0pAJc0KN0wTyAcLqmsbE+lTGKSpyh+9pEW57+k6pg2AfYR+coyHA==", + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.17.1.tgz", + "integrity": "sha512-PrEDJAFdUGbOP6xK/UsfkC5ghJsPJviKgnQOoxaDbBjwc8op68Quupwt1DeAFoG8GImPhiKXAvvsH7wDSLsu1Q==", "requires": { "clipboard": "^2.0.0" + }, + "dependencies": { + "clipboard": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.4.tgz", + "integrity": "sha512-Vw26VSLRpJfBofiVaFb/I8PVfdI1OxKcYShe6fm0sP/DtmiWQNCjhM/okTvdCo0G+lMMm1rMYbk4IK4x1X+kgQ==", + "optional": true, + "requires": { + "good-listener": "^1.2.2", + "select": "^1.1.2", + "tiny-emitter": "^2.0.0" + } + } } }, - "private": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", - "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", - "dev": true - }, "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, "prompts": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.1.0.tgz", - "integrity": "sha512-+x5TozgqYdOwWsQFZizE/Tra3fKvAoy037kOyU6cgz84n8f6zxngLOV4O32kTwt9FcLCxAqw0P/c8rOr9y+Gfg==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.2.1.tgz", + "integrity": "sha512-VObPvJiWPhpZI6C5m60XOzTfnYg/xc/an+r9VYymj9WJW3B/DIH+REzjpAACPf8brwPeP+7vz3bIim3S+AaMjw==", "dev": true, "requires": { - "kleur": "^3.0.2", - "sisteransi": "^1.0.0" + "kleur": "^3.0.3", + "sisteransi": "^1.0.3" } }, "proxy-addr": { @@ -7624,9 +4580,9 @@ "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" }, "psl": { - "version": "1.1.32", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.32.tgz", - "integrity": "sha512-MHACAkHpihU/REGGPLj4sEfc/XKW2bheigvHO1dUqjaKigMp1C8+WLQYRGgeKFMsw5PMfegZcaN8IDXK/cD0+g==" + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.4.0.tgz", + "integrity": "sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw==" }, "pump": { "version": "3.0.0", @@ -7648,34 +4604,6 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" }, - "randomatic": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", - "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", - "dev": true, - "optional": true, - "requires": { - "is-number": "^4.0.0", - "kind-of": "^6.0.0", - "math-random": "^1.0.1" - }, - "dependencies": { - "is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", - "dev": true, - "optional": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true, - "optional": true - } - } - }, "range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -7693,9 +4621,9 @@ } }, "react-is": { - "version": "16.8.6", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", - "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==", + "version": "16.9.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.9.0.tgz", + "integrity": "sha512-tJBzzzIgnnRfEm046qRcURvwQnZVXmuCbscxUO5RWrGTXpon2d4c8mI0D8WE6ydVIm29JiLB6+RslkIvym9Rjw==", "dev": true }, "read-pkg": { @@ -7739,9 +4667,9 @@ } }, "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -7765,332 +4693,14 @@ } }, "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", + "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "dev": true, - "optional": true, - "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true, - "optional": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true, - "optional": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "optional": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "optional": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "optional": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true, - "optional": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "optional": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "optional": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "optional": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true, - "optional": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true, - "optional": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "optional": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - } + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" } }, "realpath-native": { @@ -8102,39 +4712,6 @@ "util.promisify": "^1.0.0" } }, - "regenerate": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", - "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", - "dev": true - }, - "regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", - "dev": true - }, - "regenerator-transform": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz", - "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", - "dev": true, - "requires": { - "babel-runtime": "^6.18.0", - "babel-types": "^6.19.0", - "private": "^0.1.6" - } - }, - "regex-cache": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", - "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", - "dev": true, - "optional": true, - "requires": { - "is-equal-shallow": "^0.1.3" - } - }, "regex-not": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", @@ -8145,40 +4722,6 @@ "safe-regex": "^1.1.0" } }, - "regexpu-core": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", - "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", - "dev": true, - "requires": { - "regenerate": "^1.2.1", - "regjsgen": "^0.2.0", - "regjsparser": "^0.1.4" - } - }, - "regjsgen": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", - "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", - "dev": true - }, - "regjsparser": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", - "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", - "dev": true, - "requires": { - "jsesc": "~0.5.0" - }, - "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true - } - } - }, "remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", @@ -8197,15 +4740,6 @@ "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", "dev": true }, - "repeating": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", - "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", - "dev": true, - "requires": { - "is-finite": "^1.0.0" - } - }, "request": { "version": "2.88.0", "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", @@ -8283,9 +4817,9 @@ "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" }, "resolve": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.0.tgz", - "integrity": "sha512-WL2pBDjqT6pGUNSUzMw00o4T7If+z4H2x3Gz893WoUQ5KW8Vr9txp00ykiP16VBaZF5+j/OcXJHZ9+PCvdiDKw==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", + "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", "dev": true, "requires": { "path-parse": "^1.0.6" @@ -8319,9 +4853,9 @@ "dev": true }, "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", "dev": true, "requires": { "glob": "^7.1.3" @@ -8393,57 +4927,6 @@ "walker": "~1.0.5" }, "dependencies": { - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, "cross-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", @@ -8472,157 +4955,6 @@ "strip-eof": "^1.0.0" } }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, "get-stream": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", @@ -8631,94 +4963,6 @@ "requires": { "pump": "^3.0.0" } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true } } }, @@ -8734,9 +4978,9 @@ "optional": true }, "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true }, "send": { @@ -8783,9 +5027,9 @@ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, "set-value": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", "dev": true, "requires": { "extend-shallow": "^2.0.1", @@ -8843,15 +5087,15 @@ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" }, "sisteransi": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.0.tgz", - "integrity": "sha512-N+z4pHB4AmUv0SjveWRd6q1Nj5w62m5jodv+GD8lvmbY/83T/rpbJGZOnK5T149OldDj4Db07BSv9xY4K6NTPQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.3.tgz", + "integrity": "sha512-SbEG75TzH8G7eVXFSN5f9EExILKfly7SUvVY5DhhYLvfhKqhDFY0OzevWa/zwak0RLRfWS5AvfMWpd9gJvr5Yg==", "dev": true }, "slash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", "dev": true }, "snapdragon": { @@ -8887,6 +5131,12 @@ "requires": { "is-extendable": "^0.1.0" } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true } } }, @@ -8938,18 +5188,6 @@ "is-data-descriptor": "^1.0.0", "kind-of": "^6.0.2" } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true } } }, @@ -8960,13 +5198,23 @@ "dev": true, "requires": { "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } } }, "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" }, "source-map-resolve": { "version": "0.5.2", @@ -8982,12 +5230,13 @@ } }, "source-map-support": { - "version": "0.4.18", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", - "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", "dev": true, "requires": { - "source-map": "^0.5.6" + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, "source-map-url": { @@ -9023,9 +5272,9 @@ } }, "spdx-license-ids": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.4.tgz", - "integrity": "sha512-7j8LYJLeY/Yb6ACbQ7F76qy5jHkp0U6jgBfJsk97bwWlVUnUWsAgpyaCvo17h0/RQGnQ036tVDomiwoI4pDkQA==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", "dev": true }, "split-string": { @@ -9115,13 +5364,41 @@ "strip-ansi": "^4.0.0" } }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "string.prototype.trimleft": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz", + "integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, + "string.prototype.trimright": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz", + "integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", + "dev": true + } } }, "strip-ansi": { @@ -9183,21 +5460,10 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "readable-stream": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", - "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, "semver": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.1.1.tgz", - "integrity": "sha512-rWYq2e5iYW+fFe/oPPtYJxYgjBm8sC4rmoGdUOgBB7VnwKt6HrL793l2voH1UlsyYZpJ4g0wfjnTEO1s1NP2eQ==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true } } @@ -9227,6 +5493,30 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, "superagent": { "version": "3.8.3", "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz", @@ -9248,10 +5538,13 @@ } }, "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } }, "symbol-tree": { "version": "3.2.4", @@ -9297,9 +5590,9 @@ "dev": true }, "to-fast-properties": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", - "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", "dev": true }, "to-object-path": { @@ -9309,6 +5602,17 @@ "dev": true, "requires": { "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } } }, "to-regex": { @@ -9331,17 +5635,6 @@ "requires": { "is-number": "^3.0.0", "repeat-string": "^1.6.1" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - } - } } }, "toidentifier": { @@ -9411,50 +5704,18 @@ "requires": { "commander": "~2.20.0", "source-map": "~0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true - } } }, "union-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", - "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", "dev": true, "requires": { "arr-union": "^3.1.0", "get-value": "^2.0.6", "is-extendable": "^0.1.1", - "set-value": "^0.4.3" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "set-value": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", - "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.1", - "to-object-path": "^0.3.0" - } - } + "set-value": "^2.0.1" } }, "unpipe": { @@ -9499,12 +5760,6 @@ "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", "dev": true - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true } } }, @@ -9528,12 +5783,6 @@ "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", "dev": true }, - "user-home": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/user-home/-/user-home-1.1.1.tgz", - "integrity": "sha1-K1viOjK2Onyd640PKNSFcko98ZA=", - "dev": true - }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -9556,18 +5805,9 @@ "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" - }, - "v8flags": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-2.1.1.tgz", - "integrity": "sha1-qrGh+jDUX4jdMhFIh1rALAtV5bQ=", - "dev": true, - "requires": { - "user-home": "^1.1.1" - } + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", + "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==" }, "validate-npm-package-license": { "version": "3.0.4", @@ -9653,10 +5893,9 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" }, "wordwrap": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", - "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", - "dev": true + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" }, "wrap-ansi": { "version": "2.1.0", @@ -9770,73 +6009,6 @@ "which-module": "^2.0.0", "y18n": "^3.2.1", "yargs-parser": "^8.1.0" - }, - "dependencies": { - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", - "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" - }, - "invert-kv": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" - }, - "lcid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", - "requires": { - "invert-kv": "^1.0.0" - } - }, - "mem": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", - "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", - "requires": { - "mimic-fn": "^1.0.0" - } - }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" - }, - "os-locale": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", - "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", - "requires": { - "execa": "^0.7.0", - "lcid": "^1.0.0", - "mem": "^1.1.0" - } - } } }, "yargs-parser": { diff --git a/package.json b/package.json index 43266f0..52a1c16 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,6 @@ "dependencies": { "@iarna/toml": "^2.2.3", "body-parser": "^1.19.0", - "crypto": "^1.0.1", "ejs": "^2.6.2", "express": "^4.17.1", "express-rate-limit": "^5.0.0", @@ -19,8 +18,6 @@ "showdown": "^1.9.0" }, "devDependencies": { - "babel-cli": "^6.26.0", - "babel-preset-env": "^1.7.0", "coveralls": "^3.0.4", "jest": "^24.8.0", "superagent": "^5.1.0",