Compare commits

...

9 Commits

Author SHA1 Message Date
Klemek e773f53da1 Merge pull request #4 from Klemek/dev
v1.0.3
2019-06-22 14:41:38 +02:00
Klemek 35a7747d6d better warning when no config.json 2019-06-22 14:38:59 +02:00
Klemek 0d173cfcef [skip CI] updated README.md 2019-06-22 14:37:52 +02:00
Klemek 0480536a20 Updated footer for more info 2019-06-22 14:29:54 +02:00
Klemek 333bbf6eb8 updated article index.md 2019-06-22 14:25:41 +02:00
Klemek 6bdcd6872e MathJax small fix 2019-06-22 14:13:20 +02:00
Klemek 9d3c1d0847 MathJax support 2019-06-22 13:48:08 +02:00
Klemek def326676c More tests 2019-06-22 11:00:02 +02:00
Klemek d343179764 Updated code coverage files 2019-06-22 10:17:04 +02:00
13 changed files with 446 additions and 258 deletions
+1
View File
@@ -6,3 +6,4 @@
/test_data
/access.log
/error.log
/coverage
+21 -1
View File
@@ -14,6 +14,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
@@ -192,6 +193,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)
@@ -212,6 +225,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
@@ -248,4 +263,9 @@ 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)
* `speak_text`: (default: true)
activate the alternate text in equations
+70 -146
View File
@@ -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",
+11 -3
View File
@@ -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": {
@@ -9,6 +8,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",
@@ -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"
]
}
}
+62
View File
@@ -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 <b>tag</b>.
### 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)
<dl>
<dt>Definition list</dt>
@@ -146,6 +171,7 @@ Quote break.
</dl>
### 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)
<details><summary>Title of the spoiler (click)</summary><p>
Content of the spoiler
On several lines
</p></details>
### 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
<iframe width="535" height="300" src="https://www.youtube.com/embed/FTQbiNvZqaY" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
+2 -1
View File
@@ -1,5 +1,6 @@
<hr>
<footer>
<small>@<%= new Date().getFullYear() %> - Made with <a href="https://github.com/klemek/gitblog.md">GitBlog.md</a>
<small><a href="/rss">RSS feed</a> - @<%= new Date().getFullYear() %> - Made with <a
href="https://github.com/klemek/gitblog.md">GitBlog.md</a> (v<%= info.version %>)
</small>
</footer>
+5 -4
View File
@@ -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);
@@ -154,10 +158,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']];
+6 -1
View File
@@ -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
}
}
+1 -1
View File
@@ -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;
}
};
+85 -21
View File
@@ -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) + `<pre><code class="${lang} language-${lang}">` + block + '</code></pre>' + 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 = /\$([^$\n]*)\$/;
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) + `<pre><code class="${lang} language-${lang}">` + block + '</code></pre>' + 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);
});
});
});
});
}
};
+52 -26
View File
@@ -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('<item>').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 %><%- `<a href="${article.url}">reload</a>` %>');
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');
+1 -1
View File
@@ -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();
});
});
+128 -52
View File
@@ -7,18 +7,26 @@ 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
}
};
const renderer = require('../src/renderer')(config);
beforeEach(() => {
config['modules']['prism'] = true;
config['modules']['mathjax'] = true;
utils.deleteFolderSync(dataDir);
fs.mkdirSync(dataDir);
});
@@ -29,68 +37,136 @@ 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('<h1 id="hello">Hello</h1>');
done();
});
});
test('custom rules', (done) => {
renderer.renderShowDown('www.google.com', (html) => {
expect(html).toBe('<p><a href="http://www.google.com">www.google.com</a></p>');
done();
});
});
test('code format', (done) => {
renderer.renderShowDown('```python\nprint("hello")\n```\n\n```python\nprint("hello")\n```', (html) => {
expect(html).toBe('<pre><code class="python language-python">print("hello")\n</code></pre>\n<pre><code class="python language-python">print("hello")\n</code></pre>');
done();
});
});
});
test('normal file', (done) => {
fs.writeFileSync(file, `# Hello`);
renderer.render(file, (err, html) => {
expect(err).toBeNull();
expect(html).toBe('<h1 id="hello">Hello</h1>');
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('<pre><code class="python language-python">print("hello")\n</code></pre>');
expect(data.indexOf('<pre><code class="python language-python">')).toBe(0);
done();
});
});
test('prism invalid lang', (done) => {
renderer.renderPrism('```pythdon\nprint("hello")\n```', (data) => {
expect(data).not.toBe('<pre><code class="pythdon language-pythdon">print("hello")\n</code></pre>');
expect(data.indexOf('<pre><code class="pythdon language-pythdon">')).toBe(0);
done();
});
});
test('prism mutliple code blocks', (done) => {
renderer.renderPrism('```python\n\n```\n```python\n\n```', (data) => {
expect(data).toBe('<pre><code class="python language-python"></code></pre>\n<pre><code class="python language-python"></code></pre>');
done();
});
});
});
test('custom rules', (done) => {
fs.writeFileSync(file, `www.google.com`);
renderer.render(file, (err, html) => {
expect(err).toBeNull();
expect(html).toBe('<p><a href="http://www.google.com">www.google.com</a></p>');
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('<span class=\"mjx-chtml MJXc-display\" style=\"text-align: center;\">' +
'<span class=\"mjx-math\"><span class=\"mjx-mrow\"><span class=\"mjx-mi\">' +
'<span class=\"mjx-char MJXc-TeX-math-I\" style=\"padding-top: 0.519em; padding-bottom: 0.298em;\">' +
'A' +
'</span></span></span></span></span>');
done();
});
});
test('inline eq', (done) => {
renderer.renderMathJax('start $a$ end', (data) => {
expect(data).toBe('start ' +
'<span class=\"mjx-chtml\">' +
'<span class=\"mjx-math\"><span class=\"mjx-mrow\"><span class=\"mjx-mi\">' +
'<span class=\"mjx-char MJXc-TeX-math-I\" style=\"padding-top: 0.225em; padding-bottom: 0.298em;\">' +
'a' +
'</span></span></span></span></span>' +
' end');
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('' +
'<span class=\"mjx-chtml MJXc-display\" style=\"text-align: center;\">' +
'<span class=\"mjx-math\"><span class=\"mjx-mrow\"><span class=\"mjx-mi\">' +
'<span class=\"mjx-char MJXc-TeX-math-I\" style=\"padding-top: 0.519em; padding-bottom: 0.298em;\">' +
'A' +
'</span></span></span></span></span>\n' +
'start ' +
'<span class=\"mjx-chtml\">' +
'<span class=\"mjx-math\"><span class=\"mjx-mrow\"><span class=\"mjx-mi\">' +
'<span class=\"mjx-char MJXc-TeX-math-I\" style=\"padding-top: 0.225em; padding-bottom: 0.298em;\">' +
'a' +
'</span></span></span></span></span>' +
' end\n' +
'<span class=\"mjx-chtml MJXc-display\" style=\"text-align: center;\">' +
'<span class=\"mjx-math\"><span class=\"mjx-mrow\"><span class=\"mjx-mi\">' +
'<span class=\"mjx-char MJXc-TeX-math-I\" style=\"padding-top: 0.519em; padding-bottom: 0.298em;\">' +
'A' +
'</span></span></span></span></span>');
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('<pre><code class="python language-python">print("hello")\n</code></pre>\n<pre><code class="python language-python">print("hello")\n</code></pre>');
config['modules']['prism'] = true;
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('<pre><code class="python language-python">print("hello")\n</code></pre>');
expect(html.indexOf('<pre><code class="python language-python">')).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('<pre><code class="pythdon language-pythdon">print("hello")\n</code></pre>');
expect(html.indexOf('<pre><code class="pythdon language-pythdon">')).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('<pre><code class="python language-python"></code></pre>\n<pre><code class="python language-python"></code></pre>');
done();
test('normal file', (done) => {
fs.writeFileSync(file, `# Hello`);
renderer.render(file, (err, html) => {
expect(err).toBeNull();
expect(html).toBe('<h1 id="hello">Hello</h1>');
done();
});
});
});