mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-16 07:35:16 +00:00
Compare commits
416 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 856b222a61 | |||
| fade3740c1 | |||
| 803d6218a7 | |||
| 7128db842a | |||
| 0342a11da0 | |||
| af80cbcd7c | |||
| a84d02ece0 | |||
| 45b9fc2094 | |||
| a96ef569bd | |||
| 4b3ac4e827 | |||
| 7029c01806 | |||
| 11893255d1 | |||
| 53f94b317e | |||
| 4717b0a17f | |||
| 43ce7badb7 | |||
| 9e399da06a | |||
| 026b33227d | |||
| e94634bda5 | |||
| 194ebc3da9 | |||
| 16af264360 | |||
| 64ccb6b00b | |||
| 8bf3241b63 | |||
| 6a4f6eadd2 | |||
| 532df30db7 | |||
| fd1f7a5609 | |||
| febdd8a8bd | |||
| b04724ae0b | |||
| 5dbd8f5740 | |||
| f3af207e6e | |||
| 7d05f8cb89 | |||
| 9d3c397446 | |||
| 0681a51c2f | |||
| 36e548fd3c | |||
| ec149e6112 | |||
| e1bcb4ed7d | |||
| 4aae18617b | |||
| 6d00944576 | |||
| be8b11dcb4 | |||
| 1780435c4f | |||
| ebf7a5cf50 | |||
| 5f0a81d18b | |||
| b564ef6580 | |||
| 1d877db5f0 | |||
| 92678bb93f | |||
| 4f2c729d60 | |||
| 59079bc18d | |||
| 66c2a6c076 | |||
| 182bb9eb86 | |||
| 9ab738e478 | |||
| d0555e2bd0 | |||
| cdf9842d68 | |||
| a1ae5c2295 | |||
| 373a486a14 | |||
| f262a7cd89 | |||
| b63b0cc50d | |||
| 5839d4d8a8 | |||
| d9c3cb418a | |||
| 12c7ac1498 | |||
| 2c3072fd8a | |||
| bb14f48c39 | |||
| d56019dc18 | |||
| 0a6216a093 | |||
| 4c174f916e | |||
| 4db57fe5c5 | |||
| 2a78bcebab | |||
| be14a8ea4d | |||
| 37920c758b | |||
| 52f9409e24 | |||
| 05646996c0 | |||
| c439cf8513 | |||
| 1a3e4e8337 | |||
| 631cebfd2f | |||
| f1f2024489 | |||
| 227b329e78 | |||
| da8c78b3c8 | |||
| c42c5897fe | |||
| 3aea3e5d88 | |||
| 95b3b4ebb7 | |||
| ba56801352 | |||
| 48ee0e35b0 | |||
| 2fff291cad | |||
| baea45d924 | |||
| 52d0384605 | |||
| 961b9d7031 | |||
| 5b0622014a | |||
| 7253dbc476 | |||
| b4dd3e1ddc | |||
| 4b6fc0ae2f | |||
| 5d4dc499a5 | |||
| 0e7aece96d | |||
| b7c245d75a | |||
| 5fed4e1ced | |||
| c71da9955e | |||
| 3a2abb870b | |||
| e8947a2c11 | |||
| 885a161d42 | |||
| fe0d4bf024 | |||
| aa39b8e71b | |||
| 3000dcefe1 | |||
| f1405d3958 | |||
| caeadd431b | |||
| d5796ccf04 | |||
| 71cce33836 | |||
| 9eeb7304c7 | |||
| 3b5bea0eb5 | |||
| 17d921cb11 | |||
| 10ab06a32c | |||
| 1bc802e4c8 | |||
| 9a9b5a2621 | |||
| 66231f8b7f | |||
| 7037ac55bd | |||
| cf35fef3b3 | |||
| 16800cc70a | |||
| 417013a230 | |||
| 5a57a2c62a | |||
| 4cfffb26f2 | |||
| 9d4221936e | |||
| 34baae1a5d | |||
| cd0ed86589 | |||
| 2d429b731b | |||
| 03b89194d7 | |||
| 68ec88c620 | |||
| 47a27fc12d | |||
| 9cedcc6b6e | |||
| d6de8f4173 | |||
| 1d291ff2f0 | |||
| 58ae96b7f6 | |||
| 1354582c4e | |||
| 4119917555 | |||
| 9d20a9afa5 | |||
| 45683185a2 | |||
| 6ce4949df3 | |||
| 0614613b84 | |||
| 6faf6a53f0 | |||
| bced415a50 | |||
| 805886e4a9 | |||
| 83c98cf4d8 | |||
| 86848e7acb | |||
| 9ce3646d6e | |||
| 45752fc79c | |||
| a863b55c79 | |||
| 16ca48b2c0 | |||
| 87ae2481e4 | |||
| ccb5dcf559 | |||
| 84315a99f1 | |||
| 026cc8304d | |||
| cefbfdc7b8 | |||
| 4049279269 | |||
| b04befd67c | |||
| d82032298d | |||
| 34eb35699d | |||
| 3ae72bb47c | |||
| cd2d5e5101 | |||
| 5abac3a79a | |||
| d22d090b05 | |||
| 24b657f790 | |||
| 649a6c980c | |||
| 24624f0d0e | |||
| 9aaf561c41 | |||
| 8be4cbc887 | |||
| 32556e6724 | |||
| ad425b9772 | |||
| 5238fa352c | |||
| 32183b6650 | |||
| cb3d6a7f01 | |||
| c978f0ab21 | |||
| 8753897edc | |||
| da83d9e91d | |||
| 2cf507a807 | |||
| 1489129407 | |||
| b5c407cf7b | |||
| 9d8dffb6f0 | |||
| 6015defe11 | |||
| 7f28379154 | |||
| 6f98ee1846 | |||
| 42d53ec032 | |||
| 287c80b2e1 | |||
| 33ff60cee7 | |||
| 26fc021b90 | |||
| 33d7e022e6 | |||
| 09782b964b | |||
| 7938ee8ec6 | |||
| c314281da0 | |||
| 2e8e4f63d7 | |||
| bc5df83846 | |||
| f1ef1902f3 | |||
| 748c46172b | |||
| 51f4cd6dae | |||
| 64ce8bcf42 | |||
| ff42b58f77 | |||
| 7fa3e34f9f | |||
| e46cd78f80 | |||
| fab88bb47e | |||
| fa7cf3a65f | |||
| d0885491b7 | |||
| 45c54d0cd7 | |||
| f49089a7d3 | |||
| 0b1888e184 | |||
| 2257e42299 | |||
| 0e0c1a473b | |||
| a18370551a | |||
| a8aeab17d6 | |||
| 2adca5deb9 | |||
| 3b3f43a35d | |||
| ced0be930d | |||
| 6d58a90613 | |||
| 2796065886 | |||
| b9fa6c3051 | |||
| 82c2c214eb | |||
| ee35da4be5 | |||
| 24c4f41945 | |||
| fe298f2a3f | |||
| ab3d94a55d | |||
| ff31514c66 | |||
| 3f1ec310ca | |||
| b796069ade | |||
| 839904a704 | |||
| 0e74c9bad5 | |||
| d7e415b0b3 | |||
| b170b4d197 | |||
| a14ca3e268 | |||
| 9c071dade5 | |||
| 56e44651fa | |||
| 42fac98992 | |||
| 4f5513add0 | |||
| 90cbfc0596 | |||
| 38ab9fa05a | |||
| 12e20c5fcc | |||
| 6b103f4532 | |||
| d4fa5c18a8 | |||
| 2a116dfdb0 | |||
| 0349f54f3c | |||
| 695bda312c | |||
| 144bd4c97a | |||
| e105983cf8 | |||
| ea3afbed75 | |||
| d563abe3df | |||
| 70fd4ded29 | |||
| 9ce9388dfc | |||
| c575ad83ae | |||
| 936fa1545b | |||
| c9c2dde2b6 | |||
| d9a1d387c7 | |||
| bf29d187c8 | |||
| d86f9b2667 | |||
| aca4313fc3 | |||
| 4cb5bb06fb | |||
| 24988b75ea | |||
| 775e858d05 | |||
| 1e3d871c1e | |||
| 94a4310c1e | |||
| e8ee57be83 | |||
| 50a132cb2c | |||
| 59be32a54f | |||
| 4ae7cdb5d1 | |||
| 990a4efeb5 | |||
| 9c927dcf13 | |||
| 7a08b3f109 | |||
| 94d4234cb8 | |||
| fbc851bc4e | |||
| e92b661a2a | |||
| b9fe60915f | |||
| 1bf7d092f0 | |||
| 826d472511 | |||
| a6b1f4bd87 | |||
| 004f91f02c | |||
| e6c6ec4293 | |||
| a1d3acf065 | |||
| a732660f59 | |||
| e1bb0638f8 | |||
| 788c0c7e7e | |||
| 762fe16a17 | |||
| 15123805d1 | |||
| 23aecb4f2b | |||
| f1d200b81b | |||
| b566711ee1 | |||
| 22da5aac11 | |||
| f7b95102c5 | |||
| ca70c934d6 | |||
| 1cc48fa314 | |||
| b63b5574d3 | |||
| 67106e8698 | |||
| dc2ab51a88 | |||
| cedfa186f2 | |||
| 80f0d1bfed | |||
| 7178ab1f82 | |||
| bf895b707e | |||
| 996170a4ad | |||
| aa66a4f928 | |||
| 23010fa54a | |||
| 0baebc7979 | |||
| 257158321c | |||
| ddf0a41e29 | |||
| 357e1e8e96 | |||
| 7a89f9869a | |||
| 30fbd22efe | |||
| 29df73ba72 | |||
| f8676483c1 | |||
| c79a6f2fba | |||
| c38e67ea61 | |||
| 97a5f8805e | |||
| 421fef6ebf | |||
| 33406c3082 | |||
| c3851889b0 | |||
| 4a5de2da6b | |||
| 1afb23cada | |||
| a3b0f3f868 | |||
| 0aa32916d6 | |||
| ebe845d46c | |||
| edee8d2fc5 | |||
| 5f3de0876f | |||
| 2ceab04d01 | |||
| de37de44e8 | |||
| ccb87d5a13 | |||
| e2e0ca3f05 | |||
| b33a79704b | |||
| 6def174b84 | |||
| 346a18b444 | |||
| b32685c698 | |||
| 09b3618da8 | |||
| 901328f247 | |||
| b47ebaa9d9 | |||
| 465e8d6c0a | |||
| ecd86ec877 | |||
| 5d7864154c | |||
| 7b915428f7 | |||
| 0bce3517fd | |||
| 089815a8fd | |||
| 7e8f1a9632 | |||
| 8469648241 | |||
| e7a44358a5 | |||
| fb930d889f | |||
| 721a75309b | |||
| b856bff91b | |||
| 0744f000f9 | |||
| d1e226dba5 | |||
| 1923d28d71 | |||
| 484a306adb | |||
| 5bfc9f2f56 | |||
| 86a65e5287 | |||
| 956d301dd7 | |||
| 00d1a7b5a6 | |||
| 2718c9c017 | |||
| 3234c1e03f | |||
| c8f8eece92 | |||
| 7394efa9dc | |||
| 3e4241fc88 | |||
| a5ae79604c | |||
| fc4bdd68a1 | |||
| f83975b022 | |||
| 167d3957bf | |||
| a9571fd67d | |||
| ac87f48ba5 | |||
| 05403f527d | |||
| 53c72f0806 | |||
| c8c2a61f96 | |||
| 08d780d1f6 | |||
| 2e74fafa36 | |||
| 28ac9ab88a | |||
| 26acab4dfb | |||
| d4b562e001 | |||
| 61956e0646 | |||
| 7892259fe6 | |||
| b97d1c7796 | |||
| a091c5b331 | |||
| 784b3379b5 | |||
| 62446e75d2 | |||
| 8aa2f3075c | |||
| 839ee0385a | |||
| d6b60c6116 | |||
| 9013f02850 | |||
| 961d43f085 | |||
| 7bd49606e3 | |||
| d7c8635350 | |||
| 19e9ff81d2 | |||
| e137bf632a | |||
| 2fef898a1e | |||
| 0945807184 | |||
| d85663b153 | |||
| 481a7d790c | |||
| ef589a6b13 | |||
| d5c4af3377 | |||
| 0b4df4cdf2 | |||
| d6814dc48e | |||
| ce50a1d50f | |||
| a76e018d36 | |||
| b06f1fd677 | |||
| 3d8b1880dc | |||
| 1a13c8c9bb | |||
| 852fae3a09 | |||
| d342d31c8e | |||
| 5b3f54fbc8 | |||
| 308ece784c | |||
| 0758ddf04e | |||
| f24d9aef73 | |||
| f8bb55f258 | |||
| 76daebeb84 | |||
| b0d366b7e8 | |||
| 3d0054dd64 | |||
| 13bc89ff44 | |||
| bb910f984e | |||
| e576fdea14 | |||
| c7864927ec | |||
| 54956de59f | |||
| 2cbd30d1f2 | |||
| 55614f1675 | |||
| 4f7421993f | |||
| 32ad7b530e | |||
| ee5d5010f6 | |||
| cf7e5884cd | |||
| 11a0a64d9e | |||
| b6051babac | |||
| 34947b5e20 | |||
| c621380b66 | |||
| c4b54033ce | |||
| 4d85d6eb6e |
@@ -1,20 +1,11 @@
|
||||
{
|
||||
"rules": {
|
||||
"indent": [ 2, 4 ],
|
||||
"quotes": [ 2, "single" ],
|
||||
"linebreak-style": [ 2, "unix" ],
|
||||
"semi": [ 2, "always" ],
|
||||
"no-unused-vars": [ 2, {
|
||||
"vars": "all",
|
||||
"args": "none"
|
||||
} ],
|
||||
"spaced-comment": [ 2, "always" ]
|
||||
},
|
||||
"env": {
|
||||
"node": true,
|
||||
"browser": true,
|
||||
"jest": true,
|
||||
"jasmine": true
|
||||
"mocha": true
|
||||
},
|
||||
"extends": "eslint:recommended"
|
||||
}
|
||||
"globals": {
|
||||
"expect": true
|
||||
},
|
||||
"extends": "gitbook"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<!--
|
||||
IMPORTANT NOTICE
|
||||
Only post things related to the gitbook CLI
|
||||
For GitBook.com, the Editor, or anything else
|
||||
please go to
|
||||
https://github.com/GitbookIO/feedback/issues
|
||||
|
||||
Please open separate issues for each bug report.
|
||||
-->
|
||||
|
||||
|
||||
### What is the current behavior?
|
||||
|
||||
<!--
|
||||
- For bugs, provide the steps to reproduce
|
||||
- Provide the GitBook version being used (`gitbook -V`)
|
||||
- Provide system informations (OS and version)
|
||||
- Provide your username and the URL of the book
|
||||
-->
|
||||
|
||||
|
||||
|
||||
### What is the expected behavior?
|
||||
|
||||
|
||||
|
||||
<!--
|
||||
Thank you for giving us your feedback!
|
||||
-- The GitBook Team
|
||||
-->
|
||||
+5
-2
@@ -22,14 +22,17 @@ build/Release
|
||||
# Dependency directory
|
||||
# Deployed apps should consider commenting this line out:
|
||||
# see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git
|
||||
/node_modules
|
||||
node_modules
|
||||
|
||||
# vim swapfile
|
||||
*.swp
|
||||
|
||||
# GitBook with babel
|
||||
/packages/gitbook-core/lib
|
||||
/packages/gitbook-plugin/lib
|
||||
|
||||
# Output of documentation
|
||||
_book
|
||||
|
||||
book.pdf
|
||||
book.epub
|
||||
book.mobi
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
sudo: false
|
||||
language: node_js
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
node_js:
|
||||
- "stable"
|
||||
- "4.1"
|
||||
before_install:
|
||||
- npm install svgexport -g
|
||||
before_script:
|
||||
- npm run bootstrap
|
||||
after_success:
|
||||
- npm run lint
|
||||
|
||||
+58
-2
@@ -2,7 +2,63 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## 3.0.0 (pre)
|
||||
## 4.0.0
|
||||
- **Breaking Changes:** Most plugins and themes should be incompatible with this version
|
||||
- GitBook rendering is now done using React, which brings better APIs for plugins and a more interactive website.
|
||||
- *Performances*: Installation of plugins is much faster thanks to `ied`
|
||||
- *Performances*: Git conrefs are faster over large books
|
||||
- *Website Feature:* new default plugin `copy-code` to copy code blocks content in one click
|
||||
- *Website Feature:* new default plugin `heading-anchors` to have clickable headings
|
||||
- *Website Feature:* `fontsettings` is now only controlling the font size, and can work on multiple themes
|
||||
- *Plugin API*: hook `page` can no longer modify the content, only the page's attributes
|
||||
- *Plugin API*: plugins can no longer export resources, instead use the `_assets` folder
|
||||
- [ ] *Plugin API*: testing framework
|
||||
- [ ] `gitbook init` creates `.gitignore` if not found
|
||||
- [ ] Ignore files are no longer copied to the output
|
||||
|
||||
## 3.2.2
|
||||
- Fix catching parsing errors leading to possible missing pages in generated book
|
||||
- Rollback markdown parser to `kramed` until `markup-it` is stable enough
|
||||
|
||||
## 3.2.1
|
||||
- Fix bug on Firefox when navigating to an url containing an hash
|
||||
- Update nunjucks to fix JS error when page contains a lot of templating blocks
|
||||
|
||||
## 3.2.0
|
||||
- Switch markdown parser from `kramed` to `markup-it`
|
||||
- Fix support of `|` in tables
|
||||
- Fix access to `output.name` in templating
|
||||
- Fix i18n for website and PDF
|
||||
- Fix minor scrolling issues on website
|
||||
- Improve options of default search indexer (keywords, disabling per pages)
|
||||
- Improve pertinence of search results
|
||||
|
||||
## 3.1.1
|
||||
- Fix order of plugins during loading
|
||||
- Fix error when using math and conrefs
|
||||
- Fix target attribute for external links
|
||||
- Fix serve command
|
||||
|
||||
## 3.1.0
|
||||
- Always load themes after plugins
|
||||
- README/Introduction doesn't require to be the first entry in the summary
|
||||
- Highlight active entry in summary when scrolling a page with anchors
|
||||
- Support empty parts in summary
|
||||
|
||||
## 3.0.3
|
||||
- Fix redirection in `gitbook serve` when accessing a folder without trailing slash
|
||||
- Fix links/annotations for glossary not being correctly resolved
|
||||
- Fix regression for supporting uppercase filenames in structure files
|
||||
- Fix `gitbook install` when using Git URLs for plugins
|
||||
|
||||
## 3.0.2
|
||||
- Fix crash for anchor links
|
||||
|
||||
## 3.0.1
|
||||
- Fix regression in link resolution when contain anchor
|
||||
- Fix `structure.<name>` configuration not supporting filenames with dots
|
||||
|
||||
## 3.0.0
|
||||
- Summary can contain external links and anchors (Fix [#776](https://github.com/GitbookIO/gitbook/issues/776))
|
||||
- Summary can contain differents entitled sections
|
||||
- Glossary is generated as a normal page
|
||||
@@ -11,7 +67,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
- `links.sidebar` configuration is no longer supported, use summary sections instead
|
||||
- `pdf.headerTemplate` and `pdf.footerTemplate` have been replaced by a template in theme/book: `_layout/ebook/pdf_header.html` and `_layout/ebook/pdf_footer.html`
|
||||
- Markdown parser is now using CommonMark
|
||||
- Root folder for the book can be specified in a `.gitbook` file
|
||||
- Root folder for the book can be specified in the `"root"` property of the `book.json` file
|
||||
- Multi-lingual books share assets folder
|
||||
- YAML front matter is parsed and can extend page's properties
|
||||
- Fix `uk` translation
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
|
||||
# Contributing
|
||||
|
||||
Want to contribute to GitBook? That would be awesome!
|
||||
|
||||
- [Send feedback](#send-feedback)
|
||||
- [Reporting Bugs](#reporting-bugs)
|
||||
- [Asking Questions](#asking-questions)
|
||||
- [Submitting Pull Requests](#submitting-pull-requests)
|
||||
- [Running Tests](#running-tests)
|
||||
|
||||
## Send feedback
|
||||
|
||||
We’ve done our best to test but your feedback is really important. If you encounter problems or have feedback about GitBook products (GitBook.com, GitBook Editor, or the Toolchain), please log an issue in the [GitbookIO/feedback](https://github.com/GitbookIO/feedback/issues) repository.
|
||||
|
||||
|
||||
## Reporting Bugs
|
||||
|
||||
If you run into any weird behavior while using GitBook, feel free to open a new issue in this repository! To be most helpful, please include the steps to reproduce the bug as best you can, including the output of command `gitbook -V`.
|
||||
|
||||
## Asking Questions
|
||||
|
||||
Questions are very welcome :smile:! Previous questions that folks have asked are tagged with a [`question`](https://github.com/GitBookIO/gitbook/issues?q=is%3Aissue+is%3Aclosed+label%3Aquestion) label, so before opening a new issue double-check that someone hasn't asked it before. But if you don't see anything, or if you're not sure if it's the same, err on the side of asking!
|
||||
|
||||
We've also got a [GitBook Community Slack](https://slack.gitbook.com/) where you can ask questions and get answers from other people using GitBook.
|
||||
|
||||
## Submitting Pull Requests
|
||||
|
||||
All pull requests are super welcomed and greatly appreciated! Easy issues are marked with an [`easy-one`](https://github.com/GitBookIO/gitbook/issues?q=is%3Aopen+is%3Aissue+label%3Aeasy-one) label if you're looking for a simple place to get familiar with the code base.
|
||||
|
||||
Please include tests and docs with every pull request!
|
||||
|
||||
## Running Tests
|
||||
|
||||
To run the examples, you need to have the GitBook repository cloned to your computer. After that, you need to `cd` into the directory where you cloned it, and install the dependencies from `npm` using `yarn`.
|
||||
|
||||
```
|
||||
$ yarn install
|
||||
```
|
||||
|
||||
Then you'll need to bootstrap it:
|
||||
|
||||
```
|
||||
$ npm run bootstrap
|
||||
```
|
||||
|
||||
Which will also compile the source files. Then run the tests with:
|
||||
|
||||
```
|
||||
$ npm test
|
||||
```
|
||||
@@ -1,12 +1,22 @@
|
||||
GitBook
|
||||
=======
|
||||
[](https://www.gitbook.com)
|
||||
|
||||
[](http://badge.fury.io/js/gitbook)
|
||||
[](https://travis-ci.org/GitbookIO/gitbook)
|
||||
[](https://ci.appveyor.com/project/GitBook/gitbook)
|
||||
[](https://slack.gitbook.com)
|
||||
<p align="center">A <em>modern</em> customizable documentation format and toolchain</p>
|
||||
|
||||
GitBook is a command line tool (and Node.js library) for building beautiful books using GitHub/Git and Markdown (or AsciiDoc). Here is an example: [Learn Javascript](https://www.gitbook.com/book/GitBookIO/javascript).
|
||||
|
||||
<p align="center"><a href="#features"><strong>Features</strong></a> · <a href="https://www.gitbook.com"><strong>Hosting</strong></a> · <a href="http://redux.js.org/"><strong>Example</strong></a> · <a href="https://www.gitbook.com/editor"><strong>Editor</strong></a> · <a href="https://toolchain.gitbook.com/"><strong>Documentation</strong></a> · <a href="./CONTRIBUTING.md"><strong>Contributing!</strong></a> · <a href="https://twitter.com/GitBookIO"><strong>Twitter</strong></a></p>
|
||||
<br/>
|
||||
|
||||

|
||||
|
||||
|
||||
<p align="center">
|
||||
<img src="https://badge.fury.io/js/gitbook.svg" />
|
||||
<a href="https://travis-ci.org/GitbookIO/gitbook"><img src="https://travis-ci.org/GitbookIO/gitbook.png?branch=master" /></a>
|
||||
<a href="https://ci.appveyor.com/project/GitBook/gitbook"><img src="https://ci.appveyor.com/api/projects/status/63nlflxcwmb2pue6?svg=true" /></a>
|
||||
<a href="https://ci.appveyor.com/project/GitBook/gitbook"><img src="https://slack.gitbook.com/badge.svg" /></a>
|
||||
</p>
|
||||
|
||||
GitBook is a command line tool (and Node.js library) for building beautiful books using GitHub/Git and Markdown (or AsciiDoc). Here is an example: [ReduxJS documentation](http://redux.js.org/).
|
||||
|
||||
You can publish and host books easily online using [gitbook.com](https://www.gitbook.com). A desktop editor is [also available](https://www.gitbook.com/editor).
|
||||
|
||||
@@ -14,8 +24,6 @@ Check out the [GitBook Community Slack Channel](https://slack.gitbook.com), Stay
|
||||
|
||||
Complete documentation is available at [toolchain.gitbook.com](http://toolchain.gitbook.com/).
|
||||
|
||||

|
||||
|
||||
## Getting started
|
||||
|
||||
GitBook can be used either on your computer for building local books or on GitBook.com for hosting them. To get started, check out [the installation instructions in the documentation](docs/setup.md).
|
||||
|
||||
@@ -5,13 +5,18 @@ init:
|
||||
# Test against these versions of Node.js.
|
||||
environment:
|
||||
matrix:
|
||||
- nodejs_version: "6"
|
||||
- nodejs_version: "5"
|
||||
- nodejs_version: "4"
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
|
||||
# Install scripts. (runs after repo cloning)
|
||||
install:
|
||||
# Get the latest stable version of Node.js or io.js
|
||||
- ps: Install-Product node $env:nodejs_version
|
||||
- set CI=true
|
||||
# install svgexport
|
||||
- npm install svgexport -g
|
||||
# install modules
|
||||
@@ -27,3 +32,7 @@ test_script:
|
||||
|
||||
# Don't actually build.
|
||||
build: off
|
||||
|
||||
# Clone only last commit
|
||||
shallow_clone: true
|
||||
clone_depth: 1
|
||||
|
||||
@@ -5,11 +5,8 @@ module.exports = {
|
||||
root: './docs',
|
||||
title: 'GitBook Toolchain Documentation',
|
||||
|
||||
// Enforce use of GitBook v3
|
||||
gitbook: '>=3.0.0-pre.0',
|
||||
|
||||
// Use the "official" theme
|
||||
plugins: ['theme-official', 'sitemap'],
|
||||
plugins: ['sitemap'],
|
||||
|
||||
variables: {
|
||||
version: pkg.version
|
||||
|
||||
+2
-3
@@ -4,7 +4,7 @@ This document aims to be a comprehensive guide to GitBook. It contains the full
|
||||
|
||||
### What is GitBook?
|
||||
|
||||
GitBook is a command line tool (and Node.js library) for building beautiful books using GitHub/Git and Markdown (or AsciiDoc). . This documentation has been generated using GitBook.
|
||||
GitBook is a command line tool (and Node.js library) for building beautiful books using GitHub/Git and Markdown (or AsciiDoc). This documentation has been generated using GitBook.
|
||||
|
||||
GitBook can output your content as a website ([customizable](themes/README.md) and [extensibles](plugins/README.md)) or as an ebook (PDF, ePub or Mobi).
|
||||
|
||||
@@ -18,9 +18,8 @@ Check out the [GitBook Community Slack Channel](https://slack.gitbook.com), Stay
|
||||
|
||||
### FAQ
|
||||
|
||||
Some questions are frequently asked. If you have a problem you should [check this out](faq.md) first.
|
||||
Some questions are frequently asked. If you have a problem you should [check this out](faq.md) first.
|
||||
|
||||
### Contribute to this documentation
|
||||
|
||||
You can contribute to improve this documentation on [GitHub](https://github.com/GitbookIO/gitbook) by signaling issues or proposing changes.
|
||||
|
||||
|
||||
+8
-2
@@ -15,6 +15,7 @@
|
||||
* [Markdown](syntax/markdown.md)
|
||||
* [Headings](syntax/markdown.md#headings)
|
||||
* [Paragraphs](syntax/markdown.md#paragraphs)
|
||||
* [Emphasis](syntax/markdown.md#emphasis)
|
||||
* [Lists](syntax/markdown.md#lists)
|
||||
* [Links](syntax/markdown.md#links)
|
||||
* [Images](syntax/markdown.md#images)
|
||||
@@ -41,10 +42,15 @@
|
||||
* [Test your plugin](plugins/testing.md)
|
||||
* [Theming](themes/README.md)
|
||||
|
||||
### Plugin Development
|
||||
|
||||
* [Introduction](./api/README.md)
|
||||
* [Node](./api/node.md)
|
||||
* [Connect to the context](./api/connect.md)
|
||||
* [Components](./api/components.md)
|
||||
|
||||
--
|
||||
|
||||
* [FAQ](faq.md)
|
||||
* [Examples](examples.md)
|
||||
* [Release notes](https://github.com/GitbookIO/gitbook/blob/master/CHANGES.md)
|
||||
|
||||
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
{% extends template.self %}
|
||||
|
||||
{% block header_nav %}
|
||||
<a href="https://github.com/GitbookIO/gitbook/blob/master/docs/{{ file.path }}" target="_blank" class="btn btn-link pull-right hidden-xs">
|
||||
<i class="octicon octicon-mark-github"></i> Edit on GitHub
|
||||
</a>
|
||||
<a href="{{ "faq.md"|resolveFile }}" class="btn btn-link pull-right hidden-xs">
|
||||
F.A.Q
|
||||
</a>
|
||||
<a href="https://github.com/GitbookIO/gitbook/blob/master/CHANGES.md" target="_blank" class="btn btn-link pull-right hidden-xs">
|
||||
{{ book.version }}
|
||||
</a>
|
||||
{% endblock %}
|
||||
|
||||
{% block page %}
|
||||
{{ super() }}
|
||||
<hr>
|
||||
<div class="btn-group btn-group-justified">
|
||||
{% if page.previous and page.previous.path %}
|
||||
<a class="btn" href="{{ page.previous.path|resolveFile }}"><b>Previous:</b> {{ page.previous.title }}</a>
|
||||
{% endif %}
|
||||
{% if page.next and page.next.path %}
|
||||
<a class="btn" href="{{ page.next.path|resolveFile }}"><b>Next:</b> {{ page.next.title }}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,24 @@
|
||||
# Plugin Architecture
|
||||
|
||||
A GitBook plugin is a NPM package that follows a defined convention.
|
||||
|
||||
`gitbook-plugin` is a command line utility to help you create, test and release plugins.
|
||||
|
||||
### Bootstrap your first plugin
|
||||
|
||||
Install `gitbook-plugin` from NPM:
|
||||
|
||||
```
|
||||
$ npm install gitbook-plugin -g
|
||||
```
|
||||
|
||||
Then create your plugin:
|
||||
|
||||
```
|
||||
$ gitbook-plugin create
|
||||
```
|
||||
|
||||
You will be asked for a plugin name, and a few other things to complete the creation process.
|
||||
|
||||
|
||||
### Publish your plugin
|
||||
@@ -0,0 +1,86 @@
|
||||
# Components
|
||||
|
||||
|
||||
## Injection
|
||||
|
||||
Plugins can inject components by registering React components to some roles.
|
||||
|
||||
#### Register a components
|
||||
|
||||
During the initialization phase of your plugin, dispatch the `GitBook.registerComponent` action:
|
||||
|
||||
```js
|
||||
dispatch(GitBook.registerComponent(MyCustomButton, { role: 'toolbar:buttons:left' }));
|
||||
```
|
||||
|
||||
#### Roles
|
||||
|
||||
Custom roles can be use for interopability with other plugins, but GitBook and the default theme set a convention for common roles:
|
||||
|
||||
| Role | Description | Props |
|
||||
| ---- | ----------- | ----- |
|
||||
| `page:container` | DIV container for the page's content | `{ page: Page }` |
|
||||
| `summary:container` | DIV container for the whole summary | `{ summary: Summary }` |
|
||||
| `summary:parts` | DIV container for summary's parts | `{ parts: List<SummaryPart> }` |
|
||||
| `summary:part` | DIV for a specific part | `{ part: SummaryPart }` |
|
||||
| `summary:articles` | UL container for a part's articles | `{ articles: List<SummaryArticle> }` |
|
||||
| `summary:article` | LI for a specific article | `{ article: SummaryArticle }` |
|
||||
|
||||
## Default Components
|
||||
|
||||
#### `GitBook.Head`
|
||||
|
||||
Extends the meta tags of the page. This is an alias for [react-helmet](https://github.com/nfl/react-helmet).
|
||||
|
||||
```js
|
||||
<GitBook.Head
|
||||
title="My page"
|
||||
/>
|
||||
```
|
||||
|
||||
#### `GitBook.ImportCSS`
|
||||
|
||||
Import a CSS file by resolving the path correctly according to the current page:
|
||||
|
||||
```js
|
||||
<GitBook.ImportCSS href="myfile.css" />
|
||||
```
|
||||
|
||||
#### `GitBook.ImportJS`
|
||||
|
||||
Import a JS file by resolving the path correctly according to the current page:
|
||||
|
||||
```js
|
||||
<GitBook.ImportJS src="mylib.js" />
|
||||
```
|
||||
|
||||
#### `GitBook.InjectedComponent`
|
||||
|
||||
Inject a component matching a specific role:
|
||||
|
||||
```js
|
||||
<GitBook.InjectedComponent matching={{ role: 'mycustomrole' }} props={{ someProp: 1 }}>
|
||||
<b>Inner content</b>
|
||||
</GitBook.InjectedComponent>
|
||||
```
|
||||
|
||||
#### `GitBook.InjectedComponentSet`
|
||||
|
||||
Same API as `InjectedComponentSet` but render the matching components in chain instead of composed:
|
||||
|
||||
```js
|
||||
<GitBook.InjectedComponentSet matching={{ role: 'mytoolbar' }} />
|
||||
```
|
||||
|
||||
**Warning:** Children are discarded.
|
||||
|
||||
#### `GitBook.FlexLayout` and `GitBook.FlexBox`
|
||||
|
||||
A simple wrapper that provides a Flexbox layout with the given direction and style. Any additional props you set on the Flexbox are rendered.
|
||||
|
||||
```js
|
||||
<GitBook.FlexLayout column>
|
||||
<GitBook.FlexBox>First column</GitBook.FlexBox>
|
||||
<GitBook.FlexBox>Second column</GitBook.FlexBox>
|
||||
</GitBook.FlexLayout>
|
||||
```
|
||||
@@ -0,0 +1,33 @@
|
||||
# Connect to the context
|
||||
|
||||
`GitBook.connect(Component, [mapStateToProps], [mapActionsToProps])` connects a react component to the GitBook context.
|
||||
|
||||
It does not modify the component class passed to it.
|
||||
Instead, it returns a new, connected component class, for you to use.
|
||||
|
||||
### `mapStateToProps(state, [ownProps]): stateProps`
|
||||
|
||||
If specified, the component will subscribe to GitBook store updates. Any time it updates, `mapStateToProps` will be called. Its result must be a plain object, and it will be merged into the component’s props.
|
||||
|
||||
If you omit it, the component will not be subscribed to the GitBook store. If `ownProps` is specified as a second argument, its value will be the props passed to your component, and `mapStateToProps` will be additionally re-invoked whenever the component receives new props (e.g. if props received from a parent component have shallowly changed, and you use the `ownProps` argument, `mapStateToProps` is re-evaluated).
|
||||
|
||||
For example to render the title of the current page:
|
||||
|
||||
```js
|
||||
const GitBook = require('gitbook-core');
|
||||
|
||||
let PageTitle = React.createClass({
|
||||
render() {
|
||||
const { page } = this.props;
|
||||
return <h1>{page.title}</h1>;
|
||||
}
|
||||
});
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return { page: state.page };
|
||||
}
|
||||
|
||||
PageTitle = GitBook.connect(PageTitle, mapStateToProps);
|
||||
```
|
||||
|
||||
### `mapActionsToProps(actions, [dispatch])`
|
||||
@@ -0,0 +1,44 @@
|
||||
# Internationalize your plugin
|
||||
|
||||
GitBook has built-in support for internationalization. Plugins can register new languages and provide the right messages for different languages.
|
||||
|
||||
### Register locale and messages
|
||||
|
||||
The first step is to register messages for a language:
|
||||
|
||||
```js
|
||||
module.exports = GitBook.createPlugin({
|
||||
init: (dispatch, getState, { I18n }) => {
|
||||
dispatch(I18n.registerLocale('en-US', {
|
||||
MY_PLUGIN_MESSAGE: 'Hello World'
|
||||
}));
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Render a message in a component
|
||||
|
||||
`GitBook.connect` adds a `i18n` prop to access the internationalization:
|
||||
|
||||
```js
|
||||
const GitBook = require('gitbook-core');
|
||||
const { React } = GitBook;
|
||||
|
||||
const MyButton = React.createClass({
|
||||
propTypes: {
|
||||
i18n: GitBook.Shapes.i18n
|
||||
},
|
||||
|
||||
render() {
|
||||
const { i18n } = this.props;
|
||||
|
||||
return (
|
||||
<GitBook.Button>
|
||||
{i18.t('MY_PLUGIN_MESSAGE')}
|
||||
</GitBook.Button>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = GitBook.connect(MyButton);
|
||||
```
|
||||
@@ -0,0 +1,23 @@
|
||||
# Navigation
|
||||
|
||||
### Listen to url change
|
||||
|
||||
Listen for changes to the current location:
|
||||
|
||||
```js
|
||||
const onLocationChanged = (location) => {
|
||||
console.log(location.pathname);
|
||||
console.log(location.query);
|
||||
console.log(location.hash);
|
||||
};
|
||||
|
||||
module.exports = GitBook.createPlugin({
|
||||
init: (dispatch, getState, { Navigation }) => {
|
||||
dispatch(Navigation.listen(onLocationChanged));
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
The `onLocationChanged` will be triggered for initial state.
|
||||
|
||||
### Changing the url
|
||||
@@ -0,0 +1,97 @@
|
||||
# Node APIs
|
||||
|
||||
GitBooks provides different Node APIs and contexts to plugins. These APIs can vary according to the GitBook version being used, your plugin should specify the `engines.gitbook` field in `package.json` accordingly.
|
||||
|
||||
#### Book instance
|
||||
|
||||
The `Book` interface is the central point of GitBook, it centralize all access read methods.
|
||||
|
||||
```js
|
||||
// Read configuration from book.json
|
||||
var value = book.config.get('title', 'Default Value');
|
||||
|
||||
// Resolve a filename to an absolute path
|
||||
var filepath = book.resolve('README.md');
|
||||
|
||||
// Render an inline markup string
|
||||
book.renderInline('markdown', 'This is **Markdown**')
|
||||
.then(function(str) { ... })
|
||||
|
||||
// Render a markup string (block mode)
|
||||
book.renderBlock('markdown', '* This is **Markdown**')
|
||||
.then(function(str) { ... })
|
||||
```
|
||||
|
||||
#### Output instance
|
||||
|
||||
The `Output` class represent the output/write process.
|
||||
|
||||
```js
|
||||
// Return root folder for the output
|
||||
var root = output.root();
|
||||
|
||||
// Resolve a file in the output folder
|
||||
var filepath = output.resolve('myimage.png');
|
||||
|
||||
// Convert a filename to an URL (returns a path to an html file)
|
||||
var fileurl = output.toURL('mychapter/README.md');
|
||||
|
||||
// Write a file in the output folder
|
||||
output.writeFile('hello.txt', 'Hello World')
|
||||
.then(function() { ... });
|
||||
|
||||
// Copy a file to the output folder
|
||||
output.copyFile('./myfile.jpg', 'cover.jpg')
|
||||
.then(function() { ... });
|
||||
|
||||
// Verify that a file exists
|
||||
output.hasFile('hello.txt')
|
||||
.then(function(exists) { ... });
|
||||
```
|
||||
|
||||
#### Page instance
|
||||
|
||||
A page instance represent the current parsed page.
|
||||
|
||||
```js
|
||||
// Title of the page (from SUMMARY)
|
||||
page.title
|
||||
|
||||
// Content of the page (Markdown/Asciidoc/HTML according to the stage)
|
||||
page.content
|
||||
|
||||
// Relative path in the book
|
||||
page.path
|
||||
|
||||
// Absolute path to the file
|
||||
page.rawPath
|
||||
|
||||
// Type of parser used for this file
|
||||
page.type ('markdown' or 'asciidoc')
|
||||
```
|
||||
|
||||
#### Context for Blocks and Filters
|
||||
|
||||
Blocks and filters have access to the same context, this context is bind to the template engine execution:
|
||||
|
||||
```js
|
||||
{
|
||||
// Current templating syntax
|
||||
"ctx": {
|
||||
// For example, after a {% set message = "hello" %}
|
||||
"message": "hello"
|
||||
},
|
||||
|
||||
// Book instance
|
||||
"book" <Book>,
|
||||
|
||||
// Output instance
|
||||
"output": <Output>
|
||||
}
|
||||
```
|
||||
|
||||
For example a filter or block function can access the current book using: `this.book`.
|
||||
|
||||
#### Context for Hooks
|
||||
|
||||
Hooks only have access to the `<Book>` instance using `this.book`.
|
||||
+10
-3
@@ -7,6 +7,7 @@ GitBook allows you to customize your book using a flexible configuration. These
|
||||
| Variable | Description |
|
||||
| -------- | ----------- |
|
||||
| `root` | Path to the root folder containing all the book's files, except `book.json`|
|
||||
| `structure` | To specify paths for Readme, Summary, Glossary etc. See [Structure paragraph](#structure). |
|
||||
| `title` | Title of your book, default value is extracted from the README. On GitBook.com this field is pre-filled. |
|
||||
| `description` | Description of your book, default value is extracted from the README. On GitBook.com this field is pre-filled. |
|
||||
| `author` | Name of the author. On GitBook.com this field is pre-filled. |
|
||||
@@ -19,18 +20,24 @@ GitBook allows you to customize your book using a flexible configuration. These
|
||||
|
||||
Plugins and their configurations are specified in the `book.json`. See [the plugins section](plugins/README.md) for more details.
|
||||
|
||||
Since version 3.0.0, GitBook can use themes. See [the theming section](themes/README.md) for more details.
|
||||
|
||||
| Variable | Description |
|
||||
| -------- | ----------- |
|
||||
| `plugins` | List of plugins to load |
|
||||
| `pluginsConfig` |Configuration for plugins |
|
||||
|
||||
### Theme
|
||||
### Structure
|
||||
|
||||
Since version 3.0.0, GitBook can use themes. See [the theming section](themes/README.md) for more details.
|
||||
In addition to the `root` variable, you can tell Gitbook the name of the files for Readme, Summary, Glossary, Languages (instead of using the default names such as `README.md`).
|
||||
These files must be at the root of your book (or the root of every language book). Paths such as `dir/MY_README.md` are not accepted.
|
||||
|
||||
| Variable | Description |
|
||||
| -------- | ----------- |
|
||||
| `theme` | The theme to use for the book |
|
||||
| `structure.readme` | Readme file name (defaults to `README.md`) |
|
||||
| `structure.summary` | Summary file name (defaults to `SUMMARY.md`) |
|
||||
| `structure.glossary` | Glossary file name (defaults to `GLOSSARY.md`) |
|
||||
| `structure.languages` | Languages file name (defaults to `LANGS.md`) |
|
||||
|
||||
### PDF Options
|
||||
|
||||
|
||||
+18
-3
@@ -17,6 +17,20 @@ $ gitbook mobi ./ ./mybook.mobi
|
||||
|
||||
`ebook-convert` is required to generate ebooks (epub, mobi, pdf).
|
||||
|
||||
##### GNU/Linux
|
||||
|
||||
Install the [Calibre application](https://calibre-ebook.com/download).
|
||||
|
||||
```
|
||||
$ sudo aptitude install calibre
|
||||
```
|
||||
|
||||
In some GNU/Linux distributions node is installed as nodejs, you need to manually create a symlink:
|
||||
|
||||
```
|
||||
$sudo ln -s /usr/bin/nodejs /usr/bin/node
|
||||
```
|
||||
|
||||
##### OS X
|
||||
|
||||
Download the [Calibre application](https://calibre-ebook.com/download). After moving the `calibre.app` to your Applications folder create a symbolic link to the ebook-convert tool:
|
||||
@@ -29,12 +43,13 @@ You can replace `/usr/bin` with any directory that is in your $PATH.
|
||||
|
||||
### Cover
|
||||
|
||||
Covers are used for all the ebook formats. It's an important part of an ebook brandline.
|
||||
Covers are used for all the ebook formats. You can either provide one yourself, or generate one using the [autocover plugin](https://plugins.gitbook.com/plugin/autocover).
|
||||
|
||||
To provide a cover, place a **`cover.jpg`** file at the root directory of your book. Adding a **`cover_small.jpg`** will specify a smaller version of the cover. The cover should be a **JPEG** file.
|
||||
|
||||
A good cover should respect the following guidelines:
|
||||
|
||||
* Size of 1800x2360 (pixels)
|
||||
* Size of 1800x2360 pixels for `cover.jpg`, 200x262 for `cover_small.jpg`
|
||||
* No border
|
||||
* Clearly visible book title
|
||||
* Any important text should be visible in the small version
|
||||
|
||||
|
||||
+2
-1
@@ -23,5 +23,6 @@ More than 50,000 books have been published on [GitBook.com](https://www.gitbook.
|
||||
### Documentation
|
||||
|
||||
- [DuckDuckHack Documentation](http://docs.duckduckhack.com) by [DuckDuckGo](https://duckduckgo.com/about)
|
||||
- [Loomio Handbook](http://loomio.coop/) and [guide to using Loomio](https://loomio.gitbooks.io/manual/content/en/index.html) both by [Loomio](https://www.loomio.org/)
|
||||
- [Enspiral Handbook](http://handbook.enspiral.com/) by [Enspiral](http://enspiral.com/)
|
||||
- This documentation
|
||||
|
||||
|
||||
+2
-2
@@ -18,7 +18,7 @@ Any text editor should work! But we advise using the [GitBook Editor](https://ww
|
||||
|
||||
The GitBook format supports right to left, and bi-directional writing. To enable it, you either need to specify a language (ex: `ar`), or force GitBook to use RTL in your `book.json`:
|
||||
|
||||
``` json
|
||||
```json
|
||||
{
|
||||
"language": "ar",
|
||||
"direction": "rtl"
|
||||
@@ -34,7 +34,7 @@ You should always use paths and the `.md` extensions when linking to your files,
|
||||
|
||||
#### Can I create a GitBook in a sub-directory of my repository?
|
||||
|
||||
Yes, GitBooks can be created in [sub-directories](structure.md#subdirectory). GitBook.com and the CLI also looks by default in a serie of [folders](structure.md).
|
||||
Yes, GitBooks can be created in [sub-directories](structure.md#subdirectory). GitBook.com and the CLI also looks by default in a series of [folders](structure.md).
|
||||
|
||||
#### Does GitBook supports RTL languages?
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 227 KiB After Width: | Height: | Size: 227 KiB |
+3
-3
@@ -2,13 +2,13 @@
|
||||
|
||||
Allows you to specify terms and their respective definitions to be displayed as annotations. Based on those terms, GitBook will automatically build an index and highlight those terms in pages.
|
||||
|
||||
The `GLOSSARY.md` format is very simple :
|
||||
The `GLOSSARY.md` format is a list of `h2` headings, along with a description paragraph:
|
||||
|
||||
```markdown
|
||||
# Term
|
||||
## Term
|
||||
Definition for this term
|
||||
|
||||
# Another term
|
||||
## Another term
|
||||
With it's definition, this can contain bold text
|
||||
and all other kinds of inline markup ...
|
||||
```
|
||||
|
||||
+1
-2
@@ -40,7 +40,6 @@ Chapters in the Table of Contents can be pointing to specific part of a file usi
|
||||
* [Better tools for authors](part2/README.md#tools)
|
||||
```
|
||||
|
||||
|
||||
##### Parts
|
||||
|
||||
The Table of Contents can be divided into parts separated by headings or horizontal lines:
|
||||
@@ -73,7 +72,7 @@ Most of the files for GitBook use the Markdown syntax by default. GitBook infers
|
||||
|
||||
##### Example of a chapter file
|
||||
|
||||
``` markdown
|
||||
```markdown
|
||||
# Title of the chapter
|
||||
|
||||
This is a great introduction.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Plugins
|
||||
|
||||
Plugins are the best way to extend GitBook functionalities (ebook and website). There exist plugins to do a lot of things: bring math formulas display support, track visits using Google Analytic, etc.
|
||||
Plugins are the best way to extend GitBook functionalities (ebook and website). There exist plugins to do a lot of things: bring math formulas display support, track visits using Google Analytics, etc.
|
||||
|
||||
### How to find plugins?
|
||||
|
||||
|
||||
+2
-2
@@ -4,7 +4,7 @@ GitBooks provides different APIs and contexts to plugins. These APIs can vary ac
|
||||
|
||||
#### Book instance
|
||||
|
||||
The `Book` class is the central point of GitBook, it centralize all access read methods. This class is defined in [book.js](https://github.com/GitbookIO/gitbook/blob/master/lib/book.js).
|
||||
The `Book` class is the central point of GitBook, it centralize all access read methods. This class is defined in [book.js](https://github.com/GitbookIO/gitbook/blob/master/book.js).
|
||||
|
||||
```js
|
||||
// Read configuration from book.json
|
||||
@@ -37,7 +37,7 @@ var filepath = output.resolve('myimage.png');
|
||||
var fileurl = output.toURL('mychapter/README.md');
|
||||
|
||||
// Write a file in the output folder
|
||||
output.write('hello.txt', 'Hello World')
|
||||
output.writeFile('hello.txt', 'Hello World')
|
||||
.then(function() { ... });
|
||||
|
||||
// Copy a file to the output folder
|
||||
|
||||
@@ -54,4 +54,4 @@ module.exports = {
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
```
|
||||
|
||||
@@ -71,7 +71,6 @@ In the `page` hook, `page.content` is the HTML generated from the markdown/ascii
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Asynchronous Operations
|
||||
|
||||
Hooks callbacks can be asynchronous and return promises.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
### Testing your plugin locally
|
||||
|
||||
Testing your plugin on your book before plushing it is possible using [npm link](https://docs.npmjs.com/cli/link).
|
||||
Testing your plugin on your book before publishing it is possible using [npm link](https://docs.npmjs.com/cli/link).
|
||||
|
||||
In the plugin's folder, run:
|
||||
|
||||
@@ -10,7 +10,7 @@ In the plugin's folder, run:
|
||||
$ npm link
|
||||
```
|
||||
|
||||
The nin your book's folder:
|
||||
Then in your book's folder:
|
||||
|
||||
```
|
||||
$ npm link gitbook-plugin-<plugin's name>
|
||||
@@ -19,4 +19,3 @@ $ npm link gitbook-plugin-<plugin's name>
|
||||
### Unit testing on Travis
|
||||
|
||||
[gitbook-tester](https://github.com/todvora/gitbook-tester) makes it easy to write **Node.js/Mocha** unit tests for your plugins. Using [Travis.org](https://travis.org), tests can be run on each commits/tags.
|
||||
|
||||
|
||||
+1
-2
@@ -14,7 +14,7 @@ It integrates well with the [GitBook Editor](https://www.gitbook.com/editor).
|
||||
|
||||
Installing GitBook is easy and straightforward. Your system just needs to meet these two requirements:
|
||||
|
||||
* NodeJS (v4.0.0 and above are adviced)
|
||||
* NodeJS (v4.0.0 and above is recommended)
|
||||
* Windows, Linux, Unix, or Mac OS X
|
||||
|
||||
##### Install with NPM
|
||||
@@ -66,4 +66,3 @@ You can use the options `--log=debug` and `--debug` to get better error messages
|
||||
```
|
||||
$ gitbook build ./ --log=debug --debug
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
/* Reduce size of logo to allow long versions like 3.0.0-pre.1 */
|
||||
.gb-page-header .container .logo h1 {
|
||||
font-size: 24px ;
|
||||
}
|
||||
@@ -12,7 +12,7 @@ This is the main entry of your book: the introduction. This file is **required**
|
||||
|
||||
### SUMMARY.adoc
|
||||
|
||||
This file defines the list of chapters and subchapters. Just like in Markdown, the `SUMMARY.adoc`'s format is simply a list of links, the name of the link is used as the chapter's name, and the target is a path to that chapter's file.
|
||||
This file defines the list of chapters and subchapters. Just like in Markdown, the `SUMMARY.adoc`'s format is simply a list of links, the name of the link is used as the chapter's name, and the target is a path to that chapter's file.
|
||||
|
||||
Subchapters are defined simply by adding a nested list to a parent chapter.
|
||||
|
||||
@@ -60,6 +60,5 @@ observer producing a sense of wonder.
|
||||
A popular web programming language, used by many large websites such
|
||||
as Facebook. Rasmus Lerdorf originally created PHP in 1994 to power
|
||||
his personal homepage (PHP originally stood for "Personal Home Page"
|
||||
but now stands for "PHP: Hypertext Preprocessor"). ```
|
||||
|
||||
|
||||
but now stands for "PHP: Hypertext Preprocessor").
|
||||
```
|
||||
|
||||
@@ -21,7 +21,7 @@ This looks up username from the context and displays it. Variable names can have
|
||||
|
||||
If a value is undefined, nothing is displayed. The following all output nothing if foo is undefined: `{{ foo }}`, `{{ foo.bar }}`, `{{ foo.bar.baz }}`.
|
||||
|
||||
GitBook provides a set of [predefined variables](variables.md) from the context.
|
||||
GitBook provides a set of [predefined variables](variables.md) from the context.
|
||||
|
||||
### Filters
|
||||
|
||||
@@ -92,7 +92,7 @@ Inclusion and inheritance is detailled in the [Content References](conrefs.md) s
|
||||
|
||||
If you want GitBook to ignore any of the special templating tags, you can use raw and anything inside of it will be output as plain text.
|
||||
|
||||
``` twig
|
||||
```twig
|
||||
{% raw %}
|
||||
this will {{ not be processed }}
|
||||
{% endraw %}
|
||||
|
||||
@@ -4,7 +4,8 @@ GitBook provides a serie of builtin filters and blocks to help you write templat
|
||||
|
||||
### Filters
|
||||
|
||||
`value|default(default, [boolean])`If value is strictly undefined, return default, otherwise value. If boolean is true, any JavaScript falsy value will return default (false, "", etc)
|
||||
`value|default(default, [boolean])`
|
||||
If value is strictly undefined, return default, otherwise value. If boolean is true, any JavaScript falsy value will return default (false, "", etc)
|
||||
|
||||
`arr|sort(reverse, caseSens, attr)`
|
||||
Sort arr with JavaScript's arr.sort function. If reverse is true, result will be reversed. Sort is case-insensitive by default, but setting caseSens to true makes it case-sensitive. If attr is passed, will compare attr from each item.
|
||||
|
||||
@@ -51,5 +51,5 @@ This is the default content
|
||||
|
||||
# License
|
||||
|
||||
{% import "./LICENSE" %}
|
||||
{% include "./LICENSE" %}
|
||||
```
|
||||
|
||||
@@ -21,14 +21,26 @@ The following is a reference of the available data during book's parsing and the
|
||||
|
||||
| Variable | Description |
|
||||
| -------- | ----------- |
|
||||
| `book.[CONFIGURATION_DATA]` | All the `variables` set via the `book.json` are available through the book variable. |
|
||||
| `book.language` | Current language for a multilingual book |
|
||||
| `book.[value]` | All other values under `variables` in the `book.json` are accessible here |
|
||||
|
||||
For example, with this `book.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
variables: {
|
||||
hello: "everyone"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
... the following text `{{ book.hello }}` will expand to `everyone`.
|
||||
|
||||
### GitBook Variables
|
||||
|
||||
| Variable | Description |
|
||||
| -------- | ----------- |
|
||||
| `gitbook.time` | The current time (when you run the `gitbook` command) . |
|
||||
| `gitbook.time` | The current time (when you run the `gitbook` command). |
|
||||
| `gitbook.version` | Version of GitBook used to generate the book |
|
||||
|
||||
### File Variables
|
||||
|
||||
Vendored
+17
-2
@@ -12,12 +12,27 @@ A theme is a plugin containing templates and assets. Overriding any individual t
|
||||
| -------- | ----------- |
|
||||
| `_layouts` | Main folder containing all the templates |
|
||||
| `_layouts/website/page.html` | Template for a normal page |
|
||||
| `_layouts/ebook/page.html` | Template for a normal page during ebook generation (PDF< ePub, Mobi) |
|
||||
| `_layouts/ebook/page.html` | Template for a normal page during ebook generation (PDF, ePub, Mobi) |
|
||||
|
||||
|
||||
### Extend/Customize theme in a book
|
||||
|
||||
Authors can extend the templates of a theme directly from their book's source (without creating an external theme). Templates will be resolved in the `_layouts` folder of the book first, then in
|
||||
Authors can extend the templates of a theme directly from their book's source (without creating an external theme). Templates will be resolved in the `_layouts` folder of the book first, then in the installed plugins/themes.
|
||||
|
||||
### Extend instead of Forking
|
||||
|
||||
When you want to make your theme changes available to multiple books, instead of forking the default theme, you can extend it using the [templating syntax](../templating/README.md):
|
||||
|
||||
```html
|
||||
{% extends template.self %}
|
||||
|
||||
{% block body %}
|
||||
{{ super() }}
|
||||
... This will be added to the "body" block
|
||||
{% endblock %}
|
||||
```
|
||||
|
||||
Take a look at the [API](https://github.com/GitbookIO/theme-api) theme for a more complete example.
|
||||
|
||||
### Publish a theme
|
||||
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
|
||||
var matchers = {
|
||||
/**
|
||||
Verify that a file exists in a directory
|
||||
*/
|
||||
toHaveFile: function () {
|
||||
return {
|
||||
compare: function (actual, expected) {
|
||||
var filePath = path.join(actual, expected);
|
||||
var exists = fs.existsSync(filePath);
|
||||
|
||||
return {
|
||||
pass: exists
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
jasmine.getEnv().beforeEach(function () {
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 20000;
|
||||
jasmine.addMatchers(matchers);
|
||||
});
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"lerna": "2.0.0-beta.31",
|
||||
"version": "4.0.0-alpha.3"
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
var gitbook = require('../gitbook');
|
||||
|
||||
describe('satisfies', function() {
|
||||
|
||||
it('should return true for *', function() {
|
||||
expect(gitbook.satisfies('*')).toBe(true);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,16 +0,0 @@
|
||||
var tmp = require('tmp');
|
||||
var initBook = require('../init');
|
||||
|
||||
describe('initBook', function() {
|
||||
|
||||
pit('should create a README and SUMMARY for empty book', function() {
|
||||
var dir = tmp.dirSync();
|
||||
|
||||
return initBook(dir.name)
|
||||
.then(function() {
|
||||
expect(dir.name).toHaveFile('README.md');
|
||||
expect(dir.name).toHaveFile('SUMMARY.md');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,6 +0,0 @@
|
||||
|
||||
describe('GitBook', function() {
|
||||
it('should correctly export', function() {
|
||||
require('../');
|
||||
});
|
||||
});
|
||||
@@ -1,22 +0,0 @@
|
||||
var decodeConfig = require('./decodeConfig');
|
||||
|
||||
/**
|
||||
Decode changes from a JS API to a output object.
|
||||
Only the configuration can be edited by plugin's hooks
|
||||
|
||||
@param {Output} output
|
||||
@param {Object} result: result from API
|
||||
@return {Output}
|
||||
*/
|
||||
function decodeGlobal(output, result) {
|
||||
var book = output.getBook();
|
||||
var config = book.getConfig();
|
||||
|
||||
// Update config
|
||||
config = decodeConfig(config, result.config);
|
||||
book = book.set('config', config);
|
||||
|
||||
return output.set('book', book);
|
||||
}
|
||||
|
||||
module.exports = decodeGlobal;
|
||||
@@ -1,44 +0,0 @@
|
||||
var deprecate = require('./deprecate');
|
||||
|
||||
/**
|
||||
Decode changes from a JS API to a page object.
|
||||
Only the content can be edited by plugin's hooks.
|
||||
|
||||
@param {Output} output
|
||||
@param {Page} page: page instance to edit
|
||||
@param {Object} result: result from API
|
||||
@return {Page}
|
||||
*/
|
||||
function decodePage(output, page, result) {
|
||||
var originalContent = page.getContent();
|
||||
|
||||
// No returned value
|
||||
// Existing content will be used
|
||||
if (!result) {
|
||||
return page;
|
||||
}
|
||||
|
||||
deprecate.disable('page.sections');
|
||||
|
||||
// GitBook 3
|
||||
// Use returned page.content if different from original content
|
||||
if (result.content != originalContent) {
|
||||
page = page.set('content', result.content);
|
||||
}
|
||||
|
||||
// GitBook 2 compatibility
|
||||
// Finally, use page.sections
|
||||
else if (result.sections) {
|
||||
page = page.set('content',
|
||||
result.sections.map(function(section) {
|
||||
return section.content;
|
||||
}).join('\n')
|
||||
);
|
||||
}
|
||||
|
||||
deprecate.enable('page.sections');
|
||||
|
||||
return page;
|
||||
}
|
||||
|
||||
module.exports = decodePage;
|
||||
@@ -1,122 +0,0 @@
|
||||
var is = require('is');
|
||||
var objectPath = require('object-path');
|
||||
|
||||
var logged = {};
|
||||
var disabled = {};
|
||||
|
||||
/**
|
||||
Log a deprecated notice
|
||||
|
||||
@param {Book|Output} book
|
||||
@param {String} key
|
||||
@param {String} message
|
||||
*/
|
||||
function logNotice(book, key, message) {
|
||||
if (logged[key] || disabled[key]) return;
|
||||
|
||||
logged[key] = true;
|
||||
|
||||
var logger = book.getLogger();
|
||||
logger.warn.ln(message);
|
||||
}
|
||||
|
||||
/**
|
||||
Deprecate a function
|
||||
|
||||
@param {Book|Output} book
|
||||
@param {String} key: unique identitifer for the deprecated
|
||||
@param {Function} fn
|
||||
@param {String} msg: message to print when called
|
||||
@return {Function}
|
||||
*/
|
||||
function deprecateMethod(book, key, fn, msg) {
|
||||
return function() {
|
||||
logNotice(book, key, msg);
|
||||
|
||||
return fn.apply(this, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
Deprecate a property of an object
|
||||
|
||||
@param {Book|Output} book
|
||||
@param {String} key: unique identitifer for the deprecated
|
||||
@param {Object} instance
|
||||
@param {String|Function} property
|
||||
@param {String} msg: message to print when called
|
||||
@return {Function}
|
||||
*/
|
||||
function deprecateField(book, key, instance, property, value, msg) {
|
||||
var store = undefined;
|
||||
|
||||
var prepare = function() {
|
||||
if (!is.undefined(store)) return;
|
||||
|
||||
if (is.fn(value)) store = value();
|
||||
else store = value;
|
||||
};
|
||||
|
||||
var getter = function(){
|
||||
prepare();
|
||||
|
||||
logNotice(book, key, msg);
|
||||
return store;
|
||||
};
|
||||
var setter = function(v) {
|
||||
prepare();
|
||||
|
||||
logNotice(book, key, msg);
|
||||
store = v;
|
||||
return store;
|
||||
};
|
||||
|
||||
Object.defineProperty(instance, property, {
|
||||
get: getter,
|
||||
set: setter,
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
Enable a deprecation
|
||||
|
||||
@param {String} key: unique identitifer
|
||||
*/
|
||||
function enableDeprecation(key) {
|
||||
disabled[key] = false;
|
||||
}
|
||||
|
||||
/**
|
||||
Disable a deprecation
|
||||
|
||||
@param {String} key: unique identitifer
|
||||
*/
|
||||
function disableDeprecation(key) {
|
||||
disabled[key] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
Deprecate a method in favor of another one
|
||||
|
||||
@param {Book} book
|
||||
@param {String} key
|
||||
@param {Object} instance
|
||||
@param {String} oldName
|
||||
@param {String} newName
|
||||
*/
|
||||
function deprecateRenamedMethod(book, key, instance, oldName, newName, msg) {
|
||||
msg = msg || ('"' + oldName + '" is deprecated, use "' + newName + '()" instead');
|
||||
var fn = objectPath.get(instance, newName);
|
||||
|
||||
instance[oldName] = deprecateMethod(book, key, fn, msg);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
method: deprecateMethod,
|
||||
renamedMethod: deprecateRenamedMethod,
|
||||
field: deprecateField,
|
||||
enable: enableDeprecation,
|
||||
disable: disableDeprecation
|
||||
};
|
||||
@@ -1,165 +0,0 @@
|
||||
var path = require('path');
|
||||
var Promise = require('../utils/promise');
|
||||
var PathUtils = require('../utils/path');
|
||||
var fs = require('../utils/fs');
|
||||
|
||||
var Plugins = require('../plugins');
|
||||
var deprecate = require('./deprecate');
|
||||
var encodeConfig = require('./encodeConfig');
|
||||
var encodeNavigation = require('./encodeNavigation');
|
||||
var fileToURL = require('../output/helper/fileToURL');
|
||||
var defaultBlocks = require('../constants/defaultBlocks');
|
||||
var gitbook = require('../gitbook');
|
||||
|
||||
/**
|
||||
Encode a global context into a JS object
|
||||
It's the context for page's hook, etc
|
||||
|
||||
@param {Output} output
|
||||
@return {Object}
|
||||
*/
|
||||
function encodeGlobal(output) {
|
||||
var book = output.getBook();
|
||||
var bookFS = book.getContentFS();
|
||||
var logger = output.getLogger();
|
||||
var outputFolder = output.getRoot();
|
||||
var plugins = output.getPlugins();
|
||||
var blocks = Plugins.listBlocks(plugins);
|
||||
|
||||
var result = {
|
||||
log: logger,
|
||||
config: encodeConfig(output, book.getConfig()),
|
||||
|
||||
/**
|
||||
Check if the book is a multilingual book
|
||||
|
||||
@return {Boolean}
|
||||
*/
|
||||
isMultilingual: function() {
|
||||
return book.isMultilingual();
|
||||
},
|
||||
|
||||
/**
|
||||
Check if the book is a language book for a multilingual book
|
||||
|
||||
@return {Boolean}
|
||||
*/
|
||||
isLanguageBook: function() {
|
||||
return book.isLanguageBook();
|
||||
},
|
||||
|
||||
/**
|
||||
Read a file from the book
|
||||
|
||||
@param {String} fileName
|
||||
@return {Promise<Buffer>}
|
||||
*/
|
||||
readFile: function(fileName) {
|
||||
return bookFS.read(fileName);
|
||||
},
|
||||
|
||||
/**
|
||||
Read a file from the book as a string
|
||||
|
||||
@param {String} fileName
|
||||
@return {Promise<String>}
|
||||
*/
|
||||
readFileAsString: function(fileName) {
|
||||
return bookFS.readAsString(fileName);
|
||||
},
|
||||
|
||||
/**
|
||||
Resolve a file from the book root
|
||||
|
||||
@param {String} fileName
|
||||
@return {String}
|
||||
*/
|
||||
resolve: function(fileName) {
|
||||
return path.resolve(book.getContentRoot(), fileName);
|
||||
},
|
||||
|
||||
template: {
|
||||
/**
|
||||
Apply a templating block and returns its result
|
||||
|
||||
@param {String} name
|
||||
@param {Object} blockData
|
||||
@return {Promise|Object}
|
||||
*/
|
||||
applyBlock: function(name, blockData) {
|
||||
var block = blocks.get(name) || defaultBlocks.get(name);
|
||||
return Promise(block.applyBlock(blockData, result));
|
||||
}
|
||||
},
|
||||
|
||||
output: {
|
||||
/**
|
||||
Name of the generator being used
|
||||
{String}
|
||||
*/
|
||||
name: output.getGenerator(),
|
||||
|
||||
/**
|
||||
Return absolute path to the root folder of output
|
||||
@return {String}
|
||||
*/
|
||||
root: function() {
|
||||
return outputFolder;
|
||||
},
|
||||
|
||||
/**
|
||||
Convert a filepath into an url
|
||||
@return {String}
|
||||
*/
|
||||
toURL: function(filePath) {
|
||||
return fileToURL(output, filePath);
|
||||
},
|
||||
|
||||
/**
|
||||
Write a file to the output folder,
|
||||
It creates the required folder
|
||||
|
||||
@param {String} fileName
|
||||
@param {Buffer} content
|
||||
@return {Promise}
|
||||
*/
|
||||
writeFile: function(fileName, content) {
|
||||
return Promise()
|
||||
.then(function() {
|
||||
var filePath = PathUtils.resolveInRoot(outputFolder, fileName);
|
||||
|
||||
return fs.ensureFile(filePath)
|
||||
.then(function() {
|
||||
return fs.writeFile(filePath, content);
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
gitbook: {
|
||||
version: gitbook.version
|
||||
}
|
||||
};
|
||||
|
||||
// Deprecated properties
|
||||
|
||||
deprecate.renamedMethod(output, 'this.isSubBook', result, 'isSubBook', 'isLanguageBook');
|
||||
deprecate.renamedMethod(output, 'this.contentLink', result, 'contentLink', 'output.toURL');
|
||||
|
||||
deprecate.field(output, 'this.generator', result, 'generator',
|
||||
output.getGenerator(), '"this.generator" property is deprecated, use "this.output.name" instead');
|
||||
|
||||
deprecate.field(output, 'this.navigation', result, 'navigation', function() {
|
||||
return encodeNavigation(output);
|
||||
}, '"navigation" property is deprecated');
|
||||
|
||||
deprecate.field(output, 'this.book', result, 'book',
|
||||
result, '"book" property is deprecated, use "this" directly instead');
|
||||
|
||||
deprecate.field(output, 'this.options', result, 'options',
|
||||
result.config.values, '"options" property is deprecated, use config.get(key) instead');
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
module.exports = encodeGlobal;
|
||||
@@ -1,64 +0,0 @@
|
||||
var Immutable = require('immutable');
|
||||
|
||||
/**
|
||||
Encode an article for next/prev
|
||||
|
||||
@param {Map<String:Page>}
|
||||
@param {Article}
|
||||
@return {Object}
|
||||
*/
|
||||
function encodeArticle(pages, article) {
|
||||
var articlePath = article.getPath();
|
||||
|
||||
return {
|
||||
path: articlePath,
|
||||
title: article.getTitle(),
|
||||
level: article.getLevel(),
|
||||
exists: (articlePath && pages.has(articlePath)),
|
||||
external: article.isExternal()
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
this.navigation is a deprecated property from GitBook v2
|
||||
|
||||
@param {Output}
|
||||
@return {Object}
|
||||
*/
|
||||
function encodeNavigation(output) {
|
||||
var book = output.getBook();
|
||||
var pages = output.getPages();
|
||||
var summary = book.getSummary();
|
||||
var articles = summary.getArticlesAsList();
|
||||
|
||||
|
||||
var navigation = articles
|
||||
.map(function(article, i) {
|
||||
var ref = article.getRef();
|
||||
if (!ref) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
var prev = articles.get(i - 1);
|
||||
var next = articles.get(i + 1);
|
||||
|
||||
return [
|
||||
ref,
|
||||
{
|
||||
index: i,
|
||||
title: article.getTitle(),
|
||||
introduction: (i === 0),
|
||||
prev: prev? encodeArticle(pages, prev) : undefined,
|
||||
next: next? encodeArticle(pages, next) : undefined,
|
||||
level: article.getLevel()
|
||||
}
|
||||
];
|
||||
})
|
||||
.filter(function(e) {
|
||||
return Boolean(e);
|
||||
});
|
||||
|
||||
return Immutable.Map(navigation).toJS();
|
||||
}
|
||||
|
||||
module.exports = encodeNavigation;
|
||||
@@ -1,8 +0,0 @@
|
||||
|
||||
module.exports = {
|
||||
encodePage: require('./encodePage'),
|
||||
decodePage: require('./decodePage'),
|
||||
|
||||
encodeGlobal: require('./encodeGlobal'),
|
||||
decodeGlobal: require('./decodeGlobal')
|
||||
};
|
||||
@@ -1,15 +0,0 @@
|
||||
var Modifiers = require('./modifiers');
|
||||
|
||||
module.exports = {
|
||||
Parse: require('./parse'),
|
||||
|
||||
// Models
|
||||
Book: require('./models/book'),
|
||||
FS: require('./models/fs'),
|
||||
Summary: require('./models/summary'),
|
||||
Glossary: require('./models/glossary'),
|
||||
|
||||
// Modifiers
|
||||
SummaryModifier: Modifiers.Summary,
|
||||
ConfigModifier: Modifiers.Config
|
||||
};
|
||||
@@ -1,34 +0,0 @@
|
||||
var Parse = require('../parse');
|
||||
var Output = require('../output');
|
||||
var timing = require('../utils/timing');
|
||||
|
||||
var options = require('./options');
|
||||
var getBook = require('./getBook');
|
||||
var getOutputFolder = require('./getOutputFolder');
|
||||
|
||||
|
||||
module.exports = {
|
||||
name: 'build [book] [output]',
|
||||
description: 'build a book',
|
||||
options: [
|
||||
options.log,
|
||||
options.format,
|
||||
options.timing
|
||||
],
|
||||
exec: function(args, kwargs) {
|
||||
var book = getBook(args, kwargs);
|
||||
var outputFolder = getOutputFolder(args);
|
||||
|
||||
var Generator = Output.getGenerator(kwargs.format);
|
||||
|
||||
return Parse.parseBook(book)
|
||||
.then(function(resultBook) {
|
||||
return Output.generate(Generator, resultBook, {
|
||||
root: outputFolder
|
||||
});
|
||||
})
|
||||
.fin(function() {
|
||||
if (kwargs.timing) timing.dump(book.getLogger());
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -1,23 +0,0 @@
|
||||
var path = require('path');
|
||||
var Book = require('../models/book');
|
||||
var createNodeFS = require('../fs/node');
|
||||
|
||||
/**
|
||||
Return a book instance to work on from
|
||||
command line args/kwargs
|
||||
|
||||
@param {Array} args
|
||||
@param {Object} kwargs
|
||||
@return {Book}
|
||||
*/
|
||||
function getBook(args, kwargs) {
|
||||
var input = path.resolve(args[0] || process.cwd());
|
||||
var logLevel = kwargs.log;
|
||||
|
||||
var fs = createNodeFS(input);
|
||||
var book = Book.createForFS(fs);
|
||||
|
||||
return book.setLogLevel(logLevel);
|
||||
}
|
||||
|
||||
module.exports = getBook;
|
||||
@@ -1,17 +0,0 @@
|
||||
var path = require('path');
|
||||
|
||||
/**
|
||||
Return path to output folder
|
||||
|
||||
@param {Array} args
|
||||
@return {String}
|
||||
*/
|
||||
function getOutputFolder(args) {
|
||||
var bookRoot = path.resolve(args[0] || process.cwd());
|
||||
var defaultOutputRoot = path.join(bookRoot, '_book');
|
||||
var outputFolder = args[1]? path.resolve(process.cwd(), args[1]) : defaultOutputRoot;
|
||||
|
||||
return outputFolder;
|
||||
}
|
||||
|
||||
module.exports = getOutputFolder;
|
||||
@@ -1,17 +0,0 @@
|
||||
var path = require('path');
|
||||
|
||||
var options = require('./options');
|
||||
var initBook = require('../init');
|
||||
|
||||
module.exports = {
|
||||
name: 'install [book]',
|
||||
description: 'setup and create files for chapters',
|
||||
options: [
|
||||
options.log
|
||||
],
|
||||
exec: function(args, kwargs) {
|
||||
var bookRoot = path.resolve(process.cwd(), args[0] || './');
|
||||
|
||||
return initBook(bookRoot);
|
||||
}
|
||||
};
|
||||
@@ -1,105 +0,0 @@
|
||||
/* eslint-disable no-console */
|
||||
|
||||
var tinylr = require('tiny-lr');
|
||||
|
||||
var Parse = require('../parse');
|
||||
var Output = require('../output');
|
||||
var ConfigModifier = require('../modifiers').Config;
|
||||
|
||||
var Promise = require('../utils/promise');
|
||||
|
||||
var options = require('./options');
|
||||
var getBook = require('./getBook');
|
||||
var getOutputFolder = require('./getOutputFolder');
|
||||
var Server = require('./server');
|
||||
var watch = require('./watch');
|
||||
|
||||
var server, lrServer, lrPath;
|
||||
|
||||
function generateBook(args, kwargs) {
|
||||
var port = kwargs.port;
|
||||
var outputFolder = getOutputFolder(args);
|
||||
var book = getBook(args, kwargs);
|
||||
var Generator = Output.getGenerator(kwargs.format);
|
||||
|
||||
// Stop server if running
|
||||
if (server.isRunning()) console.log('Stopping server');
|
||||
|
||||
return server.stop()
|
||||
.then(function() {
|
||||
return Parse.parseBook(book)
|
||||
.then(function(resultBook) {
|
||||
// Enable livereload plugin
|
||||
resultBook = ConfigModifier.addPlugin(resultBook, 'livereload');
|
||||
|
||||
return Output.generate(Generator, resultBook, {
|
||||
root: outputFolder
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(function() {
|
||||
console.log();
|
||||
console.log('Starting server ...');
|
||||
return server.start(outputFolder, port);
|
||||
})
|
||||
.then(function() {
|
||||
console.log('Serving book on http://localhost:'+port);
|
||||
|
||||
if (lrPath) {
|
||||
// trigger livereload
|
||||
lrServer.changed({
|
||||
body: {
|
||||
files: [lrPath]
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.then(function() {
|
||||
if (!kwargs.watch) return;
|
||||
|
||||
return watch(book.getRoot())
|
||||
.then(function(filepath) {
|
||||
// set livereload path
|
||||
lrPath = filepath;
|
||||
console.log('Restart after change in file', filepath);
|
||||
console.log('');
|
||||
return generateBook(args, kwargs);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
name: 'serve [book] [output]',
|
||||
description: 'serve the book as a website for testing',
|
||||
options: [
|
||||
{
|
||||
name: 'port',
|
||||
description: 'Port for server to listen on',
|
||||
defaults: 4000
|
||||
},
|
||||
{
|
||||
name: 'lrport',
|
||||
description: 'Port for livereload server to listen on',
|
||||
defaults: 35729
|
||||
},
|
||||
{
|
||||
name: 'watch',
|
||||
description: 'Enable/disable file watcher',
|
||||
defaults: true
|
||||
},
|
||||
options.log,
|
||||
options.format
|
||||
],
|
||||
exec: function(args, kwargs) {
|
||||
server = new Server();
|
||||
lrServer = tinylr({});
|
||||
|
||||
return Promise.nfcall(lrServer.listen.bind(lrServer), kwargs.lrport)
|
||||
.then(function() {
|
||||
console.log('Live reload server started on port:', kwargs.lrport);
|
||||
console.log('Press CTRL+C to quit ...');
|
||||
console.log('');
|
||||
return generateBook(args, kwargs);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -1,109 +0,0 @@
|
||||
var events = require('events');
|
||||
var http = require('http');
|
||||
var send = require('send');
|
||||
var util = require('util');
|
||||
var url = require('url');
|
||||
|
||||
var Promise = require('../utils/promise');
|
||||
|
||||
function Server() {
|
||||
this.running = null;
|
||||
this.dir = null;
|
||||
this.port = 0;
|
||||
this.sockets = [];
|
||||
}
|
||||
util.inherits(Server, events.EventEmitter);
|
||||
|
||||
/**
|
||||
Return true if the server is running
|
||||
|
||||
@return {Boolean}
|
||||
*/
|
||||
Server.prototype.isRunning = function() {
|
||||
return !!this.running;
|
||||
};
|
||||
|
||||
/**
|
||||
Stop the server
|
||||
|
||||
@return {Promise}
|
||||
*/
|
||||
Server.prototype.stop = function() {
|
||||
var that = this;
|
||||
if (!this.isRunning()) return Promise();
|
||||
|
||||
var d = Promise.defer();
|
||||
this.running.close(function(err) {
|
||||
that.running = null;
|
||||
that.emit('state', false);
|
||||
|
||||
if (err) d.reject(err);
|
||||
else d.resolve();
|
||||
});
|
||||
|
||||
for (var i = 0; i < this.sockets.length; i++) {
|
||||
this.sockets[i].destroy();
|
||||
}
|
||||
|
||||
return d.promise;
|
||||
};
|
||||
|
||||
/**
|
||||
Start the server
|
||||
|
||||
@return {Promise}
|
||||
*/
|
||||
Server.prototype.start = function(dir, port) {
|
||||
var that = this, pre = Promise();
|
||||
port = port || 8004;
|
||||
|
||||
if (that.isRunning()) pre = this.stop();
|
||||
return pre
|
||||
.then(function() {
|
||||
var d = Promise.defer();
|
||||
|
||||
that.running = http.createServer(function(req, res){
|
||||
// Render error
|
||||
function error(err) {
|
||||
res.statusCode = err.status || 500;
|
||||
res.end(err.message);
|
||||
}
|
||||
|
||||
// Redirect to directory's index.html
|
||||
function redirect() {
|
||||
res.statusCode = 301;
|
||||
res.setHeader('Location', req.url + '/');
|
||||
res.end('Redirecting to ' + req.url + '/');
|
||||
}
|
||||
|
||||
// Send file
|
||||
send(req, url.parse(req.url).pathname, {
|
||||
root: dir
|
||||
})
|
||||
.on('error', error)
|
||||
.on('directory', redirect)
|
||||
.pipe(res);
|
||||
});
|
||||
|
||||
that.running.on('connection', function (socket) {
|
||||
that.sockets.push(socket);
|
||||
socket.setTimeout(4000);
|
||||
socket.on('close', function () {
|
||||
that.sockets.splice(that.sockets.indexOf(socket), 1);
|
||||
});
|
||||
});
|
||||
|
||||
that.running.listen(port, function(err) {
|
||||
if (err) return d.reject(err);
|
||||
|
||||
that.port = port;
|
||||
that.dir = dir;
|
||||
that.emit('state', true);
|
||||
d.resolve();
|
||||
});
|
||||
|
||||
return d.promise;
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = Server;
|
||||
@@ -1,46 +0,0 @@
|
||||
var path = require('path');
|
||||
var chokidar = require('chokidar');
|
||||
|
||||
var Promise = require('../utils/promise');
|
||||
var parsers = require('../parsers');
|
||||
|
||||
/**
|
||||
Watch a folder and resolve promise once a file is modified
|
||||
|
||||
@param {String} dir
|
||||
@return {Promise}
|
||||
*/
|
||||
function watch(dir) {
|
||||
var d = Promise.defer();
|
||||
dir = path.resolve(dir);
|
||||
|
||||
var toWatch = [
|
||||
'book.json', 'book.js', '_layouts/**'
|
||||
];
|
||||
|
||||
// Watch all parsable files
|
||||
parsers.extensions.forEach(function(ext) {
|
||||
toWatch.push('**/*'+ext);
|
||||
});
|
||||
|
||||
var watcher = chokidar.watch(toWatch, {
|
||||
cwd: dir,
|
||||
ignored: '_book/**',
|
||||
ignoreInitial: true
|
||||
});
|
||||
|
||||
watcher.once('all', function(e, filepath) {
|
||||
watcher.close();
|
||||
|
||||
d.resolve(filepath);
|
||||
});
|
||||
watcher.once('error', function(err) {
|
||||
watcher.close();
|
||||
|
||||
d.reject(err);
|
||||
});
|
||||
|
||||
return d.promise;
|
||||
}
|
||||
|
||||
module.exports = watch;
|
||||
@@ -1,6 +0,0 @@
|
||||
var Immutable = require('immutable');
|
||||
var jsonSchemaDefaults = require('json-schema-defaults');
|
||||
|
||||
var schema = require('./configSchema');
|
||||
|
||||
module.exports = Immutable.fromJS(jsonSchemaDefaults(schema));
|
||||
@@ -1,51 +0,0 @@
|
||||
var Immutable = require('immutable');
|
||||
var TemplateBlock = require('../models/templateBlock');
|
||||
|
||||
module.exports = Immutable.Map({
|
||||
html: TemplateBlock({
|
||||
name: 'html',
|
||||
process: function(blk) {
|
||||
return blk;
|
||||
}
|
||||
}),
|
||||
|
||||
code: TemplateBlock({
|
||||
name: 'code',
|
||||
process: function(blk) {
|
||||
return {
|
||||
html: false,
|
||||
body: blk.body
|
||||
};
|
||||
}
|
||||
}),
|
||||
|
||||
markdown: TemplateBlock({
|
||||
name: 'markdown',
|
||||
process: function(blk) {
|
||||
return this.book.renderInline('markdown', blk.body)
|
||||
.then(function(out) {
|
||||
return { body: out };
|
||||
});
|
||||
}
|
||||
}),
|
||||
|
||||
asciidoc: TemplateBlock({
|
||||
name: 'asciidoc',
|
||||
process: function(blk) {
|
||||
return this.book.renderInline('asciidoc', blk.body)
|
||||
.then(function(out) {
|
||||
return { body: out };
|
||||
});
|
||||
}
|
||||
}),
|
||||
|
||||
markup: TemplateBlock({
|
||||
name: 'markup',
|
||||
process: function(blk) {
|
||||
return this.book.renderInline(this.ctx.file.type, blk.body)
|
||||
.then(function(out) {
|
||||
return { body: out };
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
@@ -1,14 +0,0 @@
|
||||
var Immutable = require('immutable');
|
||||
|
||||
/*
|
||||
List of default plugins for all books,
|
||||
default plugins should be installed in node dependencies of GitBook
|
||||
*/
|
||||
module.exports = Immutable.List([
|
||||
'highlight',
|
||||
'search',
|
||||
'lunr',
|
||||
'sharing',
|
||||
'fontsettings',
|
||||
'theme-default'
|
||||
]);
|
||||
@@ -1,10 +0,0 @@
|
||||
var extend = require('extend');
|
||||
|
||||
var common = require('./browser');
|
||||
|
||||
module.exports = extend({
|
||||
initBook: require('./init'),
|
||||
createNodeFS: require('./fs/node'),
|
||||
Output: require('./output'),
|
||||
commands: require('./cli')
|
||||
}, common);
|
||||
-80
@@ -1,80 +0,0 @@
|
||||
var path = require('path');
|
||||
|
||||
var createNodeFS = require('./fs/node');
|
||||
var fs = require('./utils/fs');
|
||||
var Promise = require('./utils/promise');
|
||||
var File = require('./models/file');
|
||||
var Readme = require('./models/readme');
|
||||
var Book = require('./models/book');
|
||||
var Parse = require('./parse');
|
||||
|
||||
/**
|
||||
Initialize folder structure for a book
|
||||
Read SUMMARY to created the right chapter
|
||||
|
||||
@param {Book}
|
||||
@param {String}
|
||||
@return {Promise}
|
||||
*/
|
||||
function initBook(rootFolder) {
|
||||
var extension = '.md';
|
||||
|
||||
return fs.mkdirp(rootFolder)
|
||||
|
||||
// Parse the summary and readme
|
||||
.then(function() {
|
||||
var fs = createNodeFS(rootFolder);
|
||||
var book = Book.createForFS(fs);
|
||||
|
||||
return Parse.parseReadme(book)
|
||||
|
||||
// Setup default readme if doesn't found one
|
||||
.fail(function() {
|
||||
var readmeFile = File.createWithFilepath('README' + extension);
|
||||
var readme = Readme.create(readmeFile);
|
||||
return book.setReadme(readme);
|
||||
});
|
||||
})
|
||||
.then(Parse.parseSummary)
|
||||
|
||||
.then(function(book) {
|
||||
var logger = book.getLogger();
|
||||
var summary = book.getSummary();
|
||||
var summaryFile = summary.getFile();
|
||||
var summaryFilename = summaryFile.getPath() || ('SUMMARY' + extension);
|
||||
|
||||
var articles = summary.getArticlesAsList();
|
||||
|
||||
// Write pages
|
||||
return Promise.forEach(articles, function(article) {
|
||||
var filePath = path.join(rootFolder, article.getPath());
|
||||
if (!filePath) return;
|
||||
|
||||
return fs.assertFile(filePath, function() {
|
||||
return fs.ensureFile(filePath)
|
||||
.then(function() {
|
||||
logger.info.ln('create', article.getPath());
|
||||
return fs.writeFile(filePath, '# ' + article.getTitle() + '\n\n');
|
||||
});
|
||||
});
|
||||
})
|
||||
|
||||
// Write summary
|
||||
.then(function() {
|
||||
var filePath = path.join(rootFolder, summaryFilename);
|
||||
|
||||
return fs.ensureFile(filePath)
|
||||
.then(function() {
|
||||
logger.info.ln('create ' + path.basename(filePath));
|
||||
return fs.writeFile(filePath, summary.toText(extension));
|
||||
});
|
||||
})
|
||||
|
||||
// Log end
|
||||
.then(function() {
|
||||
logger.info.ln('initialization is finished');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = initBook;
|
||||
@@ -1,35 +0,0 @@
|
||||
var extend = require('extend');
|
||||
|
||||
var gitbook = require('../gitbook');
|
||||
var encodeSummary = require('./encodeSummary');
|
||||
var encodeGlossary = require('./encodeGlossary');
|
||||
var encodeReadme = require('./encodeReadme');
|
||||
|
||||
/**
|
||||
Encode a book to JSON
|
||||
|
||||
@param {Book}
|
||||
@return {Object}
|
||||
*/
|
||||
function encodeBookToJson(book) {
|
||||
var config = book.getConfig();
|
||||
var language = book.getLanguage();
|
||||
|
||||
var variables = config.getValue('variables', {});
|
||||
|
||||
return {
|
||||
summary: encodeSummary(book.getSummary()),
|
||||
glossary: encodeGlossary(book.getGlossary()),
|
||||
readme: encodeReadme(book.getReadme()),
|
||||
config: book.getConfig().getValues().toJS(),
|
||||
gitbook: {
|
||||
version: gitbook.version,
|
||||
time: gitbook.START_TIME
|
||||
},
|
||||
book: extend({
|
||||
language: language? language : undefined
|
||||
}, variables.toJS())
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = encodeBookToJson;
|
||||
@@ -1,22 +0,0 @@
|
||||
var encodeBook = require('./encodeBook');
|
||||
var encodePage = require('./encodePage');
|
||||
var encodeFile = require('./encodeFile');
|
||||
|
||||
/**
|
||||
Return a JSON representation of a book with a specific file
|
||||
|
||||
@param {Book} output
|
||||
@param {Page} page
|
||||
@return {Object}
|
||||
*/
|
||||
function encodeBookWithPage(book, page) {
|
||||
var file = page.getFile();
|
||||
|
||||
var result = encodeBook(book);
|
||||
result.page = encodePage(page, book.getSummary());
|
||||
result.file = encodeFile(file);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
module.exports = encodeBookWithPage;
|
||||
@@ -1,21 +0,0 @@
|
||||
|
||||
/**
|
||||
Return a JSON representation of a file
|
||||
|
||||
@param {File} file
|
||||
@return {Object}
|
||||
*/
|
||||
function encodeFileToJson(file) {
|
||||
var filePath = file.getPath();
|
||||
if (!filePath) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
path: filePath,
|
||||
mtime: file.getMTime(),
|
||||
type: file.getType()
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = encodeFileToJson;
|
||||
@@ -1,21 +0,0 @@
|
||||
var encodeFile = require('./encodeFile');
|
||||
var encodeGlossaryEntry = require('./encodeGlossaryEntry');
|
||||
|
||||
/**
|
||||
Encode a glossary to JSON
|
||||
|
||||
@param {Glossary}
|
||||
@return {Object}
|
||||
*/
|
||||
function encodeGlossary(glossary) {
|
||||
var file = glossary.getFile();
|
||||
var entries = glossary.getEntries();
|
||||
|
||||
return {
|
||||
file: encodeFile(file),
|
||||
entries: entries
|
||||
.map(encodeGlossaryEntry).toJS()
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = encodeGlossary;
|
||||
@@ -1,25 +0,0 @@
|
||||
var encodeBook = require('./encodeBook');
|
||||
|
||||
/**
|
||||
Encode an output to JSON
|
||||
|
||||
@param {Output}
|
||||
@return {Object}
|
||||
*/
|
||||
function encodeOutputToJson(output) {
|
||||
var book = output.getBook();
|
||||
var generator = output.getGenerator();
|
||||
var options = output.getOptions();
|
||||
|
||||
var result = encodeBook(book);
|
||||
|
||||
result.output = {
|
||||
name: generator
|
||||
};
|
||||
|
||||
result.options = options.toJS();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
module.exports = encodeOutputToJson;
|
||||
@@ -1,39 +0,0 @@
|
||||
var encodeSummaryArticle = require('./encodeSummaryArticle');
|
||||
|
||||
/**
|
||||
Return a JSON representation of a page
|
||||
|
||||
@param {Page} page
|
||||
@param {Summary} summary
|
||||
@return {Object}
|
||||
*/
|
||||
function encodePage(page, summary) {
|
||||
var file = page.getFile();
|
||||
var attributes = page.getAttributes();
|
||||
var article = summary.getByPath(file.getPath());
|
||||
|
||||
var result = attributes.toJS();
|
||||
|
||||
if (article) {
|
||||
result.title = article.getTitle();
|
||||
result.level = article.getLevel();
|
||||
result.depth = article.getDepth();
|
||||
|
||||
var nextArticle = summary.getNextArticle(article);
|
||||
if (nextArticle) {
|
||||
result.next = encodeSummaryArticle(nextArticle);
|
||||
}
|
||||
|
||||
var prevArticle = summary.getPrevArticle(article);
|
||||
if (prevArticle) {
|
||||
result.previous = encodeSummaryArticle(prevArticle);
|
||||
}
|
||||
}
|
||||
|
||||
result.content = page.getContent();
|
||||
result.dir = page.getDir();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
module.exports = encodePage;
|
||||
@@ -1,17 +0,0 @@
|
||||
var encodeFile = require('./encodeFile');
|
||||
|
||||
/**
|
||||
Encode a readme to JSON
|
||||
|
||||
@param {Readme}
|
||||
@return {Object}
|
||||
*/
|
||||
function encodeReadme(readme) {
|
||||
var file = readme.getFile();
|
||||
|
||||
return {
|
||||
file: encodeFile(file)
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = encodeReadme;
|
||||
@@ -1,20 +0,0 @@
|
||||
var encodeFile = require('./encodeFile');
|
||||
var encodeSummaryPart = require('./encodeSummaryPart');
|
||||
|
||||
/**
|
||||
Encode a summary to JSON
|
||||
|
||||
@param {Summary}
|
||||
@return {Object}
|
||||
*/
|
||||
function encodeSummary(summary) {
|
||||
var file = summary.getFile();
|
||||
var parts = summary.getParts();
|
||||
|
||||
return {
|
||||
file: encodeFile(file),
|
||||
parts: parts.map(encodeSummaryPart).toJS()
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = encodeSummary;
|
||||
@@ -1,27 +0,0 @@
|
||||
|
||||
/**
|
||||
Encode a SummaryArticle to JSON
|
||||
|
||||
@param {SummaryArticle}
|
||||
@return {Object}
|
||||
*/
|
||||
function encodeSummaryArticle(article, recursive) {
|
||||
var articles = undefined;
|
||||
if (recursive !== false) {
|
||||
articles = article.getArticles()
|
||||
.map(encodeSummaryArticle)
|
||||
.toJS();
|
||||
}
|
||||
|
||||
return {
|
||||
title: article.getTitle(),
|
||||
level: article.getLevel(),
|
||||
depth: article.getDepth(),
|
||||
anchor: article.getAnchor(),
|
||||
url: article.getUrl(),
|
||||
path: article.getPath(),
|
||||
articles: articles
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = encodeSummaryArticle;
|
||||
@@ -1,17 +0,0 @@
|
||||
var encodeSummaryArticle = require('./encodeSummaryArticle');
|
||||
|
||||
/**
|
||||
Encode a SummaryPart to JSON
|
||||
|
||||
@param {SummaryPart}
|
||||
@return {Object}
|
||||
*/
|
||||
function encodeSummaryPart(part) {
|
||||
return {
|
||||
title: part.getTitle(),
|
||||
articles: part.getArticles()
|
||||
.map(encodeSummaryArticle).toJS()
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = encodeSummaryPart;
|
||||
@@ -1,11 +0,0 @@
|
||||
|
||||
module.exports = {
|
||||
encodeOutput: require('./encodeOutput'),
|
||||
encodeBookWithPage: require('./encodeBookWithPage'),
|
||||
encodeBook: require('./encodeBook'),
|
||||
encodeFile: require('./encodeFile'),
|
||||
encodePage: require('./encodePage'),
|
||||
encodeSummary: require('./encodeSummary'),
|
||||
encodeSummaryArticle: require('./encodeSummaryArticle'),
|
||||
encodeReadme: require('./encodeReadme')
|
||||
};
|
||||
@@ -1,63 +0,0 @@
|
||||
jest.autoMockOff();
|
||||
|
||||
var Immutable = require('immutable');
|
||||
|
||||
describe('Config', function() {
|
||||
var Config = require('../config');
|
||||
|
||||
var config = Config.createWithValues({
|
||||
hello: {
|
||||
world: 1,
|
||||
test: 'Hello',
|
||||
isFalse: false
|
||||
}
|
||||
});
|
||||
|
||||
describe('getValue', function() {
|
||||
it('must return value as immutable', function() {
|
||||
var value = config.getValue('hello');
|
||||
expect(Immutable.Map.isMap(value)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('must return deep value', function() {
|
||||
var value = config.getValue('hello.world');
|
||||
expect(value).toBe(1);
|
||||
});
|
||||
|
||||
it('must return default value if non existant', function() {
|
||||
var value = config.getValue('hello.nonExistant', 'defaultValue');
|
||||
expect(value).toBe('defaultValue');
|
||||
});
|
||||
|
||||
it('must not return default value for falsy values', function() {
|
||||
var value = config.getValue('hello.isFalse', 'defaultValue');
|
||||
expect(value).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('setValue', function() {
|
||||
it('must set value as immutable', function() {
|
||||
var testConfig = config.setValue('hello', {
|
||||
'cool': 1
|
||||
});
|
||||
var value = testConfig.getValue('hello');
|
||||
|
||||
expect(Immutable.Map.isMap(value)).toBeTruthy();
|
||||
expect(value.size).toBe(1);
|
||||
expect(value.has('cool')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('must set deep value', function() {
|
||||
var testConfig = config.setValue('hello.world', 2);
|
||||
var hello = testConfig.getValue('hello');
|
||||
var world = testConfig.getValue('hello.world');
|
||||
|
||||
expect(Immutable.Map.isMap(hello)).toBeTruthy();
|
||||
expect(hello.size).toBe(3);
|
||||
|
||||
expect(world).toBe(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
jest.autoMockOff();
|
||||
|
||||
describe('Glossary', function() {
|
||||
var File = require('../file');
|
||||
var Glossary = require('../glossary');
|
||||
var GlossaryEntry = require('../glossaryEntry');
|
||||
|
||||
var glossary = Glossary.createFromEntries(File(), [
|
||||
{
|
||||
name: 'Hello World',
|
||||
description: 'Awesome!'
|
||||
},
|
||||
{
|
||||
name: 'JavaScript',
|
||||
description: 'This is a cool language'
|
||||
}
|
||||
]);
|
||||
|
||||
describe('createFromEntries', function() {
|
||||
it('must add all entries', function() {
|
||||
var entries = glossary.getEntries();
|
||||
expect(entries.size).toBe(2);
|
||||
});
|
||||
|
||||
it('must add entries as GlossaryEntries', function() {
|
||||
var entries = glossary.getEntries();
|
||||
var entry = entries.get('hello-world');
|
||||
expect(entry instanceof GlossaryEntry).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('toText', function() {
|
||||
pit('return as markdown', function() {
|
||||
return glossary.toText('.md')
|
||||
.then(function(text) {
|
||||
expect(text).toContain('# Glossary');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
jest.autoMockOff();
|
||||
|
||||
describe('GlossaryEntry', function() {
|
||||
var GlossaryEntry = require('../glossaryEntry');
|
||||
|
||||
describe('getID', function() {
|
||||
it('must return a normalized ID', function() {
|
||||
var entry = new GlossaryEntry({
|
||||
name: 'Hello World'
|
||||
});
|
||||
|
||||
expect(entry.getID()).toBe('hello-world');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
describe('Plugin', function() {
|
||||
var Plugin = require('../plugin');
|
||||
|
||||
describe('createFromString', function() {
|
||||
it('must parse name', function() {
|
||||
var plugin = Plugin.createFromString('hello');
|
||||
expect(plugin.getName()).toBe('hello');
|
||||
expect(plugin.getVersion()).toBe('*');
|
||||
});
|
||||
|
||||
it('must parse version', function() {
|
||||
var plugin = Plugin.createFromString('hello@1.0.0');
|
||||
expect(plugin.getName()).toBe('hello');
|
||||
expect(plugin.getVersion()).toBe('1.0.0');
|
||||
});
|
||||
});
|
||||
|
||||
describe('isLoaded', function() {
|
||||
it('must return false for empty plugin', function() {
|
||||
var plugin = Plugin.createFromString('hello');
|
||||
expect(plugin.isLoaded()).toBe(false);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
describe('PluginDependency', function() {
|
||||
var PluginDependency = require('../pluginDependency');
|
||||
|
||||
describe('createFromString', function() {
|
||||
it('must parse name', function() {
|
||||
var plugin = PluginDependency.createFromString('hello');
|
||||
expect(plugin.getName()).toBe('hello');
|
||||
expect(plugin.getVersion()).toBe('*');
|
||||
});
|
||||
|
||||
it('must parse state', function() {
|
||||
var plugin = PluginDependency.createFromString('-hello');
|
||||
expect(plugin.getName()).toBe('hello');
|
||||
expect(plugin.isEnabled()).toBe(false);
|
||||
});
|
||||
|
||||
describe('Version', function() {
|
||||
it('must parse version', function() {
|
||||
var plugin = PluginDependency.createFromString('hello@1.0.0');
|
||||
expect(plugin.getName()).toBe('hello');
|
||||
expect(plugin.getVersion()).toBe('1.0.0');
|
||||
});
|
||||
|
||||
it('must parse semver', function() {
|
||||
var plugin = PluginDependency.createFromString('hello@>=4.0.0');
|
||||
expect(plugin.getName()).toBe('hello');
|
||||
expect(plugin.getVersion()).toBe('>=4.0.0');
|
||||
});
|
||||
});
|
||||
|
||||
describe('GIT Version', function() {
|
||||
it('must handle HTTPS urls', function() {
|
||||
var plugin = PluginDependency.createFromString('hello@git+https://github.com/GitbookIO/plugin-ga.git');
|
||||
expect(plugin.getName()).toBe('hello');
|
||||
expect(plugin.getVersion()).toBe('git+https://github.com/GitbookIO/plugin-ga.git');
|
||||
});
|
||||
|
||||
it('must handle SSH urls', function() {
|
||||
var plugin = PluginDependency.createFromString('hello@git+ssh://samy@github.com/GitbookIO/plugin-ga.git');
|
||||
expect(plugin.getName()).toBe('hello');
|
||||
expect(plugin.getVersion()).toBe('git+ssh://samy@github.com/GitbookIO/plugin-ga.git');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
|
||||
describe('Summary', function() {
|
||||
var File = require('../file');
|
||||
var Summary = require('../summary');
|
||||
|
||||
var summary = Summary.createFromParts(File(), [
|
||||
{
|
||||
articles: [
|
||||
{
|
||||
title: 'My First Article',
|
||||
path: 'README.md'
|
||||
},
|
||||
{
|
||||
title: 'My Second Article',
|
||||
path: 'article.md'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Test'
|
||||
}
|
||||
]);
|
||||
|
||||
describe('createFromEntries', function() {
|
||||
it('must add all parts', function() {
|
||||
var parts = summary.getParts();
|
||||
expect(parts.size).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getByLevel', function() {
|
||||
it('can return a Part', function() {
|
||||
var part = summary.getByLevel('1');
|
||||
|
||||
expect(part).toBeDefined();
|
||||
expect(part.getArticles().size).toBe(2);
|
||||
});
|
||||
|
||||
it('can return a Part (2)', function() {
|
||||
var part = summary.getByLevel('2');
|
||||
|
||||
expect(part).toBeDefined();
|
||||
expect(part.getTitle()).toBe('Test');
|
||||
expect(part.getArticles().size).toBe(0);
|
||||
});
|
||||
|
||||
it('can return an Article', function() {
|
||||
var article = summary.getByLevel('1.1');
|
||||
|
||||
expect(article).toBeDefined();
|
||||
expect(article.getTitle()).toBe('My First Article');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getByPath', function() {
|
||||
it('return correct article', function() {
|
||||
var article = summary.getByPath('README.md');
|
||||
|
||||
expect(article).toBeDefined();
|
||||
expect(article.getTitle()).toBe('My First Article');
|
||||
});
|
||||
|
||||
it('return correct article', function() {
|
||||
var article = summary.getByPath('article.md');
|
||||
|
||||
expect(article).toBeDefined();
|
||||
expect(article.getTitle()).toBe('My Second Article');
|
||||
});
|
||||
});
|
||||
|
||||
describe('toText', function() {
|
||||
pit('return as markdown', function() {
|
||||
return summary.toText('.md')
|
||||
.then(function(text) {
|
||||
expect(text).toContain('# Summary');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
var nunjucks = require('nunjucks');
|
||||
var Immutable = require('immutable');
|
||||
var Promise = require('../../utils/promise');
|
||||
|
||||
describe('TemplateBlock', function() {
|
||||
var TemplateBlock = require('../templateBlock');
|
||||
|
||||
describe('create', function() {
|
||||
pit('must initialize a simple TemplateBlock from a function', function() {
|
||||
var templateBlock = TemplateBlock.create('sayhello', function(block) {
|
||||
return '<p>Hello, World!</p>';
|
||||
});
|
||||
|
||||
// Check basic templateBlock properties
|
||||
expect(templateBlock.getName()).toBe('sayhello');
|
||||
expect(templateBlock.getPost()).toBeNull();
|
||||
expect(templateBlock.getParse()).toBeTruthy();
|
||||
expect(templateBlock.getEndTag()).toBe('endsayhello');
|
||||
expect(templateBlock.getBlocks().size).toBe(0);
|
||||
expect(templateBlock.getShortcuts().size).toBe(0);
|
||||
expect(templateBlock.getExtensionName()).toBe('BlocksayhelloExtension');
|
||||
|
||||
// Check result of applying block
|
||||
return Promise()
|
||||
.then(function() {
|
||||
return templateBlock.applyBlock();
|
||||
})
|
||||
.then(function(result) {
|
||||
expect(result.name).toBe('sayhello');
|
||||
expect(result.body).toBe('<p>Hello, World!</p>');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('toNunjucksExt()', function() {
|
||||
pit('must create a valid nunjucks extension', function() {
|
||||
var templateBlock = TemplateBlock.create('sayhello', function(block) {
|
||||
return '<p>Hello, World!</p>';
|
||||
});
|
||||
|
||||
// Create a fresh Nunjucks environment
|
||||
var env = new nunjucks.Environment(null, { autoescape: false });
|
||||
|
||||
// Add template block to environement
|
||||
var Ext = templateBlock.toNunjucksExt();
|
||||
env.addExtension(templateBlock.getExtensionName(), new Ext());
|
||||
|
||||
// Render a template using the block
|
||||
var src = '{% sayhello %}{% endsayhello %}';
|
||||
return Promise.nfcall(env.renderString.bind(env), src)
|
||||
.then(function(res) {
|
||||
expect(res).toBe('<p>Hello, World!</p>');
|
||||
});
|
||||
});
|
||||
|
||||
pit('must apply block arguments correctly', function() {
|
||||
var templateBlock = TemplateBlock.create('sayhello', function(block) {
|
||||
return '<'+block.kwargs.tag+'>Hello, '+block.kwargs.name+'!</'+block.kwargs.tag+'>';
|
||||
});
|
||||
|
||||
// Create a fresh Nunjucks environment
|
||||
var env = new nunjucks.Environment(null, { autoescape: false });
|
||||
|
||||
// Add template block to environement
|
||||
var Ext = templateBlock.toNunjucksExt();
|
||||
env.addExtension(templateBlock.getExtensionName(), new Ext());
|
||||
|
||||
// Render a template using the block
|
||||
var src = '{% sayhello name="Samy", tag="p" %}{% endsayhello %}';
|
||||
return Promise.nfcall(env.renderString.bind(env), src)
|
||||
.then(function(res) {
|
||||
expect(res).toBe('<p>Hello, Samy!</p>');
|
||||
});
|
||||
});
|
||||
|
||||
pit('must accept an async function', function() {
|
||||
var templateBlock = TemplateBlock.create('sayhello', function(block) {
|
||||
return Promise()
|
||||
.then(function() {
|
||||
return 'Hello ' + block.body;
|
||||
});
|
||||
});
|
||||
|
||||
// Create a fresh Nunjucks environment
|
||||
var env = new nunjucks.Environment(null, { autoescape: false });
|
||||
|
||||
// Add template block to environement
|
||||
var Ext = templateBlock.toNunjucksExt();
|
||||
env.addExtension(templateBlock.getExtensionName(), new Ext());
|
||||
|
||||
// Render a template using the block
|
||||
var src = '{% sayhello %}Samy{% endsayhello %}';
|
||||
return Promise.nfcall(env.renderString.bind(env), src)
|
||||
.then(function(res) {
|
||||
expect(res).toBe('Hello Samy');
|
||||
});
|
||||
});
|
||||
|
||||
pit('must handle nested blocks', function() {
|
||||
var templateBlock = new TemplateBlock({
|
||||
name: 'yoda',
|
||||
blocks: Immutable.List(['start', 'end']),
|
||||
process: function(block) {
|
||||
var nested = {};
|
||||
|
||||
block.blocks.forEach(function(blk) {
|
||||
nested[blk.name] = blk.body.trim();
|
||||
});
|
||||
|
||||
return '<p class="yoda">'+nested.end+' '+nested.start+'</p>';
|
||||
}
|
||||
});
|
||||
|
||||
// Create a fresh Nunjucks environment
|
||||
var env = new nunjucks.Environment(null, { autoescape: false });
|
||||
|
||||
// Add template block to environement
|
||||
var Ext = templateBlock.toNunjucksExt();
|
||||
env.addExtension(templateBlock.getExtensionName(), new Ext());
|
||||
|
||||
// Render a template using the block
|
||||
var src = '{% yoda %}{% start %}this sentence should be{% end %}inverted{% endyoda %}';
|
||||
return Promise.nfcall(env.renderString.bind(env), src)
|
||||
.then(function(res) {
|
||||
expect(res).toBe('<p class="yoda">inverted this sentence should be</p>');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,51 +0,0 @@
|
||||
|
||||
describe('TemplateBlock', function() {
|
||||
var TemplateEngine = require('../templateEngine');
|
||||
|
||||
describe('create', function() {
|
||||
it('must initialize with a list of filters', function() {
|
||||
var engine = TemplateEngine.create({
|
||||
filters: {
|
||||
hello: function(name) {
|
||||
return 'Hello ' + name + '!';
|
||||
}
|
||||
}
|
||||
});
|
||||
var env = engine.toNunjucks();
|
||||
var res = env.renderString('{{ "Luke"|hello }}');
|
||||
|
||||
expect(res).toBe('Hello Luke!');
|
||||
});
|
||||
|
||||
it('must initialize with a list of globals', function() {
|
||||
var engine = TemplateEngine.create({
|
||||
globals: {
|
||||
hello: function(name) {
|
||||
return 'Hello ' + name + '!';
|
||||
}
|
||||
}
|
||||
});
|
||||
var env = engine.toNunjucks();
|
||||
var res = env.renderString('{{ hello("Luke") }}');
|
||||
|
||||
expect(res).toBe('Hello Luke!');
|
||||
});
|
||||
|
||||
it('must pass context to filters and blocks', function() {
|
||||
var engine = TemplateEngine.create({
|
||||
filters: {
|
||||
hello: function(name) {
|
||||
return 'Hello ' + name + ' ' + this.lastName + '!';
|
||||
}
|
||||
},
|
||||
context: {
|
||||
lastName: 'Skywalker'
|
||||
}
|
||||
});
|
||||
var env = engine.toNunjucks();
|
||||
var res = env.renderString('{{ "Luke"|hello }}');
|
||||
|
||||
expect(res).toBe('Hello Luke Skywalker!');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,320 +0,0 @@
|
||||
var path = require('path');
|
||||
var Immutable = require('immutable');
|
||||
var Ignore = require('ignore');
|
||||
|
||||
var Logger = require('../utils/logger');
|
||||
|
||||
var FS = require('./fs');
|
||||
var Config = require('./config');
|
||||
var Readme = require('./readme');
|
||||
var Summary = require('./summary');
|
||||
var Glossary = require('./glossary');
|
||||
var Languages = require('./languages');
|
||||
|
||||
|
||||
var Book = Immutable.Record({
|
||||
// Logger for outptu message
|
||||
logger: Logger(),
|
||||
|
||||
// Filesystem binded to the book scope to read files/directories
|
||||
fs: FS(),
|
||||
|
||||
// Ignore files parser
|
||||
ignore: Ignore(),
|
||||
|
||||
// Structure files
|
||||
config: Config(),
|
||||
readme: Readme(),
|
||||
summary: Summary(),
|
||||
glossary: Glossary(),
|
||||
languages: Languages(),
|
||||
|
||||
// ID of the language for language books
|
||||
language: String(),
|
||||
|
||||
// List of children, if multilingual (String -> Book)
|
||||
books: Immutable.OrderedMap()
|
||||
});
|
||||
|
||||
Book.prototype.getLogger = function() {
|
||||
return this.get('logger');
|
||||
};
|
||||
|
||||
Book.prototype.getFS = function() {
|
||||
return this.get('fs');
|
||||
};
|
||||
|
||||
Book.prototype.getIgnore = function() {
|
||||
return this.get('ignore');
|
||||
};
|
||||
|
||||
Book.prototype.getConfig = function() {
|
||||
return this.get('config');
|
||||
};
|
||||
|
||||
Book.prototype.getReadme = function() {
|
||||
return this.get('readme');
|
||||
};
|
||||
|
||||
Book.prototype.getSummary = function() {
|
||||
return this.get('summary');
|
||||
};
|
||||
|
||||
Book.prototype.getGlossary = function() {
|
||||
return this.get('glossary');
|
||||
};
|
||||
|
||||
Book.prototype.getLanguages = function() {
|
||||
return this.get('languages');
|
||||
};
|
||||
|
||||
Book.prototype.getBooks = function() {
|
||||
return this.get('books');
|
||||
};
|
||||
|
||||
Book.prototype.getLanguage = function() {
|
||||
return this.get('language');
|
||||
};
|
||||
|
||||
/**
|
||||
Return FS instance to access the content
|
||||
|
||||
@return {FS}
|
||||
*/
|
||||
Book.prototype.getContentFS = function() {
|
||||
var fs = this.getFS();
|
||||
var config = this.getConfig();
|
||||
var rootFolder = config.getValue('root');
|
||||
|
||||
if (rootFolder) {
|
||||
return FS.reduceScope(fs, rootFolder);
|
||||
}
|
||||
|
||||
return fs;
|
||||
};
|
||||
|
||||
/**
|
||||
Return root of the book
|
||||
|
||||
@return {String}
|
||||
*/
|
||||
Book.prototype.getRoot = function() {
|
||||
var fs = this.getFS();
|
||||
return fs.getRoot();
|
||||
};
|
||||
|
||||
/**
|
||||
Return root for content of the book
|
||||
|
||||
@return {String}
|
||||
*/
|
||||
Book.prototype.getContentRoot = function() {
|
||||
var fs = this.getContentFS();
|
||||
return fs.getRoot();
|
||||
};
|
||||
|
||||
/**
|
||||
Check if a file is ignore (should not being parsed, etc)
|
||||
|
||||
@param {String} ref
|
||||
@return {Page|undefined}
|
||||
*/
|
||||
Book.prototype.isFileIgnored = function(filename) {
|
||||
var ignore = this.getIgnore();
|
||||
var language = this.getLanguage();
|
||||
|
||||
// Ignore is always relative to the root of the main book
|
||||
if (language) {
|
||||
filename = path.join(language, filename);
|
||||
}
|
||||
|
||||
|
||||
return ignore.filter([filename]).length == 0;
|
||||
};
|
||||
|
||||
/**
|
||||
Check if a content file is ignore (should not being parsed, etc)
|
||||
|
||||
@param {String} ref
|
||||
@return {Page|undefined}
|
||||
*/
|
||||
Book.prototype.isContentFileIgnored = function(filename) {
|
||||
var config = this.getConfig();
|
||||
var rootFolder = config.getValue('root');
|
||||
|
||||
if (rootFolder) {
|
||||
filename = path.join(rootFolder, filename);
|
||||
}
|
||||
|
||||
return this.isFileIgnored(filename);
|
||||
};
|
||||
|
||||
/**
|
||||
Return a page from a book by its path
|
||||
|
||||
@param {String} ref
|
||||
@return {Page|undefined}
|
||||
*/
|
||||
Book.prototype.getPage = function(ref) {
|
||||
return this.getPages().get(ref);
|
||||
};
|
||||
|
||||
/**
|
||||
Is this book the parent of language's books
|
||||
|
||||
@return {Boolean}
|
||||
*/
|
||||
Book.prototype.isMultilingual = function() {
|
||||
return (this.getLanguages().getCount() > 0);
|
||||
};
|
||||
|
||||
/**
|
||||
Return true if book is associated to a language
|
||||
|
||||
@return {Boolean}
|
||||
*/
|
||||
Book.prototype.isLanguageBook = function() {
|
||||
return Boolean(this.getLanguage());
|
||||
};
|
||||
|
||||
/**
|
||||
Add a new language book
|
||||
|
||||
@param {String} language
|
||||
@param {Book} book
|
||||
@return {Book}
|
||||
*/
|
||||
Book.prototype.addLanguageBook = function(language, book) {
|
||||
var books = this.getBooks();
|
||||
books = books.set(language, book);
|
||||
|
||||
return this.set('books', books);
|
||||
};
|
||||
|
||||
/**
|
||||
Set the summary for this book
|
||||
|
||||
@param {Summary}
|
||||
@return {Book}
|
||||
*/
|
||||
Book.prototype.setSummary = function(summary) {
|
||||
return this.set('summary', summary);
|
||||
};
|
||||
|
||||
/**
|
||||
Set the readme for this book
|
||||
|
||||
@param {Readme}
|
||||
@return {Book}
|
||||
*/
|
||||
Book.prototype.setReadme = function(readme) {
|
||||
return this.set('readme', readme);
|
||||
};
|
||||
|
||||
/**
|
||||
Set the configuration for this book
|
||||
|
||||
@param {Config}
|
||||
@return {Book}
|
||||
*/
|
||||
Book.prototype.setConfig = function(config) {
|
||||
return this.set('config', config);
|
||||
};
|
||||
|
||||
/**
|
||||
Change log level
|
||||
|
||||
@param {String} level
|
||||
@return {Book}
|
||||
*/
|
||||
Book.prototype.setLogLevel = function(level) {
|
||||
this.getLogger().setLevel(level);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
Create a book using a filesystem
|
||||
|
||||
@param {FS} fs
|
||||
@return {Book}
|
||||
*/
|
||||
Book.createForFS = function createForFS(fs) {
|
||||
return new Book({
|
||||
fs: fs
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
Infers the default extension for files
|
||||
@return {String}
|
||||
*/
|
||||
Book.prototype.getDefaultExt = function() {
|
||||
// Inferring sources
|
||||
var clues = [
|
||||
this.getReadme(),
|
||||
this.getSummary(),
|
||||
this.getGlossary()
|
||||
];
|
||||
|
||||
// List their extensions
|
||||
var exts = clues.map(function (clue) {
|
||||
var file = clue.getFile();
|
||||
if (file.exists()) {
|
||||
return file.getParser().getExtensions().first();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
// Adds the general default extension
|
||||
exts.push('.md');
|
||||
|
||||
// Choose the first non null
|
||||
return exts.find(function (e) { return e !== null; });
|
||||
};
|
||||
|
||||
/**
|
||||
Infer the default path for a Readme.
|
||||
@return {String}
|
||||
*/
|
||||
Book.prototype.getDefaultReadmePath = function() {
|
||||
return this.getContentRoot()+'README'+this.getDefaultExt();
|
||||
};
|
||||
|
||||
/**
|
||||
Infer the default path for a Summary.
|
||||
@return {String}
|
||||
*/
|
||||
Book.prototype.getDefaultSummaryPath = function() {
|
||||
return this.getContentRoot()+'SUMMARY'+this.getDefaultExt();
|
||||
};
|
||||
|
||||
/**
|
||||
Infer the default path for a Glossary.
|
||||
@return {String}
|
||||
*/
|
||||
Book.prototype.getDefaultGlossaryPath = function() {
|
||||
return this.getContentRoot()+'GLOSSARY'+this.getDefaultExt();
|
||||
};
|
||||
|
||||
/**
|
||||
Create a language book from a parent
|
||||
|
||||
@param {Book} parent
|
||||
@param {String} language
|
||||
@return {Book}
|
||||
*/
|
||||
Book.createFromParent = function createFromParent(parent, language) {
|
||||
var ignore = parent.getIgnore();
|
||||
|
||||
return new Book({
|
||||
// Inherits config. logegr and list of ignored files
|
||||
logger: parent.getLogger(),
|
||||
config: parent.getConfig(),
|
||||
ignore: Ignore().add(ignore),
|
||||
|
||||
language: language,
|
||||
fs: FS.reduceScope(parent.getContentFS(), language)
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = Book;
|
||||
@@ -1,134 +0,0 @@
|
||||
var is = require('is');
|
||||
var Immutable = require('immutable');
|
||||
|
||||
var File = require('./file');
|
||||
var PluginDependency = require('./pluginDependency');
|
||||
var configDefault = require('../constants/configDefault');
|
||||
|
||||
var Config = Immutable.Record({
|
||||
file: File(),
|
||||
values: configDefault
|
||||
}, 'Config');
|
||||
|
||||
Config.prototype.getFile = function() {
|
||||
return this.get('file');
|
||||
};
|
||||
|
||||
Config.prototype.getValues = function() {
|
||||
return this.get('values');
|
||||
};
|
||||
|
||||
/**
|
||||
Return a configuration value by its key path
|
||||
|
||||
@param {String} key
|
||||
@return {Mixed}
|
||||
*/
|
||||
Config.prototype.getValue = function(keyPath, def) {
|
||||
var values = this.getValues();
|
||||
keyPath = Config.keyToKeyPath(keyPath);
|
||||
|
||||
if (!values.hasIn(keyPath)) {
|
||||
return Immutable.fromJS(def);
|
||||
}
|
||||
|
||||
return values.getIn(keyPath);
|
||||
};
|
||||
|
||||
/**
|
||||
Update a configuration value
|
||||
|
||||
@param {String} key
|
||||
@param {Mixed} value
|
||||
@return {Mixed}
|
||||
*/
|
||||
Config.prototype.setValue = function(keyPath, value) {
|
||||
keyPath = Config.keyToKeyPath(keyPath);
|
||||
|
||||
value = Immutable.fromJS(value);
|
||||
|
||||
var values = this.getValues();
|
||||
values = values.setIn(keyPath, value);
|
||||
|
||||
return this.set('values', values);
|
||||
};
|
||||
|
||||
/**
|
||||
Return a list of plugin dependencies
|
||||
|
||||
@return {List<PluginDependency>}
|
||||
*/
|
||||
Config.prototype.getPluginDependencies = function() {
|
||||
var plugins = this.getValue('plugins');
|
||||
|
||||
if (is.string(plugins)) {
|
||||
return PluginDependency.listFromString(plugins);
|
||||
} else {
|
||||
return PluginDependency.listFromArray(plugins);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
Update the list of plugins dependencies
|
||||
|
||||
@param {List<PluginDependency>}
|
||||
@return {Config}
|
||||
*/
|
||||
Config.prototype.setPluginDependencies = function(deps) {
|
||||
var plugins = PluginDependency.listFromArray(deps);
|
||||
|
||||
return this.setValue('plugins', plugins);
|
||||
};
|
||||
|
||||
/**
|
||||
Create a new config for a file
|
||||
|
||||
@param {File} file
|
||||
@param {Object} values
|
||||
@returns {Config}
|
||||
*/
|
||||
Config.create = function(file, values) {
|
||||
return new Config({
|
||||
file: file,
|
||||
values: Immutable.fromJS(values)
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
Create a new config
|
||||
|
||||
@param {Object} values
|
||||
@returns {Config}
|
||||
*/
|
||||
Config.createWithValues = function(values) {
|
||||
return new Config({
|
||||
values: Immutable.fromJS(values)
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
Update values for an existing configuration
|
||||
|
||||
@param {Config} config
|
||||
@param {Object} values
|
||||
@returns {Config}
|
||||
*/
|
||||
Config.updateValues = function(config, values) {
|
||||
values = Immutable.fromJS(values);
|
||||
|
||||
return config.set('values', values);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
Convert a keyPath to an array of keys
|
||||
|
||||
@param {String|Array}
|
||||
@return {Array}
|
||||
*/
|
||||
Config.keyToKeyPath = function(keyPath) {
|
||||
if (is.string(keyPath)) keyPath = keyPath.split('.');
|
||||
return keyPath;
|
||||
};
|
||||
|
||||
module.exports = Config;
|
||||
@@ -1,89 +0,0 @@
|
||||
var path = require('path');
|
||||
var Immutable = require('immutable');
|
||||
|
||||
var parsers = require('../parsers');
|
||||
|
||||
var File = Immutable.Record({
|
||||
// Path of the file, relative to the FS
|
||||
path: String(),
|
||||
|
||||
// Time when file data last modified
|
||||
mtime: Date()
|
||||
});
|
||||
|
||||
File.prototype.getPath = function() {
|
||||
return this.get('path');
|
||||
};
|
||||
|
||||
File.prototype.getMTime = function() {
|
||||
return this.get('mtime');
|
||||
};
|
||||
|
||||
/**
|
||||
Does the file exists / is set
|
||||
|
||||
@return {Boolean}
|
||||
*/
|
||||
File.prototype.exists = function() {
|
||||
return Boolean(this.getPath());
|
||||
};
|
||||
|
||||
/**
|
||||
Return type of file ('markdown' or 'asciidoc')
|
||||
|
||||
@return {String}
|
||||
*/
|
||||
File.prototype.getType = function() {
|
||||
var parser = this.getParser();
|
||||
if (parser) {
|
||||
return parser.getName();
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
Return extension of this file (lowercased)
|
||||
|
||||
@return {String}
|
||||
*/
|
||||
File.prototype.getExtension = function() {
|
||||
return path.extname(this.getPath()).toLowerCase();
|
||||
};
|
||||
|
||||
/**
|
||||
Return parser for this file
|
||||
|
||||
@return {Parser}
|
||||
*/
|
||||
File.prototype.getParser = function() {
|
||||
return parsers.getByExt(this.getExtension());
|
||||
};
|
||||
|
||||
/**
|
||||
Create a file from stats informations
|
||||
|
||||
@param {String} filepath
|
||||
@param {Object|fs.Stats} stat
|
||||
@return {File}
|
||||
*/
|
||||
File.createFromStat = function createFromStat(filepath, stat) {
|
||||
return new File({
|
||||
path: filepath,
|
||||
mtime: stat.mtime
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
Create a file with only a path
|
||||
|
||||
@param {String} filepath
|
||||
@return {File}
|
||||
*/
|
||||
File.createWithFilepath = function createWithFilepath(filepath) {
|
||||
return new File({
|
||||
path: filepath
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = File;
|
||||
@@ -1,274 +0,0 @@
|
||||
var path = require('path');
|
||||
var Immutable = require('immutable');
|
||||
|
||||
var File = require('./file');
|
||||
var Promise = require('../utils/promise');
|
||||
var error = require('../utils/error');
|
||||
var PathUtil = require('../utils/path');
|
||||
|
||||
var FS = Immutable.Record({
|
||||
root: String(),
|
||||
|
||||
fsExists: Function(),
|
||||
fsReadFile: Function(),
|
||||
fsStatFile: Function(),
|
||||
fsReadDir: Function(),
|
||||
fsLoadObject: null
|
||||
});
|
||||
|
||||
/**
|
||||
Return path to the root
|
||||
|
||||
@return {String}
|
||||
*/
|
||||
FS.prototype.getRoot = function() {
|
||||
return this.get('root');
|
||||
};
|
||||
|
||||
/**
|
||||
Verify that a file is in the fs scope
|
||||
|
||||
@param {String} filename
|
||||
@return {Boolean}
|
||||
*/
|
||||
FS.prototype.isInScope = function(filename) {
|
||||
var rootPath = this.getRoot();
|
||||
filename = path.join(rootPath, filename);
|
||||
|
||||
return PathUtil.isInRoot(rootPath, filename);
|
||||
};
|
||||
|
||||
/**
|
||||
Resolve a file in this FS
|
||||
|
||||
@param {String}
|
||||
@return {String}
|
||||
*/
|
||||
FS.prototype.resolve = function() {
|
||||
var rootPath = this.getRoot();
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
var filename = path.join.apply(path, [rootPath].concat(args));
|
||||
filename = path.normalize(filename);
|
||||
|
||||
if (!this.isInScope(filename)) {
|
||||
throw error.FileOutOfScopeError({
|
||||
filename: filename,
|
||||
root: this.root
|
||||
});
|
||||
}
|
||||
|
||||
return filename;
|
||||
};
|
||||
|
||||
/**
|
||||
Check if a file exists, run a Promise(true) if that's the case, Promise(false) otherwise
|
||||
|
||||
@param {String} filename
|
||||
@return {Promise<Boolean>}
|
||||
*/
|
||||
FS.prototype.exists = function(filename) {
|
||||
var that = this;
|
||||
|
||||
return Promise()
|
||||
.then(function() {
|
||||
filename = that.resolve(filename);
|
||||
var exists = that.get('fsExists');
|
||||
|
||||
return exists(filename);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
Read a file and returns a promise with the content as a buffer
|
||||
|
||||
@param {String} filename
|
||||
@return {Promise<Buffer>}
|
||||
*/
|
||||
FS.prototype.read = function(filename) {
|
||||
var that = this;
|
||||
|
||||
return Promise()
|
||||
.then(function() {
|
||||
filename = that.resolve(filename);
|
||||
var read = that.get('fsReadFile');
|
||||
|
||||
return read(filename);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
Read a file as a string (utf-8)
|
||||
|
||||
@param {String} filename
|
||||
@return {Promise<String>}
|
||||
*/
|
||||
FS.prototype.readAsString = function(filename, encoding) {
|
||||
encoding = encoding || 'utf8';
|
||||
|
||||
return this.read(filename)
|
||||
.then(function(buf) {
|
||||
return buf.toString(encoding);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
Read stat infos about a file
|
||||
|
||||
@param {String} filename
|
||||
@return {Promise<File>}
|
||||
*/
|
||||
FS.prototype.statFile = function(filename) {
|
||||
var that = this;
|
||||
|
||||
return Promise()
|
||||
.then(function() {
|
||||
var filepath = that.resolve(filename);
|
||||
var stat = that.get('fsStatFile');
|
||||
|
||||
return stat(filepath);
|
||||
})
|
||||
.then(function(stat) {
|
||||
return File.createFromStat(filename, stat);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
List files/directories in a directory.
|
||||
Directories ends with '/'
|
||||
|
||||
@param {String} dirname
|
||||
@return {Promise<List<String>>}
|
||||
*/
|
||||
FS.prototype.readDir = function(dirname) {
|
||||
var that = this;
|
||||
|
||||
return Promise()
|
||||
.then(function() {
|
||||
var dirpath = that.resolve(dirname);
|
||||
var readDir = that.get('fsReadDir');
|
||||
|
||||
return readDir(dirpath);
|
||||
})
|
||||
.then(function(files) {
|
||||
return Immutable.List(files);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
List only files in a diretcory
|
||||
Directories ends with '/'
|
||||
|
||||
@param {String} dirname
|
||||
@return {Promise<List<String>>}
|
||||
*/
|
||||
FS.prototype.listFiles = function(dirname) {
|
||||
return this.readDir(dirname)
|
||||
.then(function(files) {
|
||||
return files.filterNot(pathIsFolder);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
List all files in a directory
|
||||
|
||||
@param {String} dirname
|
||||
@return {Promise<List<String>>}
|
||||
*/
|
||||
FS.prototype.listAllFiles = function(folder) {
|
||||
var that = this;
|
||||
folder = folder || '.';
|
||||
|
||||
return this.readDir(folder)
|
||||
.then(function(files) {
|
||||
return Promise.reduce(files, function(out, file) {
|
||||
var isDirectory = pathIsFolder(file);
|
||||
|
||||
if (!isDirectory) {
|
||||
return out.push(path.join(folder, file));
|
||||
}
|
||||
|
||||
return that.listAllFiles(path.join(folder, file))
|
||||
.then(function(inner) {
|
||||
return out.concat(inner);
|
||||
});
|
||||
}, Immutable.List());
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
Find a file in a folder (case incensitive)
|
||||
Return the found filename
|
||||
|
||||
@param {String} dirname
|
||||
@param {String} filename
|
||||
@return {Promise<String>}
|
||||
*/
|
||||
FS.prototype.findFile = function(dirname, filename) {
|
||||
return this.listFiles(dirname)
|
||||
.then(function(files) {
|
||||
return files.find(function(file) {
|
||||
return (file.toLowerCase() == filename.toLowerCase());
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
Load a JSON file
|
||||
By default, fs only supports JSON
|
||||
|
||||
@param {String} filename
|
||||
@return {Promise<Object>}
|
||||
*/
|
||||
FS.prototype.loadAsObject = function(filename) {
|
||||
var that = this;
|
||||
var fsLoadObject = this.get('fsLoadObject');
|
||||
|
||||
return this.exists(filename)
|
||||
.then(function(exists) {
|
||||
if (!exists) {
|
||||
var err = new Error('Module doesn\'t exist');
|
||||
err.code = 'MODULE_NOT_FOUND';
|
||||
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (fsLoadObject) {
|
||||
return fsLoadObject(that.resolve(filename));
|
||||
} else {
|
||||
return that.readAsString(filename)
|
||||
.then(function(str) {
|
||||
return JSON.parse(str);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
Create a FS instance
|
||||
|
||||
@param {Object} def
|
||||
@return {FS}
|
||||
*/
|
||||
FS.create = function create(def) {
|
||||
return new FS(def);
|
||||
};
|
||||
|
||||
/**
|
||||
Create a new FS instance with a reduced scope
|
||||
|
||||
@param {FS} fs
|
||||
@param {String} scope
|
||||
@return {FS}
|
||||
*/
|
||||
FS.reduceScope = function reduceScope(fs, scope) {
|
||||
return fs.set('root', path.join(fs.getRoot(), scope));
|
||||
};
|
||||
|
||||
|
||||
// .readdir return files/folder as a list of string, folder ending with '/'
|
||||
function pathIsFolder(filename) {
|
||||
var lastChar = filename[filename.length - 1];
|
||||
return lastChar == '/' || lastChar == '\\';
|
||||
}
|
||||
|
||||
module.exports = FS;
|
||||
@@ -1,109 +0,0 @@
|
||||
var Immutable = require('immutable');
|
||||
|
||||
var error = require('../utils/error');
|
||||
var File = require('./file');
|
||||
var GlossaryEntry = require('./glossaryEntry');
|
||||
var parsers = require('../parsers');
|
||||
|
||||
var Glossary = Immutable.Record({
|
||||
file: File(),
|
||||
entries: Immutable.OrderedMap()
|
||||
});
|
||||
|
||||
Glossary.prototype.getFile = function() {
|
||||
return this.get('file');
|
||||
};
|
||||
|
||||
Glossary.prototype.getEntries = function() {
|
||||
return this.get('entries');
|
||||
};
|
||||
|
||||
/**
|
||||
Return an entry by its name
|
||||
|
||||
@param {String} name
|
||||
@return {GlossaryEntry}
|
||||
*/
|
||||
Glossary.prototype.getEntry = function(name) {
|
||||
var entries = this.getEntries();
|
||||
var id = GlossaryEntry.nameToID(name);
|
||||
|
||||
return entries.get(id);
|
||||
};
|
||||
|
||||
/**
|
||||
Render glossary as text
|
||||
|
||||
@return {Promise<String>}
|
||||
*/
|
||||
Glossary.prototype.toText = function(parser) {
|
||||
var file = this.getFile();
|
||||
var entries = this.getEntries();
|
||||
|
||||
parser = parser? parsers.getByExt(parser) : file.getParser();
|
||||
|
||||
if (!parser) {
|
||||
throw error.FileNotParsableError({
|
||||
filename: file.getPath()
|
||||
});
|
||||
}
|
||||
|
||||
return parser.renderGlossary(entries.toJS());
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
Add/Replace an entry to a glossary
|
||||
|
||||
@param {Glossary} glossary
|
||||
@param {GlossaryEntry} entry
|
||||
@return {Glossary}
|
||||
*/
|
||||
Glossary.addEntry = function addEntry(glossary, entry) {
|
||||
var id = entry.getID();
|
||||
var entries = glossary.getEntries();
|
||||
|
||||
entries = entries.set(id, entry);
|
||||
return glossary.set('entries', entries);
|
||||
};
|
||||
|
||||
/**
|
||||
Add/Replace an entry to a glossary by name/description
|
||||
|
||||
@param {Glossary} glossary
|
||||
@param {GlossaryEntry} entry
|
||||
@return {Glossary}
|
||||
*/
|
||||
Glossary.addEntryByName = function addEntryByName(glossary, name, description) {
|
||||
var entry = new GlossaryEntry({
|
||||
name: name,
|
||||
description: description
|
||||
});
|
||||
|
||||
return Glossary.addEntry(glossary, entry);
|
||||
};
|
||||
|
||||
/**
|
||||
Create a glossary from a list of entries
|
||||
|
||||
@param {String} filename
|
||||
@param {Array|List} entries
|
||||
@return {Glossary}
|
||||
*/
|
||||
Glossary.createFromEntries = function createFromEntries(file, entries) {
|
||||
entries = entries.map(function(entry) {
|
||||
if (!(entry instanceof GlossaryEntry)) {
|
||||
entry = new GlossaryEntry(entry);
|
||||
}
|
||||
|
||||
return [entry.getID(), entry];
|
||||
});
|
||||
|
||||
return new Glossary({
|
||||
file: file,
|
||||
entries: Immutable.OrderedMap(entries)
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
module.exports = Glossary;
|
||||
@@ -1,43 +0,0 @@
|
||||
var Immutable = require('immutable');
|
||||
var slug = require('github-slugid');
|
||||
|
||||
/*
|
||||
A definition represents an entry in the glossary
|
||||
*/
|
||||
|
||||
var GlossaryEntry = Immutable.Record({
|
||||
name: String(),
|
||||
description: String()
|
||||
});
|
||||
|
||||
GlossaryEntry.prototype.getName = function() {
|
||||
return this.get('name');
|
||||
};
|
||||
|
||||
GlossaryEntry.prototype.getDescription = function() {
|
||||
return this.get('description');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
Get identifier for this entry
|
||||
|
||||
@retrun {Boolean}
|
||||
*/
|
||||
GlossaryEntry.prototype.getID = function() {
|
||||
return GlossaryEntry.nameToID(this.getName());
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
Normalize a glossary entry name into a unique id
|
||||
|
||||
@param {String}
|
||||
@return {String}
|
||||
*/
|
||||
GlossaryEntry.nameToID = function nameToID(name) {
|
||||
return slug(name);
|
||||
};
|
||||
|
||||
|
||||
module.exports = GlossaryEntry;
|
||||
@@ -1,21 +0,0 @@
|
||||
var path = require('path');
|
||||
var Immutable = require('immutable');
|
||||
|
||||
var Language = Immutable.Record({
|
||||
title: String(),
|
||||
path: String()
|
||||
});
|
||||
|
||||
Language.prototype.getTitle = function() {
|
||||
return this.get('title');
|
||||
};
|
||||
|
||||
Language.prototype.getPath = function() {
|
||||
return this.get('path');
|
||||
};
|
||||
|
||||
Language.prototype.getID = function() {
|
||||
return path.basename(this.getPath());
|
||||
};
|
||||
|
||||
module.exports = Language;
|
||||
@@ -1,71 +0,0 @@
|
||||
var Immutable = require('immutable');
|
||||
|
||||
var File = require('./file');
|
||||
var Language = require('./language');
|
||||
|
||||
var Languages = Immutable.Record({
|
||||
file: File(),
|
||||
list: Immutable.OrderedMap()
|
||||
});
|
||||
|
||||
Languages.prototype.getFile = function() {
|
||||
return this.get('file');
|
||||
};
|
||||
|
||||
Languages.prototype.getList = function() {
|
||||
return this.get('list');
|
||||
};
|
||||
|
||||
/**
|
||||
Get default languages
|
||||
|
||||
@return {Language}
|
||||
*/
|
||||
Languages.prototype.getDefaultLanguage = function() {
|
||||
return this.getList().first();
|
||||
};
|
||||
|
||||
/**
|
||||
Get a language by its ID
|
||||
|
||||
@param {String} lang
|
||||
@return {Language}
|
||||
*/
|
||||
Languages.prototype.getLanguage = function(lang) {
|
||||
return this.getList().get(lang);
|
||||
};
|
||||
|
||||
/**
|
||||
Return count of langs
|
||||
|
||||
@return {Number}
|
||||
*/
|
||||
Languages.prototype.getCount = function() {
|
||||
return this.getList().size;
|
||||
};
|
||||
|
||||
/**
|
||||
Create a languages list from a JS object
|
||||
|
||||
@param {File}
|
||||
@param {Array}
|
||||
@return {Language}
|
||||
*/
|
||||
Languages.createFromList = function(file, langs) {
|
||||
var list = Immutable.OrderedMap();
|
||||
|
||||
langs.forEach(function(lang) {
|
||||
lang = Language({
|
||||
title: lang.title,
|
||||
path: lang.ref
|
||||
});
|
||||
list = list.set(lang.getID(), lang);
|
||||
});
|
||||
|
||||
return Languages({
|
||||
file: file,
|
||||
list: list
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = Languages;
|
||||
@@ -1,93 +0,0 @@
|
||||
var Immutable = require('immutable');
|
||||
|
||||
var Book = require('./book');
|
||||
|
||||
var Output = Immutable.Record({
|
||||
book: Book(),
|
||||
|
||||
// Name of the generator being used
|
||||
generator: String(),
|
||||
|
||||
// Map of plugins to use (String -> Plugin)
|
||||
plugins: Immutable.OrderedMap(),
|
||||
|
||||
// Map pages to generation (String -> Page)
|
||||
pages: Immutable.OrderedMap(),
|
||||
|
||||
// List assets (String)
|
||||
assets: Immutable.List(),
|
||||
|
||||
// Option for the generation
|
||||
options: Immutable.Map(),
|
||||
|
||||
// Internal state for the generation
|
||||
state: Immutable.Map()
|
||||
});
|
||||
|
||||
Output.prototype.getBook = function() {
|
||||
return this.get('book');
|
||||
};
|
||||
|
||||
Output.prototype.getGenerator = function() {
|
||||
return this.get('generator');
|
||||
};
|
||||
|
||||
Output.prototype.getPlugins = function() {
|
||||
return this.get('plugins');
|
||||
};
|
||||
|
||||
Output.prototype.getPages = function() {
|
||||
return this.get('pages');
|
||||
};
|
||||
|
||||
Output.prototype.getOptions = function() {
|
||||
return this.get('options');
|
||||
};
|
||||
|
||||
Output.prototype.getAssets = function() {
|
||||
return this.get('assets');
|
||||
};
|
||||
|
||||
Output.prototype.getState = function() {
|
||||
return this.get('state');
|
||||
};
|
||||
|
||||
/**
|
||||
Get root folder for output
|
||||
|
||||
@return {String}
|
||||
*/
|
||||
Output.prototype.getRoot = function() {
|
||||
return this.getOptions().get('root');
|
||||
};
|
||||
|
||||
/**
|
||||
Update state of output
|
||||
|
||||
@param {Map} newState
|
||||
@return {Output}
|
||||
*/
|
||||
Output.prototype.setState = function(newState) {
|
||||
return this.set('state', newState);
|
||||
};
|
||||
|
||||
/**
|
||||
Update options
|
||||
|
||||
@param {Map} newOptions
|
||||
@return {Output}
|
||||
*/
|
||||
Output.prototype.setOptions = function(newOptions) {
|
||||
return this.set('options', newOptions);
|
||||
};
|
||||
|
||||
/**
|
||||
Return logegr for this output (same as book)
|
||||
|
||||
@return {Logger}
|
||||
*/
|
||||
Output.prototype.getLogger = function() {
|
||||
return this.getBook().getLogger();
|
||||
};
|
||||
|
||||
module.exports = Output;
|
||||
@@ -1,55 +0,0 @@
|
||||
var Immutable = require('immutable');
|
||||
|
||||
var File = require('./file');
|
||||
|
||||
var Page = Immutable.Record({
|
||||
file: File(),
|
||||
|
||||
// Attributes extracted from the YAML header
|
||||
attributes: Immutable.Map(),
|
||||
|
||||
// Content of the page
|
||||
content: String(),
|
||||
|
||||
// Direction of the text
|
||||
dir: String('ltr')
|
||||
});
|
||||
|
||||
Page.prototype.getFile = function() {
|
||||
return this.get('file');
|
||||
};
|
||||
|
||||
Page.prototype.getAttributes = function() {
|
||||
return this.get('attributes');
|
||||
};
|
||||
|
||||
Page.prototype.getContent = function() {
|
||||
return this.get('content');
|
||||
};
|
||||
|
||||
Page.prototype.getDir = function() {
|
||||
return this.get('dir');
|
||||
};
|
||||
|
||||
/**
|
||||
Return path of the page
|
||||
|
||||
@return {String}
|
||||
*/
|
||||
Page.prototype.getPath = function() {
|
||||
return this.getFile().getPath();
|
||||
};
|
||||
|
||||
/**
|
||||
Create a page for a file
|
||||
|
||||
@param {File} file
|
||||
@return {Page}
|
||||
*/
|
||||
Page.createForFile = function(file) {
|
||||
return new Page({
|
||||
file: file
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = Page;
|
||||
@@ -1,117 +0,0 @@
|
||||
var Immutable = require('immutable');
|
||||
var Promise = require('../utils/promise');
|
||||
|
||||
var Parser = Immutable.Record({
|
||||
name: String(),
|
||||
|
||||
// List of extensions that can be processed using this parser
|
||||
extensions: Immutable.List(),
|
||||
|
||||
// Parsing functions
|
||||
readme: Function(),
|
||||
langs: Function(),
|
||||
summary: Function(),
|
||||
glossary: Function(),
|
||||
page: Function(),
|
||||
inline: Function()
|
||||
});
|
||||
|
||||
Parser.prototype.getName = function() {
|
||||
return this.get('name');
|
||||
};
|
||||
|
||||
Parser.prototype.getExtensions = function() {
|
||||
return this.get('extensions');
|
||||
};
|
||||
|
||||
// PARSE
|
||||
|
||||
Parser.prototype.parseReadme = function(content) {
|
||||
var readme = this.get('readme');
|
||||
return Promise(readme(content));
|
||||
};
|
||||
|
||||
Parser.prototype.parseSummary = function(content) {
|
||||
var summary = this.get('summary');
|
||||
return Promise(summary(content));
|
||||
};
|
||||
|
||||
Parser.prototype.parseGlossary = function(content) {
|
||||
var glossary = this.get('glossary');
|
||||
return Promise(glossary(content));
|
||||
};
|
||||
|
||||
Parser.prototype.preparePage = function(content) {
|
||||
var page = this.get('page');
|
||||
if (!page.prepare) {
|
||||
return Promise(content);
|
||||
}
|
||||
|
||||
return Promise(page.prepare(content));
|
||||
};
|
||||
|
||||
Parser.prototype.parsePage = function(content) {
|
||||
var page = this.get('page');
|
||||
return Promise(page(content));
|
||||
};
|
||||
|
||||
Parser.prototype.parseLanguages = function(content) {
|
||||
var langs = this.get('langs');
|
||||
return Promise(langs(content));
|
||||
};
|
||||
|
||||
Parser.prototype.parseInline = function(content) {
|
||||
var inline = this.get('inline');
|
||||
return Promise(inline(content));
|
||||
};
|
||||
|
||||
// TO TEXT
|
||||
|
||||
Parser.prototype.renderLanguages = function(content) {
|
||||
var langs = this.get('langs');
|
||||
return Promise(langs.toText(content));
|
||||
};
|
||||
|
||||
Parser.prototype.renderSummary = function(content) {
|
||||
var summary = this.get('summary');
|
||||
return Promise(summary.toText(content));
|
||||
};
|
||||
|
||||
Parser.prototype.renderGlossary = function(content) {
|
||||
var glossary = this.get('glossary');
|
||||
return Promise(glossary.toText(content));
|
||||
};
|
||||
|
||||
/**
|
||||
Test if this parser matches an extension
|
||||
|
||||
@param {String} ext
|
||||
@return {Boolean}
|
||||
*/
|
||||
Parser.prototype.matchExtension = function(ext) {
|
||||
var exts = this.getExtensions();
|
||||
return exts.includes(ext.toLowerCase());
|
||||
};
|
||||
|
||||
/**
|
||||
Create a new parser using a module (gitbook-markdown, etc)
|
||||
|
||||
@param {String} name
|
||||
@param {Array<String>} extensions
|
||||
@param {Object} module
|
||||
@return {Parser}
|
||||
*/
|
||||
Parser.create = function(name, extensions, module) {
|
||||
return new Parser({
|
||||
name: name,
|
||||
extensions: Immutable.List(extensions),
|
||||
readme: module.readme,
|
||||
langs: module.langs,
|
||||
summary: module.summary,
|
||||
glossary: module.glossary,
|
||||
page: module.page,
|
||||
inline: module.inline
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = Parser;
|
||||
@@ -1,165 +0,0 @@
|
||||
var Immutable = require('immutable');
|
||||
|
||||
var TemplateBlock = require('./templateBlock');
|
||||
var PREFIX = require('../constants/pluginPrefix');
|
||||
var DEFAULT_VERSION = '*';
|
||||
|
||||
var Plugin = Immutable.Record({
|
||||
name: String(),
|
||||
|
||||
// Requirement version (ex: ">1.0.0")
|
||||
version: String(DEFAULT_VERSION),
|
||||
|
||||
// Path to load this plugin
|
||||
path: String(),
|
||||
|
||||
// Depth of this plugin in the dependency tree
|
||||
depth: Number(0),
|
||||
|
||||
// Content of the "package.json"
|
||||
package: Immutable.Map(),
|
||||
|
||||
// Content of the package itself
|
||||
content: Immutable.Map()
|
||||
}, 'Plugin');
|
||||
|
||||
Plugin.prototype.getName = function() {
|
||||
return this.get('name');
|
||||
};
|
||||
|
||||
Plugin.prototype.getPath = function() {
|
||||
return this.get('path');
|
||||
};
|
||||
|
||||
Plugin.prototype.getVersion = function() {
|
||||
return this.get('version');
|
||||
};
|
||||
|
||||
Plugin.prototype.getPackage = function() {
|
||||
return this.get('package');
|
||||
};
|
||||
|
||||
Plugin.prototype.getContent = function() {
|
||||
return this.get('content');
|
||||
};
|
||||
|
||||
Plugin.prototype.getDepth = function() {
|
||||
return this.get('depth');
|
||||
};
|
||||
|
||||
/**
|
||||
Return the ID on NPM for this plugin
|
||||
|
||||
@return {String}
|
||||
*/
|
||||
Plugin.prototype.getNpmID = function() {
|
||||
return Plugin.nameToNpmID(this.getName());
|
||||
};
|
||||
|
||||
/**
|
||||
Check if a plugin is loaded
|
||||
|
||||
@return {Boolean}
|
||||
*/
|
||||
Plugin.prototype.isLoaded = function() {
|
||||
return Boolean(this.getPackage().size > 0);
|
||||
};
|
||||
|
||||
/**
|
||||
Return map of hooks
|
||||
@return {Map<String:Function>}
|
||||
*/
|
||||
Plugin.prototype.getHooks = function() {
|
||||
return this.getContent().get('hooks') || Immutable.Map();
|
||||
};
|
||||
|
||||
/**
|
||||
Return infos about resources for a specific type
|
||||
|
||||
@param {String} type
|
||||
@return {Map<String:Mixed>}
|
||||
*/
|
||||
Plugin.prototype.getResources = function(type) {
|
||||
if (type != 'website' && type != 'ebook') {
|
||||
throw new Error('Invalid assets type ' + type);
|
||||
}
|
||||
|
||||
var content = this.getContent();
|
||||
return (content.get(type)
|
||||
|| (type == 'website'? content.get('book') : null)
|
||||
|| Immutable.Map());
|
||||
};
|
||||
|
||||
/**
|
||||
Return map of filters
|
||||
@return {Map<String:Function>}
|
||||
*/
|
||||
Plugin.prototype.getFilters = function() {
|
||||
return this.getContent().get('filters');
|
||||
};
|
||||
|
||||
/**
|
||||
Return map of blocks
|
||||
@return {Map<String:TemplateBlock>}
|
||||
*/
|
||||
Plugin.prototype.getBlocks = function() {
|
||||
var blocks = this.getContent().get('blocks');
|
||||
blocks = blocks || Immutable.Map();
|
||||
|
||||
return blocks
|
||||
.map(function(block, blockName) {
|
||||
return TemplateBlock.create(blockName, block);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
Return a specific hook
|
||||
|
||||
@param {String} name
|
||||
@return {Function|undefined}
|
||||
*/
|
||||
Plugin.prototype.getHook = function(name) {
|
||||
return this.getHooks().get(name);
|
||||
};
|
||||
|
||||
/**
|
||||
Create a plugin from a string
|
||||
|
||||
@param {String}
|
||||
@return {Plugin}
|
||||
*/
|
||||
Plugin.createFromString = function(s) {
|
||||
var parts = s.split('@');
|
||||
var name = parts[0];
|
||||
var version = parts.slice(1).join('@');
|
||||
|
||||
return new Plugin({
|
||||
name: name,
|
||||
version: version || DEFAULT_VERSION
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
Create a plugin from a dependency
|
||||
|
||||
@param {PluginDependency}
|
||||
@return {Plugin}
|
||||
*/
|
||||
Plugin.createFromDep = function(dep) {
|
||||
return new Plugin({
|
||||
name: dep.getName(),
|
||||
version: dep.getVersion()
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
Return NPM id for a plugin name
|
||||
|
||||
@param {String}
|
||||
@return {String}
|
||||
*/
|
||||
Plugin.nameToNpmID = function(s) {
|
||||
return PREFIX + s;
|
||||
};
|
||||
|
||||
module.exports = Plugin;
|
||||
@@ -1,115 +0,0 @@
|
||||
var is = require('is');
|
||||
var Immutable = require('immutable');
|
||||
|
||||
var DEFAULT_VERSION = '*';
|
||||
|
||||
/*
|
||||
PluginDependency represents the informations about a plugin
|
||||
stored in config.plugins
|
||||
*/
|
||||
var PluginDependency = Immutable.Record({
|
||||
name: String(),
|
||||
|
||||
// Requirement version (ex: ">1.0.0")
|
||||
version: String(DEFAULT_VERSION),
|
||||
|
||||
// Is this plugin enabled or disabled?
|
||||
enabled: Boolean(true)
|
||||
}, 'PluginDependency');
|
||||
|
||||
PluginDependency.prototype.getName = function() {
|
||||
return this.get('name');
|
||||
};
|
||||
|
||||
PluginDependency.prototype.getVersion = function() {
|
||||
return this.get('version');
|
||||
};
|
||||
|
||||
PluginDependency.prototype.isEnabled = function() {
|
||||
return this.get('enabled');
|
||||
};
|
||||
|
||||
/**
|
||||
Create a plugin from a string
|
||||
|
||||
@param {String}
|
||||
@return {Plugin|undefined}
|
||||
*/
|
||||
PluginDependency.createFromString = function(s) {
|
||||
var parts = s.split('@');
|
||||
var name = parts[0];
|
||||
var version = parts.slice(1).join('@');
|
||||
var enabled = true;
|
||||
|
||||
if (name[0] === '-') {
|
||||
enabled = false;
|
||||
name = name.slice(1);
|
||||
}
|
||||
|
||||
return new PluginDependency({
|
||||
name: name,
|
||||
version: version || DEFAULT_VERSION,
|
||||
enabled: enabled
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
Create a PluginDependency from a string
|
||||
|
||||
@param {String}
|
||||
@return {List<PluginDependency>}
|
||||
*/
|
||||
PluginDependency.listFromString = function(s) {
|
||||
var parts = s.split(',');
|
||||
return PluginDependency.listFromArray(parts);
|
||||
};
|
||||
|
||||
/**
|
||||
Create a PluginDependency from an array
|
||||
|
||||
@param {Array}
|
||||
@return {List<PluginDependency>}
|
||||
*/
|
||||
PluginDependency.listFromArray = function(arr) {
|
||||
return Immutable.List(arr)
|
||||
.map(function(entry) {
|
||||
if (is.string(entry)) {
|
||||
return PluginDependency.createFromString(entry);
|
||||
} else {
|
||||
return PluginDependency({
|
||||
name: entry.name,
|
||||
version: entry.version
|
||||
});
|
||||
}
|
||||
})
|
||||
.filter(function(dep) {
|
||||
return Boolean(dep.getName());
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
Export plugin dependencies as an array
|
||||
|
||||
@param {List<PluginDependency>}
|
||||
@return {Array<String>}
|
||||
*/
|
||||
PluginDependency.listToArray = function(arr) {
|
||||
return arr
|
||||
.map(function(dep) {
|
||||
var result;
|
||||
|
||||
if (dep.isEnabled()) {
|
||||
result += '-';
|
||||
}
|
||||
|
||||
result += dep.getName();
|
||||
if (dep.getVersion() !== DEFAULT_VERSION) {
|
||||
result += '@' + dep.getVersion();
|
||||
}
|
||||
|
||||
return result;
|
||||
})
|
||||
.toJS();
|
||||
};
|
||||
|
||||
module.exports = PluginDependency;
|
||||
@@ -1,40 +0,0 @@
|
||||
var Immutable = require('immutable');
|
||||
|
||||
var File = require('./file');
|
||||
|
||||
var Readme = Immutable.Record({
|
||||
file: File(),
|
||||
title: String(),
|
||||
description: String()
|
||||
});
|
||||
|
||||
Readme.prototype.getFile = function() {
|
||||
return this.get('file');
|
||||
};
|
||||
|
||||
Readme.prototype.getTitle = function() {
|
||||
return this.get('title');
|
||||
};
|
||||
|
||||
Readme.prototype.getDescription = function() {
|
||||
return this.get('description');
|
||||
};
|
||||
|
||||
/**
|
||||
Create a new readme
|
||||
|
||||
@param {File} file
|
||||
@param {Object} def
|
||||
@return {Readme}
|
||||
*/
|
||||
Readme.create = function(file, def) {
|
||||
def = def || {};
|
||||
|
||||
return new Readme({
|
||||
file: file,
|
||||
title: def.title || '',
|
||||
description: def.description || ''
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = Readme;
|
||||
@@ -1,223 +0,0 @@
|
||||
var is = require('is');
|
||||
var Immutable = require('immutable');
|
||||
|
||||
var error = require('../utils/error');
|
||||
var LocationUtils = require('../utils/location');
|
||||
var File = require('./file');
|
||||
var SummaryPart = require('./summaryPart');
|
||||
var SummaryArticle = require('./summaryArticle');
|
||||
var parsers = require('../parsers');
|
||||
|
||||
var Summary = Immutable.Record({
|
||||
file: File(),
|
||||
parts: Immutable.List()
|
||||
}, 'Summary');
|
||||
|
||||
Summary.prototype.getFile = function() {
|
||||
return this.get('file');
|
||||
};
|
||||
|
||||
Summary.prototype.getParts = function() {
|
||||
return this.get('parts');
|
||||
};
|
||||
|
||||
/**
|
||||
Return a part by its index
|
||||
|
||||
@param {Number}
|
||||
@return {Part}
|
||||
*/
|
||||
Summary.prototype.getPart = function(i) {
|
||||
var parts = this.getParts();
|
||||
return parts.get(i);
|
||||
};
|
||||
|
||||
/**
|
||||
Return an article using an iterator to find it.
|
||||
if "partIter" is set, it can also return a Part.
|
||||
|
||||
@param {Function} iter
|
||||
@param {Function} partIter
|
||||
@return {Article|Part}
|
||||
*/
|
||||
Summary.prototype.getArticle = function(iter, partIter) {
|
||||
var parts = this.getParts();
|
||||
|
||||
return parts.reduce(function(result, part) {
|
||||
if (result) return result;
|
||||
|
||||
if (partIter && partIter(part)) return part;
|
||||
return SummaryArticle.findArticle(part, iter);
|
||||
}, null);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
Return a part/article by its level
|
||||
|
||||
@param {String} level
|
||||
@return {Article|Part}
|
||||
*/
|
||||
Summary.prototype.getByLevel = function(level) {
|
||||
function iterByLevel(article) {
|
||||
return (article.getLevel() === level);
|
||||
}
|
||||
|
||||
return this.getArticle(iterByLevel, iterByLevel);
|
||||
};
|
||||
|
||||
/**
|
||||
Return an article by its path
|
||||
|
||||
@param {String} filePath
|
||||
@return {Article}
|
||||
*/
|
||||
Summary.prototype.getByPath = function(filePath) {
|
||||
return this.getArticle(function(article) {
|
||||
return (LocationUtils.areIdenticalPaths(article.getPath(), filePath));
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
Return the first article
|
||||
|
||||
@return {Article}
|
||||
*/
|
||||
Summary.prototype.getFirstArticle = function() {
|
||||
return this.getArticle(function(article) {
|
||||
return true;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
Return next article of an article
|
||||
|
||||
@param {Article} current
|
||||
@return {Article}
|
||||
*/
|
||||
Summary.prototype.getNextArticle = function(current) {
|
||||
var level = is.string(current)? current : current.getLevel();
|
||||
var wasPrev = false;
|
||||
|
||||
return this.getArticle(function(article) {
|
||||
if (wasPrev) return true;
|
||||
|
||||
wasPrev = article.getLevel() == level;
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
Return previous article of an article
|
||||
|
||||
@param {Article} current
|
||||
@return {Article}
|
||||
*/
|
||||
Summary.prototype.getPrevArticle = function(current) {
|
||||
var level = is.string(current)? current : current.getLevel();
|
||||
var prev = undefined;
|
||||
|
||||
this.getArticle(function(article) {
|
||||
if (article.getLevel() == level) {
|
||||
return true;
|
||||
}
|
||||
|
||||
prev = article;
|
||||
return false;
|
||||
});
|
||||
|
||||
return prev;
|
||||
};
|
||||
|
||||
/**
|
||||
Return the parent article, or parent part of an article
|
||||
|
||||
@param {String|Article} current
|
||||
@return {Article|Part|Null}
|
||||
*/
|
||||
Summary.prototype.getParent = function (level) {
|
||||
// Coerce to level
|
||||
level = is.string(level)? level : level.getLevel();
|
||||
|
||||
// Get parent level
|
||||
var parentLevel = getParentLevel(level);
|
||||
if (!parentLevel) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get parent of the position
|
||||
var parentArticle = this.getByLevel(parentLevel);
|
||||
return parentArticle || null;
|
||||
};
|
||||
|
||||
/**
|
||||
Render summary as text
|
||||
|
||||
@param {String} parseExt Extension of the parser to use
|
||||
@return {Promise<String>}
|
||||
*/
|
||||
Summary.prototype.toText = function(parseExt) {
|
||||
var file = this.getFile();
|
||||
var parts = this.getParts();
|
||||
|
||||
var parser = parseExt? parsers.getByExt(parseExt) : file.getParser();
|
||||
|
||||
if (!parser) {
|
||||
throw error.FileNotParsableError({
|
||||
filename: file.getPath()
|
||||
});
|
||||
}
|
||||
|
||||
return parser.renderSummary({
|
||||
parts: parts.toJS()
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
Return all articles as a list
|
||||
|
||||
@return {List<Article>}
|
||||
*/
|
||||
Summary.prototype.getArticlesAsList = function() {
|
||||
var accu = [];
|
||||
|
||||
this.getArticle(function(article) {
|
||||
accu.push(article);
|
||||
});
|
||||
|
||||
return Immutable.List(accu);
|
||||
};
|
||||
|
||||
/**
|
||||
Create a new summary for a list of parts
|
||||
|
||||
@param {Lust|Array} parts
|
||||
@return {Summary}
|
||||
*/
|
||||
Summary.createFromParts = function createFromParts(file, parts) {
|
||||
parts = parts.map(function(part, i) {
|
||||
if (part instanceof SummaryPart) {
|
||||
return part;
|
||||
}
|
||||
|
||||
return SummaryPart.create(part, i + 1);
|
||||
});
|
||||
|
||||
return new Summary({
|
||||
file: file,
|
||||
parts: new Immutable.List(parts)
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
Returns parent level of a level
|
||||
|
||||
@param {String} level
|
||||
@return {String}
|
||||
*/
|
||||
function getParentLevel(level) {
|
||||
var parts = level.split('.');
|
||||
return parts.slice(0, -1).join('.');
|
||||
}
|
||||
|
||||
module.exports = Summary;
|
||||
@@ -1,150 +0,0 @@
|
||||
var Immutable = require('immutable');
|
||||
|
||||
var location = require('../utils/location');
|
||||
|
||||
/*
|
||||
An article represents an entry in the Summary / table of Contents
|
||||
*/
|
||||
|
||||
var SummaryArticle = Immutable.Record({
|
||||
level: String(),
|
||||
title: String(),
|
||||
ref: String(),
|
||||
articles: Immutable.List()
|
||||
}, 'SummaryArticle');
|
||||
|
||||
SummaryArticle.prototype.getLevel = function() {
|
||||
return this.get('level');
|
||||
};
|
||||
|
||||
SummaryArticle.prototype.getTitle = function() {
|
||||
return this.get('title');
|
||||
};
|
||||
|
||||
SummaryArticle.prototype.getRef = function() {
|
||||
return this.get('ref');
|
||||
};
|
||||
|
||||
SummaryArticle.prototype.getArticles = function() {
|
||||
return this.get('articles');
|
||||
};
|
||||
|
||||
/**
|
||||
Return how deep the article is
|
||||
|
||||
@return {Number}
|
||||
*/
|
||||
SummaryArticle.prototype.getDepth = function() {
|
||||
return this.getLevel().split('.').length;
|
||||
};
|
||||
|
||||
/**
|
||||
Get path (without anchor) to the pointing file
|
||||
|
||||
@return {String}
|
||||
*/
|
||||
SummaryArticle.prototype.getPath = function() {
|
||||
if (this.isExternal()) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
var ref = this.getRef();
|
||||
if (!ref) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
var parts = ref.split('#');
|
||||
|
||||
var pathname = (parts.length > 1? parts.slice(0, -1).join('#') : ref);
|
||||
|
||||
// Normalize path to remove ('./', etc)
|
||||
return location.normalize(pathname);
|
||||
};
|
||||
|
||||
/**
|
||||
Return url if article is external
|
||||
|
||||
@return {String}
|
||||
*/
|
||||
SummaryArticle.prototype.getUrl = function() {
|
||||
return this.isExternal()? this.getRef() : undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
Get anchor for this article (or undefined)
|
||||
|
||||
@return {String}
|
||||
*/
|
||||
SummaryArticle.prototype.getAnchor = function() {
|
||||
var ref = this.getRef();
|
||||
var parts = ref.split('#');
|
||||
|
||||
var anchor = (parts.length > 1? '#' + parts[parts.length - 1] : undefined);
|
||||
return anchor;
|
||||
};
|
||||
|
||||
/**
|
||||
Is article pointing to a page of an absolute url
|
||||
|
||||
@return {Boolean}
|
||||
*/
|
||||
SummaryArticle.prototype.isPage = function() {
|
||||
return !this.isExternal() && this.getRef();
|
||||
};
|
||||
|
||||
/**
|
||||
Is article pointing to aan absolute url
|
||||
|
||||
@return {Boolean}
|
||||
*/
|
||||
SummaryArticle.prototype.isExternal = function() {
|
||||
return location.isExternal(this.getRef());
|
||||
};
|
||||
|
||||
/**
|
||||
Create a SummaryArticle
|
||||
|
||||
@param {Object} def
|
||||
@return {SummaryArticle}
|
||||
*/
|
||||
SummaryArticle.create = function(def, level) {
|
||||
var articles = (def.articles || []).map(function(article, i) {
|
||||
if (article instanceof SummaryArticle) {
|
||||
return article;
|
||||
}
|
||||
return SummaryArticle.create(article, [level, i + 1].join('.'));
|
||||
});
|
||||
|
||||
return new SummaryArticle({
|
||||
level: level,
|
||||
title: def.title,
|
||||
ref: def.ref || def.path || '',
|
||||
articles: Immutable.List(articles)
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
Find an article from a base one
|
||||
|
||||
@param {Article|Part} base
|
||||
@param {Function(article)} iter
|
||||
@return {Article}
|
||||
*/
|
||||
SummaryArticle.findArticle = function(base, iter) {
|
||||
var articles = base.getArticles();
|
||||
|
||||
return articles.reduce(function(result, article) {
|
||||
if (result) return result;
|
||||
|
||||
if (iter(article)) {
|
||||
return article;
|
||||
}
|
||||
|
||||
return SummaryArticle.findArticle(article, iter);
|
||||
}, null);
|
||||
};
|
||||
|
||||
|
||||
module.exports = SummaryArticle;
|
||||
@@ -1,48 +0,0 @@
|
||||
var Immutable = require('immutable');
|
||||
|
||||
var SummaryArticle = require('./summaryArticle');
|
||||
|
||||
/*
|
||||
A part represents a section in the Summary / table of Contents
|
||||
*/
|
||||
|
||||
var SummaryPart = Immutable.Record({
|
||||
level: String(),
|
||||
title: String(),
|
||||
articles: Immutable.List()
|
||||
});
|
||||
|
||||
SummaryPart.prototype.getLevel = function() {
|
||||
return this.get('level');
|
||||
};
|
||||
|
||||
SummaryPart.prototype.getTitle = function() {
|
||||
return this.get('title');
|
||||
};
|
||||
|
||||
SummaryPart.prototype.getArticles = function() {
|
||||
return this.get('articles');
|
||||
};
|
||||
|
||||
/**
|
||||
Create a SummaryPart
|
||||
|
||||
@param {Object} def
|
||||
@return {SummaryPart}
|
||||
*/
|
||||
SummaryPart.create = function(def, level) {
|
||||
var articles = (def.articles || []).map(function(article, i) {
|
||||
if (article instanceof SummaryArticle) {
|
||||
return article;
|
||||
}
|
||||
return SummaryArticle.create(article, [level, i + 1].join('.'));
|
||||
});
|
||||
|
||||
return new SummaryPart({
|
||||
level: String(level),
|
||||
title: def.title,
|
||||
articles: Immutable.List(articles)
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = SummaryPart;
|
||||
@@ -1,310 +0,0 @@
|
||||
var is = require('is');
|
||||
var extend = require('extend');
|
||||
var Immutable = require('immutable');
|
||||
|
||||
var Promise = require('../utils/promise');
|
||||
var genKey = require('../utils/genKey');
|
||||
|
||||
var NODE_ENDARGS = '%%endargs%%';
|
||||
|
||||
var blockBodies = {};
|
||||
|
||||
var TemplateBlock = Immutable.Record({
|
||||
// Name of block, also the start tag
|
||||
name: String(),
|
||||
|
||||
// End tag, default to "end<name>"
|
||||
end: String(),
|
||||
|
||||
// Function to process the block content
|
||||
process: Function(),
|
||||
|
||||
// List of String, for inner block tags
|
||||
blocks: Immutable.List(),
|
||||
|
||||
// List of shortcuts to replace with this block
|
||||
shortcuts: Immutable.List(),
|
||||
|
||||
// Function to execute in post processing
|
||||
post: null,
|
||||
|
||||
parse: true
|
||||
}, 'TemplateBlock');
|
||||
|
||||
TemplateBlock.prototype.getName = function() {
|
||||
return this.get('name');
|
||||
};
|
||||
|
||||
TemplateBlock.prototype.getPost = function() {
|
||||
return this.get('post');
|
||||
};
|
||||
|
||||
TemplateBlock.prototype.getParse = function() {
|
||||
return this.get('parse');
|
||||
};
|
||||
|
||||
TemplateBlock.prototype.getEndTag = function() {
|
||||
return this.get('end') || ('end' + this.getName());
|
||||
};
|
||||
|
||||
TemplateBlock.prototype.getProcess = function() {
|
||||
return this.get('process');
|
||||
};
|
||||
|
||||
TemplateBlock.prototype.getBlocks = function() {
|
||||
return this.get('blocks');
|
||||
};
|
||||
|
||||
TemplateBlock.prototype.getShortcuts = function() {
|
||||
return this.get('shortcuts');
|
||||
};
|
||||
|
||||
/**
|
||||
Return name for the nunjucks extension
|
||||
|
||||
@return {String}
|
||||
*/
|
||||
TemplateBlock.prototype.getExtensionName = function() {
|
||||
return 'Block' + this.getName() + 'Extension';
|
||||
};
|
||||
|
||||
/**
|
||||
Return a nunjucks extension to represents this block
|
||||
|
||||
@return {Nunjucks.Extension}
|
||||
*/
|
||||
TemplateBlock.prototype.toNunjucksExt = function(mainContext) {
|
||||
var that = this;
|
||||
var name = this.getName();
|
||||
var endTag = this.getEndTag();
|
||||
var blocks = this.getBlocks().toJS();
|
||||
|
||||
function Ext() {
|
||||
this.tags = [name];
|
||||
|
||||
this.parse = function(parser, nodes) {
|
||||
var lastBlockName = null;
|
||||
var lastBlockArgs = null;
|
||||
var allBlocks = blocks.concat([endTag]);
|
||||
|
||||
// Parse first block
|
||||
var tok = parser.nextToken();
|
||||
lastBlockArgs = parser.parseSignature(null, true);
|
||||
parser.advanceAfterBlockEnd(tok.value);
|
||||
|
||||
var args = new nodes.NodeList();
|
||||
var bodies = [];
|
||||
var blockNamesNode = new nodes.Array(tok.lineno, tok.colno);
|
||||
var blockArgCounts = new nodes.Array(tok.lineno, tok.colno);
|
||||
|
||||
// Parse while we found "end<block>"
|
||||
do {
|
||||
// Read body
|
||||
var currentBody = parser.parseUntilBlocks.apply(parser, allBlocks);
|
||||
|
||||
// Handle body with previous block name and args
|
||||
blockNamesNode.addChild(new nodes.Literal(args.lineno, args.colno, lastBlockName));
|
||||
blockArgCounts.addChild(new nodes.Literal(args.lineno, args.colno, lastBlockArgs.children.length));
|
||||
bodies.push(currentBody);
|
||||
|
||||
// Append arguments of this block as arguments of the run function
|
||||
lastBlockArgs.children.forEach(function(child) {
|
||||
args.addChild(child);
|
||||
});
|
||||
|
||||
// Read new block
|
||||
lastBlockName = parser.nextToken().value;
|
||||
|
||||
// Parse signature and move to the end of the block
|
||||
if (lastBlockName != endTag) {
|
||||
lastBlockArgs = parser.parseSignature(null, true);
|
||||
}
|
||||
|
||||
parser.advanceAfterBlockEnd(lastBlockName);
|
||||
} while (lastBlockName != endTag);
|
||||
|
||||
args.addChild(blockNamesNode);
|
||||
args.addChild(blockArgCounts);
|
||||
args.addChild(new nodes.Literal(args.lineno, args.colno, NODE_ENDARGS));
|
||||
|
||||
return new nodes.CallExtensionAsync(this, 'run', args, bodies);
|
||||
};
|
||||
|
||||
this.run = function(context) {
|
||||
var fnArgs = Array.prototype.slice.call(arguments, 1);
|
||||
|
||||
var args;
|
||||
var blocks = [];
|
||||
var bodies = [];
|
||||
var blockNames;
|
||||
var blockArgCounts;
|
||||
var callback;
|
||||
|
||||
// Extract callback
|
||||
callback = fnArgs.pop();
|
||||
|
||||
// Detect end of arguments
|
||||
var endArgIndex = fnArgs.indexOf(NODE_ENDARGS);
|
||||
|
||||
// Extract arguments and bodies
|
||||
args = fnArgs.slice(0, endArgIndex);
|
||||
bodies = fnArgs.slice(endArgIndex + 1);
|
||||
|
||||
// Extract block counts
|
||||
blockArgCounts = args.pop();
|
||||
blockNames = args.pop();
|
||||
|
||||
// Recreate list of blocks
|
||||
blockNames.forEach(function(name, i) {
|
||||
var countArgs = blockArgCounts[i];
|
||||
var blockBody = bodies.shift();
|
||||
|
||||
var blockArgs = countArgs > 0? args.slice(0, countArgs) : [];
|
||||
args = args.slice(countArgs);
|
||||
var blockKwargs = extractKwargs(blockArgs);
|
||||
|
||||
blocks.push({
|
||||
name: name,
|
||||
body: blockBody(),
|
||||
args: blockArgs,
|
||||
kwargs: blockKwargs
|
||||
});
|
||||
});
|
||||
|
||||
var mainBlock = blocks.shift();
|
||||
mainBlock.blocks = blocks;
|
||||
|
||||
Promise()
|
||||
.then(function() {
|
||||
var ctx = extend({
|
||||
ctx: context
|
||||
}, mainContext || {});
|
||||
|
||||
return that.applyBlock(mainBlock, ctx);
|
||||
})
|
||||
.then(function(result) {
|
||||
return that.blockResultToHtml(result);
|
||||
})
|
||||
.nodeify(callback);
|
||||
};
|
||||
}
|
||||
|
||||
return Ext;
|
||||
};
|
||||
|
||||
/**
|
||||
Apply a block to a content
|
||||
@param {Object} inner
|
||||
@param {Object} context
|
||||
@return {Promise<String>|String}
|
||||
*/
|
||||
TemplateBlock.prototype.applyBlock = function(inner, context) {
|
||||
var processFn = this.getProcess();
|
||||
|
||||
inner = inner || {};
|
||||
inner.args = inner.args || [];
|
||||
inner.kwargs = inner.kwargs || {};
|
||||
inner.blocks = inner.blocks || [];
|
||||
|
||||
var r = processFn.call(context, inner);
|
||||
|
||||
if (Promise.isPromiseAlike(r)) {
|
||||
return r.then(this.handleBlockResult.bind(this));
|
||||
} else {
|
||||
return this.handleBlockResult(r);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
Handle result from a block process function
|
||||
|
||||
@param {Object} result
|
||||
@return {Object}
|
||||
*/
|
||||
TemplateBlock.prototype.handleBlockResult = function(result) {
|
||||
if (is.string(result)) {
|
||||
result = { body: result };
|
||||
}
|
||||
result.name = this.getName();
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
Convert a block result to HTML
|
||||
|
||||
@param {Object} result
|
||||
@return {String}
|
||||
*/
|
||||
TemplateBlock.prototype.blockResultToHtml = function(result) {
|
||||
var parse = this.getParse();
|
||||
var indexedKey;
|
||||
var toIndex = (!parse) || (this.getPost() !== undefined);
|
||||
|
||||
if (toIndex) {
|
||||
indexedKey = TemplateBlock.indexBlockResult(result);
|
||||
}
|
||||
|
||||
// Parsable block, just return it
|
||||
if (parse) {
|
||||
return result.body;
|
||||
}
|
||||
|
||||
// Return it as a position marker
|
||||
return '{{-%' + indexedKey + '%-}}';
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
Index a block result, and return the indexed key
|
||||
|
||||
@param {Object} blk
|
||||
@return {String}
|
||||
*/
|
||||
TemplateBlock.indexBlockResult = function(blk) {
|
||||
var key = genKey();
|
||||
blockBodies[key] = blk;
|
||||
|
||||
return key;
|
||||
};
|
||||
|
||||
/**
|
||||
Get a block results indexed for a specific key
|
||||
|
||||
@param {String} key
|
||||
@return {Object|undefined}
|
||||
*/
|
||||
TemplateBlock.getBlockResultByKey = function(key) {
|
||||
return blockBodies[key];
|
||||
};
|
||||
|
||||
/**
|
||||
Create a template block from a function or an object
|
||||
|
||||
@param {String} blockName
|
||||
@param {Object} block
|
||||
@return {TemplateBlock}
|
||||
*/
|
||||
TemplateBlock.create = function(blockName, block) {
|
||||
if (is.fn(block)) {
|
||||
block = new Immutable.Map({
|
||||
process: block
|
||||
});
|
||||
}
|
||||
|
||||
block = block.set('name', blockName);
|
||||
return new TemplateBlock(block);
|
||||
};
|
||||
|
||||
/**
|
||||
Extract kwargs from an arguments array
|
||||
|
||||
@param {Array} args
|
||||
@return {Object}
|
||||
*/
|
||||
function extractKwargs(args) {
|
||||
var last = args[args.length - 1];
|
||||
return (is.object(last) && last.__keywords)? args.pop() : {};
|
||||
}
|
||||
|
||||
module.exports = TemplateBlock;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user